guessit-js
August 15, 2026 · View on GitHub
Extract metadata (title, year, season, episode, codec, language, etc.) from media filenames — TypeScript port of Python guessit.
Features
- 100% compatibility with Python guessit 4.4.0 (1342/1342 fixtures passing, including Python's own grown 4.x corpus) — and more correct: ships fixes for 32 historical upstream bugs, Python's still-open #875 and #877, and 112 of the 276 cross-parser cases Python admits failing (see Differences from Python)
- 3.5x faster than Python (6.87ms vs 23.86ms per parse)
- 50 properties detected: title, year, season, episode, resolution, codec, language, and more — with a machine-readable schema
- Single dependency (rebulk-js)
- Dual format: ESM and CommonJS
- TypeScript: full, precise type definitions (typed
GuessItResult, enum'd value fields) - WASM: runs in any WASI-compatible runtime, bit-identical to the JS build
Install
npm install guessit-js
CLI
Drop-in replacement for the Python guessit command — same flags, same
output formats (verified against the real Python CLI in CI). Installs both
guessit-js and guessit binaries:
npx guessit-js "The.Dark.Knight.2008.1080p.BluRay.x264-GROUP.mkv"
# title: The Dark Knight
# year: 2008
# ...
npx guessit-js -j "Mob Psycho 100 - 09.mkv" # JSON (-y YAML, --jsonl streaming)
npx guessit-js -P title "Movie.2020.mkv" # single property
npx guessit-js -t episode "ambiguous.file.mkv" # force type
npx guessit-js @list.txt # filenames from file (also -f list.txt)
find . -name '*.mkv' | npx guessit-js --jsonl # from stdin, one JSON per line
npx guessit-js -a "Movie.2020.mkv" # advanced: value/raw/start/end
npx guessit-js -p # list detectable properties (-V with values)
Parsing options match the Python CLI: -n name-only, -Y/-D date order,
-L/-C allowed languages/countries, -E episode-prefer-number,
-T/-G expected title/group, --includes/--excludes, -s single-value,
-v verbose. User config is auto-loaded from ~/.guessit/options.json and
~/.config/guessit/options.json (.yaml/.yml too; disable with
--no-user-config), plus explicit -c <file>. See npx guessit-js --help.
Extras beyond the Python CLI:
npx guessit-js --serve 3847 # instant REST API (see below)
npx guessit-js --benchmark 1000 "Movie.2020.1080p.mkv" # throughput report
eval "$(npx guessit-js --completion bash)" # shell completion (bash/zsh)
Usage
import { guessit } from 'guessit-js';
const result = guessit('The.Dark.Knight.2008.1080p.BluRay.x264-GROUP.mkv');
// {
// title: 'The Dark Knight',
// year: 2008,
// screen_size: '1080p',
// source: 'Blu-ray',
// video_codec: 'H.264',
// release_group: 'GROUP',
// container: 'mkv',
// type: 'movie'
// }
// Short titles are handled correctly too:
guessit('X2.2003.720p.DSNP.WEB-DL.DDP5.1.H.264-EVO.mkv');
// { title: 'X2', year: 2003, screen_size: '720p',
// streaming_service: 'Disney+', source: 'Web', ... }
CommonJS
const { guessit } = require('guessit-js');
const result = guessit('Breaking.Bad.S01E02.720p.BluRay.x264-DEMAND.mkv');
console.log(result.title); // 'Breaking Bad'
console.log(result.season); // 1
console.log(result.episode); // 2
Options
guessit('file.mkv', { type: 'episode' });
guessit('my 720p show S01E02', { expected_title: ['my 720p show'] });
guessit('file.mkv', { allowed_languages: ['en', 'fr'] });
guessit('file.mkv', { excludes: ['release_group'] });
import { version } from 'guessit-js'; // package version string
Detected Properties
| Category | Properties |
|---|---|
| Title | title, alternative_title, episode_title |
| Episode | season, episode, episode_details, episode_count, season_count, absolute_episode, disc, part |
| Date | year, date |
| Video | screen_size, aspect_ratio, frame_rate, video_codec, video_profile, color_depth |
| Audio | audio_codec, audio_profile, audio_channels, audio_bit_rate |
| Source | source, streaming_service |
| Release | release_group, edition, other, proper_count |
| File | container (video / subtitle / archive / image / nfo / torrent / nzb), mimetype, size, crc32, uuid |
| Metadata | language, subtitle_language, country, type |
Output schema
The result is fully typed — import GuessItResult for autocomplete and type-checking:
import { guessit, properties, GUESSIT_SCHEMA, type GuessItResult } from 'guessit-js';
const r: GuessItResult = guessit('The.Dark.Knight.2008.1080p.BluRay.x264-GRP.mkv');
r.source; // typed as the closed enum: "Blu-ray" | "Web" | "HDTV" | …
properties(); // { source: ["Blu-ray", "Web", …], type: ["episode","movie"], … } for all 50 properties
GUESSIT_SCHEMA.source.enum; // the allowed values, programmatically
properties()— returns every emittable property with its possible values (value-constrained props list their full enum; free/computed props list[null]), mirroring Python guessit'sproperties().GUESSIT_SCHEMA— the machine-readable schema (type, cardinality, enum) for all properties.docs/output-schema.json— a JSON Schema (draft-07) of the output, for validating results or generating clients in other languages.
Regenerate the schema (after parsing changes) with npm run schema. A test (test/schema.test.ts) guarantees it never goes stale — every value emitted across the corpus must be in the schema.
REST API
Hosted demo (testing only):
curl "https://guessit.opensubtitles.com/api/guessit?filename=Movie.2024.1080p.mkv"
curl -X POST https://guessit.opensubtitles.com/api/guessit \
-H 'Content-Type: application/json' \
-d '{"filenames": ["A.2020.mkv", "B.S01E02.mkv"]}'
⚠️ For testing and evaluation only — never use in production. It runs on a free Cloudflare Workers tier with a hard daily request cap and no uptime guarantee; it can be throttled, rate-limited, or removed at any time. For production, self-host (
npx guessit-js --serve, one command) or better, call the library in-process (~1.5 ms per parse, no HTTP at all).
Runs on Cloudflare Workers at the edge, CORS enabled, batch up to 500 filenames per POST, redeployed automatically with every release.
Self-hosted — no install needed beyond the package itself:
npx guessit-js --serve # port 3847 (or --serve 8080 / PORT env)
curl "http://localhost:3847/api/guessit?filename=Movie.2024.1080p.mkv"
curl -X POST http://localhost:3847/api/guessit \
-H 'Content-Type: application/json' \
-d '{"filenames": ["A.2020.mkv", "B.S01E02.mkv"], "options": {"type": "episode"}}'
CLI parsing flags become server defaults: guessit-js --serve -t episode -L en.
The repo also ships a fuller dev server (npm start) with the demo page,
Swagger UI at /docs, and static WASM serving.
WASM
For non-JS environments (Rust, Go, C++, edge compute). Uses Javy (QuickJS → WASM).
npm run wasm
echo '{"filename":"Movie.2024.1080p.mkv"}' | wasmtime wasm/guessit.wasm
The WASM build is bit-identical to the JS build across the entire test corpus
(1035/1035, including accented titles) — verified by test/wasm-full.test.ts.
Differences from Python guessit
guessit-js is a faithful port (1342/1342 fixtures, tracking Python 4.4.0), but it is not bug-for-bug identical — where Python has a genuine parsing bug, guessit-js is corrected. Highlights:
- Cases the released Python 4.4.0 still gets wrong (each verified against the
real Python CLI; the demo shows
live side-by-sides):
X2.2003…(Python:bonus: 2, no title), Python's still-open #875 and #877 (Season 3 - 11, anchored weak episodes,0x539ids), Python's deadRemoveLessSpecificSeasonEpisode("episode")pass (#961, our fix submitted as #962), and 112 of the 276 cross-parser cases Python's own test data admits failing — fullwidth CJK bracket groups, UFC event numbers, anime compound dash titles, fully-bracketed titles, and more. - 32 historical upstream bugs were fixed here ahead of upstream during the
4.x catch-up (many since adopted by Python 4.4 — e.g.
Us.2019,The.Collector,grown-ish,imdb_id/tmdb_id,volume, artwork and month-date detection are now correct in both). Full ledger with dispositions:docs/upstream-issues.md. - Typed, schema-described output: a precise
GuessItResultinterface with enum'd value fields,properties()mirroring Python's API, and a JSON Schema (draft-07) of the output — none of which Python provides as machine-readable artifacts. - No Python runtime: zero runtime dependencies, ESM + CJS + WASM, ~3.5x faster.
- Intentional divergences (cases where guessit-js is more correct than
Python) are catalogued per-example in
docs/python-parity.md.
Known issues
- Music files are not supported (#599):
Artist - Album/01 Track.flacis parsed with the video vocabulary (title / alternative_title), notartist/album/track. Out of scope for now. - No composite
qualityfield (#802):screen_size,sourceandvideo_codecare returned separately, not combined into one string (the request is underspecified). - A few ambiguous anime conventions remain
(#690/#696/#747):
e.g.
Re ZERO …- Season 2 - 15, romaji +(English title)— no unambiguous correct parse. - 12 debatable cases vs Python (neither clearly right) are listed under
"② NEUTRAL" in
docs/python-parity.md.
Performance
Warm per-parse, measured on one machine (absolute numbers are hardware-dependent — the live demo times it in your browser):
| Runtime | ms/parse | Notes |
|---|---|---|
| Browser (V8) | ~2–3 ms | fastest; JIT-compiled |
| Node.js 22 (V8) | ~4.4 ms | ~3.5× faster than Python (same machine) — recommended for servers/CLI |
| Python 3.8 | ~15.5 ms | reference |
| WASM (QuickJS/Javy) | ~35 ms warm · ~150 ms cold | for portability, not speed (see below) |
About the WASM build. It exists so you can run guessit in environments without a JS engine (Rust, Go, C/C++, edge/WASI runtimes). Javy compiles the bundle to QuickJS, which is an interpreter (no JIT), so per-parse compute (~35 ms) is slower than V8 and even than Python — that's inherent to the engine, not the code. If you want speed, use the Node/browser build (V8). If you need WASM, the levers that actually help:
- Amortize startup — instantiate the module once and parse many filenames; most of the ~150 ms single-shot cost is wasmtime + module init, not parsing.
- AOT-compile the module (
wasmtime compile guessit.wasm -o guessit.cwasm) to skip per-run JIT of the wasm itself (~30 ms off cold start). - The ~35 ms warm floor is QuickJS interpretation; beating it would require a JIT-capable WASI JS engine (none production-ready) or a native port — out of scope. WASM correctness is bit-identical to the JS build.
License
Copyright (C) 2024-2026 OpenSubtitles
Licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0), the same license as the original Python guessit library this project is ported from.
Build-time and embedded third-party components (Javy, QuickJS) are covered in THIRD_PARTY_NOTICES.md.