Skip to content

Commit 94468fb

Browse files
committed
fix(service): skip screenscraper for games with more files than its catalogue can index
1 parent fb66122 commit 94468fb

1 file changed

Lines changed: 50 additions & 15 deletions

File tree

  • service/src/providers/screenscraper/matching

service/src/providers/screenscraper/matching/game.rs

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,9 @@ fn match_game_to_screenscraper(
119119
let mut redis_conn = client.redis_conn().clone();
120120
let system_id = get_game_platform_screenscraper_id(&game, &db_conn).await?;
121121

122-
if let Some(()) =
123-
try_match_by_hashes(&game, system_id, &client, &db_conn, &mut redis_conn).await?
124-
{
125-
return Ok(());
122+
match try_match_by_hashes(&game, system_id, &client, &db_conn, &mut redis_conn).await? {
123+
HashPassOutcome::Matched | HashPassOutcome::SkippedTooMany => return Ok(()),
124+
HashPassOutcome::Missed => {}
126125
}
127126

128127
if client.is_quota_exhausted() {
@@ -296,16 +295,44 @@ async fn run_rung(
296295
}
297296
}
298297

298+
/// ScreenScraper indexes whole games, not file chunks. Anything above the
299+
/// cap gets a `Failed/TooManyFiles` row without an API call.
300+
const MAX_GAME_FILES_FOR_SCREENSCRAPER: usize = 5;
301+
302+
enum HashPassOutcome {
303+
Matched,
304+
SkippedTooMany,
305+
Missed,
306+
}
307+
299308
async fn try_match_by_hashes(
300309
game: &Model,
301310
system_id: i32,
302311
client: &ScreenScraperClient,
303312
db_conn: &DbConn,
304313
redis_conn: &mut redis::aio::MultiplexedConnection,
305-
) -> anyhow::Result<Option<()>> {
314+
) -> anyhow::Result<HashPassOutcome> {
306315
let files = get_game_files_from_game_id(game.id, db_conn).await?;
307316
if files.is_empty() {
308-
return Ok(None);
317+
return Ok(HashPassOutcome::Missed);
318+
}
319+
if files.len() > MAX_GAME_FILES_FOR_SCREENSCRAPER {
320+
debug!(
321+
"ScreenScraper skip for Game \"{}\": {} files exceeds cap of {}",
322+
game.name,
323+
files.len(),
324+
MAX_GAME_FILES_FOR_SCREENSCRAPER
325+
);
326+
write_auto_match_failed(
327+
"screenscraper",
328+
MetadataProviderEnum::Screenscraper,
329+
Target::Game(game.id),
330+
FailedMatchReasonEnum::TooManyFiles,
331+
db_conn,
332+
redis_conn,
333+
)
334+
.await?;
335+
return Ok(HashPassOutcome::SkippedTooMany);
309336
}
310337

311338
let parsed_dat = parse_name(&game.name);
@@ -317,7 +344,7 @@ async fn try_match_by_hashes(
317344

318345
for file in &files {
319346
if client.is_quota_exhausted() {
320-
return Ok(None);
347+
return Ok(HashPassOutcome::Missed);
321348
}
322349
let rom_name = file.file_name.as_str();
323350
let rom_size = file.file_size_in_bytes;
@@ -341,7 +368,7 @@ async fn try_match_by_hashes(
341368
match found {
342369
Some(found) => {
343370
let winning = pick_winning_hash(&found, sha1, md5, crc, &submitted);
344-
record_hash_match(
371+
let wrote = record_hash_match(
345372
game,
346373
&found,
347374
reason_for(winning),
@@ -350,11 +377,16 @@ async fn try_match_by_hashes(
350377
redis_conn,
351378
)
352379
.await?;
380+
if wrote {
381+
for h in &submitted {
382+
let outcome = if *h == winning { "hit" } else { "miss" };
383+
crate::metrics::record_match_rung("screenscraper", rung_for(*h), outcome);
384+
}
385+
return Ok(HashPassOutcome::Matched);
386+
}
353387
for h in &submitted {
354-
let outcome = if *h == winning { "hit" } else { "miss" };
355-
crate::metrics::record_match_rung("screenscraper", rung_for(*h), outcome);
388+
crate::metrics::record_match_rung("screenscraper", rung_for(*h), "miss");
356389
}
357-
return Ok(Some(()));
358390
}
359391
None => {
360392
for h in &submitted {
@@ -364,7 +396,7 @@ async fn try_match_by_hashes(
364396
}
365397
}
366398

367-
Ok(None)
399+
Ok(HashPassOutcome::Missed)
368400
}
369401

370402
/// `Ord` derive is load-bearing: variants are listed weakest to strongest so
@@ -453,20 +485,22 @@ fn pick_winning_hash(
453485
best.unwrap_or(strongest_submitted)
454486
}
455487

488+
/// `Ok(false)` when the response carried no game id and no row was written;
489+
/// the caller treats it as a miss and moves on to the next file.
456490
async fn record_hash_match(
457491
game: &Model,
458492
found: &SsGame,
459493
reason: AutomaticMatchReasonEnum,
460494
dat_ss_regions: &[&str],
461495
db_conn: &DbConn,
462496
redis_conn: &mut redis::aio::MultiplexedConnection,
463-
) -> anyhow::Result<()> {
497+
) -> anyhow::Result<bool> {
464498
let Some(found_id) = found.id else {
465499
debug!(
466500
"ScreenScraper hash response missing id for Game \"{}\"; skipping",
467501
game.name
468502
);
469-
return Ok(());
503+
return Ok(false);
470504
};
471505
debug!(
472506
"Matched Game \"{}\" to ScreenScraper Game ID {} ({:?})",
@@ -487,7 +521,8 @@ async fn record_hash_match(
487521
db_conn,
488522
redis_conn,
489523
)
490-
.await
524+
.await?;
525+
Ok(true)
491526
}
492527

493528
async fn get_game_platform_screenscraper_id(game: &Model, db_conn: &DbConn) -> anyhow::Result<i32> {

0 commit comments

Comments
 (0)