From 09f12daf77e1c6e33e12d3da2859884064664789 Mon Sep 17 00:00:00 2001 From: Fuwn Date: Wed, 29 Nov 2023 16:22:31 -0800 Subject: feat(tools): sequel spy --- src/lib/AniList/prequels.ts | 150 +++++++++++++++++++++++++++++++ src/lib/List/Anime/CleanAnimeList.svelte | 2 +- src/lib/List/Manga/CleanMangaList.svelte | 2 +- src/lib/List/MediaTitle.svelte | 28 ------ src/lib/List/MediaTitleDisplay.svelte | 28 ++++++ src/lib/Tools/SequelSpy.svelte | 56 ++++++++++++ 6 files changed, 236 insertions(+), 30 deletions(-) create mode 100644 src/lib/AniList/prequels.ts delete mode 100644 src/lib/List/MediaTitle.svelte create mode 100644 src/lib/List/MediaTitleDisplay.svelte create mode 100644 src/lib/Tools/SequelSpy.svelte (limited to 'src/lib') diff --git a/src/lib/AniList/prequels.ts b/src/lib/AniList/prequels.ts new file mode 100644 index 00000000..82e470f4 --- /dev/null +++ b/src/lib/AniList/prequels.ts @@ -0,0 +1,150 @@ +import type { AniListAuthorisation } from './identity'; + +export interface MediaPrequel { + id: number; + title: { english: string; romaji: string }; + episodes: number; + seen: number; +} + +interface PrequelRelations { + edges: { + relationType: string; + node: { + title: { + english: string; + romaji: string; + }; + episodes: number; + mediaListEntry: { + status: string; + progress: number; + }; + }; + }[]; +} + +interface PrequelsPage { + data: { + Page: { + media: { + title: { + english: string; + romaji: string; + }; + id: number; + relations: PrequelRelations; + }[]; + pageInfo: { + hasNextPage: boolean; + }; + }; + }; +} + +const prequelsPage = async ( + page: number, + anilistAuthorisation: AniListAuthorisation, + year: number, + season: 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL' +): Promise => + await ( + await fetch('https://graphql.anilist.co', { + method: 'POST', + headers: { + Authorization: `${anilistAuthorisation.tokenType} ${anilistAuthorisation.accessToken}`, + 'Content-Type': 'application/json', + Accept: 'application/json' + }, + body: JSON.stringify({ + query: `{ + Page(page: ${page}) { + pageInfo { + hasNextPage + } + media(season: ${season}, seasonYear: ${year}) { + title { + english + romaji + } + id + relations { + edges { + relationType + node { + title { + english + romaji + } + episodes + mediaListEntry { + status + progress + } + } + } + } + } + } +}` + }) + }) + ).json(); + +export const prequels = async ( + anilistAuthorisation: AniListAuthorisation, + year: number, + season: 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL' +): Promise => { + const candidates = []; + let page = 1; + let currentPage = await prequelsPage(page, anilistAuthorisation, year, season); + + for (const candidate of currentPage.data.Page.media) { + candidates.push(candidate); + } + + while (currentPage['data']['Page']['pageInfo']['hasNextPage']) { + for (const candidate of currentPage.data.Page.media) { + candidates.push(candidate); + } + + page += 1; + currentPage = await prequelsPage(page, anilistAuthorisation, year, season); + } + + const media: MediaPrequel[] = []; + + for (const candidate of candidates) { + let episodes = 0; + let seen = 0; + + for (const relation of candidate.relations.edges) { + if (relation.relationType === 'PREQUEL') { + if ( + relation.node.mediaListEntry === null || + relation.node.mediaListEntry.status !== 'COMPLETED' + ) { + episodes += relation.node.episodes; + + if (relation.node.mediaListEntry !== null) { + seen += relation.node.mediaListEntry.progress || 0; + } + } + } + } + + if (media.some((m) => m.id === candidate.id)) continue; + + if (episodes !== 0) { + media.push({ + id: candidate.id, + title: candidate.title, + episodes, + seen + }); + } + } + + return media; +}; diff --git a/src/lib/List/Anime/CleanAnimeList.svelte b/src/lib/List/Anime/CleanAnimeList.svelte index 2da182d5..e7388066 100644 --- a/src/lib/List/Anime/CleanAnimeList.svelte +++ b/src/lib/List/Anime/CleanAnimeList.svelte @@ -6,7 +6,7 @@ import { airingTime, cleanCache, totalEpisodes, updateMedia } from '$lib/Media/anime'; import type { AniListAuthorisation, UserIdentity } from '$lib/AniList/identity'; import ListTitle from '../ListTitle.svelte'; - import MediaTitle from '../MediaTitle.svelte'; + import MediaTitle from '../MediaTitleDisplay.svelte'; export let media: Media[]; export let title: string; diff --git a/src/lib/List/Manga/CleanMangaList.svelte b/src/lib/List/Manga/CleanMangaList.svelte index 009fb868..c08c9ef6 100644 --- a/src/lib/List/Manga/CleanMangaList.svelte +++ b/src/lib/List/Manga/CleanMangaList.svelte @@ -3,7 +3,7 @@ import { volumeCount } from '$lib/Media/manga'; import settings from '../../../stores/settings'; import ListTitle from '../ListTitle.svelte'; - import MediaTitle from '../MediaTitle.svelte'; + import MediaTitle from '../MediaTitleDisplay.svelte'; export let media: Media[]; export let cleanCache: () => void; diff --git a/src/lib/List/MediaTitle.svelte b/src/lib/List/MediaTitle.svelte deleted file mode 100644 index b98f4125..00000000 --- a/src/lib/List/MediaTitle.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - -{#if $settings.displayNativeTitles} - {#if $settings.displayFurigana} - - - {media.title.native} - - {wanakana.toKana(media.title.romaji)} - - - - {:else} - - {media.title.native} - - {/if} -{:else} - - {media.title.english || media.title.romaji || media.title.native} - -{/if} diff --git a/src/lib/List/MediaTitleDisplay.svelte b/src/lib/List/MediaTitleDisplay.svelte new file mode 100644 index 00000000..b98f4125 --- /dev/null +++ b/src/lib/List/MediaTitleDisplay.svelte @@ -0,0 +1,28 @@ + + +{#if $settings.displayNativeTitles} + {#if $settings.displayFurigana} + + + {media.title.native} + + {wanakana.toKana(media.title.romaji)} + + + + {:else} + + {media.title.native} + + {/if} +{:else} + + {media.title.english || media.title.romaji || media.title.native} + +{/if} diff --git a/src/lib/Tools/SequelSpy.svelte b/src/lib/Tools/SequelSpy.svelte new file mode 100644 index 00000000..4e92f199 --- /dev/null +++ b/src/lib/Tools/SequelSpy.svelte @@ -0,0 +1,56 @@ + + +

+ + +

+ +{#await currentPrequels} +

Loading ...

+{:then currentPrequels} +
    + {#each currentPrequels as prequel} +
  • + + + + | + {prequel.seen}/{prequel.episodes} +
  • + {/each} +
+{/await} + +

+ The count ratio is the number of episodes you've seen of any direct prequels, and the total number + of episodes of all direct prequels. +

-- cgit v1.2.3