feat: media cache

This commit is contained in:
ThaUnknown 2024-02-08 23:13:36 +01:00
parent 4cabb017cb
commit 45b03c068a
3 changed files with 103 additions and 71 deletions

View file

@ -109,7 +109,7 @@ export type Media = {
}
}
export type MediaList = {
export type Following = {
status: string
score: number
progress: number
@ -121,28 +121,30 @@ export type MediaList = {
}
}
export type MediaListMedia = {
id: number
status: string
mediaListEntry: {
progress: number
}
nextAiringEpisode?: {
episode: number
}
relations?: {
edges: {
relationType: string
node: {
id: number
}
}[]
}
}
export type MediaListCollection = {
lists: {
status: string
entries: {
media: {
id: number
status: string
mediaListEntry: {
progress: number
}
nextAiringEpisode?: {
episode: number
}
relations?: {
edges: {
relationType: string
node: {
id: number
}
}[]
}
}
media: MediaListMedia
}[]
}[]
}

View file

@ -182,6 +182,9 @@ class AnilistClient {
userID
/** @type {Record<number, import('./al.d.ts').Media>} */
mediaCache = {}
constructor () {
this.limiter.on('failed', async (error, jobInfo) => {
printError(error)
@ -315,8 +318,7 @@ class AnilistClient {
}
}
/** @returns {Promise<import('./al.d.ts').PagedQuery<{media: import('./al.d.ts').Media[]}>>} */
searchName (variables = {}) {
async searchName (variables = {}) {
const query = /* js */`
query($page: Int, $perPage: Int, $sort: [MediaSort], $name: String, $status: [MediaStatus], $year: Int, $isAdult: Boolean){
Page(page: $page, perPage: $perPage){
@ -331,11 +333,15 @@ class AnilistClient {
variables.isAdult = variables.isAdult ?? false
return this.alRequest(query, variables)
/** @type {import('./al.d.ts').PagedQuery<{media: import('./al.d.ts').Media[]}>} */
const res = await this.alRequest(query, variables)
this.updateCache(res.data.Page.media)
return res
}
/** @returns {Promise<import('./al.d.ts').Query<{Media: import('./al.d.ts').Media}>>} */
searchIDSingle (variables) {
async searchIDSingle (variables) {
const query = /* js */`
query($id: Int){
Media(id: $id, type: ANIME){
@ -343,24 +349,33 @@ class AnilistClient {
}
}`
return this.alRequest(query, variables)
/** @type {import('./al.d.ts').Query<{Media: import('./al.d.ts').Media}>} */
const res = await this.alRequest(query, variables)
this.updateCache([res.data.Media])
return res
}
/** @returns {Promise<import('./al.d.ts').PagedQuery<{media: import('./al.d.ts').Media[]}>>} */
searchIDS (variables) {
async searchIDS (variables) {
const query = /* js */`
query($id: [Int], $page: Int, $perPage: Int, $status: [MediaStatus], $onList: Boolean, $sort: [MediaSort], $search: String, $season: MediaSeason, $year: Int, $genre: String, $format: MediaFormat){
Page(page: $page, perPage: $perPage){
pageInfo{
hasNextPage
},
media(id_in: $id, type: ANIME, status_in: $status, onList: $onList, search: $search, sort: $sort, season: $season, seasonYear: $year, genre: $genre, format: $format){
${queryObjects}
}
query($id: [Int], $page: Int, $perPage: Int, $status: [MediaStatus], $onList: Boolean, $sort: [MediaSort], $search: String, $season: MediaSeason, $year: Int, $genre: String, $format: MediaFormat){
Page(page: $page, perPage: $perPage){
pageInfo{
hasNextPage
},
media(id_in: $id, type: ANIME, status_in: $status, onList: $onList, search: $search, sort: $sort, season: $season, seasonYear: $year, genre: $genre, format: $format){
${queryObjects}
}
}`
}
}`
return this.alRequest(query, variables)
/** @type {import('./al.d.ts').PagedQuery<{media: import('./al.d.ts').Media[]}>} */
const res = await this.alRequest(query, variables)
this.updateCache(res.data.Page.media)
return res
}
/** @returns {Promise<import('./al.d.ts').Query<{ Viewer: import('./al.d.ts').Viewer }>>} */
@ -418,6 +433,7 @@ class AnilistClient {
}
}`
// this doesn't need to be cached, as SearchIDStatus is already cached, which is the only thing that uses this
return await this.alRequest(query, variables)
}
@ -437,27 +453,31 @@ class AnilistClient {
return await this.alRequest(query, variables)
}
/** @returns {Promise<import('./al.d.ts').PagedQuery<{ airingSchedule: { timeUntilAiring: number, airingAt: number, episode: number, media: import('./al.d.ts').Media[]}}>>} */
searchAiringSchedule (variables = {}) {
async searchAiringSchedule (variables = {}) {
variables.to = (variables.from + 7 * 24 * 60 * 60)
const query = /* js */`
query($page: Int, $perPage: Int, $from: Int, $to: Int){
Page(page: $page, perPage: $perPage){
pageInfo{
hasNextPage
},
airingSchedules(airingAt_greater: $from, airingAt_lesser: $to){
episode,
timeUntilAiring,
airingAt,
media{
${queryObjects}
}
query($page: Int, $perPage: Int, $from: Int, $to: Int){
Page(page: $page, perPage: $perPage){
pageInfo{
hasNextPage
},
airingSchedules(airingAt_greater: $from, airingAt_lesser: $to){
episode,
timeUntilAiring,
airingAt,
media{
${queryObjects}
}
}
}`
}
}`
return this.alRequest(query, variables)
/** @type {import('./al.d.ts').PagedQuery<{ airingSchedules: { timeUntilAiring: number, airingAt: number, episode: number, media: import('./al.d.ts').Media}[]}>} */
const res = await this.alRequest(query, variables)
this.updateCache(res.data.Page.airingSchedules?.map(({ media }) => media))
return res
}
/** @returns {Promise<import('./al.d.ts').PagedQuery<{ airingSchedules: { airingAt: number, episode: number }[]}>>} */
@ -475,21 +495,26 @@ class AnilistClient {
return this.alRequest(query, variables)
}
/** @returns {Promise<import('./al.d.ts').PagedQuery<{media: import('./al.d.ts').Media[]}>>} */
search (variables = {}) {
async search (variables = {}) {
variables.sort ||= 'SEARCH_MATCH'
const query = /* js */`
query($page: Int, $perPage: Int, $sort: [MediaSort], $search: String, $onList: Boolean, $status: MediaStatus, $season: MediaSeason, $year: Int, $genre: String, $format: MediaFormat){
Page(page: $page, perPage: $perPage){
pageInfo{
hasNextPage
},
media(type: ANIME, search: $search, sort: $sort, onList: $onList, status: $status, season: $season, seasonYear: $year, genre: $genre, format: $format, format_not: MUSIC){
${queryObjects}
}
query($page: Int, $perPage: Int, $sort: [MediaSort], $search: String, $onList: Boolean, $status: MediaStatus, $season: MediaSeason, $year: Int, $genre: String, $format: MediaFormat){
Page(page: $page, perPage: $perPage){
pageInfo{
hasNextPage
},
media(type: ANIME, search: $search, sort: $sort, onList: $onList, status: $status, season: $season, seasonYear: $year, genre: $genre, format: $format, format_not: MUSIC){
${queryObjects}
}
}`
return this.alRequest(query, variables)
}
}`
/** @type {import('./al.d.ts').PagedQuery<{media: import('./al.d.ts').Media[]}>} */
const res = await this.alRequest(query, variables)
this.updateCache(res.data.Page.media)
return res
}
/** @returns {Promise<import('./al.d.ts').Query<{ AiringSchedule: { airingAt: number }}>>} */
@ -504,7 +529,7 @@ class AnilistClient {
return this.alRequest(query, variables)
}
/** @returns {Promise<import('./al.d.ts').PagedQuery<{ mediaList: import('./al.d.ts').MediaList[]}>>} */
/** @returns {Promise<import('./al.d.ts').PagedQuery<{ mediaList: import('./al.d.ts').Following[]}>>} */
following (variables) {
const query = /* js */`
query($id: Int){
@ -571,6 +596,11 @@ class AnilistClient {
return this.alRequest(query, variables)
}
/** @param {import('./al.d.ts').Media[]} medias */
updateCache (medias) {
this.mediaCache = { ...this.mediaCache, ...Object.fromEntries(medias.map(media => [media.id, media])) }
}
}
export const anilistClient = new AnilistClient()

View file

@ -5,12 +5,12 @@ const maxEntries = 1000
// LocalStorage is structured as an array of objects with the following properties:
// mediaId, episode, currentTime, safeduration, createdAt, updatedAt
function loadFromLocalStorage() {
function loadFromLocalStorage () {
const data = localStorage.getItem('animeEpisodeProgress')
return data ? JSON.parse(data) : []
}
function saveToLocalStorage(data) {
function saveToLocalStorage (data) {
localStorage.setItem('animeEpisodeProgress', JSON.stringify(data))
animeProgressStore.set(data)
}
@ -18,7 +18,7 @@ function saveToLocalStorage(data) {
const animeProgressStore = writable(loadFromLocalStorage())
// Return an object with the progress of each episode in percent (0-100), keyed by episode number
export function liveAnimeProgress (mediaId){
export function liveAnimeProgress (mediaId) {
return derived(animeProgressStore, (data) => {
if (!mediaId) return {}
const results = data.filter(item => item.mediaId === mediaId)
@ -42,13 +42,13 @@ export function liveAnimeEpisodeProgress (mediaId, episode) {
}
// Return an individual episode's record { mediaId, episode, currentTime, safeduration, createdAt, updatedAt }
export function getAnimeProgress(mediaId, episode) {
export function getAnimeProgress (mediaId, episode) {
const data = loadFromLocalStorage()
return data.find(item => item.mediaId === mediaId && item.episode === episode)
}
// Set an individual episode's progress
export function setAnimeProgress({ mediaId, episode, currentTime, safeduration }) {
export function setAnimeProgress ({ mediaId, episode, currentTime, safeduration }) {
if (!mediaId || !episode || !currentTime || !safeduration) return
const data = loadFromLocalStorage()
// Update the existing entry or create a new one