From 35bc9a48c0fa8e4dfc573a08c6b340deaaecde73 Mon Sep 17 00:00:00 2001 From: ThaUnknown <6506529+ThaUnknown@users.noreply.github.com> Date: Fri, 18 Jul 2025 13:17:21 +0200 Subject: [PATCH] fix: improve the performance of episode scoping --- package.json | 2 +- src/lib/components/EpisodesList.svelte | 2 +- src/lib/modules/anilist/util.ts | 2 +- src/lib/modules/extensions/extensions.ts | 45 ++++++++++++++++-------- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 8ff7eb5..970e511 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ui", - "version": "6.4.54", + "version": "6.4.55", "license": "BUSL-1.1", "private": true, "packageManager": "pnpm@9.15.5", diff --git a/src/lib/components/EpisodesList.svelte b/src/lib/components/EpisodesList.svelte index 078011e..74e7117 100644 --- a/src/lib/components/EpisodesList.svelte +++ b/src/lib/components/EpisodesList.svelte @@ -31,7 +31,7 @@ } } - $: episodeList = media && makeEpisodeList(episodeCount, media, eps) + $: episodeList = media && makeEpisodeList(media, eps) const perPage = 16 diff --git a/src/lib/modules/anilist/util.ts b/src/lib/modules/anilist/util.ts index d8c3ce2..d5ec9c6 100644 --- a/src/lib/modules/anilist/util.ts +++ b/src/lib/modules/anilist/util.ts @@ -150,7 +150,6 @@ export function episodeByAirDate (alDate: Date | undefined, episodes: Episodes, // 1 is key for episod 1, not index // find closest episodes by air date, multiple episodes can have the same air date distance - // ineffcient but reliable const closestEpisodes: Episode[] = Object.values(episodes).reduce((prev, curr) => { if (!prev[0]) return [curr] const prevDate = Math.abs(+new Date(prev[0].airdate ?? 0) - +alDate) @@ -165,6 +164,7 @@ export function episodeByAirDate (alDate: Date | undefined, episodes: Episodes, if (!closestEpisodes.length) return episodes[Number(episode)] ?? episodes[episode] + // if multiple episodes have the same air date, return the one closest to the requested episode number return closestEpisodes.reduce((prev, curr) => { return Math.abs(Number(curr.episode) - episode) < Math.abs(Number(prev.episode) - episode) ? curr : prev }) diff --git a/src/lib/modules/extensions/extensions.ts b/src/lib/modules/extensions/extensions.ts index 4213578..59549ea 100644 --- a/src/lib/modules/extensions/extensions.ts +++ b/src/lib/modules/extensions/extensions.ts @@ -53,7 +53,8 @@ export interface SingleEpisode { } // TODO: https://anilist.co/anime/13055/ -export function makeEpisodeList (count: number, media: Media, episodesRes?: EpisodesResponse | null) { +export function makeEpisodeList (media: Media, episodesRes?: EpisodesResponse | null) { + const count = episodes(media) ?? episodesRes?.episodeCount ?? 0 const alSchedule: Record = {} for (const { a: airingAt, e: episode } of dedupeAiring(media)) { @@ -65,26 +66,42 @@ export function makeEpisodeList (count: number, media: Media, episodesRes?: Epis } const episodeList: SingleEpisode[] = [] - for (let i = 0; i < count; i++) { - const episode = i + 1 - + const filtered = { ...episodesRes?.episodes ?? {} } + const hasSpecial = !!episodesRes?.specialCount + const hasCountMatch = (episodes(media) ?? 0) === (episodesRes?.episodeCount ?? 0) + // this code... doesn't scale well into the thousands, it takes almost a second or two to run for one piece + for (let episode = 1; episode <= count; episode++) { const airingAt = alSchedule[episode] - const hasSpecial = !!episodesRes?.specialCount const hasEpisode = episodesRes?.episodes?.[Number(episode)] - const hasCountMatch = (episodes(media) ?? 0) === (episodesRes?.episodeCount ?? 0) + // If there are special episodes AND (no episode data exists OR episode count doesn't match), + // then we need to validate by matching episodes with air dates const needsValidation = !(!hasSpecial || (hasEpisode && hasCountMatch)) // handle special cases where anilist reports that 3 episodes aired at the same time because of pre-releases, simply don't allow the same episode to be re-used, but only walk forwards in dates - const filtered = Object.fromEntries(Object.entries(episodesRes?.episodes ?? {}).filter(([_, ep]) => !episodeList.some(e => { - if (ep.anidbEid != null && e.anidbEid === ep.anidbEid) return true - if (ep.airdate != null && new Date(ep.airdate) < new Date(e.airdate ?? Date.now())) return true - return false - }))) + // const filtered = Object.fromEntries(Object.entries(episodesRes?.episodes ?? {}).filter(([_, ep]) => !episodeList.some(e => { + // if (ep.anidbEid != null && e.anidbEid === ep.anidbEid) return true + // if (ep.airdate != null && new Date(ep.airdate) < new Date(e.airdate ?? Date.now())) return true + // return false + // }))) - const { image, summary, overview, rating, title, length, airdate, anidbEid } = (needsValidation ? episodeByAirDate(airingAt, filtered, episode) : episodesRes?.episodes?.[Number(episode)]) ?? {} + const resolvedEpisode = (needsValidation ? episodeByAirDate(airingAt, filtered, episode) : episodesRes?.episodes?.[Number(episode)]) + // we want to exclude episodes which were previously consumed + if (needsValidation && resolvedEpisode) { + for (const [key, value] of Object.entries(filtered)) { + if ( + (value.anidbEid != null && value.anidbEid === resolvedEpisode.anidbEid) || + (value.airdate != null && new Date(value.airdate) < new Date(resolvedEpisode.airdate ?? Date.now())) + ) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete filtered[key as keyof typeof filtered] + } + } + } + + const { image, summary, overview, rating, title, length, airdate, anidbEid } = resolvedEpisode ?? {} const res = { - episode, image, summary: summary ?? overview, rating, title, length, airdate, airingAt, filler: !!fillerEpisodes[media.id]?.includes(i + 1), anidbEid + episode, image, summary: summary ?? overview, rating, title, length, airdate, airingAt, filler: !!fillerEpisodes[media.id]?.includes(episode + 1), anidbEid } episodeList.push(res) } @@ -234,7 +251,7 @@ export const extensions = new class Extensions { } async ALtoAniDBEpisode ({ media, episode }: {media: Media, episode: number}, episodesRes: EpisodesResponse) { - return makeEpisodeList(Math.max(episodes(media) ?? 0, episodesRes.episodeCount ?? 0), media, episodesRes)[episode - 1] ?? undefined + return makeEpisodeList(media, episodesRes)[episode - 1] ?? undefined } dedupe }> (entries: T[]): T[] {