From 63995c24fdbf05ee5aa3efa2f61c049ed2f91109 Mon Sep 17 00:00:00 2001 From: Inrixia Date: Fri, 31 Jan 2025 04:52:12 +1300 Subject: [PATCH] RealMAX - Attempt to improve resiliency --- plugins/RealMAX/src/index.ts | 79 ++++++++++++++++++++++------ plugins/_lib/Caches/AsyncCachable.ts | 4 +- plugins/_lib/MaxTrack.ts | 2 +- plugins/_lib/api/tidal/isrc.ts | 2 +- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/plugins/RealMAX/src/index.ts b/plugins/RealMAX/src/index.ts index 1d21d416..26a2d300 100644 --- a/plugins/RealMAX/src/index.ts +++ b/plugins/RealMAX/src/index.ts @@ -13,28 +13,75 @@ import { MaxTrack } from "@inrixia/lib/MaxTrack"; import { ContextMenu } from "@inrixia/lib/ContextMenu"; import { AlbumCache } from "@inrixia/lib/Caches/AlbumCache"; import { settings } from "./Settings"; +import type { PlayQueueItem } from "neptune-types/tidal"; export { Settings } from "./Settings"; -const unloadIntercept = intercept( - "playbackControls/MEDIA_PRODUCT_TRANSITION", - debounce(async () => { - const { elements, currentIndex } = store.getState().playQueue; - const queueId = elements[currentIndex]?.mediaItemId; - const nextQueueId = elements[currentIndex + 1]?.mediaItemId; +const maxQueueItem = async (elements: readonly PlayQueueItem[], currentIndex: number, jumpTo?: number) => { + jumpTo ??= currentIndex; + const newElements = [...elements]; + const currentId = newElements[currentIndex].mediaItemId; + const maxItem = await MaxTrack.getMaxTrack(currentId); + MaxTrack.getMaxTrack(newElements[currentIndex + 1].mediaItemId); + if (maxItem !== false && maxItem.id !== undefined) { + newElements[currentIndex].mediaItemId = maxItem.id; + actions.playQueue.reset({ + elements: newElements, + currentIndex: jumpTo, + }); + return true; + } + return false; +}; - const maxItem = await MaxTrack.getMaxTrack(queueId); - if (maxItem === false) return; - if (maxItem.id !== undefined && nextQueueId !== maxItem.id) { - if (settings.displayInfoPopups) trace.msg.log(`Found Max quality for ${maxItem.title}! Adding to queue and skipping...`); - actions.playQueue.addNext({ mediaItemIds: [maxItem.id], context: { type: "user" } }); +const unloadTransition = intercept("playbackControls/MEDIA_PRODUCT_TRANSITION", ([{ mediaProduct }]) => { + actions.playbackControls.pause(); + (async () => { + const productId: string = (mediaProduct).productId; + const maxItem = await MaxTrack.getMaxTrack(productId); + if (maxItem !== false && maxItem.id !== undefined) { + actions.playQueue.addNext({ mediaItemIds: [maxItem.id], context: { type: "UNKNOWN" } }); actions.playQueue.moveNext(); } - // Preload next two - MaxTrack.getMaxTrack(elements[currentIndex + 1]?.mediaItemId); - MaxTrack.getMaxTrack(elements[currentIndex + 2]?.mediaItemId); - }, 125) -); + actions.playbackControls.play(); + })(); +}); + +const unloadAddNow = intercept("playQueue/ADD_NOW", ([payload]) => { + (async () => { + const mediaItemIds = [...payload.mediaItemIds]; + const currentIndex = payload.fromIndex ?? 0; + const maxItem = await MaxTrack.getMaxTrack(mediaItemIds[currentIndex]); + if (maxItem !== false && maxItem.id !== undefined) mediaItemIds[currentIndex] = maxItem.id; + actions.playQueue.addNow({ ...payload, mediaItemIds }); + })(); + return true; +}); + +const unloadSkip = intercept(["playQueue/MOVE_TO", "playQueue/MOVE_NEXT", "playQueue/MOVE_PREVIOUS"], ([payload, action]) => { + (async () => { + const { elements, currentIndex } = store.getState().playQueue; + switch (action) { + case "playQueue/MOVE_NEXT": + if ((await maxQueueItem(elements, currentIndex + 1)) === false) actions.playQueue.moveNext(); + break; + case "playQueue/MOVE_PREVIOUS": + if ((await maxQueueItem(elements, currentIndex - 1)) === false) actions.playQueue.movePrevious(); + break; + case "playQueue/MOVE_TO": + if ((await maxQueueItem(elements, payload ?? currentIndex)) === false) actions.playQueue.moveTo(payload ?? currentIndex); + break; + } + actions.playbackControls.play(); + })(); + return true; +}); + +const unloadIntercept = () => { + unloadTransition(); + unloadAddNow(); + unloadSkip(); +}; ContextMenu.onOpen(async (contextSource, contextMenu, trackItems) => { document.getElementById("realMax-button")?.remove(); diff --git a/plugins/_lib/Caches/AsyncCachable.ts b/plugins/_lib/Caches/AsyncCachable.ts index 7ea35504..1be1ea23 100644 --- a/plugins/_lib/Caches/AsyncCachable.ts +++ b/plugins/_lib/Caches/AsyncCachable.ts @@ -1,7 +1,9 @@ export const AsyncCachable = (generator: (key: K) => Promise) => { const _cache: Record> = {}; - return (key: K): Promise => { + const _func = (key: K): Promise => { if (key in _cache) return _cache[key]; return (_cache[key] = generator(key)); }; + _func._cache = _cache; + return _func; }; diff --git a/plugins/_lib/MaxTrack.ts b/plugins/_lib/MaxTrack.ts index c309ac22..e5d81a7c 100644 --- a/plugins/_lib/MaxTrack.ts +++ b/plugins/_lib/MaxTrack.ts @@ -60,7 +60,7 @@ export class MaxTrack { if (track.type !== "tracks") continue; if (filter && !filter(track)) continue; const trackItem = await MediaItemCache.ensureTrack(track.id); - if (trackItem !== undefined) yield trackItem; + if (trackItem?.id !== undefined) yield trackItem; } } public static hasHiRes(trackItem: TrackItem): boolean { diff --git a/plugins/_lib/api/tidal/isrc.ts b/plugins/_lib/api/tidal/isrc.ts index bb54cf63..b22fddcc 100644 --- a/plugins/_lib/api/tidal/isrc.ts +++ b/plugins/_lib/api/tidal/isrc.ts @@ -19,6 +19,6 @@ export async function* fetchIsrcIterable(isrc: string): AsyncIterable const resp: TApiTracks = await fetchTidal(next); if (resp?.data === undefined || resp.data.length === 0) break; yield* resp.data; - next = `${baseURL}${resp.links.next}`; + next = resp.links.next === undefined ? undefined : `${baseURL}${resp.links.next}`; } }