aboutsummaryrefslogtreecommitdiff
path: root/src/lib/Media
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-10-09 00:41:20 -0700
committerFuwn <[email protected]>2024-10-09 00:41:43 -0700
commit998b63a35256ac985a5a2714dd1ca451af4dfd8a (patch)
tree50796121a9d5ab0330fdc5d7e098bda2860d9726 /src/lib/Media
parentfeat(graphql): add badgeCount field (diff)
downloaddue.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.tar.xz
due.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.zip
chore(prettier): use spaces instead of tabs
Diffstat (limited to 'src/lib/Media')
-rw-r--r--src/lib/Media/Anime/Airing/AiringTime.svelte266
-rw-r--r--src/lib/Media/Anime/Airing/Subtitled/match.ts344
-rw-r--r--src/lib/Media/Anime/Airing/Subtitled/subsPlease.ts16
-rw-r--r--src/lib/Media/Anime/Airing/time.ts222
-rw-r--r--src/lib/Media/Anime/cache.ts40
-rw-r--r--src/lib/Media/Anime/episodes.ts2
-rw-r--r--src/lib/Media/Anime/season.ts18
-rw-r--r--src/lib/Media/Cover/HoverCover.svelte88
-rw-r--r--src/lib/Media/Cover/hoverCover.ts76
-rw-r--r--src/lib/Media/Manga/cache.ts8
-rw-r--r--src/lib/Media/Manga/chapters.ts386
-rw-r--r--src/lib/Media/Manga/volumes.ts2
-rw-r--r--src/lib/Media/links.ts88
13 files changed, 778 insertions, 778 deletions
diff --git a/src/lib/Media/Anime/Airing/AiringTime.svelte b/src/lib/Media/Anime/Airing/AiringTime.svelte
index 2bbbde84..53a39c39 100644
--- a/src/lib/Media/Anime/Airing/AiringTime.svelte
+++ b/src/lib/Media/Anime/Airing/AiringTime.svelte
@@ -1,139 +1,139 @@
<script lang="ts">
- import type { Media } from '$lib/Data/AniList/media';
- import settings from '$stores/settings';
- import { onDestroy, onMount } from 'svelte';
- import type { MediaPrequel } from '$lib/Data/AniList/prequels';
- import tooltip from '$lib/Tooltip/tooltip';
- import locale from '$stores/locale';
-
- export let originalAnime: Media;
- export let upcoming = false;
-
- const anime = originalAnime;
- let opacity = 100;
- let timeFrame = '';
- let time = '';
- let nextEpisode = anime.nextAiringEpisode?.episode || 0;
- let few = true;
- let dateString = '';
- let updateInterval: NodeJS.Timeout;
-
- onMount(() => {
- const setAiringTime = () => {
- time = '';
- timeFrame = '';
- dateString = '';
-
- const airingAt = anime.nextAiringEpisode?.airingAt;
- const untilAiring = airingAt
- ? Math.round((airingAt - Date.now() / 1000) * 100) / 100
- : undefined;
- let hours = null;
- const shortenCountdown = $settings.displayShortCountdown;
-
- time = new Date(airingAt ? airingAt * 1000 : 0).toLocaleTimeString([], {
- hour12: !$settings.display24HourTime,
- hour: 'numeric',
- minute: '2-digit'
- });
-
- 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 class="opaque">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 = Math.round(untilAiring / 60);
-
- few = true;
-
- if (minutes > 60) {
- hours = minutes / 60;
-
- if (hours > 24) {
- const days = Math.floor(hours / 24);
- const weeks = Math.floor(days / 7);
-
- few = false;
-
- if (weeks >= 1.5) {
- timeFrame = `${weeks}${shortenCountdown ? 'w' : ' week'}${
- weeks === 1 || shortenCountdown ? '' : 's'
- }`;
-
- const residualDays = days % 7;
-
- if (residualDays > 0)
- timeFrame += `${shortenCountdown ? '' : ' '}${residualDays}${
- shortenCountdown ? 'd' : ' day'
- }${residualDays === 1 || shortenCountdown ? '' : 's'}`;
- } else {
- timeFrame += `${days}${shortenCountdown ? 'd' : ' day'}${
- days === 1 || shortenCountdown ? '' : 's'
- }`;
- }
-
- const residualHours = Math.floor(hours - days * 24);
-
- if (residualHours > 0)
- timeFrame += `${shortenCountdown ? '' : ' '}${residualHours}${
- shortenCountdown ? 'h' : ' hour'
- }${residualHours === 1 || shortenCountdown ? '' : 's'}`;
- } else {
- const residualMinutes = Math.round(minutes - Math.floor(hours) * 60);
-
- timeFrame += `${Math.floor(hours).toFixed(0)}${shortenCountdown ? 'h' : ' hour'}${
- Math.floor(hours) === 1 || shortenCountdown ? '' : 's'
- }`;
-
- if (residualMinutes > 0)
- timeFrame += `${shortenCountdown ? '' : ' '}${residualMinutes}${
- shortenCountdown ? 'm' : ' minute'
- }${residualMinutes === 1 || shortenCountdown ? '' : 's'}`;
- }
- } else {
- minutes = Math.round(minutes);
-
- timeFrame += `${minutes}${shortenCountdown ? 'm' : ' minute'}${
- minutes === 1 || shortenCountdown ? '' : 's'
- }`;
- }
-
- opacity = Math.max(50, 100 - (untilAiring / 60 / 60 / 24 / 7) * 50);
- dateString = $locale().dateFormatter(new Date(airingAt ? airingAt * 1000 : 0));
- }
- };
-
- setAiringTime();
-
- updateInterval = setInterval(setAiringTime, 30000);
- });
-
- onDestroy(() => clearInterval(updateInterval));
+ import type { Media } from '$lib/Data/AniList/media';
+ import settings from '$stores/settings';
+ import { onDestroy, onMount } from 'svelte';
+ import type { MediaPrequel } from '$lib/Data/AniList/prequels';
+ import tooltip from '$lib/Tooltip/tooltip';
+ import locale from '$stores/locale';
+
+ export let originalAnime: Media;
+ export let upcoming = false;
+
+ const anime = originalAnime;
+ let opacity = 100;
+ let timeFrame = '';
+ let time = '';
+ let nextEpisode = anime.nextAiringEpisode?.episode || 0;
+ let few = true;
+ let dateString = '';
+ let updateInterval: NodeJS.Timeout;
+
+ onMount(() => {
+ const setAiringTime = () => {
+ time = '';
+ timeFrame = '';
+ dateString = '';
+
+ const airingAt = anime.nextAiringEpisode?.airingAt;
+ const untilAiring = airingAt
+ ? Math.round((airingAt - Date.now() / 1000) * 100) / 100
+ : undefined;
+ let hours = null;
+ const shortenCountdown = $settings.displayShortCountdown;
+
+ time = new Date(airingAt ? airingAt * 1000 : 0).toLocaleTimeString([], {
+ hour12: !$settings.display24HourTime,
+ hour: 'numeric',
+ minute: '2-digit'
+ });
+
+ 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 class="opaque">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 = Math.round(untilAiring / 60);
+
+ few = true;
+
+ if (minutes > 60) {
+ hours = minutes / 60;
+
+ if (hours > 24) {
+ const days = Math.floor(hours / 24);
+ const weeks = Math.floor(days / 7);
+
+ few = false;
+
+ if (weeks >= 1.5) {
+ timeFrame = `${weeks}${shortenCountdown ? 'w' : ' week'}${
+ weeks === 1 || shortenCountdown ? '' : 's'
+ }`;
+
+ const residualDays = days % 7;
+
+ if (residualDays > 0)
+ timeFrame += `${shortenCountdown ? '' : ' '}${residualDays}${
+ shortenCountdown ? 'd' : ' day'
+ }${residualDays === 1 || shortenCountdown ? '' : 's'}`;
+ } else {
+ timeFrame += `${days}${shortenCountdown ? 'd' : ' day'}${
+ days === 1 || shortenCountdown ? '' : 's'
+ }`;
+ }
+
+ const residualHours = Math.floor(hours - days * 24);
+
+ if (residualHours > 0)
+ timeFrame += `${shortenCountdown ? '' : ' '}${residualHours}${
+ shortenCountdown ? 'h' : ' hour'
+ }${residualHours === 1 || shortenCountdown ? '' : 's'}`;
+ } else {
+ const residualMinutes = Math.round(minutes - Math.floor(hours) * 60);
+
+ timeFrame += `${Math.floor(hours).toFixed(0)}${shortenCountdown ? 'h' : ' hour'}${
+ Math.floor(hours) === 1 || shortenCountdown ? '' : 's'
+ }`;
+
+ if (residualMinutes > 0)
+ timeFrame += `${shortenCountdown ? '' : ' '}${residualMinutes}${
+ shortenCountdown ? 'm' : ' minute'
+ }${residualMinutes === 1 || shortenCountdown ? '' : 's'}`;
+ }
+ } else {
+ minutes = Math.round(minutes);
+
+ timeFrame += `${minutes}${shortenCountdown ? 'm' : ' minute'}${
+ minutes === 1 || shortenCountdown ? '' : 's'
+ }`;
+ }
+
+ opacity = Math.max(50, 100 - (untilAiring / 60 / 60 / 24 / 7) * 50);
+ dateString = $locale().dateFormatter(new Date(airingAt ? airingAt * 1000 : 0));
+ }
+ };
+
+ setAiringTime();
+
+ updateInterval = setInterval(setAiringTime, 30000);
+ });
+
+ onDestroy(() => clearInterval(updateInterval));
</script>
{#if upcoming}
- <span title={dateString} use:tooltip style={`opacity: ${opacity}%;`}>
- {nextEpisode}{#if anime.episodes !== null}<span class="opaque">/{anime.episodes}</span>
- {/if} in {timeFrame}
- <span class="opaque">
- {#if few && $settings.displayCoverModeAnime}<br />{/if}{few ? `(${time})` : ''}
- </span>
- </span>
+ <span title={dateString} use:tooltip style={`opacity: ${opacity}%;`}>
+ {nextEpisode}{#if anime.episodes !== null}<span class="opaque">/{anime.episodes}</span>
+ {/if} in {timeFrame}
+ <span class="opaque">
+ {#if few && $settings.displayCoverModeAnime}<br />{/if}{few ? `(${time})` : ''}
+ </span>
+ </span>
{:else}
- <span title={dateString} use:tooltip style={`opacity: ${opacity}%;`}>
- {nextEpisode} in {#if few && $settings.displayCoverModeAnime}<br />{/if}{#if few}<b>
- {timeFrame}
- </b>{:else}{timeFrame}{/if}
- {few ? `(${time})` : ''}
- </span>
+ <span title={dateString} use:tooltip style={`opacity: ${opacity}%;`}>
+ {nextEpisode} in {#if few && $settings.displayCoverModeAnime}<br />{/if}{#if few}<b>
+ {timeFrame}
+ </b>{:else}{timeFrame}{/if}
+ {few ? `(${time})` : ''}
+ </span>
{/if}
diff --git a/src/lib/Media/Anime/Airing/Subtitled/match.ts b/src/lib/Media/Anime/Airing/Subtitled/match.ts
index 09daba4f..dd2f45e6 100644
--- a/src/lib/Media/Anime/Airing/Subtitled/match.ts
+++ b/src/lib/Media/Anime/Airing/Subtitled/match.ts
@@ -7,201 +7,201 @@ import excludeMatch from '$lib/Data/Static/matchExclude.json';
import { season } from '../../season';
export interface Time {
- title: string;
- time: string;
- day: string;
+ 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();
+ 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;
+ if (dayDifference < 0) dayDifference += 7;
- const targetDate = new Date(now);
+ const targetDate = new Date(now);
- targetDate.setDate(now.getDate() + dayDifference);
- targetDate.setHours(targetHour, targetMinute, 0, 0);
+ targetDate.setDate(now.getDate() + dayDifference);
+ targetDate.setHours(targetHour, targetMinute, 0, 0);
- const secondsDifference = (Number(targetDate) - Number(now)) / 1000;
+ const secondsDifference = (Number(targetDate) - Number(now)) / 1000;
- return secondsDifference > 0 ? secondsDifference : secondsDifference + 7 * 24 * 60 * 60;
+ return secondsDifference > 0 ? secondsDifference : secondsDifference + 7 * 24 * 60 * 60;
};
const preprocessTitle = (title: string): string => {
- return title
- .toLowerCase()
- .replace(/\b(season|s|part|cour)\b/g, ' ')
- .replace(/[^a-z0-9\s]/gi, '')
- .trim()
- .split(/\s+/)
- .join(' ');
+ return title
+ .toLowerCase()
+ .replace(/\b(season|s|part|cour)\b/g, ' ')
+ .replace(/[^a-z0-9\s]/gi, '')
+ .trim()
+ .split(/\s+/)
+ .join(' ');
};
const calculateWeightedSimilarity = (title1: string, title2: string): number => {
- const tokens1 = title1.split(' ');
- const tokens2 = title2.split(' ');
- const set2 = new Set(tokens2);
- let score = 0;
-
- tokens1.forEach((token) => {
- if (set2.has(token)) {
- score += /^\d+$/.test(token) ? 2 : 1;
- }
- });
-
- return score / (Math.max(tokens1.length, tokens2.length) * 2);
+ const tokens1 = title1.split(' ');
+ const tokens2 = title2.split(' ');
+ const set2 = new Set(tokens2);
+ let score = 0;
+
+ tokens1.forEach((token) => {
+ if (set2.has(token)) {
+ score += /^\d+$/.test(token) ? 2 : 1;
+ }
+ });
+
+ return score / (Math.max(tokens1.length, tokens2.length) * 2);
};
export const findClosestMatch = (times: Time[], anime: Media): Time | null => {
- if (excludeMatch.includes(anime.id)) return null;
-
- let bestMatch: Time | null = null;
- let bestScore = 0;
-
- [anime.title.romaji, anime.title.english, ...anime.synonyms]
- .filter(Boolean)
- .forEach((searchTitle) => {
- if (searchTitle.includes('OVA') || searchTitle.includes('Special')) return;
-
- const normalizedSearchTitle = preprocessTitle(searchTitle);
-
- times.forEach((time) => {
- const normalizedTimeTitle = preprocessTitle(time.title);
- const similarityScore = calculateWeightedSimilarity(
- normalizedSearchTitle,
- normalizedTimeTitle
- );
-
- if (
- similarityScore > bestScore &&
- time.day ===
- new Date((anime.nextAiringEpisode?.airingAt || 0) * 1000).toLocaleString('en-US', {
- weekday: 'long'
- })
- ) {
- bestScore = similarityScore;
- bestMatch = time;
- }
- });
- });
-
- return bestMatch;
+ if (excludeMatch.includes(anime.id)) return null;
+
+ let bestMatch: Time | null = null;
+ let bestScore = 0;
+
+ [anime.title.romaji, anime.title.english, ...anime.synonyms]
+ .filter(Boolean)
+ .forEach((searchTitle) => {
+ if (searchTitle.includes('OVA') || searchTitle.includes('Special')) return;
+
+ const normalizedSearchTitle = preprocessTitle(searchTitle);
+
+ times.forEach((time) => {
+ const normalizedTimeTitle = preprocessTitle(time.title);
+ const similarityScore = calculateWeightedSimilarity(
+ normalizedSearchTitle,
+ normalizedTimeTitle
+ );
+
+ if (
+ similarityScore > bestScore &&
+ time.day ===
+ new Date((anime.nextAiringEpisode?.airingAt || 0) * 1000).toLocaleString('en-US', {
+ weekday: 'long'
+ })
+ ) {
+ bestScore = similarityScore;
+ bestMatch = time;
+ }
+ });
+ });
+
+ return bestMatch;
};
const normalizeTitle = (title: string | null) =>
- (title || '')
- .toLowerCase()
- .replace(/\b(s|season|part|cour)\s*\d+/g, '')
- .replace(/[\W_]+/g, ' ')
- .trim();
+ (title || '')
+ .toLowerCase()
+ .replace(/\b(s|season|part|cour)\s*\d+/g, '')
+ .replace(/[\W_]+/g, ' ')
+ .trim();
export const findClosestMedia = (media: Media[], matchFor: string) => {
- if (!matchFor) return null;
-
- let bestFitMedia: Media | null = null;
- let smallestDistance = -Infinity;
-
- media.forEach((m) => {
- const titles = [m.title.romaji, m.title.english, ...m.synonyms].filter(Boolean);
-
- if (
- titles.some(
- (title) => title.toLowerCase().includes('special') || title.toLowerCase().includes('ova')
- )
- )
- return;
-
- titles.forEach((title) => {
- const normalisedTitle = normalizeTitle(title);
- const normalisedMatchFor = normalizeTitle(matchFor);
- const distance = stringSimilarity.compareTwoStrings(normalisedMatchFor, normalisedTitle);
-
- if (
- distance > smallestDistance &&
- (normalisedMatchFor
- .split(' ')
- .filter((word) => titles.some((title) => normalizeTitle(title).includes(word))).length >=
- normalisedMatchFor.split(' ').length ||
- titles.some((title) => normalizeTitle(title).includes(normalisedMatchFor)))
- ) {
- smallestDistance = distance;
- bestFitMedia = m;
- }
- });
- });
-
- return bestFitMedia as Media | null;
+ if (!matchFor) return null;
+
+ let bestFitMedia: Media | null = null;
+ let smallestDistance = -Infinity;
+
+ media.forEach((m) => {
+ const titles = [m.title.romaji, m.title.english, ...m.synonyms].filter(Boolean);
+
+ if (
+ titles.some(
+ (title) => title.toLowerCase().includes('special') || title.toLowerCase().includes('ova')
+ )
+ )
+ return;
+
+ titles.forEach((title) => {
+ const normalisedTitle = normalizeTitle(title);
+ const normalisedMatchFor = normalizeTitle(matchFor);
+ const distance = stringSimilarity.compareTwoStrings(normalisedMatchFor, normalisedTitle);
+
+ if (
+ distance > smallestDistance &&
+ (normalisedMatchFor
+ .split(' ')
+ .filter((word) => titles.some((title) => normalizeTitle(title).includes(word))).length >=
+ normalisedMatchFor.split(' ').length ||
+ titles.some((title) => normalizeTitle(title).includes(normalisedMatchFor)))
+ ) {
+ smallestDistance = distance;
+ bestFitMedia = m;
+ }
+ });
+ });
+
+ return bestFitMedia as Media | null;
};
export const injectAiringTime = (anime: Media, subsPlease: SubsPlease | null) => {
- if (season() !== anime.season) return anime;
-
- const airingAt = anime.nextAiringEpisode?.airingAt;
- const now = new Date();
- // const nativeUntilAiring = airingAt
- // ? Math.round((airingAt - Date.now() / 1000) * 100) / 100
- // : undefined;
- const nativeTime = new Date(airingAt ? airingAt * 1000 : 0);
- let untilAiring;
- let time = new Date(airingAt ? airingAt * 1000 : 0);
- let nextEpisode = anime.nextAiringEpisode?.episode || 0;
-
- if (
- !(
- (get(settings).displayNativeCountdown || !subsPlease)
- // || !(nativeUntilAiring !== undefined && nativeUntilAiring < 24 * 60 * 60)
- )
- ) {
- 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
- });
- }
- }
-
- if ((anime.nextAiringEpisode?.episode || 0) > 1) {
- const foundTime: Time | null = findClosestMatch(times, anime);
-
- if (foundTime) {
- untilAiring = secondsUntil((foundTime as Time).time, (foundTime as Time).day);
- time = new Date(Date.now() + untilAiring * 1000);
- }
- }
- }
-
- const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1000;
-
- if (nativeTime > time) {
- nextEpisode -= 1;
- }
-
- if (nativeTime.getTime() - now.getTime() > SEVEN_DAYS) {
- const beforeTime = time;
-
- time = nativeTime;
-
- time.setHours(beforeTime.getHours());
- time.setMinutes(beforeTime.getMinutes());
- }
-
- return {
- ...anime,
- nextAiringEpisode: {
- episode: nextEpisode,
- airingAt: time.getTime() / 1000,
- nativeAiringAt: nativeTime.getTime() / 1000
- }
- } as Media;
+ if (season() !== anime.season) return anime;
+
+ const airingAt = anime.nextAiringEpisode?.airingAt;
+ const now = new Date();
+ // const nativeUntilAiring = airingAt
+ // ? Math.round((airingAt - Date.now() / 1000) * 100) / 100
+ // : undefined;
+ const nativeTime = new Date(airingAt ? airingAt * 1000 : 0);
+ let untilAiring;
+ let time = new Date(airingAt ? airingAt * 1000 : 0);
+ let nextEpisode = anime.nextAiringEpisode?.episode || 0;
+
+ if (
+ !(
+ (get(settings).displayNativeCountdown || !subsPlease)
+ // || !(nativeUntilAiring !== undefined && nativeUntilAiring < 24 * 60 * 60)
+ )
+ ) {
+ 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
+ });
+ }
+ }
+
+ if ((anime.nextAiringEpisode?.episode || 0) > 1) {
+ const foundTime: Time | null = findClosestMatch(times, anime);
+
+ if (foundTime) {
+ untilAiring = secondsUntil((foundTime as Time).time, (foundTime as Time).day);
+ time = new Date(Date.now() + untilAiring * 1000);
+ }
+ }
+ }
+
+ const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1000;
+
+ if (nativeTime > time) {
+ nextEpisode -= 1;
+ }
+
+ if (nativeTime.getTime() - now.getTime() > SEVEN_DAYS) {
+ const beforeTime = time;
+
+ time = nativeTime;
+
+ time.setHours(beforeTime.getHours());
+ time.setMinutes(beforeTime.getMinutes());
+ }
+
+ return {
+ ...anime,
+ nextAiringEpisode: {
+ episode: nextEpisode,
+ airingAt: time.getTime() / 1000,
+ nativeAiringAt: nativeTime.getTime() / 1000
+ }
+ } as Media;
};
diff --git a/src/lib/Media/Anime/Airing/Subtitled/subsPlease.ts b/src/lib/Media/Anime/Airing/Subtitled/subsPlease.ts
index 3815259d..69f56286 100644
--- a/src/lib/Media/Anime/Airing/Subtitled/subsPlease.ts
+++ b/src/lib/Media/Anime/Airing/Subtitled/subsPlease.ts
@@ -1,13 +1,13 @@
export interface SubsPlease {
- tz: string;
- schedule: {
- [key in string]: SubsPleaseEpisode;
- }[];
+ tz: string;
+ schedule: {
+ [key in string]: SubsPleaseEpisode;
+ }[];
}
export interface SubsPleaseEpisode {
- title: string;
- page: string;
- image_url: string;
- time: string;
+ title: string;
+ page: string;
+ image_url: string;
+ time: string;
}
diff --git a/src/lib/Media/Anime/Airing/time.ts b/src/lib/Media/Anime/Airing/time.ts
index dcaac944..76d51668 100644
--- a/src/lib/Media/Anime/Airing/time.ts
+++ b/src/lib/Media/Anime/Airing/time.ts
@@ -7,116 +7,116 @@ import { totalEpisodes } from '../episodes';
import { get } from 'svelte/store';
export const airingTime = (
- originalAnime: Media,
- subsPlease: SubsPlease | null,
- upcoming = false,
- forceDays = false
+ originalAnime: Media,
+ subsPlease: SubsPlease | null,
+ upcoming = false,
+ forceDays = false
) => {
- const anime = injectAiringTime(originalAnime, subsPlease);
- const airingAt = anime.nextAiringEpisode?.airingAt;
- const untilAiring = airingAt ? Math.round((airingAt - Date.now() / 1000) * 100) / 100 : undefined;
- const time = new Date(airingAt ? airingAt * 1000 : 0).toLocaleTimeString([], {
- hour12: !settings.get().display24HourTime,
- hour: 'numeric',
- minute: '2-digit'
- });
- let timeFrame = '';
- let hours = null;
- const shortenCountdown = get(settings).displayShortCountdown;
-
- 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 class="opaque">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(hours / 24) / 7;
-
- few = false;
-
- if (weeks >= 1.5 && !forceDays) {
- weeks = Math.round(weeks);
-
- timeFrame = `${weeks}${shortenCountdown ? 'w' : ' week'}${
- weeks === 1 || shortenCountdown ? '' : 's'
- }`;
- } else {
- const days = Math.round(Math.floor(hours / 24));
- const residualHours = Math.floor(hours - days * 24);
-
- timeFrame += `${days.toFixed(0)}${shortenCountdown ? 'd' : ' day'}${
- days === 1 || shortenCountdown ? '' : 's'
- }`;
-
- if (residualHours > 0)
- timeFrame += `${shortenCountdown ? '' : ' '}${residualHours}${
- shortenCountdown ? 'h' : ' hour'
- }${residualHours === 1 || shortenCountdown ? '' : 's'}`;
- }
- } else {
- const residualMinutes = Math.round(minutes - Math.floor(hours) * 60);
-
- timeFrame += `${hours.toFixed(0)}${shortenCountdown ? 'h' : ' hour'}${
- hours === 1 || shortenCountdown ? '' : 's'
- }`;
-
- if (residualMinutes > 0)
- timeFrame += `${shortenCountdown ? '' : ' '}${residualMinutes}${
- shortenCountdown ? 'm' : ' minute'
- }${residualMinutes === 1 || shortenCountdown ? '' : 's'}`;
- }
- } else {
- minutes = Math.round(minutes);
-
- timeFrame += `${minutes}${shortenCountdown ? 'm' : ' minute'}${
- minutes === 1 || shortenCountdown ? '' : 's'
- }`;
- }
-
- const opacity = Math.max(50, 100 - (untilAiring / 60 / 60 / 24 / 7) * 50);
- const nextEpisode =
- anime.nextAiringEpisode?.nativeAiringAt &&
- !upcoming &&
- anime.nextAiringEpisode.nativeAiringAt < Date.now() / 1000 + 1 * 24 * 60 * 60
- ? anime.nextAiringEpisode.episode - 1
- : anime.nextAiringEpisode?.episode || 0;
- const dateString =
- new Date(airingAt ? airingAt * 1000 : 0).toLocaleDateString([], {
- weekday: 'long',
- year: 'numeric',
- month: 'long',
- day: 'numeric'
- }) +
- ' ' +
- time;
-
- if (upcoming)
- return `<span title="${dateString}" style="opacity: ${opacity}%;">${nextEpisode}${totalEpisodes(
- anime
- )} in ${timeFrame} <span class="opaque">${
- few && get(settings).displayCoverModeAnime ? '<br>' : ''
- }${few ? `(${time})` : ''}</span></span>`;
- else
- return `<span title="${dateString}" style="opacity: ${opacity}%;">${nextEpisode} in ${
- few && get(settings).displayCoverModeAnime ? '<br>' : ''
- }${few ? '<b>' : ''}${timeFrame}${few ? '</b>' : ''} ${few ? `(${time})` : ''}</span>`;
- }
-
- return '';
+ const anime = injectAiringTime(originalAnime, subsPlease);
+ const airingAt = anime.nextAiringEpisode?.airingAt;
+ const untilAiring = airingAt ? Math.round((airingAt - Date.now() / 1000) * 100) / 100 : undefined;
+ const time = new Date(airingAt ? airingAt * 1000 : 0).toLocaleTimeString([], {
+ hour12: !settings.get().display24HourTime,
+ hour: 'numeric',
+ minute: '2-digit'
+ });
+ let timeFrame = '';
+ let hours = null;
+ const shortenCountdown = get(settings).displayShortCountdown;
+
+ 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 class="opaque">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(hours / 24) / 7;
+
+ few = false;
+
+ if (weeks >= 1.5 && !forceDays) {
+ weeks = Math.round(weeks);
+
+ timeFrame = `${weeks}${shortenCountdown ? 'w' : ' week'}${
+ weeks === 1 || shortenCountdown ? '' : 's'
+ }`;
+ } else {
+ const days = Math.round(Math.floor(hours / 24));
+ const residualHours = Math.floor(hours - days * 24);
+
+ timeFrame += `${days.toFixed(0)}${shortenCountdown ? 'd' : ' day'}${
+ days === 1 || shortenCountdown ? '' : 's'
+ }`;
+
+ if (residualHours > 0)
+ timeFrame += `${shortenCountdown ? '' : ' '}${residualHours}${
+ shortenCountdown ? 'h' : ' hour'
+ }${residualHours === 1 || shortenCountdown ? '' : 's'}`;
+ }
+ } else {
+ const residualMinutes = Math.round(minutes - Math.floor(hours) * 60);
+
+ timeFrame += `${hours.toFixed(0)}${shortenCountdown ? 'h' : ' hour'}${
+ hours === 1 || shortenCountdown ? '' : 's'
+ }`;
+
+ if (residualMinutes > 0)
+ timeFrame += `${shortenCountdown ? '' : ' '}${residualMinutes}${
+ shortenCountdown ? 'm' : ' minute'
+ }${residualMinutes === 1 || shortenCountdown ? '' : 's'}`;
+ }
+ } else {
+ minutes = Math.round(minutes);
+
+ timeFrame += `${minutes}${shortenCountdown ? 'm' : ' minute'}${
+ minutes === 1 || shortenCountdown ? '' : 's'
+ }`;
+ }
+
+ const opacity = Math.max(50, 100 - (untilAiring / 60 / 60 / 24 / 7) * 50);
+ const nextEpisode =
+ anime.nextAiringEpisode?.nativeAiringAt &&
+ !upcoming &&
+ anime.nextAiringEpisode.nativeAiringAt < Date.now() / 1000 + 1 * 24 * 60 * 60
+ ? anime.nextAiringEpisode.episode - 1
+ : anime.nextAiringEpisode?.episode || 0;
+ const dateString =
+ new Date(airingAt ? airingAt * 1000 : 0).toLocaleDateString([], {
+ weekday: 'long',
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ }) +
+ ' ' +
+ time;
+
+ if (upcoming)
+ return `<span title="${dateString}" style="opacity: ${opacity}%;">${nextEpisode}${totalEpisodes(
+ anime
+ )} in ${timeFrame} <span class="opaque">${
+ few && get(settings).displayCoverModeAnime ? '<br>' : ''
+ }${few ? `(${time})` : ''}</span></span>`;
+ else
+ return `<span title="${dateString}" style="opacity: ${opacity}%;">${nextEpisode} in ${
+ few && get(settings).displayCoverModeAnime ? '<br>' : ''
+ }${few ? '<b>' : ''}${timeFrame}${few ? '</b>' : ''} ${few ? `(${time})` : ''}</span>`;
+ }
+
+ return '';
};
diff --git a/src/lib/Media/Anime/cache.ts b/src/lib/Media/Anime/cache.ts
index a47a655e..9aabb2ab 100644
--- a/src/lib/Media/Anime/cache.ts
+++ b/src/lib/Media/Anime/cache.ts
@@ -5,27 +5,27 @@ import lastPruneTimes from '$stores/lastPruneTimes';
import type { AniListAuthorisation, UserIdentity } from '../../Data/AniList/identity';
export const cleanCache = (user: AniListAuthorisation, identity: UserIdentity) =>
- mediaListCollection(user, identity, Type.Anime, get(anime), get(lastPruneTimes).anime, {
- forcePrune: true
- });
+ mediaListCollection(user, identity, Type.Anime, get(anime), get(lastPruneTimes).anime, {
+ forcePrune: true
+ });
export const incrementMediaProgress = (
- id: number,
- progress: number | undefined,
- user: AniListAuthorisation,
- callback: () => void
+ id: number,
+ progress: number | undefined,
+ user: AniListAuthorisation,
+ callback: () => void
) => {
- fetch('https://graphql.anilist.co', {
- method: 'POST',
- headers: {
- Authorization: `${user.tokenType} ${user.accessToken}`,
- 'Content-Type': 'application/json',
- Accept: 'application/json'
- },
- body: JSON.stringify({
- query: `mutation { SaveMediaListEntry(mediaId: ${id}, progress: ${
- (progress || 0) + 1
- }) { id } }`
- })
- }).then(callback);
+ fetch('https://graphql.anilist.co', {
+ method: 'POST',
+ headers: {
+ Authorization: `${user.tokenType} ${user.accessToken}`,
+ 'Content-Type': 'application/json',
+ Accept: 'application/json'
+ },
+ body: JSON.stringify({
+ query: `mutation { SaveMediaListEntry(mediaId: ${id}, progress: ${
+ (progress || 0) + 1
+ }) { id } }`
+ })
+ }).then(callback);
};
diff --git a/src/lib/Media/Anime/episodes.ts b/src/lib/Media/Anime/episodes.ts
index 3cdbdb98..f4994f83 100644
--- a/src/lib/Media/Anime/episodes.ts
+++ b/src/lib/Media/Anime/episodes.ts
@@ -1,4 +1,4 @@
import type { Media } from '$lib/Data/AniList/media';
export const totalEpisodes = (anime: Media) =>
- anime.episodes === null ? '' : `<span class="opaque">/${anime.episodes}</span>`;
+ anime.episodes === null ? '' : `<span class="opaque">/${anime.episodes}</span>`;
diff --git a/src/lib/Media/Anime/season.ts b/src/lib/Media/Anime/season.ts
index d7922e2b..d0cd6c25 100644
--- a/src/lib/Media/Anime/season.ts
+++ b/src/lib/Media/Anime/season.ts
@@ -1,11 +1,11 @@
export const season = () => {
- if (new Date().getMonth() >= 0 && new Date().getMonth() <= 2)
- return 'WINTER' as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL';
- else if (new Date().getMonth() >= 3 && new Date().getMonth() <= 5)
- return 'SPRING' as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL';
- else if (new Date().getMonth() >= 6 && new Date().getMonth() <= 8)
- return 'SUMMER' as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL';
- else if (new Date().getMonth() >= 9 && new Date().getMonth() <= 11)
- return 'FALL' as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL';
- else return 'WINTER' as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL';
+ if (new Date().getMonth() >= 0 && new Date().getMonth() <= 2)
+ return 'WINTER' as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL';
+ else if (new Date().getMonth() >= 3 && new Date().getMonth() <= 5)
+ return 'SPRING' as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL';
+ else if (new Date().getMonth() >= 6 && new Date().getMonth() <= 8)
+ return 'SUMMER' as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL';
+ else if (new Date().getMonth() >= 9 && new Date().getMonth() <= 11)
+ return 'FALL' as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL';
+ else return 'WINTER' as 'WINTER' | 'SPRING' | 'SUMMER' | 'FALL';
};
diff --git a/src/lib/Media/Cover/HoverCover.svelte b/src/lib/Media/Cover/HoverCover.svelte
index eecbabe5..51cbf5d2 100644
--- a/src/lib/Media/Cover/HoverCover.svelte
+++ b/src/lib/Media/Cover/HoverCover.svelte
@@ -1,53 +1,53 @@
<script lang="ts">
- import settings from '$stores/settings';
- import type { HoverCoverResponse } from './hoverCover';
+ import settings from '$stores/settings';
+ import type { HoverCoverResponse } from './hoverCover';
- export let options: HoverCoverResponse;
- export let width = 250;
+ export let options: HoverCoverResponse;
+ export let width = 250;
</script>
{#if options.hovering}
- <img
- class="hover-image show card card-small"
- src={options.media
- ? $settings.displayDataSaver
- ? options.media.coverImage.medium
- : options.media.coverImage.extraLarge
- : `https://subsplease.org${options.item?.image_url}`}
- alt="Media Cover"
- loading="lazy"
- style={`width: ${width}px; ${options.style}`}
- />
+ <img
+ class="hover-image show card card-small"
+ src={options.media
+ ? $settings.displayDataSaver
+ ? options.media.coverImage.medium
+ : options.media.coverImage.extraLarge
+ : `https://subsplease.org${options.item?.image_url}`}
+ alt="Media Cover"
+ loading="lazy"
+ style={`width: ${width}px; ${options.style}`}
+ />
{/if}
<style lang="scss">
- $coverTransitionTime: 200ms;
-
- .hover-image {
- position: fixed;
- height: auto;
- display: none;
- border-radius: 8px;
- transition: opacity $coverTransitionTime ease-in-out, top 0.3s ease, left 0.3s ease;
- }
-
- .show {
- display: block;
- }
-
- .hover-image {
- animation: dropIn $coverTransitionTime ease-in-out;
- }
-
- @keyframes dropIn {
- 0% {
- opacity: 0;
- transform: translateY(-1rem);
- }
-
- 100% {
- opacity: 1;
- transform: translateY(0);
- }
- }
+ $coverTransitionTime: 200ms;
+
+ .hover-image {
+ position: fixed;
+ height: auto;
+ display: none;
+ border-radius: 8px;
+ transition: opacity $coverTransitionTime ease-in-out, top 0.3s ease, left 0.3s ease;
+ }
+
+ .show {
+ display: block;
+ }
+
+ .hover-image {
+ animation: dropIn $coverTransitionTime ease-in-out;
+ }
+
+ @keyframes dropIn {
+ 0% {
+ opacity: 0;
+ transform: translateY(-1rem);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
</style>
diff --git a/src/lib/Media/Cover/hoverCover.ts b/src/lib/Media/Cover/hoverCover.ts
index c2b6697c..3b364349 100644
--- a/src/lib/Media/Cover/hoverCover.ts
+++ b/src/lib/Media/Cover/hoverCover.ts
@@ -4,53 +4,53 @@ import type { SubsPleaseEpisode } from '../Anime/Airing/Subtitled/subsPlease';
import settings from '$stores/settings';
export interface HoverCoverResponse {
- // OnMouseEnterLeave
- hovering?: boolean;
- item?: SubsPleaseEpisode | null;
- media?: Media | null;
-
- // OnMouseMove
- height?: number;
- style?: string;
+ // OnMouseEnterLeave
+ hovering?: boolean;
+ item?: SubsPleaseEpisode | null;
+ media?: Media | null;
+
+ // OnMouseMove
+ height?: number;
+ style?: string;
}
export const onMouseEnter = (
- media: Media | Partial<Media> | null,
- item: SubsPleaseEpisode | null = null
+ media: Media | Partial<Media> | null,
+ item: SubsPleaseEpisode | null = null
) => {
- if (!get(settings).displayHoverCover && !item)
- return { hovering: false, item: null, media: null } as HoverCoverResponse;
+ if (!get(settings).displayHoverCover && !item)
+ return { hovering: false, item: null, media: null } as HoverCoverResponse;
- return { hovering: true, item, media } as HoverCoverResponse;
+ return { hovering: true, item, media } as HoverCoverResponse;
};
export const onMouseLeave = () => {
- return { hovering: false, item: null, media: null } as HoverCoverResponse;
+ return { hovering: false, item: null, media: null } as HoverCoverResponse;
};
export const onMouseMove = (event: MouseEvent, imageWidth = 250) => {
- const offset = 10;
- let imageLeft = 0;
- let imageTop = 0;
- const elements = document.getElementsByClassName('hover-image');
-
- if (elements.length === 0) return { height: 0, style: '' } as HoverCoverResponse;
-
- const response: HoverCoverResponse = {
- height: (elements[0] as HTMLImageElement).height,
- style: ''
- };
- const height = response.height || 0;
-
- imageLeft =
- event.pageX + height + offset > window.innerWidth
- ? event.pageX - imageWidth - offset
- : event.pageX + offset;
- imageTop =
- event.pageY - window.scrollY + height + offset > window.innerHeight
- ? event.pageY - window.scrollY - height - offset
- : event.pageY - window.scrollY + offset;
- response.style = `top: ${imageTop}px; left: ${imageLeft}px;`;
-
- return response;
+ const offset = 10;
+ let imageLeft = 0;
+ let imageTop = 0;
+ const elements = document.getElementsByClassName('hover-image');
+
+ if (elements.length === 0) return { height: 0, style: '' } as HoverCoverResponse;
+
+ const response: HoverCoverResponse = {
+ height: (elements[0] as HTMLImageElement).height,
+ style: ''
+ };
+ const height = response.height || 0;
+
+ imageLeft =
+ event.pageX + height + offset > window.innerWidth
+ ? event.pageX - imageWidth - offset
+ : event.pageX + offset;
+ imageTop =
+ event.pageY - window.scrollY + height + offset > window.innerHeight
+ ? event.pageY - window.scrollY - height - offset
+ : event.pageY - window.scrollY + offset;
+ response.style = `top: ${imageTop}px; left: ${imageLeft}px;`;
+
+ return response;
};
diff --git a/src/lib/Media/Manga/cache.ts b/src/lib/Media/Manga/cache.ts
index d35bf64a..6bd248dc 100644
--- a/src/lib/Media/Manga/cache.ts
+++ b/src/lib/Media/Manga/cache.ts
@@ -2,9 +2,9 @@ import { database } from '../../Database/IDB/chapters';
import manga from '$stores/manga';
export const pruneAllManga = async () => {
- const all = await database.chapters.toArray();
- const ids = all.map((m) => m.id);
+ const all = await database.chapters.toArray();
+ const ids = all.map((m) => m.id);
- manga.set('');
- await database.chapters.bulkDelete(ids);
+ manga.set('');
+ await database.chapters.bulkDelete(ids);
};
diff --git a/src/lib/Media/Manga/chapters.ts b/src/lib/Media/Manga/chapters.ts
index a601fdc8..32e1f0fc 100644
--- a/src/lib/Media/Manga/chapters.ts
+++ b/src/lib/Media/Manga/chapters.ts
@@ -6,202 +6,202 @@ import type { UserIdentity } from '../../Data/AniList/identity';
import { database } from '../../Database/IDB/chapters';
const getManga = async (
- statusIn: string,
- year: number,
- native: string | null,
- english: string | null,
- romaji: string | null
+ statusIn: string,
+ year: number,
+ native: string | null,
+ english: string | null,
+ romaji: string | null
) => {
- let status = '';
- let error = false;
-
- switch (statusIn) {
- case 'FINISHED':
- {
- status = 'completed';
- }
- break;
- case 'RELEASING':
- {
- status = 'ongoing';
- }
- break;
- case 'HIATUS':
- {
- status = 'hiatus';
- }
- break;
- case 'CANCELLED':
- {
- status = 'cancelled';
- }
- break;
- }
-
- const nullIfNullString = (s: string | null) => (s == 'null' ? null : s);
- const get = async (title: string) => {
- try {
- return await (
- await fetch(
- proxy(
- `https://api.mangadex.org/manga?title=${encodeURIComponent(
- title
- )}&year=${year}&status[]=${status}`
- )
- )
- ).json();
- } catch {
- error = true;
- }
- };
-
- let mangadexData = await get(
- nullIfNullString(native) || nullIfNullString(english) || nullIfNullString(romaji) || ''
- );
-
- if (error) return new Response('rate-limited');
-
- if (mangadexData['data'] === undefined || mangadexData['data'].length === 0) {
- mangadexData = await get(nullIfNullString(english) || '');
-
- if (mangadexData['data'] === undefined || mangadexData['data'].length === 0) {
- mangadexData = await get(nullIfNullString(romaji) || '');
- }
- }
-
- return Response.json(mangadexData, {
- headers: {
- 'Cache-Control': 'max-age=300'
- }
- });
+ let status = '';
+ let error = false;
+
+ switch (statusIn) {
+ case 'FINISHED':
+ {
+ status = 'completed';
+ }
+ break;
+ case 'RELEASING':
+ {
+ status = 'ongoing';
+ }
+ break;
+ case 'HIATUS':
+ {
+ status = 'hiatus';
+ }
+ break;
+ case 'CANCELLED':
+ {
+ status = 'cancelled';
+ }
+ break;
+ }
+
+ const nullIfNullString = (s: string | null) => (s == 'null' ? null : s);
+ const get = async (title: string) => {
+ try {
+ return await (
+ await fetch(
+ proxy(
+ `https://api.mangadex.org/manga?title=${encodeURIComponent(
+ title
+ )}&year=${year}&status[]=${status}`
+ )
+ )
+ ).json();
+ } catch {
+ error = true;
+ }
+ };
+
+ let mangadexData = await get(
+ nullIfNullString(native) || nullIfNullString(english) || nullIfNullString(romaji) || ''
+ );
+
+ if (error) return new Response('rate-limited');
+
+ if (mangadexData['data'] === undefined || mangadexData['data'].length === 0) {
+ mangadexData = await get(nullIfNullString(english) || '');
+
+ if (mangadexData['data'] === undefined || mangadexData['data'].length === 0) {
+ mangadexData = await get(nullIfNullString(romaji) || '');
+ }
+ }
+
+ return Response.json(mangadexData, {
+ headers: {
+ 'Cache-Control': 'max-age=300'
+ }
+ });
};
export const chapterCount = async (
- identity: UserIdentity,
- manga: Media,
- disableGuessing: boolean
- // preferActivity = false
+ identity: UserIdentity,
+ manga: Media,
+ disableGuessing: boolean
+ // preferActivity = false
): Promise<number | null> => {
- const chapters = await database.chapters.get(manga.id);
-
- if (chapters !== undefined) return chapters.chapters === -1 ? null : chapters.chapters;
-
- // if (preferActivity) {
- // return await recentMediaActivities(identity, manga);
- // }
-
- const tryRecentMediaActivities = async () => {
- if (disableGuessing) {
- await database.chapters.put({
- id: manga.id,
- chapters: -1,
- volumes: null
- });
-
- return null;
- }
-
- const anilistData = await recentMediaActivities(
- identity,
- manga,
- settings.get().calculateGuessMethod
- );
-
- await database.chapters.put({
- id: manga.id,
- chapters: anilistData ? anilistData : -1,
- volumes: null
- });
-
- return anilistData;
- };
-
- if (manga.format === 'NOVEL') return await tryRecentMediaActivities();
-
- let lastChapter = 0;
- let completedVolumes = null;
-
- if (!settings.get().calculatePreferNativeChapterCount) {
- const mangadexData = await getManga(
- manga.status,
- manga.startDate.year,
- manga.title.native,
- manga.title.english,
- manga.title.romaji
- );
-
- if ((await mangadexData.clone().text()) === 'rate-limited') return -22;
-
- const mangadexDataJson = await mangadexData.json();
-
- if (mangadexDataJson['data'] === undefined || mangadexDataJson['data'].length === 0)
- return await tryRecentMediaActivities();
-
- const mangadexId = mangadexDataJson['data'][0]['id'];
- const lastChapterDataJson = await (
- await fetch(
- proxy(
- `https://api.mangadex.org/manga/${mangadexId}/feed?order[chapter]=desc&translatedLanguage[]=en&limit=1&contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic`
- )
- )
- ).json();
-
- if (lastChapterDataJson['data'] === undefined || lastChapterDataJson['data'].length === 0)
- return await tryRecentMediaActivities();
-
- lastChapter = lastChapterDataJson['data'][0]['attributes']['chapter'];
- completedVolumes = null;
-
- if ((manga.mediaListEntry || { progress: 0 }).progress > lastChapter && !disableGuessing) {
- const anilistData = await recentMediaActivities(
- identity,
- manga,
- settings.get().calculateGuessMethod
- );
-
- if (anilistData !== null && anilistData > lastChapter) lastChapter = anilistData;
- }
-
- if (!settings.get().calculateDisableOutOfDateVolumeWarning) {
- const volumeOfChapterData = await (
- await fetch(
- proxy(
- `https://api.mangadex.org/chapter?manga=${mangadexId}&chapter=${manga.mediaListEntry?.progress}&contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic&limit=1`
- )
- )
- ).json();
- let lastAvailableVolume = lastChapterDataJson['data'][0]['attributes']['volume'];
-
- if (lastAvailableVolume === null) {
- let chapterIndex = 0;
-
- while (chapterIndex < lastChapterDataJson['data'].length && lastAvailableVolume === null) {
- if (lastChapterDataJson['data'][chapterIndex]['attributes']['volume'] !== null)
- lastAvailableVolume = lastChapterDataJson['data'][chapterIndex]['attributes']['volume'];
-
- chapterIndex += 1;
- }
- }
-
- if (volumeOfChapterData['data'] !== undefined && volumeOfChapterData['data'].length > 0) {
- const volumeOfChapter = volumeOfChapterData['data'][0]['attributes']['volume'];
-
- if (volumeOfChapter !== null) completedVolumes = volumeOfChapter;
-
- if (completedVolumes === volumeOfChapter) completedVolumes -= 1;
- }
- }
- } else {
- lastChapter = (await getChapterCount(manga.title.native)) || 0;
- }
-
- if (lastChapter == 0) lastChapter = -1;
-
- await database.chapters.put({
- id: manga.id,
- chapters: Number(lastChapter),
- volumes: completedVolumes
- });
-
- return Number(lastChapter);
+ const chapters = await database.chapters.get(manga.id);
+
+ if (chapters !== undefined) return chapters.chapters === -1 ? null : chapters.chapters;
+
+ // if (preferActivity) {
+ // return await recentMediaActivities(identity, manga);
+ // }
+
+ const tryRecentMediaActivities = async () => {
+ if (disableGuessing) {
+ await database.chapters.put({
+ id: manga.id,
+ chapters: -1,
+ volumes: null
+ });
+
+ return null;
+ }
+
+ const anilistData = await recentMediaActivities(
+ identity,
+ manga,
+ settings.get().calculateGuessMethod
+ );
+
+ await database.chapters.put({
+ id: manga.id,
+ chapters: anilistData ? anilistData : -1,
+ volumes: null
+ });
+
+ return anilistData;
+ };
+
+ if (manga.format === 'NOVEL') return await tryRecentMediaActivities();
+
+ let lastChapter = 0;
+ let completedVolumes = null;
+
+ if (!settings.get().calculatePreferNativeChapterCount) {
+ const mangadexData = await getManga(
+ manga.status,
+ manga.startDate.year,
+ manga.title.native,
+ manga.title.english,
+ manga.title.romaji
+ );
+
+ if ((await mangadexData.clone().text()) === 'rate-limited') return -22;
+
+ const mangadexDataJson = await mangadexData.json();
+
+ if (mangadexDataJson['data'] === undefined || mangadexDataJson['data'].length === 0)
+ return await tryRecentMediaActivities();
+
+ const mangadexId = mangadexDataJson['data'][0]['id'];
+ const lastChapterDataJson = await (
+ await fetch(
+ proxy(
+ `https://api.mangadex.org/manga/${mangadexId}/feed?order[chapter]=desc&translatedLanguage[]=en&limit=1&contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic`
+ )
+ )
+ ).json();
+
+ if (lastChapterDataJson['data'] === undefined || lastChapterDataJson['data'].length === 0)
+ return await tryRecentMediaActivities();
+
+ lastChapter = lastChapterDataJson['data'][0]['attributes']['chapter'];
+ completedVolumes = null;
+
+ if ((manga.mediaListEntry || { progress: 0 }).progress > lastChapter && !disableGuessing) {
+ const anilistData = await recentMediaActivities(
+ identity,
+ manga,
+ settings.get().calculateGuessMethod
+ );
+
+ if (anilistData !== null && anilistData > lastChapter) lastChapter = anilistData;
+ }
+
+ if (!settings.get().calculateDisableOutOfDateVolumeWarning) {
+ const volumeOfChapterData = await (
+ await fetch(
+ proxy(
+ `https://api.mangadex.org/chapter?manga=${mangadexId}&chapter=${manga.mediaListEntry?.progress}&contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic&limit=1`
+ )
+ )
+ ).json();
+ let lastAvailableVolume = lastChapterDataJson['data'][0]['attributes']['volume'];
+
+ if (lastAvailableVolume === null) {
+ let chapterIndex = 0;
+
+ while (chapterIndex < lastChapterDataJson['data'].length && lastAvailableVolume === null) {
+ if (lastChapterDataJson['data'][chapterIndex]['attributes']['volume'] !== null)
+ lastAvailableVolume = lastChapterDataJson['data'][chapterIndex]['attributes']['volume'];
+
+ chapterIndex += 1;
+ }
+ }
+
+ if (volumeOfChapterData['data'] !== undefined && volumeOfChapterData['data'].length > 0) {
+ const volumeOfChapter = volumeOfChapterData['data'][0]['attributes']['volume'];
+
+ if (volumeOfChapter !== null) completedVolumes = volumeOfChapter;
+
+ if (completedVolumes === volumeOfChapter) completedVolumes -= 1;
+ }
+ }
+ } else {
+ lastChapter = (await getChapterCount(manga.title.native)) || 0;
+ }
+
+ if (lastChapter == 0) lastChapter = -1;
+
+ await database.chapters.put({
+ id: manga.id,
+ chapters: Number(lastChapter),
+ volumes: completedVolumes
+ });
+
+ return Number(lastChapter);
};
diff --git a/src/lib/Media/Manga/volumes.ts b/src/lib/Media/Manga/volumes.ts
index d6fda96d..05ae571d 100644
--- a/src/lib/Media/Manga/volumes.ts
+++ b/src/lib/Media/Manga/volumes.ts
@@ -2,4 +2,4 @@ import type { Media } from '$lib/Data/AniList/media';
import { database } from '../../Database/IDB/chapters';
export const volumeCount = async (manga: Media): Promise<number | null> =>
- (await database.chapters.get(manga.id))?.volumes as number | null;
+ (await database.chapters.get(manga.id))?.volumes as number | null;
diff --git a/src/lib/Media/links.ts b/src/lib/Media/links.ts
index 85b696d9..626e892a 100644
--- a/src/lib/Media/links.ts
+++ b/src/lib/Media/links.ts
@@ -2,50 +2,50 @@ import type { Media } from '$lib/Data/AniList/media';
import type { PrequelRelationNode } from '$lib/Data/AniList/prequels';
export const outboundLink = (
- media: Media | PrequelRelationNode | null,
- type: 'anime' | 'manga',
- setting: 'anilist' | 'livechartme' | 'animeschedule' | 'myanimelist',
- search = false,
- title: string | null = null
+ media: Media | PrequelRelationNode | null,
+ type: 'anime' | 'manga',
+ setting: 'anilist' | 'livechartme' | 'animeschedule' | 'myanimelist',
+ search = false,
+ title: string | null = null
) => {
- media = media as Media;
+ media = media as Media;
- if (type === 'manga')
- switch (setting) {
- case 'livechartme':
- case 'animeschedule':
- return `https://anilist.co/${type}/${media.id}/`;
- case 'myanimelist':
- return media.idMal
- ? `https://myanimelist.net/manga/${media.idMal}`
- : `https://myanimelist.net/manga.php?q=${encodeURIComponent(
- media.title.native || media.title.english || media.title.romaji
- )}&cat=manga`;
- default:
- return `https://anilist.co/${type}/${media.id}/`;
- }
- else
- switch (setting) {
- case 'anilist':
- return search
- ? `https://anilist.co/search?search=${encodeURIComponent(title || '')}`
- : `https://anilist.co/${type}/${media.id}/`;
- case 'livechartme':
- return `https://www.livechart.me/search?q=${encodeURIComponent(
- title || media.title.native || media.title.english || media.title.romaji
- )}`;
- case 'animeschedule':
- return `https://animeschedule.net/shows?q=${encodeURIComponent(
- title || media.title.native || media.title.english || media.title.romaji
- )}`;
- case 'myanimelist': {
- return search
- ? `https://myanimelist.net/anime.php?q=${title}&cat=anime`
- : media.idMal
- ? `https://myanimelist.net/anime/${media.idMal}`
- : `https://myanimelist.net/anime.php?q=${encodeURIComponent(
- media.title.native || media.title.english || media.title.romaji
- )}&cat=anime`;
- }
- }
+ if (type === 'manga')
+ switch (setting) {
+ case 'livechartme':
+ case 'animeschedule':
+ return `https://anilist.co/${type}/${media.id}/`;
+ case 'myanimelist':
+ return media.idMal
+ ? `https://myanimelist.net/manga/${media.idMal}`
+ : `https://myanimelist.net/manga.php?q=${encodeURIComponent(
+ media.title.native || media.title.english || media.title.romaji
+ )}&cat=manga`;
+ default:
+ return `https://anilist.co/${type}/${media.id}/`;
+ }
+ else
+ switch (setting) {
+ case 'anilist':
+ return search
+ ? `https://anilist.co/search?search=${encodeURIComponent(title || '')}`
+ : `https://anilist.co/${type}/${media.id}/`;
+ case 'livechartme':
+ return `https://www.livechart.me/search?q=${encodeURIComponent(
+ title || media.title.native || media.title.english || media.title.romaji
+ )}`;
+ case 'animeschedule':
+ return `https://animeschedule.net/shows?q=${encodeURIComponent(
+ title || media.title.native || media.title.english || media.title.romaji
+ )}`;
+ case 'myanimelist': {
+ return search
+ ? `https://myanimelist.net/anime.php?q=${title}&cat=anime`
+ : media.idMal
+ ? `https://myanimelist.net/anime/${media.idMal}`
+ : `https://myanimelist.net/anime.php?q=${encodeURIComponent(
+ media.title.native || media.title.english || media.title.romaji
+ )}&cat=anime`;
+ }
+ }
};