diff options
| author | Fuwn <[email protected]> | 2023-12-22 06:53:35 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2023-12-22 06:53:35 -0800 |
| commit | 8a8cf810e4a03453b8254eaad56c27529dc107ef (patch) | |
| tree | ef2f83e3bb47f5c0e925d91f33ed592a99611da4 /src/lib | |
| parent | fix(activity): only show current year (diff) | |
| download | due.moe-8a8cf810e4a03453b8254eaad56c27529dc107ef.tar.xz due.moe-8a8cf810e4a03453b8254eaad56c27529dc107ef.zip | |
feat(wrapped): genres and tags
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/AniList/media.ts | 18 | ||||
| -rw-r--r-- | src/lib/AniList/wrapped.ts | 52 | ||||
| -rw-r--r-- | src/lib/Tools/Wrapped.svelte | 74 |
3 files changed, 127 insertions, 17 deletions
diff --git a/src/lib/AniList/media.ts b/src/lib/AniList/media.ts index 039b9db6..6fc83296 100644 --- a/src/lib/AniList/media.ts +++ b/src/lib/AniList/media.ts @@ -342,3 +342,21 @@ export const recentMediaActivities = async ( return null; }; + +export const mediaCover = async (id: number) => + ( + await ( + await fetch('https://graphql.anilist.co', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json' + }, + body: JSON.stringify({ + query: `{ + Media(id: ${id}) { coverImage { extraLarge } } + }` + }) + }) + ).json() + )['data']['Media']['coverImage']['extraLarge']; diff --git a/src/lib/AniList/wrapped.ts b/src/lib/AniList/wrapped.ts index d3c67fa2..35ce7554 100644 --- a/src/lib/AniList/wrapped.ts +++ b/src/lib/AniList/wrapped.ts @@ -1,21 +1,33 @@ import type { AniListAuthorisation, UserIdentity } from './identity'; +export interface WrappedMediaFormat { + startYears: { + startYear: number; + minutesWatched: number; + count: number; + }; + genres: { + meanScore: number; + minutesWatched: number; + chaptersRead: number; + genre: string; + mediaIds: number[]; + }[]; + tags: { + meanScore: number; + minutesWatched: number; + chaptersRead: number; + tag: { + name: string; + }; + mediaIds: number[]; + }[]; +} + export interface Wrapped { statistics: { - anime: { - startYears: { - startYear: number; - minutesWatched: number; - count: number; - }[]; - }; - manga: { - startYears: { - startYear: number; - chaptersRead: number; - count: number; - }[]; - }; + anime: WrappedMediaFormat; + manga: WrappedMediaFormat; }; activities: { statusCount: number; @@ -104,8 +116,16 @@ export const wrapped = async ( User(name: "${identity.name}") { avatar { large } statistics { - anime { startYears { startYear minutesWatched count } } - manga { startYears { startYear chaptersRead count } } + anime { + startYears { startYear minutesWatched count } + genres(sort: [ MEAN_SCORE_DESC ]) { meanScore minutesWatched chaptersRead genre mediaIds } + tags(sort: [ MEAN_SCORE_DESC ]) { meanScore minutesWatched chaptersRead tag { name } mediaIds } + } + manga { + startYears { startYear chaptersRead count } + genres(sort: [ MEAN_SCORE_DESC ]) { meanScore minutesWatched chaptersRead genre mediaIds } + tags(sort: [ MEAN_SCORE_DESC ]) { meanScore minutesWatched chaptersRead tag { name } mediaIds } + } } } }` diff --git a/src/lib/Tools/Wrapped.svelte b/src/lib/Tools/Wrapped.svelte index 57ec09b7..0d8f0834 100644 --- a/src/lib/Tools/Wrapped.svelte +++ b/src/lib/Tools/Wrapped.svelte @@ -10,7 +10,7 @@ fullActivityHistory, activityHistory as getActivityHistory } from '$lib/AniList/activity.js'; - import { Type, mediaListCollection, type Media } from '$lib/AniList/media.js'; + import { Type, mediaListCollection, type Media, mediaCover } from '$lib/AniList/media.js'; import anime from '../../stores/anime.js'; import lastPruneTimes from '../../stores/lastPruneTimes.js'; import manga from '../../stores/manga.js'; @@ -50,6 +50,7 @@ let excludedKeywordsInput = ''; let excludedKeywords: string[] = []; let useFullActivityHistory = false; + let topGenresTags = true; $: { if (browser && mounted) { @@ -344,6 +345,17 @@ const pruneFullYear = async () => { await database.activities.bulkDelete((await database.activities.toArray()).map((m) => m.page)); }; + + const mergeArraySort = (a: any, b: any) => [...a, ...b].sort((a, b) => b.meanScore - a.meanScore); + + const randomCoverFromTop10 = ( + statistics: { anime: any; manga: any }, + mode: 'tags' | 'genres' + ) => { + const top = mergeArraySort(statistics.anime[mode], statistics.manga[mode]); + + return mediaCover(top[Math.floor(Math.random() * top.length)].mediaIds[0]); + }; </script> {#if currentUserIdentity.id === -2} @@ -470,6 +482,64 @@ </div> </div> </div> + {#if topGenresTags} + <div class="categories-grid" style="padding-top: 0;"> + <div class="category-grid pure-category category"> + <div class="grid-item image-grid"> + {#await randomCoverFromTop10(wrapped.statistics, 'genres') then cover} + <img + src={proxy(cover)} + alt="Highest Rated Genre Cover" + class="cover-image" + on:load={updateWidth} + /> + {/await} + <div> + <b>Highest Rated Genres</b> + <ol> + {#each mergeArraySort(wrapped.statistics.anime.genres, wrapped.statistics.manga.genres).slice(0, highestRatedCount) as genre} + <li> + <a + href={`https://anilist.co/search/anime?genres=${genre.genre}`} + target="_blank" + > + {genre.genre}: {genre.meanScore}% + </a> + </li> + {/each} + </ol> + </div> + </div> + </div> + <div class="category-grid pure-category category"> + <div class="grid-item image-grid"> + {#await randomCoverFromTop10(wrapped.statistics, 'tags') then cover} + <img + src={proxy(cover)} + alt="Highest Rated Tag Cover" + class="cover-image" + on:load={updateWidth} + /> + {/await} + <div> + <b>Highest Rated Tags</b> + <ol> + {#each mergeArraySort(wrapped.statistics.anime.tags, wrapped.statistics.manga.tags).slice(0, highestRatedCount) as tag} + <li> + <a + href={`https://anilist.co/search/anime?genres=${tag.tag.name}`} + target="_blank" + > + {tag.tag.name}: {tag.meanScore}% + </a> + </li> + {/each} + </ol> + </div> + </div> + </div> + </div> + {/if} {#if !disableActivityHistory} <div class="categories-grid bottom-category" style="padding-top: 0;"> <div class="category-grid pure-category"> @@ -501,6 +571,8 @@ <input type="checkbox" bind:checked={transparency} /> Enable background transparency<br /> <input type="checkbox" bind:checked={lightMode} /> Enable light mode<br /> + <input type="checkbox" bind:checked={topGenresTags} /> + Enable top genres and tags<br /> <input type="checkbox" bind:checked={disableActivityHistory} /> Disable activity history<br /> {#if !disableActivityHistory} |