(WIP) add qobuz
This commit is contained in:
parent
304eb78d7b
commit
1134680c88
8 changed files with 514 additions and 29 deletions
10
index.html
10
index.html
|
|
@ -3173,6 +3173,16 @@
|
|||
<div class="settings-tab-content" id="settings-tab-audio">
|
||||
<div class="settings-list">
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Music Provider</span>
|
||||
<span class="description">Default service for searching and streaming</span>
|
||||
</div>
|
||||
<select id="music-provider-setting">
|
||||
<option value="tidal">Tidal</option>
|
||||
<option value="qobuz">Qobuz</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Streaming Quality</span>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//js/app.js
|
||||
import { LosslessAPI } from './api.js';
|
||||
import { MusicAPI } from './music-api.js';
|
||||
import {
|
||||
apiSettings,
|
||||
themeManager,
|
||||
|
|
@ -275,7 +276,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
console.log('[App] Neutralino object NOT detected.');
|
||||
}
|
||||
|
||||
const api = new LosslessAPI(apiSettings);
|
||||
const api = new MusicAPI(apiSettings);
|
||||
|
||||
const audioPlayer = document.getElementById('audio-player');
|
||||
|
||||
|
|
|
|||
172
js/music-api.js
Normal file
172
js/music-api.js
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
// js/music-api.js
|
||||
// Unified API wrapper that supports both Tidal and Qobuz
|
||||
|
||||
import { LosslessAPI } from './api.js';
|
||||
import { QobuzAPI } from './qobuz-api.js';
|
||||
import { musicProviderSettings } from './storage.js';
|
||||
|
||||
export class MusicAPI {
|
||||
constructor(settings) {
|
||||
this.tidalAPI = new LosslessAPI(settings);
|
||||
this.qobuzAPI = new QobuzAPI();
|
||||
this._settings = settings;
|
||||
}
|
||||
|
||||
getCurrentProvider() {
|
||||
return musicProviderSettings.getProvider();
|
||||
}
|
||||
|
||||
// Get the appropriate API based on provider
|
||||
getAPI(provider = null) {
|
||||
const p = provider || this.getCurrentProvider();
|
||||
return p === 'qobuz' ? this.qobuzAPI : this.tidalAPI;
|
||||
}
|
||||
|
||||
// Search methods
|
||||
async searchTracks(query, options = {}) {
|
||||
const provider = options.provider || this.getCurrentProvider();
|
||||
return this.getAPI(provider).searchTracks(query, options);
|
||||
}
|
||||
|
||||
async searchArtists(query, options = {}) {
|
||||
const provider = options.provider || this.getCurrentProvider();
|
||||
return this.getAPI(provider).searchArtists(query, options);
|
||||
}
|
||||
|
||||
async searchAlbums(query, options = {}) {
|
||||
const provider = options.provider || this.getCurrentProvider();
|
||||
return this.getAPI(provider).searchAlbums(query, options);
|
||||
}
|
||||
|
||||
async searchPlaylists(query, options = {}) {
|
||||
const provider = options.provider || this.getCurrentProvider();
|
||||
if (provider === 'qobuz') {
|
||||
// Qobuz doesn't support playlist search, return empty
|
||||
return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 };
|
||||
}
|
||||
return this.tidalAPI.searchPlaylists(query, options);
|
||||
}
|
||||
|
||||
// Get methods
|
||||
async getTrack(id, quality, provider = null) {
|
||||
const p = provider || this.getProviderFromId(id) || this.getCurrentProvider();
|
||||
const api = this.getAPI(p);
|
||||
const cleanId = this.stripProviderPrefix(id);
|
||||
return api.getTrack(cleanId, quality);
|
||||
}
|
||||
|
||||
async getTrackMetadata(id, provider = null) {
|
||||
const p = provider || this.getProviderFromId(id) || this.getCurrentProvider();
|
||||
const api = this.getAPI(p);
|
||||
const cleanId = this.stripProviderPrefix(id);
|
||||
return api.getTrackMetadata(cleanId);
|
||||
}
|
||||
|
||||
async getAlbum(id, provider = null) {
|
||||
const p = provider || this.getProviderFromId(id) || this.getCurrentProvider();
|
||||
const api = this.getAPI(p);
|
||||
const cleanId = this.stripProviderPrefix(id);
|
||||
return api.getAlbum(cleanId);
|
||||
}
|
||||
|
||||
async getArtist(id, provider = null) {
|
||||
const p = provider || this.getProviderFromId(id) || this.getCurrentProvider();
|
||||
const api = this.getAPI(p);
|
||||
const cleanId = this.stripProviderPrefix(id);
|
||||
return api.getArtist(cleanId);
|
||||
}
|
||||
|
||||
async getPlaylist(id, provider = null) {
|
||||
// Playlists are always Tidal for now
|
||||
return this.tidalAPI.getPlaylist(id);
|
||||
}
|
||||
|
||||
async getMix(id, provider = null) {
|
||||
// Mixes are always Tidal for now
|
||||
return this.tidalAPI.getMix(id);
|
||||
}
|
||||
|
||||
// Stream methods
|
||||
async getStreamUrl(id, quality, provider = null) {
|
||||
const p = provider || this.getProviderFromId(id) || this.getCurrentProvider();
|
||||
const api = this.getAPI(p);
|
||||
const cleanId = this.stripProviderPrefix(id);
|
||||
return api.getStreamUrl(cleanId, quality);
|
||||
}
|
||||
|
||||
// Cover/artwork methods
|
||||
getCoverUrl(id, size = '320') {
|
||||
if (typeof id === 'string' && id.startsWith('q:')) {
|
||||
return this.qobuzAPI.getCoverUrl(id.slice(2), size);
|
||||
}
|
||||
return this.tidalAPI.getCoverUrl(id, size);
|
||||
}
|
||||
|
||||
getArtistPictureUrl(id, size = '320') {
|
||||
if (typeof id === 'string' && id.startsWith('q:')) {
|
||||
return this.qobuzAPI.getArtistPictureUrl(id.slice(2), size);
|
||||
}
|
||||
return this.tidalAPI.getArtistPictureUrl(id, size);
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
getProviderFromId(id) {
|
||||
if (typeof id === 'string') {
|
||||
if (id.startsWith('q:')) return 'qobuz';
|
||||
if (id.startsWith('t:')) return 'tidal';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
stripProviderPrefix(id) {
|
||||
if (typeof id === 'string') {
|
||||
if (id.startsWith('q:') || id.startsWith('t:')) {
|
||||
return id.slice(2);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
// Download methods
|
||||
async downloadTrack(id, quality, filename, options = {}) {
|
||||
const provider = this.getProviderFromId(id) || this.getCurrentProvider();
|
||||
const api = this.getAPI(provider);
|
||||
const cleanId = this.stripProviderPrefix(id);
|
||||
return api.downloadTrack(cleanId, quality, filename, options);
|
||||
}
|
||||
|
||||
// Similar/recommendation methods
|
||||
async getSimilarArtists(artistId) {
|
||||
const provider = this.getProviderFromId(artistId) || this.getCurrentProvider();
|
||||
const api = this.getAPI(provider);
|
||||
const cleanId = this.stripProviderPrefix(artistId);
|
||||
return api.getSimilarArtists(cleanId);
|
||||
}
|
||||
|
||||
async getSimilarAlbums(albumId) {
|
||||
const provider = this.getProviderFromId(albumId) || this.getCurrentProvider();
|
||||
const api = this.getAPI(provider);
|
||||
const cleanId = this.stripProviderPrefix(albumId);
|
||||
return api.getSimilarAlbums(cleanId);
|
||||
}
|
||||
|
||||
async getRecommendedTracksForPlaylist(tracks, limit = 20) {
|
||||
// Use Tidal for recommendations
|
||||
return this.tidalAPI.getRecommendedTracksForPlaylist(tracks, limit);
|
||||
}
|
||||
|
||||
// Cache methods
|
||||
async clearCache() {
|
||||
await this.tidalAPI.clearCache();
|
||||
// Qobuz doesn't have cache yet
|
||||
}
|
||||
|
||||
getCacheStats() {
|
||||
return this.tidalAPI.getCacheStats();
|
||||
}
|
||||
|
||||
// Settings accessor for compatibility
|
||||
get settings() {
|
||||
return this._settings;
|
||||
}
|
||||
}
|
||||
255
js/qobuz-api.js
Normal file
255
js/qobuz-api.js
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
// js/qobuz-api.js
|
||||
// Qobuz API integration for Monochrome Music
|
||||
|
||||
const QOBUZ_API_BASE = 'https://qobuz.squid.wtf/api';
|
||||
|
||||
export class QobuzAPI {
|
||||
constructor() {
|
||||
this.baseUrl = QOBUZ_API_BASE;
|
||||
}
|
||||
|
||||
async fetchWithRetry(endpoint, options = {}) {
|
||||
const url = `${this.baseUrl}${endpoint}`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url, { signal: options.signal });
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') throw error;
|
||||
console.error('Qobuz API request failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Search tracks
|
||||
async searchTracks(query, options = {}) {
|
||||
try {
|
||||
const data = await this.fetchWithRetry(`/get-music?q=${encodeURIComponent(query)}`);
|
||||
|
||||
if (!data.success || !data.data) {
|
||||
return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 };
|
||||
}
|
||||
|
||||
// Transform Qobuz tracks to match Tidal format
|
||||
const tracks = (data.data.tracks?.items || []).map((track) => this.transformTrack(track));
|
||||
|
||||
return {
|
||||
items: tracks,
|
||||
limit: data.data.tracks?.limit || tracks.length,
|
||||
offset: data.data.tracks?.offset || 0,
|
||||
totalNumberOfItems: data.data.tracks?.total || tracks.length,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') throw error;
|
||||
console.error('Qobuz track search failed:', error);
|
||||
return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// Search albums
|
||||
async searchAlbums(query, options = {}) {
|
||||
try {
|
||||
const data = await this.fetchWithRetry(`/get-music?q=${encodeURIComponent(query)}`);
|
||||
|
||||
if (!data.success || !data.data) {
|
||||
return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 };
|
||||
}
|
||||
|
||||
// Transform Qobuz albums to match Tidal format
|
||||
const albums = (data.data.albums?.items || []).map((album) => this.transformAlbum(album));
|
||||
|
||||
return {
|
||||
items: albums,
|
||||
limit: data.data.albums?.limit || albums.length,
|
||||
offset: data.data.albums?.offset || 0,
|
||||
totalNumberOfItems: data.data.albums?.total || albums.length,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') throw error;
|
||||
console.error('Qobuz album search failed:', error);
|
||||
return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// Search artists
|
||||
async searchArtists(query, options = {}) {
|
||||
try {
|
||||
const data = await this.fetchWithRetry(`/get-music?q=${encodeURIComponent(query)}`);
|
||||
|
||||
if (!data.success || !data.data) {
|
||||
return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 };
|
||||
}
|
||||
|
||||
// Transform Qobuz artists to match Tidal format
|
||||
const artists = (data.data.artists?.items || []).map((artist) => this.transformArtist(artist));
|
||||
|
||||
return {
|
||||
items: artists,
|
||||
limit: data.data.artists?.limit || artists.length,
|
||||
offset: data.data.artists?.offset || 0,
|
||||
totalNumberOfItems: data.data.artists?.total || artists.length,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') throw error;
|
||||
console.error('Qobuz artist search failed:', error);
|
||||
return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// Get track details
|
||||
async getTrack(id) {
|
||||
try {
|
||||
// For Qobuz, we'll need to search or get from album
|
||||
// Since the API might not have a direct track endpoint
|
||||
const data = await this.fetchWithRetry(`/get-music?q=${encodeURIComponent(id)}`);
|
||||
|
||||
if (!data.success || !data.data) {
|
||||
throw new Error('Track not found');
|
||||
}
|
||||
|
||||
const track = data.data.tracks?.items?.find((t) => t.id === id || t.isrc === id);
|
||||
if (!track) {
|
||||
throw new Error('Track not found');
|
||||
}
|
||||
|
||||
return this.transformTrack(track);
|
||||
} catch (error) {
|
||||
console.error('Qobuz getTrack failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Get album details
|
||||
async getAlbum(id) {
|
||||
try {
|
||||
const data = await this.fetchWithRetry(`/get-album?album_id=${encodeURIComponent(id)}`);
|
||||
|
||||
if (!data.success || !data.data) {
|
||||
throw new Error('Album not found');
|
||||
}
|
||||
|
||||
const album = this.transformAlbum(data.data);
|
||||
const tracks = (data.data.tracks || []).map((track) => this.transformTrack(track, data.data));
|
||||
|
||||
return { album, tracks };
|
||||
} catch (error) {
|
||||
console.error('Qobuz getAlbum failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Get artist details
|
||||
async getArtist(id) {
|
||||
try {
|
||||
const data = await this.fetchWithRetry(`/get-artist?artist_id=${encodeURIComponent(id)}`);
|
||||
|
||||
if (!data.success || !data.data) {
|
||||
throw new Error('Artist not found');
|
||||
}
|
||||
|
||||
const artist = this.transformArtist(data.data);
|
||||
const albums = (data.data.albums || []).map((album) => this.transformAlbum(album));
|
||||
|
||||
return { ...artist, albums, eps: [], tracks: [] };
|
||||
} catch (error) {
|
||||
console.error('Qobuz getArtist failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Transform Qobuz track to Tidal-like format
|
||||
transformTrack(track, albumData = null) {
|
||||
return {
|
||||
id: `q:${track.id}`,
|
||||
title: track.title,
|
||||
duration: track.duration,
|
||||
artist: track.artist ? this.transformArtist(track.artist) : null,
|
||||
artists: track.artists ? track.artists.map((a) => this.transformArtist(a)) : [],
|
||||
album: albumData ? this.transformAlbum(albumData) : track.album ? this.transformAlbum(track.album) : null,
|
||||
audioQuality: this.mapQuality(track.streaming_quality),
|
||||
explicit: track.parental_warning || false,
|
||||
trackNumber: track.track_number,
|
||||
volumeNumber: track.media_number || 1,
|
||||
isrc: track.isrc,
|
||||
provider: 'qobuz',
|
||||
originalId: track.id,
|
||||
};
|
||||
}
|
||||
|
||||
// Transform Qobuz album to Tidal-like format
|
||||
transformAlbum(album) {
|
||||
return {
|
||||
id: `q:${album.id}`,
|
||||
title: album.title,
|
||||
artist: album.artist ? this.transformArtist(album.artist) : null,
|
||||
artists: album.artists ? album.artists.map((a) => this.transformArtist(a)) : [],
|
||||
numberOfTracks: album.tracks_count || 0,
|
||||
releaseDate: album.release_date_original || album.release_date,
|
||||
cover: album.image?.large || album.image?.medium || album.image?.small,
|
||||
explicit: album.parental_warning || false,
|
||||
type: album.album_type === 'ep' ? 'EP' : album.album_type === 'single' ? 'SINGLE' : 'ALBUM',
|
||||
provider: 'qobuz',
|
||||
originalId: album.id,
|
||||
};
|
||||
}
|
||||
|
||||
// Transform Qobuz artist to Tidal-like format
|
||||
transformArtist(artist) {
|
||||
return {
|
||||
id: `q:${artist.id}`,
|
||||
name: artist.name,
|
||||
picture: artist.image?.large || artist.image?.medium || artist.image?.small,
|
||||
provider: 'qobuz',
|
||||
originalId: artist.id,
|
||||
};
|
||||
}
|
||||
|
||||
// Map Qobuz quality to Tidal quality format
|
||||
mapQuality(qobuzQuality) {
|
||||
const qualityMap = {
|
||||
MP3: 'HIGH',
|
||||
FLAC: 'LOSSLESS',
|
||||
HiRes: 'HI_RES_LOSSLESS',
|
||||
};
|
||||
return qualityMap[qobuzQuality] || 'LOSSLESS';
|
||||
}
|
||||
|
||||
// Get cover URL
|
||||
getCoverUrl(coverId, size = '320') {
|
||||
if (!coverId) {
|
||||
return `https://picsum.photos/seed/${Math.random()}/${size}`;
|
||||
}
|
||||
|
||||
// Qobuz cover URLs are usually full URLs
|
||||
if (typeof coverId === 'string' && coverId.startsWith('http')) {
|
||||
return coverId;
|
||||
}
|
||||
|
||||
return coverId;
|
||||
}
|
||||
|
||||
// Get stream URL for a track
|
||||
async getStreamUrl(trackId) {
|
||||
try {
|
||||
const cleanId = trackId.replace(/^q:/, '');
|
||||
const data = await this.fetchWithRetry(`/download-music?track_id=${encodeURIComponent(cleanId)}`);
|
||||
|
||||
if (!data.success || !data.data?.url) {
|
||||
throw new Error('Stream URL not available');
|
||||
}
|
||||
|
||||
return data.data.url;
|
||||
} catch (error) {
|
||||
console.error('Qobuz getStreamUrl failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const qobuzAPI = new QobuzAPI();
|
||||
48
js/router.js
48
js/router.js
|
|
@ -29,39 +29,57 @@ export function createRouter(ui) {
|
|||
const page = parts[0];
|
||||
const param = parts.slice(1).join('/');
|
||||
|
||||
// Helper to strip /t/ prefix from params (for Tidal ID format like /album/t/123)
|
||||
const stripTidalPrefix = (p) => (p.startsWith('t/') ? p.slice(2) : p);
|
||||
// Helper to extract provider prefix and ID from params
|
||||
// Supports formats like: /track/t/123 (Tidal), /track/q/123 (Qobuz), /track/123 (default)
|
||||
const extractProviderAndId = (p) => {
|
||||
if (p.startsWith('t/')) {
|
||||
return { provider: 'tidal', id: p.slice(2) };
|
||||
}
|
||||
if (p.startsWith('q/')) {
|
||||
return { provider: 'qobuz', id: p.slice(2) };
|
||||
}
|
||||
return { provider: null, id: p };
|
||||
};
|
||||
|
||||
switch (page) {
|
||||
case 'search':
|
||||
await ui.renderSearchPage(decodeURIComponent(param));
|
||||
break;
|
||||
case 'album':
|
||||
await ui.renderAlbumPage(stripTidalPrefix(param));
|
||||
case 'album': {
|
||||
const { provider, id } = extractProviderAndId(param);
|
||||
await ui.renderAlbumPage(id, provider);
|
||||
break;
|
||||
case 'artist':
|
||||
await ui.renderArtistPage(stripTidalPrefix(param));
|
||||
}
|
||||
case 'artist': {
|
||||
const { provider, id } = extractProviderAndId(param);
|
||||
await ui.renderArtistPage(id, provider);
|
||||
break;
|
||||
case 'playlist':
|
||||
await ui.renderPlaylistPage(stripTidalPrefix(param), 'api');
|
||||
}
|
||||
case 'playlist': {
|
||||
const { provider, id } = extractProviderAndId(param);
|
||||
await ui.renderPlaylistPage(id, 'api', provider);
|
||||
break;
|
||||
}
|
||||
case 'userplaylist':
|
||||
await ui.renderPlaylistPage(param, 'user');
|
||||
break;
|
||||
case 'folder':
|
||||
await ui.renderFolderPage(param);
|
||||
break;
|
||||
case 'mix':
|
||||
await ui.renderMixPage(stripTidalPrefix(param));
|
||||
case 'mix': {
|
||||
const { provider, id } = extractProviderAndId(param);
|
||||
await ui.renderMixPage(id, provider);
|
||||
break;
|
||||
case 'track':
|
||||
const trackParam = stripTidalPrefix(param);
|
||||
if (trackParam.startsWith('tracker-')) {
|
||||
await ui.renderTrackerTrackPage(trackParam);
|
||||
}
|
||||
case 'track': {
|
||||
const { provider, id } = extractProviderAndId(param);
|
||||
if (id.startsWith('tracker-')) {
|
||||
await ui.renderTrackerTrackPage(id);
|
||||
} else {
|
||||
await ui.renderTrackPage(trackParam);
|
||||
await ui.renderTrackPage(id, provider);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'library':
|
||||
await ui.renderLibraryPage();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import {
|
|||
settingsUiState,
|
||||
pwaUpdateSettings,
|
||||
contentBlockingSettings,
|
||||
musicProviderSettings,
|
||||
} from './storage.js';
|
||||
import { audioContextManager, EQ_PRESETS } from './audio-context.js';
|
||||
import { getButterchurnPresets } from './visualizers/butterchurn.js';
|
||||
|
|
@ -696,6 +697,17 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
|||
renderCustomThemeEditor();
|
||||
});
|
||||
|
||||
// Music Provider setting
|
||||
const musicProviderSetting = document.getElementById('music-provider-setting');
|
||||
if (musicProviderSetting) {
|
||||
musicProviderSetting.value = musicProviderSettings.getProvider();
|
||||
musicProviderSetting.addEventListener('change', (e) => {
|
||||
musicProviderSettings.setProvider(e.target.value);
|
||||
// Reload page to apply changes
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
// Streaming Quality setting
|
||||
const streamingQualitySetting = document.getElementById('streaming-quality-setting');
|
||||
if (streamingQualitySetting) {
|
||||
|
|
|
|||
|
|
@ -1707,6 +1707,22 @@ export const pwaUpdateSettings = {
|
|||
},
|
||||
};
|
||||
|
||||
export const musicProviderSettings = {
|
||||
STORAGE_KEY: 'music-provider',
|
||||
|
||||
getProvider() {
|
||||
try {
|
||||
return localStorage.getItem(this.STORAGE_KEY) || 'tidal';
|
||||
} catch {
|
||||
return 'tidal';
|
||||
}
|
||||
},
|
||||
|
||||
setProvider(provider) {
|
||||
localStorage.setItem(this.STORAGE_KEY, provider);
|
||||
},
|
||||
};
|
||||
|
||||
export const contentBlockingSettings = {
|
||||
BLOCKED_ARTISTS_KEY: 'blocked-artists',
|
||||
BLOCKED_TRACKS_KEY: 'blocked-tracks',
|
||||
|
|
|
|||
27
js/ui.js
27
js/ui.js
|
|
@ -1910,11 +1910,12 @@ export class UIRenderer {
|
|||
const signal = this.searchAbortController.signal;
|
||||
|
||||
try {
|
||||
const provider = this.api.getCurrentProvider();
|
||||
const [tracksResult, artistsResult, albumsResult, playlistsResult] = await Promise.all([
|
||||
this.api.searchTracks(query, { signal }),
|
||||
this.api.searchArtists(query, { signal }),
|
||||
this.api.searchAlbums(query, { signal }),
|
||||
this.api.searchPlaylists(query, { signal }),
|
||||
this.api.searchTracks(query, { signal, provider }),
|
||||
this.api.searchArtists(query, { signal, provider }),
|
||||
this.api.searchAlbums(query, { signal, provider }),
|
||||
this.api.searchPlaylists(query, { signal, provider }),
|
||||
]);
|
||||
|
||||
let finalTracks = tracksResult.items;
|
||||
|
|
@ -2001,7 +2002,7 @@ export class UIRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
async renderAlbumPage(albumId) {
|
||||
async renderAlbumPage(albumId, provider = null) {
|
||||
this.showPage('album');
|
||||
|
||||
const imageEl = document.getElementById('album-detail-image');
|
||||
|
|
@ -2032,7 +2033,7 @@ export class UIRenderer {
|
|||
`;
|
||||
|
||||
try {
|
||||
const { album, tracks } = await this.api.getAlbum(albumId);
|
||||
const { album, tracks } = await this.api.getAlbum(albumId, provider);
|
||||
|
||||
const coverUrl = this.api.getCoverUrl(album.cover);
|
||||
imageEl.src = coverUrl;
|
||||
|
|
@ -2329,7 +2330,7 @@ export class UIRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
async renderPlaylistPage(playlistId, source = null) {
|
||||
async renderPlaylistPage(playlistId, source = null, provider = null) {
|
||||
this.showPage('playlist');
|
||||
|
||||
// Reset search input for new playlist
|
||||
|
|
@ -2659,7 +2660,7 @@ export class UIRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
async renderMixPage(mixId) {
|
||||
async renderMixPage(mixId, provider = null) {
|
||||
this.showPage('mix');
|
||||
|
||||
const imageEl = document.getElementById('mix-detail-image');
|
||||
|
|
@ -2689,7 +2690,7 @@ export class UIRenderer {
|
|||
`;
|
||||
|
||||
try {
|
||||
const { mix, tracks } = await this.api.getMix(mixId);
|
||||
const { mix, tracks } = await this.api.getMix(mixId, provider);
|
||||
|
||||
if (mix.cover) {
|
||||
imageEl.src = mix.cover;
|
||||
|
|
@ -2754,7 +2755,7 @@ export class UIRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
async renderArtistPage(artistId) {
|
||||
async renderArtistPage(artistId, provider = null) {
|
||||
this.showPage('artist');
|
||||
|
||||
const imageEl = document.getElementById('artist-detail-image');
|
||||
|
|
@ -2783,7 +2784,7 @@ export class UIRenderer {
|
|||
if (similarSection) similarSection.style.display = 'block';
|
||||
|
||||
try {
|
||||
const artist = await this.api.getArtist(artistId);
|
||||
const artist = await this.api.getArtist(artistId, provider);
|
||||
|
||||
// Handle Artist Mix Button
|
||||
const mixBtn = document.getElementById('artist-mix-btn');
|
||||
|
|
@ -3446,7 +3447,7 @@ export class UIRenderer {
|
|||
);
|
||||
}
|
||||
|
||||
async renderTrackPage(trackId) {
|
||||
async renderTrackPage(trackId, provider = null) {
|
||||
this.showPage('track');
|
||||
|
||||
document.body.classList.add('sidebar-collapsed');
|
||||
|
|
@ -3489,7 +3490,7 @@ export class UIRenderer {
|
|||
}
|
||||
|
||||
try {
|
||||
const track = await this.api.getTrackMetadata(trackId);
|
||||
const track = await this.api.getTrackMetadata(trackId, provider);
|
||||
const displayTitle = getTrackTitle(track);
|
||||
const artistName = getTrackArtists(track);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue