From 57025734d0bcac7acab49cacb5cf5dd6af5be7d3 Mon Sep 17 00:00:00 2001 From: Fuwn Date: Fri, 27 Mar 2026 11:05:26 +0000 Subject: fix(manga): restore progress-based volume recommendations --- apps/proxy/src/index.js | 86 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 16 deletions(-) (limited to 'apps/proxy/src/index.js') diff --git a/apps/proxy/src/index.js b/apps/proxy/src/index.js index 4f18ba44..26121f10 100644 --- a/apps/proxy/src/index.js +++ b/apps/proxy/src/index.js @@ -135,6 +135,7 @@ const parseMangaPayload = async (request) => { return manga .map((entry) => ({ anilistId: Number(entry?.anilistId), + progress: entry?.progress ? Number(entry.progress) : 0, status: String(entry?.status || ""), startYear: entry?.startYear ? Number(entry.startYear) : null, nativeTitle: entry?.nativeTitle || null, @@ -203,6 +204,43 @@ const queueBootstrap = (env, manga) => }) .filter(Boolean); +const parseOptionalNumber = (value) => { + if (value === null || value === undefined || value === "") return null; + + const number = Number.parseFloat(String(value)); + + return Number.isFinite(number) ? number : null; +}; + +const recommendedVolumeText = (volumeChapterBoundaries, progress) => { + if (!volumeChapterBoundaries || !Number.isFinite(progress) || progress <= 0) + return null; + + let recommended = null; + let recommendedNumber = null; + + for (const [volumeText, chapterBoundary] of Object.entries( + volumeChapterBoundaries, + )) { + const volumeNumber = parseOptionalNumber(volumeText); + const boundaryNumber = parseOptionalNumber(chapterBoundary); + + if ( + volumeNumber === null || + boundaryNumber === null || + boundaryNumber > progress + ) + continue; + + if (recommendedNumber === null || volumeNumber > recommendedNumber) { + recommended = String(volumeNumber); + recommendedNumber = volumeNumber; + } + } + + return recommended; +}; + const handleMangaChapterCounts = async (request, env, ctx) => { if (!hasSupabaseConfig(env)) return jsonResponse( @@ -215,25 +253,35 @@ const handleMangaChapterCounts = async (request, env, ctx) => { if (!manga.length) return jsonResponse(request, { data: {} }); + const mangaById = new Map(manga.map((entry) => [entry.anilistId, entry])); const anilistIds = manga.map((entry) => entry.anilistId); const [existingRows, failureRows] = await Promise.all([ getMangadexRowsByAniListIds(env, anilistIds), getMangadexFailureRowsByAniListIds(env, anilistIds), ]); - const existingIds = new Set(existingRows.map((row) => row.anilist_id)); + const existingRowsById = new Map( + existingRows.map((row) => [row.anilist_id, row]), + ); const recentFailures = new Set( failureRows .filter((row) => isRecentFailure(row, bootstrapRetryMinutes(env))) .map((row) => row.anilist_id), ); - const missingRows = manga.filter( - (entry) => - !existingIds.has(entry.anilistId) && !recentFailures.has(entry.anilistId), - ); - const pendingRows = missingRows.filter((entry) => + const rowsNeedingBackfill = manga.filter((entry) => { + const row = existingRowsById.get(entry.anilistId); + + if (!row) return !recentFailures.has(entry.anilistId); + + return ( + entry.progress > 0 && + row.volume_chapter_boundaries === null && + !recentFailures.has(entry.anilistId) + ); + }); + const pendingRows = rowsNeedingBackfill.filter((entry) => bootstrapInFlight.has(entry.anilistId), ); - const queueableRows = missingRows.filter( + const queueableRows = rowsNeedingBackfill.filter( (entry) => !bootstrapInFlight.has(entry.anilistId), ); @@ -245,15 +293,21 @@ const handleMangaChapterCounts = async (request, env, ctx) => { ); const data = Object.fromEntries( - existingRows.map((row) => [ - String(row.anilist_id), - { - chapter: row.latest_en_chapter_number, - ...(row.latest_en_volume_text === null - ? {} - : { volumeText: row.latest_en_volume_text }), - }, - ]), + existingRows.map((row) => { + const entry = mangaById.get(row.anilist_id); + const volumeText = recommendedVolumeText( + row.volume_chapter_boundaries, + entry?.progress || 0, + ); + + return [ + String(row.anilist_id), + { + chapter: row.latest_en_chapter_number, + ...(volumeText === null ? {} : { volumeText }), + }, + ]; + }), ); const pending = [ ...new Set( -- cgit v1.2.3