From b8ae21195686259e29b09e243cda2b49db9930df Mon Sep 17 00:00:00 2001 From: Rushil Perera Date: Wed, 23 Apr 2025 09:32:52 -0400 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(aniwatch):=20Improves=20title?= =?UTF-8?q?=20matching=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Enhances title matching accuracy when fetching Aniwatch IDs. - Prioritizes user-preferred titles and falls back to English titles. - Ensures only one fetch call is made per title if both english and userPreferred title are same. - Adds a score threshold to filter low-quality matches. - Fixes a bug where the episode list was not being returned. --- .../episodes/getByAniListId/aniwatch.ts | 51 +++++++++++++---- .../episodes/getByAniListId/index.ts | 11 +++- src/libs/findBestMatchingTitle.ts | 55 ++++++++++++++++--- 3 files changed, 96 insertions(+), 21 deletions(-) diff --git a/src/controllers/episodes/getByAniListId/aniwatch.ts b/src/controllers/episodes/getByAniListId/aniwatch.ts index e0c09d2..fdfc4b6 100644 --- a/src/controllers/episodes/getByAniListId/aniwatch.ts +++ b/src/controllers/episodes/getByAniListId/aniwatch.ts @@ -119,17 +119,43 @@ async function fetchEpisodes( }); } +// function updateTitles(title: Partial<{ english: string; userPreferred: string }>) { +// const english = title.english?.toLowerCase(); +// const userPreferred = title.userPreferred?.toLowerCase(); + +// if (english?.match(/my hero academia.+[0-9]$/)) { + +// } +// } + function getAniwatchId( animeTitle: Partial<{ english: string; userPreferred: string }>, ): Promise { - return Promise.allSettled([ - fetch( - `https://aniwatch.up.railway.app/api/v2/hianime/search?q=${encodeURIComponent(animeTitle.english!)}`, - ), - fetch( - `https://aniwatch.up.railway.app/api/v2/hianime/search?q=${encodeURIComponent(animeTitle.userPreferred!)}`, - ), - ]) + animeTitle = { + english: animeTitle?.english?.toLowerCase(), + userPreferred: animeTitle?.userPreferred?.toLowerCase(), + }; + const promises = []; + if (animeTitle.userPreferred) { + promises.push( + fetch( + `https://aniwatch.up.railway.app/api/v2/hianime/search?q=${encodeURIComponent( + animeTitle.userPreferred, + )}`, + ), + ); + } + if (animeTitle.english && animeTitle.english !== animeTitle.userPreferred) { + promises.push( + fetch( + `https://aniwatch.up.railway.app/api/v2/hianime/search?q=${encodeURIComponent( + animeTitle.english, + )}`, + ), + ); + } + + return Promise.allSettled(promises) .then((responses) => { return responses.reduce( async (current, res) => { @@ -164,16 +190,21 @@ function getAniwatchId( return; } - const bestMatchingTitle = findBestMatchingTitle( + const { title: bestMatchingTitle, score } = findBestMatchingTitle( animeTitle, animes.map((anime) => ({ english: anime.name, userPreferred: anime.jname, })), ); + if (score < 0.8) { + return; + } + return animes.find( (anime) => - anime.name === bestMatchingTitle || anime.jname === bestMatchingTitle, + anime.name?.toLowerCase() === bestMatchingTitle || + anime.jname?.toLowerCase() === bestMatchingTitle, )?.id; }); } diff --git a/src/controllers/episodes/getByAniListId/index.ts b/src/controllers/episodes/getByAniListId/index.ts index 00e8b1b..71f95c1 100644 --- a/src/controllers/episodes/getByAniListId/index.ts +++ b/src/controllers/episodes/getByAniListId/index.ts @@ -81,18 +81,23 @@ export function fetchEpisodes( app.openapi(route, async (c) => { const aniListId = Number(c.req.param("aniListId")); - const { result: episodes, errorOccurred } = await fetchEpisodes( + const { result, errorOccurred } = await fetchEpisodes( aniListId, env(c, "workerd"), ); - if (errorOccurred) { + if (errorOccurred || !result) { return c.json(ErrorResponse, { status: 500 }); } + const { episodes, providerId } = result; + if (!episodes || episodes.length === 0) { + return c.json(ErrorResponse, { status: 404 }); + } + return c.json({ success: true, - result: episodes ?? [], + result: { providerId, episodes }, }); }); diff --git a/src/libs/findBestMatchingTitle.ts b/src/libs/findBestMatchingTitle.ts index e4dd718..f9197e7 100644 --- a/src/libs/findBestMatchingTitle.ts +++ b/src/libs/findBestMatchingTitle.ts @@ -1,17 +1,40 @@ function findBestMatch(mainString: string, targets: string[]): string | null { - if (targets.length === 0) return null; + if (targets.length === 0) { + return null; + } + if (targets.length === 1) { + return targets[0]; + } let bestMatch = targets[0]; let highestScore = stringMatchingAlgorithm(mainString, bestMatch); for (let i = 1; i < targets.length; i++) { const currentScore = stringMatchingAlgorithm(mainString, targets[i]); + if (currentScore === 1) { + bestMatch = targets[i]; + break; + } + + console.log( + "searching best match", + `'${mainString}'`, + `'${targets[i]}'`, + currentScore, + highestScore, + ); if (currentScore > highestScore) { highestScore = currentScore; bestMatch = targets[i]; } } + console.log( + "findBestMatch", + `'${mainString}'`, + `'${bestMatch}'`, + highestScore, + ); return bestMatch; } @@ -23,23 +46,39 @@ type Title = { export const findBestMatchingTitle = ( title: Partial, titlesToSearch: Partial<Title>[], -): string | null => { +): { title: string | null; score: number } => { const { english, userPreferred } = title; + let match; + let expectedSuffix = ""; + if ((match = english?.match(/my hero academia.+([0-9])$/))) { + expectedSuffix = match[1]; + } + + console.log("searching best user preferred title", userPreferred); const userPreferredBestMatch = userPreferred ? findBestMatch( userPreferred, titlesToSearch - .map((title) => title.userPreferred) - .filter((title) => title !== undefined), + .map((title) => title.userPreferred?.toLowerCase()) + .filter( + (title) => + title !== undefined && + (!expectedSuffix || title.endsWith(expectedSuffix)), + ), ) : null; + console.log("searching best english title", english); const englishBestMatch = english ? findBestMatch( english, titlesToSearch - .map((title) => title.english) - .filter((title) => title !== undefined), + .map((title) => title.english?.toLowerCase()) + .filter( + (title) => + title !== undefined && + (!expectedSuffix || title.endsWith(expectedSuffix)), + ), ) : null; @@ -54,10 +93,10 @@ export const findBestMatchingTitle = ( console.log(title.userPreferred, userPreferredScore); if (userPreferredScore >= englishScore) { console.log("User preferred", userPreferredBestMatch); - return userPreferredBestMatch; + return { title: userPreferredBestMatch, score: userPreferredScore }; } else { console.log("English", englishBestMatch); - return englishBestMatch; + return { title: englishBestMatch, score: englishScore }; } };