diff options
| author | Fuwn <[email protected]> | 2024-10-09 00:41:20 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-10-09 00:41:43 -0700 |
| commit | 998b63a35256ac985a5a2714dd1ca451af4dfd8a (patch) | |
| tree | 50796121a9d5ab0330fdc5d7e098bda2860d9726 /src/lib/Media/Anime/Airing/Subtitled | |
| parent | feat(graphql): add badgeCount field (diff) | |
| download | due.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.ts | 344 | ||||
| -rw-r--r-- | src/lib/Media/Anime/Airing/Subtitled/subsPlease.ts | 16 |
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; } |