aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-03-01 13:09:43 -0800
committerFuwn <[email protected]>2026-03-01 13:09:43 -0800
commit8aba91a2dcc1db14815f9ef0e25f18026f4abea0 (patch)
tree748b4f164486f254180451411fbdbb9b4d1ec422 /src
parentfeat(match): add confidence gate for subtitle title matching (diff)
downloaddue.moe-8aba91a2dcc1db14815f9ef0e25f18026f4abea0.tar.xz
due.moe-8aba91a2dcc1db14815f9ef0e25f18026f4abea0.zip
feat(match): skip ambiguous subtitle matches via score margin
Diffstat (limited to 'src')
-rw-r--r--src/lib/Media/Anime/Airing/Subtitled/match.ts6
1 files changed, 6 insertions, 0 deletions
diff --git a/src/lib/Media/Anime/Airing/Subtitled/match.ts b/src/lib/Media/Anime/Airing/Subtitled/match.ts
index dc8dbff9..661d0d23 100644
--- a/src/lib/Media/Anime/Airing/Subtitled/match.ts
+++ b/src/lib/Media/Anime/Airing/Subtitled/match.ts
@@ -67,6 +67,7 @@ const isMeaningfulToken = (token: string): boolean =>
const MIN_MATCH_SCORE = 0.3;
const MIN_TOKEN_OVERLAP = 2;
+const MIN_MATCH_MARGIN = 0.08;
interface SimilarityAnalysis {
score: number;
@@ -124,6 +125,7 @@ export const findClosestMatch = (times: Time[], anime: Media): Time | null => {
}));
let bestMatch: Time | null = null;
let bestScore = 0;
+ let secondBestScore = 0;
let bestTokenOverlap = 0;
let bestNumericTokenOverlap = 0;
const searchTitles = [anime.title.romaji, anime.title.english, ...anime.synonyms].filter(Boolean);
@@ -137,15 +139,19 @@ export const findClosestMatch = (times: Time[], anime: Media): Time | null => {
const similarity = calculateWeightedSimilarity(normalizedSearchTitle, normalized);
if (similarity.score > bestScore) {
+ secondBestScore = bestScore;
bestScore = similarity.score;
bestTokenOverlap = similarity.tokenOverlap;
bestNumericTokenOverlap = similarity.numericTokenOverlap;
bestMatch = time;
+ } else if (similarity.score > secondBestScore) {
+ secondBestScore = similarity.score;
}
}
}
if (bestScore < MIN_MATCH_SCORE) return null;
+ if (bestScore - secondBestScore < MIN_MATCH_MARGIN) return null;
if (bestNumericTokenOverlap === 0 && bestTokenOverlap < MIN_TOKEN_OVERLAP) return null;
return bestMatch;