From 2f0d8300ceae48d98d196efac4d80d83095e57a2 Mon Sep 17 00:00:00 2001 From: Breadway Date: Tue, 21 Jul 2026 21:38:00 +0800 Subject: [PATCH] Check eligibility before queuing a low-confidence match for review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit process_item queued anything landing in the matcher's "needs review" confidence band immediately, without ever checking whether the show/ season/episode/movie actually needed anything — that check only ran on the auto-match path. A low-confidence match against an already-complete show gained nothing from a human's yes/no, it was just noise that reappeared every cycle the source kept re-listing the same old release (verified live: fully-complete shows' season-pack re-releases piling up in the review queue indefinitely). Reordered so the eligibility check (movie/season-pack/episode) runs first regardless of confidence, and only a genuinely-needed release ever reaches the queue-for-review decision. --- breadarrd/src/scheduler.rs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/breadarrd/src/scheduler.rs b/breadarrd/src/scheduler.rs index ba55675..f47f812 100644 --- a/breadarrd/src/scheduler.rs +++ b/breadarrd/src/scheduler.rs @@ -474,12 +474,9 @@ async fn process_item( let parsed = parser::parse(&item.title); - let candidate = match matcher.match_title(conn, &parsed.title_normalized)? { - MatchOutcome::Auto(c) => c, - MatchOutcome::NeedsReview(c) => { - matcher::queue_for_review(conn, &item.title, &c, Some(&item.link), Some(source_id))?; - return Ok(ProcessOutcome::QueuedForReview); - } + let (candidate, needs_review) = match matcher.match_title(conn, &parsed.title_normalized)? { + MatchOutcome::Auto(c) => (c, false), + MatchOutcome::NeedsReview(c) => (c, true), MatchOutcome::NoMatch => return Ok(ProcessOutcome::NoMatch), }; @@ -488,6 +485,16 @@ async fn process_item( let mut episode_id: Option = None; let mut season_pack_number: Option = None; + // Whether this match needs a human's confirmation (queued for review) + // or was confident enough to act on automatically, the show/season/ + // episode/movie still has to actually need *something* first — a + // low-confidence title match against an already-complete show gains + // nothing from a human's yes/no, it's just noise that reappears every + // cycle the source keeps re-listing the same old release (verified + // live: fully-complete shows' season-pack re-releases piling up in the + // review queue indefinitely because this check only ever ran on the + // auto-match path). So this eligibility check runs before the + // queue-for-review decision below, not only on the auto-grab path. if media_item.kind == "movie" { // A release carrying a season/episode/absolute-episode marker // title-matched a movie by name alone — it's actually an episode @@ -527,6 +534,17 @@ async fn process_item( episode_id = Some(eid); } + if needs_review { + matcher::queue_for_review( + conn, + &item.title, + &candidate, + Some(&item.link), + Some(source_id), + )?; + return Ok(ProcessOutcome::QueuedForReview); + } + let anime = match media_item.tvdb_id { Some(id) => is_anime(conn, id)?, None => false,