From b48ee588aa348845c49299e12006010dfc0b94a7 Mon Sep 17 00:00:00 2001 From: Daniel <790119+DanTheMan827@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:26:49 -0500 Subject: [PATCH 1/2] fix(api): don't cache direct queries This also adds a global `window.allTidal` flag to force all queries to go through the API, which is useful for testing. --- js/api.js | 76 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/js/api.js b/js/api.js index 383a2e7..2b6f394 100644 --- a/js/api.js +++ b/js/api.js @@ -21,7 +21,7 @@ import { isCustomFormat } from './ffmpegFormats.ts'; import { DownloadProgress } from './progressEvents.js'; import { resolveDownloadTotalBytes } from './downloadProgressUtils.js'; import { readableStreamIterator } from './readableStreamIterator.js'; -import { HiFiClient } from './HiFi.ts'; +import { HiFiClient, TidalResponse } from './HiFi.ts'; export const DASH_MANIFEST_UNAVAILABLE_CODE = 'DASH_MANIFEST_UNAVAILABLE'; export { resolveDownloadTotalBytes }; @@ -58,7 +58,7 @@ export class LosslessAPI { const type = options.type || 'api'; const instanceRoutes = ['/track', '/album/similar', '/artist/similar', '/video']; - if (!instanceRoutes.some((route) => relativePath.startsWith(route))) { + if (window.allTidal == true || !instanceRoutes.some((route) => relativePath.startsWith(route))) { try { console.log(relativePath); return await client.queryResponse(relativePath); @@ -435,7 +435,9 @@ export class LosslessAPI { items: preparedTracks, }; - await this.cache.set('search_tracks', query, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('search_tracks', query, result); + } return result; } catch (error) { if (error.name === 'AbortError') throw error; @@ -457,7 +459,9 @@ export class LosslessAPI { items: normalized.items.map((a) => this.prepareArtist(a)), }; - await this.cache.set('search_artists', query, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('search_artists', query, result); + } return result; } catch (error) { if (error.name === 'AbortError') throw error; @@ -480,7 +484,9 @@ export class LosslessAPI { items: this.deduplicateAlbums(preparedItems), }; - await this.cache.set('search_albums', query, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('search_albums', query, result); + } return result; } catch (error) { if (error.name === 'AbortError') throw error; @@ -502,7 +508,9 @@ export class LosslessAPI { items: normalized.items.map((p) => this.preparePlaylist(p)), }; - await this.cache.set('search_playlists', query, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('search_playlists', query, result); + } return result; } catch (error) { if (error.name === 'AbortError') throw error; @@ -527,7 +535,9 @@ export class LosslessAPI { items: normalized.items.map((v) => this.prepareVideo(v)), }; - await this.cache.set('search_videos', query, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('search_videos', query, result); + } return result; } catch (error) { if (error.name === 'AbortError') throw error; @@ -554,7 +564,9 @@ export class LosslessAPI { originalTrackUrl: data.OriginalTrackUrl || null, }; - await this.cache.set('video', id, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('video', id, result); + } return result; } @@ -683,7 +695,9 @@ export class LosslessAPI { const result = { album, tracks }; - await this.cache.set('album', id, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('album', id, result); + } return result; } @@ -793,7 +807,9 @@ export class LosslessAPI { const result = { playlist, tracks }; - await this.cache.set('playlist', id, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('playlist', id, result); + } return result; } @@ -827,7 +843,9 @@ export class LosslessAPI { }; const result = { mix, tracks }; - await this.cache.set('mix', id, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('mix', id, result); + } return result; } @@ -984,7 +1002,9 @@ export class LosslessAPI { const result = { ...artist, albums, eps, tracks, videos }; - await this.cache.set('artist', cacheKey, result); + if (!(primaryResponse instanceof TidalResponse) && !(contentResponse instanceof TidalResponse)) { + await this.cache.set('artist', cacheKey, result); + } return result; } @@ -1004,7 +1024,9 @@ export class LosslessAPI { const result = items.map((artist) => this.prepareArtist(artist)); - await this.cache.set('similar_artists', artistId, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('similar_artists', artistId, result); + } return result; } catch (e) { console.warn('Failed to fetch similar artists:', e); @@ -1032,7 +1054,9 @@ export class LosslessAPI { text: data.text, source: data.source || 'Tidal', }; - await this.cache.set('artist', cacheKey, bio); + if (!(response instanceof TidalResponse)) { + await this.cache.set('artist', cacheKey, bio); + } return bio; } } @@ -1057,7 +1081,9 @@ export class LosslessAPI { const result = items.map((album) => this.prepareAlbum(album)); - await this.cache.set('similar_albums', albumId, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('similar_albums', albumId, result); + } return result; } catch (e) { console.warn('Failed to fetch similar albums:', e); @@ -1197,7 +1223,9 @@ export class LosslessAPI { if (found) { track = this.prepareTrack(found.item || found); - await this.cache.set('track', cacheKey, track); + if (!(response instanceof TidalResponse)) { + await this.cache.set('track', cacheKey, track); + } return track; } @@ -1219,7 +1247,9 @@ export class LosslessAPI { const items = data.items || []; const tracks = items.map((item) => this.prepareTrack(item.track || item)); - await this.cache.set('recommendations', id, tracks); + if (!(response instanceof TidalResponse)) { + await this.cache.set('recommendations', id, tracks); + } return tracks; } catch (error) { console.error('Failed to fetch recommendations:', error); @@ -1236,7 +1266,9 @@ export class LosslessAPI { const jsonResponse = await response.json(); const result = this.parseTrackLookup(this.normalizeTrackResponse(jsonResponse)); - await this.cache.set('track', cacheKey, result); + if (!(response instanceof TidalResponse)) { + await this.cache.set('track', cacheKey, result); + } return result; } @@ -1259,7 +1291,9 @@ export class LosslessAPI { } } - this.streamCache.set(cacheKey, streamUrl); + if (!(lookup instanceof TidalResponse)) { + this.streamCache.set(cacheKey, streamUrl); + } return streamUrl; } @@ -1304,7 +1338,9 @@ export class LosslessAPI { throw new Error(`Could not resolve video stream URL for ID: ${id}`); } - this.streamCache.set(cacheKey, streamUrl); + if (!(lookup instanceof TidalResponse)) { + this.streamCache.set(cacheKey, streamUrl); + } return streamUrl; } From 8edca3678ddd1d5b8be2caf71b999de3df2eef89 Mon Sep 17 00:00:00 2001 From: Daniel <790119+DanTheMan827@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:57:38 -0500 Subject: [PATCH 2/2] fix(api): remove domain checks from searchVideos --- js/api.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/api.js b/js/api.js index 2b6f394..70b9138 100644 --- a/js/api.js +++ b/js/api.js @@ -526,7 +526,6 @@ export class LosslessAPI { try { const response = await this.fetchWithRetry(`/search/?v=${encodeURIComponent(query)}`, { ...options, - allowedDomains: ['api.monochrome.tf', 'arran.monochrome.tf'], }); const data = await response.json(); const normalized = this.normalizeSearchResponse(data, 'videos');