Getting Started
Parsium is a blazingly fast medianame filename parser targeting TypeScript. It is built to consistently extract the most granular details from movie and TV show release filenames.
bolt Why Parsium?
- ✦ Dependency-free architecture
- ✦ Handles thousands of filenames per second
- ✦ Over 1,200+ unit test cases to ensure correctness
- ✦ First-class TypeScript with strict static guarantees
Installation
We recommend using preferred package manager (npm, pnpm, yarn or bun). Parsium requires Node.js version 22.0.0 or higher (or Bun/Deno).
pnpm add parsium-media Basic Usage
Pass any release filename to the parse function. The parser will immediately return a strongly typed object mapping out traits extracted from the name.
import { parse } from 'parsium-media';
// Analyze a title
const result = parse("The.Matrix.1999.1080p.BluRay.x264-YTS");
console.log(result.title); // "The Matrix"
console.log(result.year); // 1999
console.log(result.resolution); // "1080p" Options parse(filename, options)
Pass an options object as the second argument to parse() to enable additional output.
const result = parse('Movie.2024.1080p.mkv', { explain: true });
// result.explanation = the step-by-step trace
// { proposer: 'resolution', value: '1080p', range: [11, 16], reason: '...' }
const why = parse('Movie.2024.1080p.mkv', { explain: 'summary' });
// why.explain.fields.resolution = what it was decided on, and what lost
// 'full' adds every claim and rejection the parse considered const result = parse('Movie.2024.1080p.mkv', { fieldConfidence: true });
// result.confidence = 1 (always present, no option needed)
// result.fieldConfidence = {
// resolution: { certainty: 1, contested: 0 },
// title: { certainty: 0.5, contested: 0 }
// }
// An evidence margin, NOT a probability. It ranks and compares;
// you cannot read 0.97 as "97% likely correct". | Option | Type | Description |
|---|---|---|
| explain | boolean | 'summary' | 'full' | true gives the step-by-step trace in result.explanation. 'summary' gives the decision record in result.explain: per field, the evidence it won on and what lost. 'full' adds every candidate considered. |
| fieldConfidence | boolean | Adds certainty and contested per field. The whole-result confidence is always present without this. |
| context | ParseContext | What you already know about the work: titles, contentType, year, seasons, episodes, absoluteEpisode, episodeTitle. It guides the parse and never writes to the output: the file is parsed first, and a value you supply is applied only where text in that filename actually spells it. Where the file states something explicitly, the file wins; anything the filename does not carry is refused and reported as a context-unwitnessed warning. |
API Reference ParseResult
Complete list of all properties returned by parse(). Only truthy values are included in the result by default.
| Property | Type | Description |
|---|---|---|
| title | string | The identified show or movie title. |
| altTitle | string | undefined | Alternate/original-language title from parentheses. |
| year | number | undefined | Four-digit release year if available. |
| contentType | 'movie' | 'series' | 'unknown' | Detected content type. |
| contentSubtype | 'anime' | undefined | Content subtype if detected (e.g. anime). |
| seasons | number[] | Array of season numbers (e.g. [1, 2]). |
| episodes | number[] | Array of episode numbers. |
| episodeTitle | string | undefined | Episode title if embedded in filename. |
| resolution | string | undefined | Resolution (1080p, 2160p, 720p, etc.). |
| source | string | undefined | Media source (BluRay, WEB-DL, HDTV, etc.). |
| codec | string | undefined | Video codec (x264, x265, HEVC, AV1, etc.). |
| hdr | string[] | undefined | HDR formats, atomic and ordered (DV, HDR10, HDR10+, HLG). e.g. ["DV","HDR10"]. |
| audio | string[] | undefined | Audio codecs/layers, atomic and ordered. e.g. ["TrueHD","Atmos"], ["DD+"]. |
| channels | string[] | undefined | Channel layouts, ordered (2.0, 5.1, 7.1, 7.1.4). e.g. ["5.1"]. |
| releaseGroup | string | undefined | Release group name (e.g. FLUX, YTS). |
| container | string | undefined | File container (mkv, mp4, avi, etc.). |
| languages | Language[] | Detected languages with code & label. |
| isSeasonPack | boolean | Whether this is a full season pack. |
| isRemux | boolean | Whether the file is a remux. |
| editions | string[] | undefined | Special editions (IMAX, Director's Cut, etc.). |
| streamingService | string | undefined | Streaming platform (Netflix, Amazon Prime Video, Disney+, etc.). |
| bitDepth | string | undefined | Color bit depth (8-bit, 10-bit, 12-bit). |
| frameRate | string | undefined | Frame rate when present (24fps, 60fps, etc.). |
| absoluteEpisode | number | undefined | Absolute episode number (common in anime). |
| absoluteEpisodeRange | { from, to } | undefined | Contiguous absolute episode range (anime batches). |
| episodeRange | { from, to } | undefined | Contiguous episode range (e.g. E01-E12). |
| date | string | undefined | Date-based episode (YYYY-MM-DD) for daily shows. |
| subtitleLanguages | Language[] | undefined | Languages detected as subtitles (VOSTFR, Sub, etc.). |
| isMultiSubtitle | boolean | undefined | True for multi-subtitle tracks (Multi Subs, MSub). |
| isCompleteSeries | boolean | True for complete series packs. |
| isBatchRelease | boolean | True for batch episode releases. |
| isMultiLanguage | boolean | True when MULTI or 3+ languages detected. |
| isDualAudio | boolean | True for dual audio releases. |
| isRepack | boolean | True for repacked releases. |
| isProper | boolean | True for proper releases. |
| is3D | boolean | True for 3D releases (3D, H-SBS, H-OU). |
| isHybrid | boolean | True for hybrid releases (combined sources). |
| isUpscaled | boolean | True when AI-upscaled (AI Upscale, Neural Upscale). |
CLI npx parsium-media
Run Parsium directly from the command line via npx, no installation required.
# Parse a single filename
npx parsium-media "The.Matrix.1999.1080p.BluRay.x264-GROUP.mkv"
# With step-by-step extraction reasoning
npx parsium-media --explain "Movie.2024.1080p.BluRay.x265-GROUP.mkv"
# JSON output (truthy fields only)
npx parsium-media --json "Movie.2024.mkv"
# Parse from a file (one filename per line)
npx parsium-media --file torrents.txt --json --progress | Flag | Description |
|---|---|
| --explain | Show debug explanation for each extraction |
| --confidence | Show per-field confidence scores |
| --json | Output as JSON (only present fields) |
| --json-full | Output as JSON (all fields including false/empty) |
| --file <path> | Read filenames from a file (one per line) |
| --progress | Show progress during batch parsing |
Plugin System Advanced
Extend the parser with custom proposers without modifying core code. A proposer emits claims, each carrying the evidence for its reading, and is arbitrated against the 14 built-ins by the same rules: if a built-in wants the same span, the better-evidenced reading wins. A plugin may also register a postProcess hook that sees the assembled result.
import { createParser, claim, ev } from 'parsium-media';
import type { ParsiumPlugin } from 'parsium-media';
const TRACKER = /\[(INTERNAL|FREELEECH)\]/i;
const trackerTags: ParsiumPlugin = {
name: 'tracker-tags',
proposers: [{
name: 'tracker-meta',
propose(ctx) {
const m = TRACKER.exec(ctx.normalized);
if (!m) return { claims: [], rejections: [] };
return {
claims: [claim({
field: 'trackerTag' as never,
span: { start: m.index, end: m.index + m[0].length },
tokSpan: [0, 1],
value: m[1].toUpperCase(),
evidence: [ev('vocab-exact'), ev('in-bracket')],
origin: 'tracker-meta',
})],
rejections: [],
};
},
}],
};
const parser = createParser({ plugins: [trackerTags] });
parser.parse('[INTERNAL] Movie.2024.mkv');
// -> { title: 'Movie', year: 2024, trackerTag: 'INTERNAL' } Built-in proposer priorities
priority is optional: omit it and your proposer runs after every built-in, which is usually what you want. Use a fractional priority (e.g. 9.5) only to propose before a built-in tier. There is no title proposer — the title is decided after selection, from whatever text no claim owns.
| Priority | Proposer | Description |
|---|---|---|
| 1 | resolution | 1080p, 4K, 2160p |
| 2 | source | BluRay, WEB-DL, HDTV |
| 3 | codec | x264, x265, HEVC, AV1 |
| 3 | date | Date-based episodes (2025.01.15) |
| 3 | fps | Frame rate (24fps, 60fps) |
| 4 | audio | TrueHD, DTS-HD, AAC |
| 5 | hdr | HDR10, DV, HLG |
| 6 | language | Multi-language detection |
| 7 | year | Release year |
| 8 | edition | Director's Cut, Extended |
| 9 | misc | REPACK, PROPER, REMUX |
| 10 | season-episode | S01E02 patterns |
| 11 | anime-episode | Absolute episodes, batch |
| 12 | release-group | Trailing group name |
HTTP API REST
Parsium exposes a lightweight HTTP API for non-Node.js consumers. Built with Hono, deployable to Vercel, Cloudflare Workers, Docker, or any Node.js host.
A live demo instance is available at https://api.parsium.nepiraw.com. Prepend it to the paths below, for example https://api.parsium.nepiraw.com/v1/parse.
It is recommended to use this demo instance for testing purposes only.
Deploy your own for production use.
POST /v1/parse
Parse a single filename.
{
"filename": "The.Boys.S05E02.1080p.WEB.H264-TyHD.mkv",
"options": {
"explain": false,
"fieldConfidence": false
}
} {
"success": true,
"data": {
"title": "The Boys",
"contentType": "series",
"seasons": [5],
"episodes": [2],
"resolution": "1080p",
"codec": "H.264",
"releaseGroup": "TyHD",
"container": "mkv",
"confidence": 1
},
"meta": { "parseTimeMs": 0.6 }
} GET /v1/parse
Parse a single filename straight from the browser or a link, using query parameters. Same response shape as the POST form.
GET https://api.parsium.nepiraw.com/v1/parse?filename=The.Boys.S05E02.1080p.WEB.H264-TyHD.mkv Optional query flags, set to true: full, explain, fieldConfidence.
POST /v1/parse/batch
Parse multiple filenames in a single request (max 10).
{
"filenames": [
"Movie.2024.2160p.UHD.BluRay.x265-GROUP.mkv",
"Anime.S01E01.1080p.WEB.H264-Sub.mkv"
]
} info Rate Limiting
The HTTP API applies IP-based rate limiting at 50 requests/minute. For high-throughput needs, use the npm package directly.
Performance Tuning
Parsium processes thousands of filenames per second with zero dependencies. For high-throughput scenarios, use the cached parser.
import { createCachedParser } from 'parsium-media';
// LRU cache with 10,000 entries
const { parse, size, clear } = createCachedParser(10000);
const result = parse('Same.File.Again.mkv');
// Second call with same input is instant (cache hit) import { parseBatch } from 'parsium-media';
const results = parseBatch(filenames, {
onProgress: (completed, total) => {
console.log(`${completed}/${total}`);
},
}); tips_and_updates Tips
- ✦ Use
createCachedParser()when parsing recurring filenames (torrent clients, media servers) - ✦ Use
parseBatch()withonProgressfor UI feedback on large lists - ✦ Parsium is fully synchronous: no async overhead, no event loop blocking on single parses
- ✦ Zero dependencies and pure ESM, so it bundles directly for the browser (Vite, webpack, esbuild) with no configuration