diff options
| author | Fuwn <[email protected]> | 2026-03-03 22:54:56 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-03-03 22:54:56 -0800 |
| commit | 07a00c3ea35b4df7eb23275704dd26f842db76be (patch) | |
| tree | 097ee52433779830aa4ddd6666e85ed42878cfc9 /src/lib/Media | |
| parent | refactor(effect): migrate svelte json hotspots to typed decoders (diff) | |
| download | due.moe-07a00c3ea35b4df7eb23275704dd26f842db76be.tar.xz due.moe-07a00c3ea35b4df7eb23275704dd26f842db76be.zip | |
fix(anime): align due cover rendering with due classification
Diffstat (limited to 'src/lib/Media')
| -rw-r--r-- | src/lib/Media/Anime/Airing/Subtitled/match.ts | 13 | ||||
| -rw-r--r-- | src/lib/Media/Anime/Airing/classify.test.ts | 94 |
2 files changed, 106 insertions, 1 deletions
diff --git a/src/lib/Media/Anime/Airing/Subtitled/match.ts b/src/lib/Media/Anime/Airing/Subtitled/match.ts index 0c91f3ac..40dda4b8 100644 --- a/src/lib/Media/Anime/Airing/Subtitled/match.ts +++ b/src/lib/Media/Anime/Airing/Subtitled/match.ts @@ -530,6 +530,9 @@ const buildInjectAiringTimeCacheKey = ( [ anime.id, anime.status, + anime.mediaListEntry?.status || "", + anime.mediaListEntry?.progress || 0, + anime.mediaListEntry?.updatedAt || 0, anime.nextAiringEpisode?.episode || 0, anime.nextAiringEpisode?.airingAt || 0, displayNativeCountdown ? 1 : 0, @@ -540,6 +543,14 @@ const buildInjectAiringTimeCacheKey = ( const cloneInjectedMedia = (media: Media): Media => ({ ...media, + mediaListEntry: media.mediaListEntry + ? { + ...media.mediaListEntry, + startedAt: { ...media.mediaListEntry.startedAt }, + completedAt: { ...media.mediaListEntry.completedAt }, + customLists: { ...media.mediaListEntry.customLists }, + } + : undefined, nextAiringEpisode: media.nextAiringEpisode ? { ...media.nextAiringEpisode } : undefined, @@ -634,3 +645,5 @@ export const injectAiringTime = ( return cloneInjectedMedia(cachedValue); }; + +export const clearInjectAiringTimeCache = () => injectAiringTimeCache.clear(); diff --git a/src/lib/Media/Anime/Airing/classify.test.ts b/src/lib/Media/Anime/Airing/classify.test.ts index fb7f2c8f..b05be83b 100644 --- a/src/lib/Media/Anime/Airing/classify.test.ts +++ b/src/lib/Media/Anime/Airing/classify.test.ts @@ -6,7 +6,10 @@ import { hasDueEpisodes, getAnimeEpisodeState, } from "$lib/Media/Anime/Airing/classify"; -import { injectAiringTime } from "$lib/Media/Anime/Airing/Subtitled/match"; +import { + clearInjectAiringTimeCache, + injectAiringTime, +} from "$lib/Media/Anime/Airing/Subtitled/match"; import type { SubsPlease } from "$lib/Media/Anime/Airing/Subtitled/subsPlease"; const toScheduleTime = (epochSeconds: number) => { @@ -196,4 +199,93 @@ describe("injectAiringTime cache safety", () => { expect(typeof second.nextAiringEpisode?.airingAt).toBe("number"); expect(typeof second.nextAiringEpisode?.nativeAiringAt).toBe("number"); }); + + it("does not reuse stale progress across cache hits for media 194028", () => { + clearInjectAiringTimeCache(); + + const media = baseMedia(194028); + const subtitledAiringAt = Math.floor(Date.now() / 1000) + 24 * 60 * 60; + const nativeAiringAt = subtitledAiringAt + 12 * 60 * 60; + const nativeAiringDate = new Date(nativeAiringAt * 1000); + const airingDay = nativeAiringDate.toLocaleString("en-US", { + weekday: "long", + }); + const subsPlease = { + tz: "America/Los_Angeles", + schedule: { + [airingDay]: [ + { + title: media.title.romaji, + page: "", + image_url: "", + time: toScheduleTime(subtitledAiringAt), + }, + ], + }, + } as unknown as SubsPlease; + + media.nextAiringEpisode = { + episode: 10, + airingAt: nativeAiringAt, + }; + + settings.setKey("displayNativeCountdown", false); + + const caughtUp = { + ...media, + mediaListEntry: { + ...(media.mediaListEntry || {}), + progress: 9, + }, + } as Media; + const behind = { + ...media, + mediaListEntry: { + ...(media.mediaListEntry || {}), + progress: 8, + }, + } as Media; + const cachedCaughtUp = injectAiringTime(caughtUp, subsPlease); + const updatedBehind = injectAiringTime(behind, subsPlease); + + expect(hasDueEpisodes(cachedCaughtUp)).toBe(false); + expect(hasDueEpisodes(updatedBehind)).toBe(true); + }); + + it("does not let caller mutate cached mediaListEntry progress", () => { + clearInjectAiringTimeCache(); + + const media = baseMedia(194028); + const subtitledAiringAt = Math.floor(Date.now() / 1000) + 24 * 60 * 60; + const nativeAiringAt = subtitledAiringAt + 12 * 60 * 60; + const nativeAiringDate = new Date(nativeAiringAt * 1000); + const airingDay = nativeAiringDate.toLocaleString("en-US", { + weekday: "long", + }); + const subsPlease = { + tz: "America/Los_Angeles", + schedule: { + [airingDay]: [ + { + title: media.title.romaji, + page: "", + image_url: "", + time: toScheduleTime(subtitledAiringAt), + }, + ], + }, + } as unknown as SubsPlease; + + settings.setKey("displayNativeCountdown", false); + + const originalProgress = media.mediaListEntry?.progress || 0; + const first = injectAiringTime(media, subsPlease); + + if (first.mediaListEntry) first.mediaListEntry.progress = 999; + + const second = injectAiringTime(media, subsPlease); + + expect(media.mediaListEntry?.progress).toBe(originalProgress); + expect(second.mediaListEntry?.progress).toBe(originalProgress); + }); }); |