From 8422acb8e86c0a38220a19db3bed623506bb69df Mon Sep 17 00:00:00 2001 From: Inrixia Date: Sat, 22 Jun 2024 11:27:26 +1200 Subject: [PATCH] Self populating TrackItemCache --- lib/Caches/ExtendedTrackItem.ts | 28 +++++++++++++--------------- lib/Caches/TrackItemCache.ts | 12 +++++++++++- plugins/LastFM/src/index.ts | 16 ++++++++-------- plugins/RealMAX/src/index.ts | 11 +++-------- plugins/SongDownloader/src/index.ts | 4 +++- plugins/TidalTags/src/index.ts | 2 +- 6 files changed, 39 insertions(+), 34 deletions(-) diff --git a/lib/Caches/ExtendedTrackItem.ts b/lib/Caches/ExtendedTrackItem.ts index 3dcbc0c..c4a553b 100644 --- a/lib/Caches/ExtendedTrackItem.ts +++ b/lib/Caches/ExtendedTrackItem.ts @@ -10,20 +10,23 @@ import { libTrace } from "../trace"; export class ExtendedTrackItem { public readonly trackId: ItemId; - private _trackItem?: TrackItem; + public readonly trackItem: TrackItem; private _album?: Album; private _recording?: Recording; private _releaseAlbum?: Release; private static readonly _cache: Record = {}; - private constructor(trackId: ItemId) { + private constructor(trackId: ItemId, trackItem: TrackItem) { this.trackId = trackId; + this.trackItem = trackItem; } - public static get(trackId: ItemId) { + public static async get(trackId: ItemId) { if (trackId === undefined) return undefined; - return this._cache[trackId] ?? (this._cache[trackId] = new this(trackId)); + const trackItem = await TrackItemCache.ensure(trackId); + if (trackItem === undefined) return undefined; + return this._cache[trackId] ?? (this._cache[trackId] = new this(trackId, trackItem)); } public async isrcs(): Promise | undefined> { let isrcs = []; @@ -31,27 +34,22 @@ export class ExtendedTrackItem { const recording = await this.recording(); if (recording?.isrcs) isrcs.push(...recording.isrcs); - const trackItem = this.trackItem(); - if (trackItem?.isrc) isrcs.push(trackItem.isrc); + const trackItem = this.trackItem; + if (trackItem.isrc) isrcs.push(trackItem.isrc); return new Set(isrcs); } - public trackItem(): TrackItem | undefined { - if (this._trackItem !== undefined) return this._trackItem; - - return (this._trackItem = TrackItemCache.get(this.trackId)); - } public async album(): Promise { if (this._album !== undefined) return this._album; - return (this._album = await AlbumCache.get(this.trackItem()?.album?.id)); + return (this._album = await AlbumCache.get(this.trackItem.album?.id)); } public async recording(): Promise { if (this._recording !== undefined) return this._recording; - this._recording = await MusicBrainz.getRecording(this.trackItem()?.isrc).catch(libTrace.warn.withContext("MusicBrainz.getRecording")); + this._recording = await MusicBrainz.getRecording(this.trackItem.isrc).catch(libTrace.warn.withContext("MusicBrainz.getRecording")); if (this._recording !== undefined) return this._recording; - const trackItem = this.trackItem(); + const trackItem = this.trackItem; if (trackItem === undefined) return undefined; const releaseAlbum = await this.releaseAlbum(); @@ -73,7 +71,7 @@ export class ExtendedTrackItem { public async everything() { return { - trackItem: this.trackItem(), + trackItem: this.trackItem, album: await this.album(), releaseAlbum: await this.releaseAlbum(), recording: await this.recording(), diff --git a/lib/Caches/TrackItemCache.ts b/lib/Caches/TrackItemCache.ts index e84552e..28a4868 100644 --- a/lib/Caches/TrackItemCache.ts +++ b/lib/Caches/TrackItemCache.ts @@ -1,9 +1,10 @@ import { store } from "@neptune"; import type { TrackItem, MediaItem, ItemId } from "neptune-types/tidal"; +import { interceptPromise } from "../intercept/interceptPromise"; export class TrackItemCache { private static readonly _cache: Record = {}; - public static get(trackId?: ItemId) { + public static async ensure(trackId?: ItemId) { if (trackId === undefined) return undefined; let mediaItem = this._cache[trackId]; @@ -16,6 +17,15 @@ export class TrackItemCache { this._cache[itemId] = item; } + if (this._cache[trackId] === undefined) { + const currentPage = window.location.pathname; + await interceptPromise(() => neptune.actions.router.replace(`/track/${trackId}`), ["page/IS_DONE_LOADING"], []); + neptune.actions.router.replace(currentPage); + const mediaItems: Record = store.getState().content.mediaItems; + const trackItem = mediaItems[+trackId]?.item; + if (trackItem?.contentType === "track") this._cache[trackId] = trackItem; + } + return this._cache[trackId]; } } diff --git a/plugins/LastFM/src/index.ts b/plugins/LastFM/src/index.ts index c102aa5..f0af81d 100644 --- a/plugins/LastFM/src/index.ts +++ b/plugins/LastFM/src/index.ts @@ -74,7 +74,7 @@ const intercepters = [ .then((res) => trace.log("scrobbled", res)); }); } else { - const trackTitle = currentTrack.extTrackItem.trackItem()?.title; + const trackTitle = currentTrack.extTrackItem.trackItem.title; const noScrobbleMessage = `skipped scrobbling ${trackTitle} - Listened for ${(totalPlayTime / 1000).toFixed(0)}s, need ${(minPlayTime / 1000).toFixed(0)}s`; if (storage.displaySkippedScrobbles) trace.msg.log(`${noScrobbleMessage}`); } @@ -90,13 +90,13 @@ const getTrackParams = async ({ extTrackItem, playbackContext, playbackStart }: const { trackItem, releaseAlbum, recording, album } = await extTrackItem.everything(); let artist; - const sharedAlbumArtist = trackItem?.artists?.find((artist) => artist?.id === album?.artist?.id); + const sharedAlbumArtist = trackItem.artists?.find((artist) => artist?.id === album?.artist?.id); if (sharedAlbumArtist?.name !== undefined) artist = formatArtists([sharedAlbumArtist.name]); - else if (trackItem?.artist?.name !== undefined) artist = formatArtists([trackItem.artist.name]); - else if ((trackItem?.artists?.length ?? -1) > 0) artist = formatArtists(trackItem?.artists?.map(({ name }) => name)); + else if (trackItem.artist?.name !== undefined) artist = formatArtists([trackItem.artist.name]); + else if ((trackItem.artists?.length ?? -1) > 0) artist = formatArtists(trackItem.artists?.map(({ name }) => name)); const params: ScrobbleOpts = { - track: recording?.title ?? fullTitle(trackItem), + track: recording?.title ?? fullTitle(trackItem), artist: artist!, timestamp: (playbackStart / 1000).toFixed(0), }; @@ -109,9 +109,9 @@ const getTrackParams = async ({ extTrackItem, playbackContext, playbackStart }: if (!!releaseAlbum?.title) { params.album = releaseAlbum?.title; if (!!releaseAlbum.disambiguation) params.album += ` (${releaseAlbum.disambiguation})`; - } else if (!!trackItem?.album?.title) params.album = trackItem.album.title; + } else if (!!trackItem.album?.title) params.album = trackItem.album.title; - if (!!trackItem?.trackNumber) params.trackNumber = trackItem.trackNumber.toString(); + if (!!trackItem.trackNumber) params.trackNumber = trackItem.trackNumber.toString(); if (!!playbackContext.actualDuration) params.duration = playbackContext.actualDuration.toFixed(0); return params; @@ -131,7 +131,7 @@ const getCurrentTrack = async (playbackContext?: PlaybackContext): Promisestore.getState().playbackControls.playbackContext; if (!playbackContext) throw new Error("No playbackContext found"); - const extTrackItem = ExtendedTrackItem.get(playbackContext.actualProductId); + const extTrackItem = await ExtendedTrackItem.get(playbackContext.actualProductId); if (extTrackItem === undefined) throw new Error("Failed to get extTrackItem"); const currentTrack = { extTrackItem, playbackContext, playbackStart }; diff --git a/plugins/RealMAX/src/index.ts b/plugins/RealMAX/src/index.ts index 9233104..256676a 100644 --- a/plugins/RealMAX/src/index.ts +++ b/plugins/RealMAX/src/index.ts @@ -28,7 +28,7 @@ class MaxTrack { if (idMapping !== undefined) return idMapping; const extTrackItem = await ExtendedTrackItem.get(itemId); - const trackItem = extTrackItem?.trackItem(); + const trackItem = extTrackItem?.trackItem; if (trackItem !== undefined && hasHiRes(trackItem)) return false; const isrcs = await extTrackItem?.isrcs(); @@ -39,7 +39,7 @@ class MaxTrack { for await (const { resource } of fetchIsrcIterable(isrc)) { if (resource?.id !== undefined && hasHiRes(resource)) { if (resource.artifactType !== "track") continue; - const maxTrackItem = TrackItemCache.get(resource?.id); + const maxTrackItem = await TrackItemCache.ensure(resource?.id); if (maxTrackItem !== undefined && !hasHiRes(maxTrackItem)) continue; else return resource; } @@ -60,12 +60,7 @@ export const onUnload = intercept( const maxItem = await MaxTrack.getMaxId(queueId); if (maxItem === false) return; if (maxItem.id !== undefined && nextQueueId !== maxItem.id) { - if (TrackItemCache.get(maxItem.id) === undefined) { - // Force load - const currentPage = window.location.pathname; - actions.router.replace(`/album/${maxItem.album!.id}`); - setTimeout(() => actions.router.replace(currentPage), 50); - } + await TrackItemCache.ensure(maxItem.id); trace.msg.log(`Found Max quality for ${maxItem.title}! Adding to queue and skipping...`); actions.playQueue.addNext({ mediaItemIds: [maxItem.id], context: { type: "user" } }); actions.playQueue.moveNext(); diff --git a/plugins/SongDownloader/src/index.ts b/plugins/SongDownloader/src/index.ts index c800887..9747998 100644 --- a/plugins/SongDownloader/src/index.ts +++ b/plugins/SongDownloader/src/index.ts @@ -88,7 +88,9 @@ const onPlaylist = async (playlistUUID: ItemId) => { }; const queueMediaIds = (mediaIds: ItemId[]) => { - downloadItems(mediaIds.map((mediaId) => TrackItemCache.get(mediaId)).filter((item) => item !== undefined)); + Promise.all(mediaIds.map(TrackItemCache.ensure)) + .then((tracks) => tracks.filter((item) => item !== undefined)) + .then(downloadItems); }; const downloadItems = (items: (TrackItem | VideoItem)[]) => diff --git a/plugins/TidalTags/src/index.ts b/plugins/TidalTags/src/index.ts index b52c82e..820328b 100644 --- a/plugins/TidalTags/src/index.ts +++ b/plugins/TidalTags/src/index.ts @@ -43,7 +43,7 @@ const updateTrackRows = async (trackRows: NodeListOf) => { const trackId = trackRow.getAttribute("data-track-id"); if (trackId == null) return; - const trackItem = TrackItemCache.get(trackId); + const trackItem = await TrackItemCache.ensure(trackId); if (trackItem?.contentType !== "track") continue; if (storage.showTags) setQualityTags(trackRow, trackId, trackItem);