Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

User Manual — Hunch

Installation, CLI usage, library API, and all 49 properties.


Installation

Homebrew (macOS / Linux)

brew install lijunzh/hunch/hunch

Cargo (from source)

cargo install hunch

Pre-built binaries

Download from GitHub Releases. Also supports cargo-binstall:

cargo binstall hunch

As a library

cargo add hunch

CLI Usage

Basic

$ hunch "The.Walking.Dead.S05E03.720p.BluRay.x264-DEMAND.mkv"
{
  "container": "mkv",
  "episode": 3,
  "release_group": "DEMAND",
  "screen_size": "720p",
  "season": 5,
  "source": "Blu-ray",
  "title": "The Walking Dead",
  "type": "episode",
  "video_codec": "H.264"
}

Multiple files:

hunch "Movie.2024.1080p.mkv" "Show.S01E01.mkv"

Cross-file context

For CJK, anime, or ambiguous filenames, sibling files improve accuracy:

# Single file with context from its directory
hunch --context ./Season1/ "(BD)十二国記 第13話「月の影 影の海 終章」(1440x1080 x264-10bpp flac).mkv"

# Batch mode: parse all files in a directory (mutual context)
hunch --batch ./Season1/ --json

# Recursive batch: parse an entire media library (RECOMMENDED)
hunch --batch /path/to/tv/ -r -j
hunch --batch /path/to/movies/ -r -j

💡 Important: For media libraries, always use --batch -r from the library root (e.g., tv/, movies/) rather than running --batch on each subdirectory individually. The -r flag preserves full relative paths like tv/Anime/Show/Extra/Menu.mkv, which gives the parser critical context from directory names (tv/, Anime/, Season 1/) for accurate type detection and title extraction.

Without -r, files in deep subdirectories lose their path context. For example, Extra/Menu 1-1.mkv would be classified as a movie, but tv/Anime/Show/Extra/Menu 1-1.mkv is correctly classified as an episode because the parser sees the tv/ and Anime/ components.

Options

FlagDescription
--context <DIR>Use sibling files for better title detection
--batch <DIR>Parse all media files in a directory
-r, --recursiveRecurse into subdirectories (with --batch). Symlinks are skipped (loop-safe, sandbox-safe), and traversal stops at 32 levels deep.
-j, --jsonCompact JSON output (default is pretty-printed)
-v, --verboseEnable debug logging

Logging

Hunch uses the log crate for diagnostic output. This is invaluable for debugging misparses.

# Debug level via --verbose
hunch -v "Movie.2024.1080p.BluRay.x264-GROUP.mkv"

# Fine-grained control via RUST_LOG
RUST_LOG=hunch=trace hunch "Movie.2024.1080p.mkv"
LevelWhat it shows
debugPipeline stage transitions, match counts, title decisions
traceEvery match span, conflict evictions, zone rule filtering

Library API

Basic usage

use hunch::hunch;

fn main() {
    let result = hunch("The.Walking.Dead.S05E03.720p.BluRay.x264-DEMAND.mkv");
    assert_eq!(result.title(), Some("The Walking Dead"));
    assert_eq!(result.season(), Some(5));
    assert_eq!(result.episode(), Some(3));
    assert_eq!(result.source(), Some("Blu-ray"));
    assert_eq!(result.video_codec(), Some("H.264"));
    assert_eq!(result.release_group(), Some("DEMAND"));
    assert_eq!(result.container(), Some("mkv"));
}

Cross-file context

use hunch::hunch_with_context;

fn main() {
    let result = hunch_with_context(
        "(BD)十二国記 第13話「月の影 影の海 終章」(1440x1080 x264-10bpp flac).mkv",
        &[
            "(BD)十二国記 第01話「月の影 影の海 一章」(1440x1080 x264-10bpp flac).mkv",
            "(BD)十二国記 第02話「月の影 影の海 二章」(1440x1080 x264-10bpp flac).mkv",
        ],
    );
    assert_eq!(result.title(), Some("十二国記"));
}

Pipeline reuse

For batch processing, reuse the Pipeline to avoid re-compiling TOML rules on each call:

use hunch::Pipeline;

fn main() {
    let pipeline = Pipeline::new();
    let filenames = vec!["Movie.2024.mkv", "Show.S01E01.mkv"];

    for name in filenames {
        let result = pipeline.run(name);
        println!("{}: {}", name, result.to_json());
    }
}

Confidence

use hunch::{hunch, Confidence};

fn main() {
    let result = hunch("ambiguous_file.mkv");
    match result.confidence() {
        Confidence::High   => println!("Confident parse"),
        Confidence::Medium => println!("Reasonable parse"),
        Confidence::Low    => println!("Consider using --context"),
        // `Confidence` is `#[non_exhaustive]` so future variants land
        // without forcing a major-version bump. Add a wildcard arm to
        // your `match`es:
        _                  => println!("Unknown confidence level"),
    }
}

Media-type checks (added in v2.0.0)

Three convenience helpers route a result to the right downstream lookup (e.g., TMDb for movies vs. TVDb for episodes) without an explicit MediaType import:

use hunch::hunch;

fn main() {
    let r = hunch("Breaking.Bad.S05E16.720p.BluRay.x264-DEMAND.mkv");
    if r.is_episode() {
        // route to TVDb
    }
    if r.is_movie() {
        // route to TMDb
    }
    if r.is_extra() {
        // bonus content / specials / NCOP / NCED — may not have a DB entry
    }
}

All three return false when the media type is unknown (rather than defaulting to a guess). Callers that need to distinguish “definitely not X” from “unknown” should use media_type() directly.

Bit rate and MIME type (added in v2.0.0)

The bit_rate property is split by unit (Kbps → audio, Mbps → video); MIME type is derived from the container extension:

use hunch::hunch;

fn main() {
    let r = hunch("Movie.2024.DD5.1.448Kbps.x264.5500Kbps.mp4");
    assert_eq!(r.audio_bit_rate(), Some("448Kbps"));
    assert_eq!(r.video_bit_rate(), Some("5500Kbps"));
    assert_eq!(r.mimetype(),       Some("video/mp4"));
}

MIME type returns None when the container is unknown rather than fabricating a value — callers that need a fallback should provide it at the call site.

Full API reference

See docs.rs/hunch for all 49 Property variants and HunchResult accessors.


All 49 Properties

Structural (always unambiguous)

PropertyExample valueExample input
titleThe Walking DeadThe.Walking.Dead.S05E03
season5S05E03
episode3S05E03
year2024Movie.2024.1080p
date2024-03-15Show.2024.03.15
containermkvmovie.mkv
typeepisode / movie(inferred)

Video

PropertyExample valueExample input
video_codecH.264x264
screen_size1080p1080p
frame_rate23.976fps23.976fps
color_depth10-bit10bit
video_profileHigh 10Hi10P
video_apiDXVADXVA
aspect_ratio16:916x9

Audio

PropertyExample valueExample input
audio_codecAACAAC
audio_channels5.15.1ch
audio_profileHD MADTS-HD.MA
audio_bit_rate320Kbps320kbps
video_bit_rate19.1Mbps19.1mbps

Source & Edition

PropertyExample valueExample input
sourceBlu-rayBluRay
streaming_serviceNetflixNF
editionDirector’s CutDirectors.Cut
otherProper, Repack, 3D, …PROPER

Release metadata

PropertyExample valueExample input
release_groupDEMAND-DEMAND
websiterarbg.to[rarbg.to]
crc32ABCD1234[ABCD1234]
uuid{uuid}
size1.4 GB1.4GB
proper_count1PROPER
version2v2

Episode details

PropertyExample valueExample input
episode_titleThe Brain In The Bot(text after episode marker)
film_title(multi-film sets)
alternative_title(AKA titles)
bonus1x01
bonus_title(bonus feature title)
episode_detailsSpecialSpecial
episode_formatMiniseriesMiniseries
episode_count2424eps
season_count55seasons
absolute_episode45(anime absolute numbering)
week12Week.12
film2Film.2
disc1Disc.1
cd2CD2
cd_count33CDs
part1Part.1

Language

PropertyExample valueExample input
languageEnglishEnglish
subtitle_languageFrenchsub.French
countryUSUS

FAQ

Why is the title wrong?

Title extraction is the hardest problem. The engine finds the gap before the first tech anchor — if it can’t find anchors, the title boundary is a guess. Use --context to provide sibling files for structural evidence.

For batch processing, use --batch -r from the library root to give the parser full path context. See Cross-file context.

Why is the year detected as title content?

Year-like numbers (e.g., “2001” in “2001.A.Space.Odyssey.1968”) are ambiguous. With --context, siblings reveal which numbers are invariant (title) vs variant (metadata).

How fast is it?

Single-file parsing: ~50–150µs. Batch mode with 100 files: ~5–15ms. All regex is linear-time (Thompson NFA). No backtracking, ever.

Does it work with non-Latin scripts?

Yes. CJK, Cyrillic, Arabic filenames all work. Cross-file context (--context / --batch) significantly improves CJK title extraction.

How do I debug a misparse?

hunch -v "problematic.filename.mkv"
# or for maximum detail:
RUST_LOG=hunch=trace hunch "problematic.filename.mkv"

The trace output shows every match, eviction, and decision.