aboutsummaryrefslogtreecommitdiff
path: root/src/lib/Media
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-03-29 06:32:12 +0000
committerFuwn <[email protected]>2026-03-29 06:32:12 +0000
commit4140869d8e5a26db6f9d63c400051bd440c575bd (patch)
tree9ef09729aa5907f0265388e12e9bb8c15cde6b57 /src/lib/Media
parentfix(proxy): improve native chapter source routing (diff)
downloaddue.moe-4140869d8e5a26db6f9d63c400051bd440c575bd.tar.xz
due.moe-4140869d8e5a26db6f9d63c400051bd440c575bd.zip
perf(manga): progressively hydrate native chapter counts
Diffstat (limited to 'src/lib/Media')
-rw-r--r--src/lib/Media/Manga/chapters.ts87
1 files changed, 76 insertions, 11 deletions
diff --git a/src/lib/Media/Manga/chapters.ts b/src/lib/Media/Manga/chapters.ts
index ff861609..0f51af4a 100644
--- a/src/lib/Media/Manga/chapters.ts
+++ b/src/lib/Media/Manga/chapters.ts
@@ -27,6 +27,7 @@ interface NativeChapterCountsResponse {
const chapterMemoryCache = new Map<number, number | null>();
const MAX_PENDING_RETRIES = 2;
const DEFAULT_PENDING_RETRY_MS = 750;
+const NATIVE_PROGRESSIVE_CHUNK_SIZE = 10;
const browserChapterCacheDisabled = () =>
env.PUBLIC_DISABLE_MANGA_BROWSER_CACHE === "true";
@@ -77,6 +78,17 @@ const writeCachedChapterCount = async (
});
};
+const writeNativeChapterCount = async (
+ mangaId: number,
+ chapters: number | null,
+) => {
+ const existing = browserChapterCacheDisabled()
+ ? undefined
+ : await database.chapters.get(mangaId);
+
+ await writeCachedChapterCount(mangaId, chapters, existing?.volumes ?? null);
+};
+
const getActivityChapterCount = async (
identity: UserIdentity,
manga: Media,
@@ -233,10 +245,54 @@ const fetchNativeChapterCounts = async (manga: Media[]) => {
return { data, failedIds };
};
+export const hydrateNativeChapterCountsProgressively = async (
+ manga: Media[],
+ onChunkResolved?: () => void,
+) => {
+ const uniqueManga = manga
+ .filter((entry) => entry.format !== "NOVEL")
+ .filter(
+ (entry, index, array) =>
+ array.findIndex((candidate) => candidate.id === entry.id) === index,
+ );
+
+ for (
+ let index = 0;
+ index < uniqueManga.length;
+ index += NATIVE_PROGRESSIVE_CHUNK_SIZE
+ ) {
+ const chunk = uniqueManga.slice(
+ index,
+ index + NATIVE_PROGRESSIVE_CHUNK_SIZE,
+ );
+ const { data: nativeCounts, failedIds } =
+ await fetchNativeChapterCounts(chunk);
+ let changed = false;
+
+ for (const entry of chunk) {
+ if (failedIds.has(entry.id)) continue;
+
+ const nativeCount = nativeCounts[String(entry.id)]?.chapter;
+
+ if (nativeCount === null || nativeCount === undefined) continue;
+
+ await writeNativeChapterCount(entry.id, nativeCount);
+ changed = true;
+ }
+
+ if (changed) onChunkResolved?.();
+ }
+};
+
export const hydrateChapterCounts = async (
identity: UserIdentity,
manga: Media[],
disableGuessing: boolean,
+ options: {
+ deferNative?: boolean;
+ startDeferredNative?: boolean;
+ onNativeChunkResolved?: () => void;
+ } = {},
): Promise<{ rateLimited: boolean }> => {
const uniqueManga = manga.filter(
(entry, index, array) =>
@@ -270,23 +326,32 @@ export const hydrateChapterCounts = async (
}
if (nativeCountManga.length) {
- const { data: nativeCounts, failedIds } =
- await fetchNativeChapterCounts(nativeCountManga);
+ if (options.deferNative) {
+ unresolvedManga.push(...nativeCountManga);
+ if (options.startDeferredNative)
+ void hydrateNativeChapterCountsProgressively(
+ nativeCountManga,
+ options.onNativeChunkResolved,
+ );
+ } else {
+ const { data: nativeCounts, failedIds } =
+ await fetchNativeChapterCounts(nativeCountManga);
- for (const entry of nativeCountManga) {
- if (failedIds.has(entry.id)) continue;
+ for (const entry of nativeCountManga) {
+ if (failedIds.has(entry.id)) continue;
- const nativeCount = nativeCounts[String(entry.id)]?.chapter;
+ const nativeCount = nativeCounts[String(entry.id)]?.chapter;
- if (nativeCount === null) {
- unresolvedManga.push(entry);
+ if (nativeCount === null) {
+ unresolvedManga.push(entry);
- continue;
- }
+ continue;
+ }
- if (nativeCount === undefined) continue;
+ if (nativeCount === undefined) continue;
- await writeCachedChapterCount(entry.id, nativeCount);
+ await writeNativeChapterCount(entry.id, nativeCount);
+ }
}
}