aboutsummaryrefslogtreecommitdiff
path: root/src/lib/Media/Anime/Airing/Subtitled
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/Anime/Airing/Subtitled
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/Anime/Airing/Subtitled')
-rw-r--r--src/lib/Media/Anime/Airing/Subtitled/match.ts344
-rw-r--r--src/lib/Media/Anime/Airing/Subtitled/subsPlease.ts16
2 files changed, 180 insertions, 180 deletions
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;
}