aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorFuwn <[email protected]>2023-12-22 06:53:35 -0800
committerFuwn <[email protected]>2023-12-22 06:53:35 -0800
commit8a8cf810e4a03453b8254eaad56c27529dc107ef (patch)
treeef2f83e3bb47f5c0e925d91f33ed592a99611da4 /src/lib
parentfix(activity): only show current year (diff)
downloaddue.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.ts18
-rw-r--r--src/lib/AniList/wrapped.ts52
-rw-r--r--src/lib/Tools/Wrapped.svelte74
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}