8d79c7c378
COMPLETE before the season tag was weak junk, so it stayed in the title and exact-title matching failed silently (#135). It now closes the boundary wherever it lands. S01-S01 produced no claim because the dash-range branch required last > season; a degenerate range is one season's pack, matching the rule match_episode lifted.
474 lines
16 KiB
Rust
474 lines
16 KiB
Rust
//! Table-driven checks over representative release names. The full fixture
|
|
//! corpus pulled from real indexers is issue #9; these rows pin the parser's
|
|
//! behaviour per shape of name.
|
|
|
|
use arr_parse::{
|
|
parse, Codec, Edition, EpisodeClaim, HdrMarker, LanguageMarker, NameClaims, Resolution, Source,
|
|
};
|
|
use serde as _;
|
|
|
|
struct Case {
|
|
name: &'static str,
|
|
want: NameClaims,
|
|
}
|
|
|
|
#[allow(clippy::unnecessary_wraps)]
|
|
fn s(v: &str) -> Option<String> {
|
|
Some(v.to_string())
|
|
}
|
|
|
|
#[allow(clippy::too_many_lines)]
|
|
fn cases() -> Vec<Case> {
|
|
vec![
|
|
Case {
|
|
name: "Movie.Title.2024.2160p.UHD.BluRay.DV.HDR10.x265.TrueHD-GROUP",
|
|
want: NameClaims {
|
|
title: s("Movie Title"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P2160),
|
|
source: Some(Source::BluRay),
|
|
codec: Some(Codec::X265),
|
|
hdr: vec![HdrMarker::DolbyVision, HdrMarker::Hdr10],
|
|
group: s("GROUP"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// "Web" before the year is title, not a source claim.
|
|
name: "Charlottes.Web.2006.1080p.BluRay.x264-GRP",
|
|
want: NameClaims {
|
|
title: s("Charlottes Web"),
|
|
year: Some(2006),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::BluRay),
|
|
codec: Some(Codec::X264),
|
|
group: s("GRP"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "Dune Part Two (2024) [2160p] [WEB-DL] [HDR10+] [YTS.MX]",
|
|
want: NameClaims {
|
|
title: s("Dune Part Two"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P2160),
|
|
source: Some(Source::WebDl),
|
|
hdr: vec![HdrMarker::Hdr10Plus],
|
|
group: s("YTS.MX"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "O.Auto.da.Compadecida.2000.NACIONAL.1080p.WEB-DL.x264-BRA",
|
|
want: NameClaims {
|
|
title: s("O Auto da Compadecida"),
|
|
year: Some(2000),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::WebDl),
|
|
codec: Some(Codec::X264),
|
|
languages: vec![LanguageMarker::PtBr],
|
|
group: s("BRA"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// Repeated pt-BR markers dedupe to one claim.
|
|
name: "Filme.2023.Dublado.PT-BR.1080p.WEBRip.x264",
|
|
want: NameClaims {
|
|
title: s("Filme"),
|
|
year: Some(2023),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::WebRip),
|
|
codec: Some(Codec::X264),
|
|
languages: vec![LanguageMarker::PtBr],
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "Filme.Infantil.2023.1080p.WEB-DL.Dual.Áudio.x264",
|
|
want: NameClaims {
|
|
title: s("Filme Infantil"),
|
|
year: Some(2023),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::WebDl),
|
|
codec: Some(Codec::X264),
|
|
languages: vec![LanguageMarker::Dual],
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "Movie.2024.MULTi.1080p.WEB.x265-GRP",
|
|
want: NameClaims {
|
|
title: s("Movie"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::WebDl),
|
|
codec: Some(Codec::X265),
|
|
languages: vec![LanguageMarker::Multi],
|
|
group: s("GRP"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// Season/episode tags bound the title; no year needed.
|
|
name: "Serie.S01E02.720p.HDTV.x264-LOL",
|
|
want: NameClaims {
|
|
title: s("Serie"),
|
|
resolution: Some(Resolution::P720),
|
|
source: Some(Source::Hdtv),
|
|
codec: Some(Codec::X264),
|
|
group: s("LOL"),
|
|
episode: Some(EpisodeClaim::Episodes {
|
|
season: 1,
|
|
episodes: vec![2],
|
|
}),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// Remux beats the BluRay marker riding along.
|
|
name: "Movie.2024.2160p.BluRay.REMUX.HEVC.TrueHD.7.1.Atmos-FGT",
|
|
want: NameClaims {
|
|
title: s("Movie"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P2160),
|
|
source: Some(Source::Remux),
|
|
codec: Some(Codec::X265),
|
|
group: s("FGT"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "Movie.2024.Extended.1080p.BluRay.x264",
|
|
want: NameClaims {
|
|
title: s("Movie"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::BluRay),
|
|
codec: Some(Codec::X264),
|
|
editions: vec![Edition::Extended],
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// Edition between title and year is stripped off the title tail.
|
|
name: "Movie.Directors.Cut.2024.1080p.BluRay",
|
|
want: NameClaims {
|
|
title: s("Movie"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::BluRay),
|
|
editions: vec![Edition::DirectorsCut],
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// First year-like token is the title, second is the year.
|
|
name: "2012.2009.1080p.BluRay.x264",
|
|
want: NameClaims {
|
|
title: s("2012"),
|
|
year: Some(2009),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::BluRay),
|
|
codec: Some(Codec::X264),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "Blade.Runner.2049.2017.2160p.WEB-DL.DDP5.1.HDR.HEVC-XEBEC",
|
|
want: NameClaims {
|
|
title: s("Blade Runner 2049"),
|
|
year: Some(2017),
|
|
resolution: Some(Resolution::P2160),
|
|
source: Some(Source::WebDl),
|
|
codec: Some(Codec::X265),
|
|
hdr: vec![HdrMarker::Hdr],
|
|
group: s("XEBEC"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "Movie.2024.HDTS.x264-ETRG",
|
|
want: NameClaims {
|
|
title: s("Movie"),
|
|
year: Some(2024),
|
|
source: Some(Source::Telesync),
|
|
codec: Some(Codec::X264),
|
|
group: s("ETRG"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "Movie.2024.1080p.HMAX.WEB-DL.DD5.1.H.264-GROUP",
|
|
want: NameClaims {
|
|
title: s("Movie"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::WebDl),
|
|
codec: Some(Codec::X264),
|
|
group: s("GROUP"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// Spaced dash before the group.
|
|
name: "Movie Title 2024 1080p BluRay x265 - VXT",
|
|
want: NameClaims {
|
|
title: s("Movie Title"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::BluRay),
|
|
codec: Some(Codec::X265),
|
|
group: s("VXT"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// Filename with a container extension.
|
|
name: "Movie.Title.2024.720p.WEBRip.x264.mkv",
|
|
want: NameClaims {
|
|
title: s("Movie Title"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P720),
|
|
source: Some(Source::WebRip),
|
|
codec: Some(Codec::X264),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// No markers at all: the whole thing is a title claim.
|
|
name: "Some Random Words",
|
|
want: NameClaims {
|
|
title: s("Some Random Words"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// Marker-only garbage parses partially, title absent.
|
|
name: "1080p",
|
|
want: NameClaims {
|
|
resolution: Some(Resolution::P1080),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "",
|
|
want: NameClaims::default(),
|
|
},
|
|
Case {
|
|
// Dolby Vision spelled out, dotted.
|
|
name: "Movie.2024.2160p.WEB-DL.Dolby.Vision.HDR10.HEVC-GRP",
|
|
want: NameClaims {
|
|
title: s("Movie"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P2160),
|
|
source: Some(Source::WebDl),
|
|
codec: Some(Codec::X265),
|
|
hdr: vec![HdrMarker::DolbyVision, HdrMarker::Hdr10],
|
|
group: s("GRP"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// Hyphenated title survives when nothing marks a group.
|
|
name: "Spider-Man.2002.1080p.BluRay.x264-SECTOR7",
|
|
want: NameClaims {
|
|
title: s("Spider-Man"),
|
|
year: Some(2002),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::BluRay),
|
|
codec: Some(Codec::X264),
|
|
group: s("SECTOR7"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "www.Torrenting.com - Movie.2024.1080p.WEB-DL.x264",
|
|
want: NameClaims {
|
|
title: s("Movie"),
|
|
year: Some(2024),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::WebDl),
|
|
codec: Some(Codec::X264),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// Season pack, no episode.
|
|
name: "Show.Name.S02.2160p.WEB-DL.DDP5.1.DV.HDR10.H.265-NTb",
|
|
want: NameClaims {
|
|
title: s("Show Name"),
|
|
resolution: Some(Resolution::P2160),
|
|
source: Some(Source::WebDl),
|
|
codec: Some(Codec::X265),
|
|
hdr: vec![HdrMarker::DolbyVision, HdrMarker::Hdr10],
|
|
group: s("NTb"),
|
|
episode: Some(EpisodeClaim::Season { season: 2 }),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// `COMPLETE` before the tag closes the title like any strong
|
|
// marker, wherever the group puts it.
|
|
name: "Bluey.COMPLETE.S01.1080p.WEB-DL",
|
|
want: NameClaims {
|
|
title: s("Bluey"),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::WebDl),
|
|
episode: Some(EpisodeClaim::Season { season: 1 }),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// The trailing placement already worked; it keeps working.
|
|
name: "Bluey.S01.COMPLETE.1080p.WEB-DL",
|
|
want: NameClaims {
|
|
title: s("Bluey"),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::WebDl),
|
|
episode: Some(EpisodeClaim::Season { season: 1 }),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
// A degenerate range is one season's pack.
|
|
name: "Fallout.S01-S01.1080p.BluRay.x264-GROUP",
|
|
want: NameClaims {
|
|
title: s("Fallout"),
|
|
resolution: Some(Resolution::P1080),
|
|
source: Some(Source::BluRay),
|
|
codec: Some(Codec::X264),
|
|
group: s("GROUP"),
|
|
episode: Some(EpisodeClaim::Seasons { first: 1, last: 1 }),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
Case {
|
|
name: "Movie.2019.4K.HDR.DV.2160p.BDRemux.Ita.Eng.x265-NAHOM",
|
|
want: NameClaims {
|
|
title: s("Movie"),
|
|
year: Some(2019),
|
|
resolution: Some(Resolution::P2160),
|
|
source: Some(Source::Remux),
|
|
codec: Some(Codec::X265),
|
|
hdr: vec![HdrMarker::Hdr, HdrMarker::DolbyVision],
|
|
languages: vec![LanguageMarker::Italian, LanguageMarker::English],
|
|
group: s("NAHOM"),
|
|
..NameClaims::default()
|
|
},
|
|
},
|
|
]
|
|
}
|
|
|
|
#[test]
|
|
fn corpus_rows_parse_as_expected() {
|
|
let mut failures = Vec::new();
|
|
for Case { name, want } in cases() {
|
|
let got = parse(name);
|
|
if got != want {
|
|
failures.push(format!("{name:?}\n got: {got:?}\n want: {want:?}"));
|
|
}
|
|
}
|
|
assert!(
|
|
failures.is_empty(),
|
|
"{} mismatches:\n{}",
|
|
failures.len(),
|
|
failures.join("\n")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn garbage_never_panics() {
|
|
let inputs = [
|
|
"",
|
|
" ",
|
|
"....",
|
|
"----",
|
|
"[[[]]]",
|
|
"()()()",
|
|
"ção áéíóú 日本語 🎬🎬🎬",
|
|
"-.-.-.-[]()s01e999999999999999999999",
|
|
"Movie.2024.1080p-",
|
|
"a",
|
|
&"x.".repeat(5000),
|
|
];
|
|
for input in inputs {
|
|
let _ = parse(input);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn episode_tags_separate_packs_from_single_episodes() {
|
|
let single = parse("Show.S01E02.1080p.WEB-DL.x264-GRP")
|
|
.episode
|
|
.expect("S01E02 is an episode tag");
|
|
assert_eq!(
|
|
single,
|
|
EpisodeClaim::Episodes {
|
|
season: 1,
|
|
episodes: vec![2]
|
|
}
|
|
);
|
|
assert!(!single.is_season_pack());
|
|
assert!(!single.is_multi_episode());
|
|
assert!(single.covers(1, 2));
|
|
assert!(!single.covers(1, 3));
|
|
assert!(single.covers_season(1));
|
|
|
|
let multi = parse("Show.S01E02-E04.1080p.WEB-DL.x264-GRP")
|
|
.episode
|
|
.expect("S01E02-E04 is an episode tag");
|
|
assert!(multi.is_multi_episode());
|
|
assert!(!multi.is_season_pack());
|
|
assert!(multi.covers(1, 3));
|
|
assert!(!multi.covers(1, 5));
|
|
|
|
let pack = parse("Show.S01.1080p.WEB-DL.x264-GRP")
|
|
.episode
|
|
.expect("S01 is a season tag");
|
|
assert!(pack.is_season_pack());
|
|
assert!(
|
|
pack.covers(1, 7),
|
|
"a season pack covers every episode in it"
|
|
);
|
|
assert!(!pack.covers(2, 7));
|
|
|
|
let seasons = parse("Show.S01-S03.1080p.WEB-DL.x264-GRP")
|
|
.episode
|
|
.expect("S01-S03 is a season range");
|
|
assert!(seasons.is_season_pack());
|
|
assert!(seasons.covers_season(2));
|
|
assert!(!seasons.covers_season(4));
|
|
|
|
let daily = parse("Show.2024.05.13.1080p.WEB.h264-GRP")
|
|
.episode
|
|
.expect("a dotted air date is a daily tag");
|
|
assert!(daily.covers_date(2024, 5, 13));
|
|
assert!(!daily.covers_date(2024, 5, 14));
|
|
assert!(!daily.covers(1, 1), "a daily episode has no season number");
|
|
}
|
|
|
|
#[test]
|
|
fn non_tv_names_claim_no_episode() {
|
|
for name in [
|
|
"Dune.Part.Two.2024.2160p.WEB-DL.H.265-FLUX",
|
|
"Movie.1999.1080p.BluRay.x264-GRP",
|
|
"Show.2024.13.45.1080p.WEB-DL.x264-GRP",
|
|
"Blade.Runner.2049.2017.2160p.UHD.BluRay.x265-TERMiNAL",
|
|
] {
|
|
assert_eq!(parse(name).episode, None, "{name}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn normalisation_is_separator_and_case_blind() {
|
|
let key = arr_parse::normalise("Dune.Part.Two.2024.2160p.WEB-DL.DDP5.1.Atmos");
|
|
assert_eq!(key, "dune.part.two.2024.2160p.web.dl.ddp5.1.atmos");
|
|
assert_eq!(
|
|
arr_parse::normalise("dune part two [2024] 2160p WEB DL DDP5 1 atmos "),
|
|
key,
|
|
"the same release under different separators is the same blacklist key"
|
|
);
|
|
assert_eq!(arr_parse::normalise("!!!"), "");
|
|
}
|