Skip to content

Commit

Permalink
feat: media cache
Browse files Browse the repository at this point in the history
  • Loading branch information
ThaUnknown committed Feb 8, 2024
1 parent 4cabb01 commit 45b03c0
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 71 deletions.
40 changes: 21 additions & 19 deletions common/modules/al.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export type Media = {
}
}

export type MediaList = {
export type Following = {
status: string
score: number
progress: number
Expand All @@ -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
}[]
}[]
}
Expand Down
124 changes: 77 additions & 47 deletions common/modules/anilist.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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){
Expand All @@ -331,36 +333,49 @@ 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){
${queryObjects}
}
}`

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 }>>} */
Expand Down Expand Up @@ -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)
}

Expand All @@ -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 }[]}>>} */
Expand All @@ -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 }}>>} */
Expand All @@ -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){
Expand Down Expand Up @@ -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()
10 changes: 5 additions & 5 deletions common/modules/animeprogress.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ 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)
}

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)
Expand All @@ -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
Expand Down

0 comments on commit 45b03c0

Please sign in to comment.