zantetsu 0.2.0

Ultra-fast, intelligent library for anime metadata extraction and normalization
Documentation

zantetsu

Ultra-fast anime metadata extraction and canonical title matching for Rust.

crates.io docs.rs license: MIT

What it does

zantetsu parses anime release filenames into structured metadata and optionally resolves the parsed title to a canonical entry with cross-referenced Kitsu / AniList / MAL identifiers.

[SubsPlease] Frieren - Beyond Journey's End - 01 (1080p) [A1B2C3D4].mkv
     │                  │                     │    │
  group              title                episode  resolution

No machine learning. No network calls required for parsing. No runtime downloads. Entirely self-contained.

Install

[dependencies]
zantetsu = "0.2"

Requires Rust 1.85+.

Quick start

Parse a filename

use zantetsu::{EpisodeSpec, Zantetsu};

let engine = Zantetsu::new()?;
let result = engine.parse("[SubsPlease] Cowboy Bebop - 01 [1080p][HEVC].mkv")?;

assert_eq!(result.title.as_deref(),  Some("Cowboy Bebop"));
assert_eq!(result.group.as_deref(),  Some("SubsPlease"));
assert_eq!(result.episode,           Some(EpisodeSpec::Single(1)));
println!("resolution: {:?}", result.resolution); // Some(P1080)
# Ok::<(), Box<dyn std::error::Error>>(())

Match to a canonical title (local Kitsu dump)

use zantetsu::{MatchSource, TitleMatcher};

let matcher = TitleMatcher::new(
    MatchSource::kitsu_dump("/home/user/.local/share/zantetsu/kitsu-dumps"),
)?;

if let Some(hit) = matcher.match_title("Sousou no Frieren")? {
    println!("canonical: {}", hit.canonical_title);   // "Frieren: Beyond Journey's End"
    println!("score    : {:.2}", hit.score);           // 0.97
    println!("anilist  : {:?}", hit.ids.anilist);      // Some(154587)
}
# Ok::<(), Box<dyn std::error::Error>>(())

Match to a canonical title (remote GraphQL)

use zantetsu::{MatchSource, TitleMatcher};

let matcher = TitleMatcher::new(
    MatchSource::remote_endpoint("https://graphql.anilist.co"),
)?;

if let Some(hit) = matcher.match_title("Spy x Family")? {
    println!("{} ({})", hit.canonical_title, hit.ids.anilist.unwrap_or(0));
}
# Ok::<(), Box<dyn std::error::Error>>(())

Parsed fields

Field Type Example
title Option<String> "Cowboy Bebop"
group Option<String> "SubsPlease"
episode Option<EpisodeSpec> Single(1), Range(1,3), Special(0.5)
resolution Option<Resolution> P1080, P720, P2160
video_codec Option<VideoCodec> H265, H264, AV1
audio_codec Option<AudioCodec> AAC, FLAC, AC3
source Option<MediaSource> BluRay, WebDL, HDTV
confidence f32 0.92

Quality scoring

Score a parse result against a configurable quality profile:

use zantetsu::{QualityProfile, Zantetsu};

let engine = Zantetsu::new()?;
let result = engine.parse("[EMBER] Violet Evergarden - 01 [BD 1080p HEVC FLAC].mkv")?;
let profile = QualityProfile::default();
let scores  = engine.score(&result, &profile);

println!("video score: {}", scores.video);
println!("audio score: {}", scores.audio);
# Ok::<(), Box<dyn std::error::Error>>(())

Crate family

zantetsu is a façade over two focused sub-crates. Depend on the façade for most use cases; depend on the sub-crates directly if you need finer control.

Crate Purpose
zantetsu This crate — unified public API
zantetsu-core Heuristic filename parser, quality scoring, types
zantetsu-vecdb Canonical title matching, TitleMatcher

Error handling

All fallible functions return Result<T, ZantetsuError>. Errors implement std::error::Error via thiserror.

use zantetsu::{Zantetsu, ZantetsuError};

let engine = Zantetsu::new()?;
match engine.parse("") {
    Err(ZantetsuError::EmptyInput) => eprintln!("filename is empty"),
    Err(e)  => eprintln!("error: {e}"),
    Ok(res) => println!("{:?}", res.title),
}
# Ok::<(), Box<dyn std::error::Error>>(())

License

MIT