fix(core): guard movie id lane against episode tags

This commit is contained in:
Miguel Palhas
2026-08-23 19:13:49 +01:00
parent a378b22306
commit 64e711feec
+40 -8
View File
@@ -6,9 +6,10 @@
//! nearly free, because the feed is read again ten minutes later and the
//! targeted search still runs. So every rule here is deliberately strict:
//!
//! - an ID the tracker itself supplied beats anything read off the name, and
//! an ID that matches nothing wanted ends the comparison rather than
//! falling back to the title;
//! - an ID the tracker itself supplied beats anything read off the name,
//! except a season or episode tag in the name, which makes the release a
//! TV one whatever id came with it; and an ID that matches nothing wanted
//! ends the comparison rather than falling back to the title;
//! - two supplied IDs that disagree about which title this is are an
//! ambiguity like any other, and match nothing;
//! - a title match needs the years to agree, and a wanted title with a known
@@ -75,6 +76,13 @@ pub fn match_movie(
ids: &ReleaseIds,
claims: &NameClaims,
) -> Option<MovieMatch> {
// A season or episode tag makes this a TV release, whatever the title
// or the tracker-supplied id says. Movies are the only thing RSS grabs
// today (§13, phase 4), and trackers do mislabel ids.
if claims.episode.is_some() {
return None;
}
let imdb_id = ids.imdb_id.as_deref().and_then(imdb_key);
if ids.tmdb_id.is_some() || imdb_id.is_some() {
// A title matched by both ids is still one title, so each wanted
@@ -95,11 +103,6 @@ pub fn match_movie(
}));
}
// A season or episode tag makes this a TV release, whatever the title
// says. Movies are the only thing RSS grabs today (§13, phase 4).
if claims.episode.is_some() {
return None;
}
let title = match_key(claims.title.as_deref()?);
if title.is_empty() {
return None;
@@ -428,6 +431,35 @@ mod tests {
assert_eq!(match_movie(&wanted, &ReleaseIds::default(), &claims), None);
}
/// The same rule on the id lane (#139): trackers mislabel ids, so a
/// season or episode tag in the name vetoes an id that matches too.
#[test]
fn an_episode_tag_beats_a_matching_supplied_id() {
let wanted = vec![WantedMovie {
id: MovieId(1),
tmdb_id: Some(693_134),
imdb_id: Some("tt15239678".to_owned()),
title: "Dune: Part Two".to_owned(),
year: Some(2024),
}];
let cases = [
("Fallout.S01E03.1080p.WEB-DL", &ids(Some(693_134), None)),
("Fallout.S01.1080p.BluRay-GROUP", &ids(Some(693_134), None)),
(
"Fallout.2024.S01E03.1080p.WEB-DL",
&ids(None, Some("tt15239678")),
),
("Fallout.S01E03.1080p.WEB-DL", &ids(None, Some("15239678"))),
];
for (name, release_ids) in cases {
assert_eq!(
match_movie(&wanted, release_ids, &arr_parse::parse(name)),
None,
"{name}"
);
}
}
#[test]
fn two_wanted_titles_that_both_match_are_an_ambiguity() {
let wanted = vec![