feat: auto-match metadata when 100% confidence and matching book count

When multiple provider results exist but the best has 100% confidence,
compare local book count with external total_volumes. If they match,
treat it as an auto-match and link+sync series and book metadata
automatically instead of requiring manual review.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-18 21:44:31 +01:00
parent 037ede2750
commit e9bb951d97

View File

@@ -632,7 +632,7 @@ enum SearchOutcome {
async fn search_and_evaluate( async fn search_and_evaluate(
pool: &PgPool, pool: &PgPool,
_library_id: Uuid, library_id: Uuid,
series_name: &str, series_name: &str,
provider_name: &str, provider_name: &str,
) -> SearchOutcome { ) -> SearchOutcome {
@@ -660,7 +660,31 @@ async fn search_and_evaluate(
// Check if best candidate has perfect confidence // Check if best candidate has perfect confidence
let best = candidates.into_iter().next().unwrap(); let best = candidates.into_iter().next().unwrap();
if (best.confidence - 1.0).abs() < f32::EPSILON { if (best.confidence - 1.0).abs() < f32::EPSILON {
// Multiple results but best is 100% — still too many results // Multiple results but best is 100% — check if book count matches to auto-match
if let Some(ext_total) = best.total_volumes {
let local_count: Option<i64> = sqlx::query_scalar(
r#"
SELECT COUNT(*) FROM books
WHERE library_id = $1
AND COALESCE(NULLIF(series, ''), 'unclassified') = $2
"#,
)
.bind(library_id)
.bind(series_name)
.fetch_one(pool)
.await
.ok();
if let Some(count) = local_count {
if count == ext_total as i64 {
info!(
"[METADATA_BATCH] Auto-match by book count: series='{}' confidence=100% local_books={} external_volumes={}",
series_name, count, ext_total
);
return SearchOutcome::AutoMatch(best);
}
}
}
return SearchOutcome::TooManyResults(1, Some(best)); // count the 100% one return SearchOutcome::TooManyResults(1, Some(best)); // count the 100% one
} }