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).

Terminal
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.

index.ts
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.

explain.ts
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
confidence.ts
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.

Terminal
# 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.

plugin.ts
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
1resolution1080p, 4K, 2160p
2sourceBluRay, WEB-DL, HDTV
3codecx264, x265, HEVC, AV1
3dateDate-based episodes (2025.01.15)
3fpsFrame rate (24fps, 60fps)
4audioTrueHD, DTS-HD, AAC
5hdrHDR10, DV, HLG
6languageMulti-language detection
7yearRelease year
8editionDirector's Cut, Extended
9miscREPACK, PROPER, REMUX
10season-episodeS01E02 patterns
11anime-episodeAbsolute episodes, batch
12release-groupTrailing 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.

public

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.

warning

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.

Request
{
  "filename": "The.Boys.S05E02.1080p.WEB.H264-TyHD.mkv",
  "options": {
    "explain": false,
    "fieldConfidence": false
  }
}
Response 200
{
  "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.

Request
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).

Request
{
  "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.

cached-parser.ts
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)
batch.ts
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() with onProgress for 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