import { DOMPARSER } from '@/modules/util.js' import { settings } from '@/modules/settings.js' import { toast } from 'svelte-sonner' import { add } from '@/modules/torrent.js' import { getEpisodeMetadataForMedia } from './anime.js' import AnimeResolver from '@/modules/animeresolver.js' import { hasNextPage } from '@/modules/sections.js' export const exclusions = ['DTS', '[EMBER]'] const isDev = location.hostname === 'localhost' const video = document.createElement('video') if (!isDev && !video.canPlayType('video/mp4; codecs="hev1.1.6.L93.B0"')) { exclusions.push('HEVC', 'x265', 'H.265') } if (!isDev && !video.canPlayType('audio/mp4; codecs="ac-3"')) { exclusions.push('AC3', 'AC-3') } video.remove() export function parseRSSNodes (nodes) { return nodes.map(item => { const pubDate = item.querySelector('pubDate')?.textContent return { title: item.querySelector('title')?.textContent || '?', link: item.querySelector('enclosure')?.attributes.url.value || item.querySelector('link')?.textContent || '?', seeders: item.querySelector('seeders')?.textContent ?? '?', leechers: item.querySelector('leechers')?.textContent ?? '?', downloads: item.querySelector('downloads')?.textContent ?? '?', size: item.querySelector('size')?.textContent ?? '?', date: pubDate && new Date(pubDate) } }) } const rssmap = { SubsPlease: settings.value.toshoURL + 'rss2?qx=1&q="[SubsPlease] "', 'Erai-raws [Multi-Sub]': settings.value.toshoURL + 'rss2?qx=1&q="[Erai-raws] "', 'Yameii [Dubbed]': settings.value.toshoURL + 'rss2?qx=1&q="[Yameii] "', 'Judas [Small Size]': settings.value.toshoURL + 'rss2?qx=1&q="[Judas] "' } export function getReleasesRSSurl (val) { const rss = rssmap[val] || val return rss && new URL(rssmap[val] ? `${rss}${settings.value.rssQuality ? `"${settings.value.rssQuality}"` : ''}` : rss) } export async function getRSSContent (url) { if (!url) return null try { const res = await fetch(url) if (!res.ok) { throw new Error('Failed fetching RSS!\n' + res.statusText) } return DOMPARSER(await res.text(), 'text/xml') } catch (e) { throw new Error('Failed fetching RSS!\n' + e.message) } } class RSSMediaManager { constructor () { this.resultMap = {} this.lastResult = null } getMediaForRSS (page, perPage, url, ignoreErrors) { const res = this._getMediaForRSS(page, perPage, url) if (!ignoreErrors) { res.catch(e => { toast.error('Search Failed', { description: 'Failed fetching RSS!\n' + e.message }) console.error('Failed to fetch rss', e) }) } return Array.from({ length: perPage }, (_, i) => ({ type: 'episode', data: this.fromPending(res, i) })) } async fromPending (result, i) { const array = await result return array[i] } async getContentChanged (page, perPage, url) { const content = await getRSSContent(getReleasesRSSurl(url)) if (!content) return false const pubDate = +(new Date(content.querySelector('pubDate').textContent)) * page * perPage if (this.resultMap[url]?.date === pubDate) return false return { content, pubDate } } async _getMediaForRSS (page, perPage, url) { const changed = await this.getContentChanged(page, perPage, url) if (!changed) return this.resultMap[url].result const index = (page - 1) * perPage const targetPage = [...changed.content.querySelectorAll('item')].slice(index, index + perPage) const items = parseRSSNodes(targetPage) hasNextPage.value = items.length === perPage const result = items.map(item => this.resolveAnimeFromRSSItem(item)) this.resultMap[url] = { date: changed.pubDate, result } return result } resolveAnimeFromRSSItem (item) { this.lastResult = this.queueResolve(item) return this.lastResult } async queueResolve ({ title, link, date }) { await this.lastResult const res = { ...(await AnimeResolver.resolveFileAnime(title))[0], episodeData: undefined, date: undefined, onclick: undefined } if (res.media?.id) { try { res.episodeData = (await getEpisodeMetadataForMedia(res.media))?.[res.episode] } catch (e) { console.warn('failed fetching episode metadata', e) } } res.date = date res.onclick = () => add(link) return res } } export const RSSManager = new RSSMediaManager()