aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorFuwn <[email protected]>2023-12-17 04:36:30 -0800
committerFuwn <[email protected]>2023-12-17 04:36:30 -0800
commite3f24a4f0fd4848bdd225ed5788f69f814b10ee0 (patch)
treea208fcbcd6ba6e25de0d220404d9699f72b8002b /src/lib
parentfix(sequelspy): always use native release (diff)
downloaddue.moe-e3f24a4f0fd4848bdd225ed5788f69f814b10ee0.tar.xz
due.moe-e3f24a4f0fd4848bdd225ed5788f69f814b10ee0.zip
refactor(anime): move airingTime
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/List/Anime/CleanAnimeList.svelte3
-rw-r--r--src/lib/Media/Anime/airing.ts167
-rw-r--r--src/lib/Media/anime.ts164
-rw-r--r--src/lib/Tools/SequelSpy.svelte2
4 files changed, 170 insertions, 166 deletions
diff --git a/src/lib/List/Anime/CleanAnimeList.svelte b/src/lib/List/Anime/CleanAnimeList.svelte
index e43b578c..efedf61d 100644
--- a/src/lib/List/Anime/CleanAnimeList.svelte
+++ b/src/lib/List/Anime/CleanAnimeList.svelte
@@ -3,7 +3,8 @@
import settings from '../../../stores/settings';
import type { Media } from '$lib/AniList/media';
- import { airingTime, cleanCache, totalEpisodes, updateMedia } from '$lib/Media/anime';
+ import { cleanCache, totalEpisodes, updateMedia } from '$lib/Media/anime';
+ import { airingTime } from '$lib/Media/Anime/airing';
import type { AniListAuthorisation, UserIdentity } from '$lib/AniList/identity';
import ListTitle from '../ListTitle.svelte';
import MediaTitle from '../MediaTitleDisplay.svelte';
diff --git a/src/lib/Media/Anime/airing.ts b/src/lib/Media/Anime/airing.ts
new file mode 100644
index 00000000..6eaa2f39
--- /dev/null
+++ b/src/lib/Media/Anime/airing.ts
@@ -0,0 +1,167 @@
+import { get } from 'svelte/store';
+import type { Media } from '../../AniList/media';
+import settings from '../../../stores/settings';
+import type { MediaPrequel } from '$lib/AniList/prequels';
+import type { SubsPlease } from '$lib/subsPlease';
+import levenshtein from 'fast-levenshtein';
+import { totalEpisodes } from '../anime';
+
+interface Time {
+ title: string;
+ time: string;
+ day: string;
+}
+
+const secondsUntil = (targetTime: string, targetDay: string) => {
+ const now = new Date();
+ const [targetHour, targetMinute] = targetTime.split(':').map(Number);
+ let dayDifference =
+ ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'].indexOf(
+ targetDay
+ ) - now.getDay();
+
+ if (dayDifference < 0) dayDifference += 7;
+
+ const targetDate = new Date(now);
+
+ targetDate.setDate(now.getDate() + dayDifference);
+ targetDate.setHours(targetHour, targetMinute, 0, 0);
+
+ const secondsDifference = (Number(targetDate) - Number(now)) / 1000;
+
+ return secondsDifference > 0 ? secondsDifference : secondsDifference + 7 * 24 * 60 * 60;
+};
+
+const normalizeTitle = (title: string) =>
+ (title || '')
+ .toLowerCase()
+ .replace(/season \d+|s\d+/g, '')
+ .trim();
+
+const findClosestMatch = (times: Time[], titles: string[]) => {
+ let closestMatch = null;
+ let smallestDistance = Infinity;
+
+ titles.forEach((animeTitle) => {
+ const normalizedAnimeTitle = normalizeTitle(animeTitle);
+
+ times.forEach((item) => {
+ const normalizedItemTitle = normalizeTitle(item.title);
+ const distance = levenshtein.get(normalizedAnimeTitle, normalizedItemTitle);
+
+ if (distance < smallestDistance) {
+ smallestDistance = distance;
+ closestMatch = item;
+ }
+ });
+ });
+
+ return closestMatch;
+};
+
+export const airingTime = (anime: Media, subsPlease: SubsPlease | null, upcoming = false) => {
+ const airingAt = anime.nextAiringEpisode?.airingAt;
+ let untilAiring;
+ let time = new Date(airingAt ? airingAt * 1000 : 0).toLocaleTimeString([], {
+ hour12: !settings.get().display24HourTime,
+ hour: 'numeric',
+ minute: '2-digit'
+ });
+
+ if (get(settings).displayNativeCountdown || !subsPlease) {
+ untilAiring = airingAt ? Math.round((airingAt - Date.now() / 1000) * 100) / 100 : undefined;
+ } else {
+ const times: Time[] = [];
+
+ for (const [key, value] of Object.entries(subsPlease.schedule)) {
+ const flattenedValue = Array.isArray(value) ? value.flat() : [];
+
+ for (const time of flattenedValue) {
+ times.push({
+ title: time.title,
+ time: time.time,
+ day: key
+ });
+ }
+ }
+
+ const foundTime: Time | null = findClosestMatch(times, [
+ anime.title.romaji,
+ anime.title.english,
+ ...anime.synonyms
+ ]);
+
+ if (foundTime) {
+ untilAiring = secondsUntil((foundTime as Time).time, (foundTime as Time).day);
+ time = new Date(Date.now() + untilAiring * 1000).toLocaleTimeString([], {
+ hour12: !settings.get().display24HourTime,
+ hour: 'numeric',
+ minute: '2-digit'
+ });
+ } else
+ untilAiring = airingAt ? Math.round((airingAt - Date.now() / 1000) * 100) / 100 : undefined;
+ }
+
+ let timeFrame;
+ let hours = null;
+
+ if (
+ (anime as unknown as MediaPrequel).startDate &&
+ new Date(
+ anime.startDate.year,
+ (anime as unknown as MediaPrequel).startDate.month,
+ (anime as unknown as MediaPrequel).startDate.day
+ ) < new Date()
+ )
+ return `<span style="opacity: 50%">on ${new Date(
+ anime.startDate.year,
+ (anime as unknown as MediaPrequel).startDate.month,
+ (anime as unknown as MediaPrequel).startDate.day
+ ).toLocaleDateString()}</span>`;
+
+ if (untilAiring !== undefined) {
+ let minutes = untilAiring / 60;
+ let few = true;
+
+ if (minutes > 60) {
+ hours = minutes / 60;
+
+ if (hours >= 24) {
+ // let weeks = Math.floor(Math.floor(hours / 24) / 7);
+
+ few = false;
+
+ // if (weeks >= 1) {
+ // weeks = Math.round(weeks);
+
+ // timeFrame = `${weeks} week${weeks === 1 ? '' : 's'}`;
+ // } else {
+ const days = Math.round(Math.floor(hours / 24));
+
+ timeFrame = `${days.toFixed(0)} day${days === 1 ? '' : 's'}`;
+ // }
+ } else timeFrame = `${hours.toFixed(1)} hour${hours === 1 ? '' : 's'}`;
+ } else {
+ minutes = Math.round(minutes);
+
+ timeFrame = `${minutes} minute${minutes === 1 ? '' : 's'}`;
+ }
+
+ const opacity = Math.max(50, 100 - (untilAiring / 60 / 60 / 24 / 7) * 50);
+
+ if (upcoming)
+ return `<span title="${
+ hours ? `${hours.toFixed(3)} hours` : ''
+ }" style="opacity: ${opacity}%;">${anime.nextAiringEpisode?.episode}${totalEpisodes(
+ anime
+ )} in ${timeFrame} <span style="opacity: 50%">${few ? `(${time})` : ''}</span></span>`;
+ else
+ return `<span title="${
+ hours ? `${hours.toFixed(3)} hours` : ''
+ }" style="opacity: ${opacity}%;">${anime.nextAiringEpisode?.episode} in ${
+ few ? '<b>' : ''
+ }${timeFrame}${few ? '</b>' : ''} ${few ? `(${time})` : ''}</span>`;
+ }
+
+ return '';
+};
diff --git a/src/lib/Media/anime.ts b/src/lib/Media/anime.ts
index 383c8740..f0486c7d 100644
--- a/src/lib/Media/anime.ts
+++ b/src/lib/Media/anime.ts
@@ -3,16 +3,6 @@ import anime from '../../stores/anime';
import { mediaListCollection, type Media, Type } from '../AniList/media';
import lastPruneTimes from '../../stores/lastPruneTimes';
import type { AniListAuthorisation, UserIdentity } from '../AniList/identity';
-import settings from '../../stores/settings';
-import type { MediaPrequel } from '$lib/AniList/prequels';
-import type { SubsPlease } from '$lib/subsPlease';
-import levenshtein from 'fast-levenshtein';
-
-interface Time {
- title: string;
- time: string;
- day: string;
-}
export const cleanCache = (user: AniListAuthorisation, identity: UserIdentity) =>
mediaListCollection(user, identity, Type.Anime, get(anime), get(lastPruneTimes).anime, true);
@@ -23,157 +13,3 @@ export const updateMedia = (id: number, progress: number | undefined, callback:
export const totalEpisodes = (anime: Media) =>
anime.episodes === null ? '' : `<span style="opacity: 50%">/${anime.episodes}</span>`;
-
-const secondsUntil = (targetTime: string, targetDay: string) => {
- const now = new Date();
- const [targetHour, targetMinute] = targetTime.split(':').map(Number);
- let dayDifference =
- ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'].indexOf(
- targetDay
- ) - now.getDay();
-
- if (dayDifference < 0) dayDifference += 7;
-
- const targetDate = new Date(now);
-
- targetDate.setDate(now.getDate() + dayDifference);
- targetDate.setHours(targetHour, targetMinute, 0, 0);
-
- const secondsDifference = (Number(targetDate) - Number(now)) / 1000;
-
- return secondsDifference > 0 ? secondsDifference : secondsDifference + 7 * 24 * 60 * 60;
-};
-
-const normalizeTitle = (title: string) =>
- (title || '')
- .toLowerCase()
- .replace(/season \d+|s\d+/g, '')
- .trim();
-
-const findClosestMatch = (times: Time[], titles: string[]) => {
- let closestMatch = null;
- let smallestDistance = Infinity;
-
- titles.forEach((animeTitle) => {
- const normalizedAnimeTitle = normalizeTitle(animeTitle);
-
- times.forEach((item) => {
- const normalizedItemTitle = normalizeTitle(item.title);
- const distance = levenshtein.get(normalizedAnimeTitle, normalizedItemTitle);
-
- if (distance < smallestDistance) {
- smallestDistance = distance;
- closestMatch = item;
- }
- });
- });
-
- return closestMatch;
-};
-
-export const airingTime = (anime: Media, subsPlease: SubsPlease | null, upcoming = false) => {
- const airingAt = anime.nextAiringEpisode?.airingAt;
- let untilAiring;
- let time = new Date(airingAt ? airingAt * 1000 : 0).toLocaleTimeString([], {
- hour12: !settings.get().display24HourTime,
- hour: 'numeric',
- minute: '2-digit'
- });
-
- if (get(settings).displayNativeCountdown || !subsPlease) {
- untilAiring = airingAt ? Math.round((airingAt - Date.now() / 1000) * 100) / 100 : undefined;
- } else {
- const times: Time[] = [];
-
- for (const [key, value] of Object.entries(subsPlease.schedule)) {
- const flattenedValue = Array.isArray(value) ? value.flat() : [];
-
- for (const time of flattenedValue) {
- times.push({
- title: time.title,
- time: time.time,
- day: key
- });
- }
- }
-
- const foundTime: Time | null = findClosestMatch(times, [
- anime.title.romaji,
- anime.title.english,
- ...anime.synonyms
- ]);
-
- if (foundTime) {
- untilAiring = secondsUntil((foundTime as Time).time, (foundTime as Time).day);
- time = new Date(Date.now() + untilAiring * 1000).toLocaleTimeString([], {
- hour12: !settings.get().display24HourTime,
- hour: 'numeric',
- minute: '2-digit'
- });
- } else
- untilAiring = airingAt ? Math.round((airingAt - Date.now() / 1000) * 100) / 100 : undefined;
- }
-
- let timeFrame;
- let hours = null;
-
- if (
- (anime as unknown as MediaPrequel).startDate &&
- new Date(
- anime.startDate.year,
- (anime as unknown as MediaPrequel).startDate.month,
- (anime as unknown as MediaPrequel).startDate.day
- ) < new Date()
- )
- return `<span style="opacity: 50%">on ${new Date(
- anime.startDate.year,
- (anime as unknown as MediaPrequel).startDate.month,
- (anime as unknown as MediaPrequel).startDate.day
- ).toLocaleDateString()}</span>`;
-
- if (untilAiring !== undefined) {
- let minutes = untilAiring / 60;
- let few = true;
-
- if (minutes > 60) {
- hours = minutes / 60;
-
- if (hours >= 24) {
- // let weeks = Math.floor(Math.floor(hours / 24) / 7);
-
- few = false;
-
- // if (weeks >= 1) {
- // weeks = Math.round(weeks);
-
- // timeFrame = `${weeks} week${weeks === 1 ? '' : 's'}`;
- // } else {
- const days = Math.round(Math.floor(hours / 24));
-
- timeFrame = `${days.toFixed(0)} day${days === 1 ? '' : 's'}`;
- // }
- } else timeFrame = `${hours.toFixed(1)} hour${hours === 1 ? '' : 's'}`;
- } else {
- minutes = Math.round(minutes);
-
- timeFrame = `${minutes} minute${minutes === 1 ? '' : 's'}`;
- }
-
- const opacity = Math.max(50, 100 - (untilAiring / 60 / 60 / 24 / 7) * 50);
-
- if (upcoming)
- return `<span title="${
- hours ? `${hours.toFixed(3)} hours` : ''
- }" style="opacity: ${opacity}%;">${anime.nextAiringEpisode?.episode}${totalEpisodes(
- anime
- )} in ${timeFrame} <span style="opacity: 50%">${few ? `(${time})` : ''}</span></span>`;
- else
- return `<span title="${
- hours ? `${hours.toFixed(3)} hours` : ''
- }" style="opacity: ${opacity}%;">${anime.nextAiringEpisode?.episode} in ${
- few ? '<b>' : ''
- }${timeFrame}${few ? '</b>' : ''} ${few ? `(${time})` : ''}</span>`;
- }
-
- return '';
-};
diff --git a/src/lib/Tools/SequelSpy.svelte b/src/lib/Tools/SequelSpy.svelte
index 6a07a8a7..2ff7832b 100644
--- a/src/lib/Tools/SequelSpy.svelte
+++ b/src/lib/Tools/SequelSpy.svelte
@@ -4,7 +4,7 @@
import MediaTitle from '$lib/List/MediaTitleDisplay.svelte';
import { onMount } from 'svelte';
import { clearAllParameters, parseOrDefault } from './tool';
- import { airingTime } from '$lib/Media/anime';
+ import { airingTime } from '$lib/Media/Anime/airing';
import type { Media } from '$lib/AniList/media';
import { page } from '$app/stores';
import { browser } from '$app/environment';