migu/common/modules/providers/cat.js
2023-11-12 14:43:06 +01:00

147 lines
6.4 KiB
JavaScript

import { alRequest } from '@/modules/anilist.js'
import { settings } from '@/modules/settings.js'
import { findEdge, resolveSeason, getMediaMaxEp, mapBestRelease } from '../anime.js'
import { exclusions, getRSSContent, parseRSSNodes } from '../rss.js'
export default async function getRSSEntries ({ media, episode, mode, ignoreQuality }) {
// mode cuts down on the amt of queries made 'check' || 'batch'
const titles = createTitle(media).join(')|(')
const prequel = findEdge(media, 'PREQUEL')?.node
const sequel = findEdge(media, 'SEQUEL')?.node
const isBatch = media.status === 'FINISHED' && media.episodes !== 1
// if media has multiple seasons, and this S is > 1, then get the absolute episode number of the episode
const absolute = prequel && !mode && (await resolveSeason({ media, episode, force: true }))
const absoluteep = absolute?.offset + episode
const episodes = [episode]
// only use absolute episode number if its smaller than max episodes this series has, ex:
// looking for E1 of S2, S1 has 12 ep and S2 has 13, absolute will be 13
// so this would find the 13th ep of the 2nd season too if this check wasnt here
if (absolute && absoluteep < (getMediaMaxEp(media) || episode)) {
episodes.push(absoluteep)
}
let ep = ''
if (media.episodes !== 1 && mode !== 'batch') {
if (isBatch) {
const digits = Math.max(2, Math.log(media.episodes) * Math.LOG10E + 1 | 0)
ep = `"${zeropad(1, digits)}-${zeropad(media.episodes, digits)}"|"${zeropad(1, digits)}~${zeropad(media.episodes, digits)}"|"Batch"|"Complete"|"${zeropad(episode)}+"|"${zeropad(episode)}v"`
} else {
ep = `(${episodes.map(epstring).join('|')})`
}
}
const excl = exclusions.join('|')
const quality = (!ignoreQuality && (`"${settings.value.rssQuality}"` || '"1080"')) || ''
const url = new URL(`${settings.value.catURL}/?page=rss&c=1_2&f=0&s=seeders&o=desc&q=(${titles})${ep}${quality}-(${excl})`)
let nodes = [...(await getRSSContent(url)).querySelectorAll('item')]
if (absolute) {
// if this is S > 1 aka absolute ep number exists get entries for S1title + absoluteEP
// the reason this isnt done with recursion like sequelEntries is because that would include the S1 media dates
// we want the dates of the target media as the S1 title might be used for SX releases
const titles = createTitle(absolute.media).join(')|(')
const url = new URL(`${settings.value.catURL}/?page=rss&c=1_2&f=0&s=seeders&o=desc&q=(${titles})${epstring(absoluteep)}${quality}-(${excl})`)
nodes = [...nodes, ...(await getRSSContent(url)).querySelectorAll('item')]
}
let entries = parseRSSNodes(nodes)
const checkSequelDate = media.status === 'FINISHED' && (sequel?.status === 'FINISHED' || sequel?.status === 'RELEASING') && sequel.startDate
const sequelStartDate = checkSequelDate && new Date(Object.values(checkSequelDate).join(' '))
// recursive, get all entries for media sequel, and its sequel, and its sequel
const sequelEntries =
(sequel?.status === 'FINISHED' || sequel?.status === 'RELEASING') &&
(await getRSSEntries({ media: (await alRequest({ method: 'SearchIDSingle', id: sequel.id })).data.Media, episode, mode: mode || 'check' }))
const checkPrequelDate = (media.status === 'FINISHED' || media.status === 'RELEASING') && prequel?.status === 'FINISHED' && prequel?.endDate
const prequelEndDate = checkPrequelDate && new Date(Object.values(checkPrequelDate).join(' '))
// 1 month in MS, a bit of jitter for pre-releases and releasers being late as fuck, lets hope it doesnt cause issues
const month = 2674848460
if (prequelEndDate) {
entries = entries.filter(entry => entry.date > new Date(+prequelEndDate + month))
}
if (sequelStartDate && media.format === 'TV') {
entries = entries.filter(entry => entry.date < new Date(+sequelStartDate - month))
}
if (sequelEntries?.length) {
if (mode === 'check') {
entries = [...entries, ...sequelEntries]
} else {
entries = entries.filter(entry => !sequelEntries.find(sequel => sequel.link === entry.link))
}
}
// this gets entries without any episode limiting, and for batches
const batchEntries = !mode && isBatch && (await getRSSEntries({ media, episode, ignoreQuality, mode: 'batch' })).filter(entry => {
return !epNumRx.test(entry.title)
})
if (batchEntries?.length) {
entries = [...entries, ...batchEntries]
}
// some archaic shows only have shit DVD's in weird qualities, so try to look up without any quality restrictions when there are no results
if (!entries.length && !ignoreQuality && !mode) {
entries = await getRSSEntries({ media, episode, ignoreQuality: true })
}
// dedupe
const ids = entries.map(e => e.link)
return mapBestRelease(entries.filter(({ link }, index) => !ids.includes(link, index + 1)))
}
// padleft a variable with 0 ex: 1 => '01'
function zeropad (v = 1, l = 2) {
return (typeof v === 'string' ? v : v.toString()).padStart(l, '0')
}
const epstring = ep => `"E${zeropad(ep)}+"|"E${zeropad(ep)}v"|"+${zeropad(ep)}+"|"+${zeropad(ep)}v"`
// [EO]?[-EPD _—]\d{2}(?:[-v _.—]|$)
// /[EO]?[-EPD]\d{2}(?:[-v.]|$)|[EO]?[EPD ]\d{2}(?:[v .]|$)|[EO]?[EPD_]\d{2}(?:[v_.]|$)|[EO]?[EPD—]\d{2}(?:[v.—]|$)|\d{2} ?[-~—] ?\d{2}/i
// matches: OP01 ED01 EP01 E01 01v 01. -01- _01_ with spaces and stuff
const epNumRx = /[EO]?[-EPD]\d{2}(?:[-v.]|$)|[EO]?[EPD ]\d{2}(?:[v .]|$)|[EO]?[EPD_]\d{2}(?:[v_.]|$)|[EO]?[EPD—]\d{2}(?:[v.—]|$)|\d{2} ?[-~—] ?\d{2}/i
// create an array of potentially valid titles from a given media
function createTitle (media) {
// group and de-duplicate
const grouped = [
...new Set(
Object.values(media.title)
.concat(media.synonyms)
.filter(name => name != null && name.length > 3)
)
]
const titles = []
const appendTitle = t => {
// replace & with encoded
const title = t.replace(/&/g, '%26').replace(/\?/g, '%3F').replace(/#/g, '%23')
titles.push(title)
// replace Season 2 with S2, else replace 2nd Season with S2, but keep the original title
const match1 = title.match(/(\d)(?:nd|rd|th) Season/i)
const match2 = title.match(/Season (\d)/i)
if (match2) {
titles.push(title.replace(/Season \d/i, `S${match2[1]}`))
} else if (match1) {
titles.push(title.replace(/(\d)(?:nd|rd|th) Season/i, `S${match1[1]}`))
}
}
for (const t of grouped) {
appendTitle(t)
if (t.includes('-')) appendTitle(t.replaceAll('-', ''))
}
return titles
}