mirror of
https://github.com/NoCrypt/migu.git
synced 2026-03-11 17:45:32 +00:00
feat: media cache
This commit is contained in:
parent
4cabb017cb
commit
45b03c068a
3 changed files with 103 additions and 71 deletions
40
common/modules/al.d.ts
vendored
40
common/modules/al.d.ts
vendored
|
|
@ -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
|
||||
}[]
|
||||
}[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue