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 ++++++++++++
src/routes/tools/+page.svelte | 4 +
7 files changed, 240 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')
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}
-
-
-
- {: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}
+
+
+
+ {: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.
+
diff --git a/src/routes/tools/+page.svelte b/src/routes/tools/+page.svelte
index 28593f6a..2b990202 100644
--- a/src/routes/tools/+page.svelte
+++ b/src/routes/tools/+page.svelte
@@ -7,6 +7,7 @@
import CharacterBirthdays from '$lib/Tools/CharacterBirthdays.svelte';
import { page } from '$app/stores';
import { goto } from '$app/navigation';
+ import SequelSpy from '$lib/Tools/SequelSpy.svelte';
export let data;
@@ -50,6 +51,7 @@
+
@@ -63,4 +65,6 @@
{:else if tool === 'todays_character_birthdays'}
+{:else if tool === 'sequel_spy'}
+
{/if}
--
cgit v1.2.3