fix: batch resolves in 50 elements at once

fix: don't run AL when not necessary
fix: reduce compound variable count
This commit is contained in:
ThaUnknown 2024-03-24 21:18:23 +01:00
parent c0d1f97c89
commit 92e5207e3a
6 changed files with 36 additions and 16 deletions

View file

@ -178,6 +178,7 @@ class AnilistClient {
rateLimitPromise = null
/** @type {import('simple-store-svelte').Writable<ReturnType<AnilistClient['getUserLists']>>} */
userLists = writable()
userID
@ -282,16 +283,22 @@ class AnilistClient {
* @param {{key: string, title: string, year?: string, isAdult: boolean}[]} flattenedTitles
**/
async alSearchCompound (flattenedTitles) {
if (!flattenedTitles.length) return []
/** @type {Record<`v${number}`, string>} */
const requestVariables = flattenedTitles.reduce((obj, { title }, i) => {
const requestVariables = flattenedTitles.reduce((obj, { title, isAdult }, i) => {
if (isAdult) return obj
obj[`v${i}`] = title
return obj
}, {})
const queryVariables = flattenedTitles.map((_, i) => `$v${i}: String`).join(', ')
const queryVariables = flattenedTitles.reduce((arr, { isAdult }, i) => {
if (isAdult) return arr
arr.push(`$v${i}: String`)
return arr
}, []).join(', ')
const fragmentQueries = flattenedTitles.map(({ year, isAdult }, i) => /* js */`
v${i}: Page(perPage: 10) {
media(type: ANIME, search: $v${i}, status_in: [RELEASING, FINISHED], isAdult: ${!!isAdult} ${year ? `, seasonYear: ${year}` : ''}) {
media(type: ANIME, search: $v${isAdult ? i - 1 : i}, status_in: [RELEASING, FINISHED], isAdult: ${!!isAdult} ${year ? `, seasonYear: ${year}` : ''}) {
...med
}
}`)

View file

@ -1,6 +1,7 @@
import { toast } from 'svelte-sonner'
import { anilistClient } from './anilist.js'
import { anitomyscript } from './anime.js'
import { chunks } from './util.js'
const postfix = {
1: 'st', 2: 'nd', 3: 'rd'
@ -65,6 +66,7 @@ export default new class AnimeResolver {
* @param {import('anitomyscript').AnitomyResult[]} parseObjects
*/
async findAnimesByTitle (parseObjects) {
if (!parseObjects.length) return
const titleObjects = parseObjects.map(obj => {
const key = this.getCacheKeyForTitle(obj)
const titleObjects = this.alternativeTitles(obj.anime_title).map(title => ({ title, year: obj.anime_year, key, isAdult: false }))
@ -96,14 +98,16 @@ export default new class AnimeResolver {
if (!fileName) return [{}]
const parseObjs = await anitomyscript(fileName)
// batches promises in 10 at a time, because of CF burst protection, which still sometimes gets triggered :/
/** @type {Record<string, import('anitomyscript').AnitomyResult>} */
const uniq = {}
for (const obj of parseObjs) {
const key = this.getCacheKeyForTitle(obj)
if (key in this.animeNameCache) continue
uniq[key] = obj
}
await this.findAnimesByTitle(Object.values(uniq))
for (const chunk of chunks(Object.values(uniq), 50)) {
await this.findAnimesByTitle(chunk)
}
const fileAnimes = []
for (const parseObj of parseObjs) {

View file

@ -91,6 +91,7 @@ function createSections () {
if (media.status === 'FINISHED') return true
return media.mediaListEntry?.progress < media.nextAiringEpisode?.episode - 1
}).map(({ media }) => media.id)
if (!ids.length) return {}
return anilistClient.searchIDS({ page, perPage, id: ids, ...SectionsManager.sanitiseObject(variables) })
})
return SectionsManager.wrapResponse(res, perPage)
@ -106,6 +107,7 @@ function createSections () {
const ids = mediaList.flatMap(({ media }) => {
return media.relations.edges.filter(edge => edge.relationType === 'SEQUEL')
}).map(({ node }) => node.id)
if (!ids.length) return {}
return anilistClient.searchIDS({ page, perPage, id: ids, ...SectionsManager.sanitiseObject(variables), status: ['FINISHED', 'RELEASING'], onList: false })
})
return SectionsManager.wrapResponse(res, perPage)

View file

@ -25,6 +25,18 @@ const ranges = {
seconds: 1
}
/**
* @template T
* @param {T[]} arr
* @param {number} n
*/
export function * chunks (arr, n) {
for (let i = 0; i < arr.length; i += n) {
yield arr.slice(i, i + n)
}
}
/** @param {Date} date */
export function since (date) {
const secondsElapsed = (date.getTime() - Date.now()) / 1000
for (const key in ranges) {
@ -44,9 +56,7 @@ export function fastPrettyBytes (num) {
return Number((num / Math.pow(1000, exponent)).toFixed(2)) + units[exponent]
}
/**
* @type {DOMParser['parseFromString']}
*/
/** @type {DOMParser['parseFromString']} */
export const DOMPARSER = (typeof DOMParser !== 'undefined') && DOMParser.prototype.parseFromString.bind(new DOMParser())
export const sleep = t => new Promise(resolve => setTimeout(resolve, t).unref?.())
@ -65,13 +75,9 @@ export function toTS (sec, full) {
}
}
const hours = Math.floor(sec / 3600)
/**
* @type {any}
*/
/** @type {any} */
let minutes = Math.floor(sec / 60) - hours * 60
/**
* @type {any}
*/
/** @type {any} */
let seconds = full === 1 ? (sec % 60).toFixed(2) : Math.floor(sec % 60)
if (minutes < 10 && (hours > 0 || full)) minutes = '0' + minutes
if (seconds < 10) seconds = '0' + seconds

View file

@ -18,7 +18,8 @@
if (alToken) {
const userSections = ['Continue Watching', 'Sequels You Missed', 'Your List', 'Completed List', 'Paused List', 'Dropped List', 'Currently Watching List']
anilistClient.userLists.subscribe(() => {
anilistClient.userLists.subscribe(value => {
if (!value) return
for (const section of manager.sections) {
// remove preview value, to force UI to re-request data, which updates it once in viewport
if (userSections.includes(section.title)) section.preview.value = section.load(1, 15)

View file

@ -1,6 +1,6 @@
{
"name": "Miru",
"version": "5.0.1",
"version": "5.0.2",
"private": true,
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
"description": "Stream anime torrents, real-time with no waiting for downloads.",