diff --git a/js/api.js b/js/api.js index 51b5ffa..3b5bea2 100644 --- a/js/api.js +++ b/js/api.js @@ -487,27 +487,23 @@ export class LosslessAPI { const response = await this.fetchWithRetry(`/mix/?id=${id}`, { type: 'api' }); const data = await response.json(); - let mix = null; - let tracks = []; - - // Mix response structure might vary, handle likely cases - const items = data.items || data.tracks || (Array.isArray(data) ? data : []); + const mixData = data.mix; + const items = data.items || []; - // If data has mix info, use it. Otherwise, fabricate one or look for it. - if (data.mix) { - mix = data.mix; - } else if (!Array.isArray(data) && data.id) { - mix = data; + if (!mixData) { + throw new Error('Mix metadata not found'); } - if (!mix) { - // Basic placeholder if mix metadata isn't explicitly separated - mix = { id, title: 'Mix', description: 'Generated Mix' }; - } - - if (items.length > 0) { - tracks = items.map(i => this.prepareTrack(i.item || i)); - } + const tracks = items.map(i => this.prepareTrack(i.item || i)); + + const mix = { + id: mixData.id, + title: mixData.title, + subTitle: mixData.subTitle, + description: mixData.description, + mixType: mixData.mixType, + cover: mixData.images?.LARGE?.url || mixData.images?.MEDIUM?.url || mixData.images?.SMALL?.url || null + }; const result = { mix, tracks }; await this.cache.set('mix', id, result); diff --git a/js/app.js b/js/app.js index 0c4260b..301ed08 100644 --- a/js/app.js +++ b/js/app.js @@ -329,9 +329,8 @@ document.addEventListener('DOMContentLoaded', async () => { const btn = e.target.closest('#download-mix-btn'); if (btn.disabled) return; - const param = window.location.hash.split('#mix/')[1]; - if (!param) return; - const [mixId] = param.split('?'); + const mixId = window.location.hash.split('#mix/')[1]; + if (!mixId) return; btn.disabled = true; const originalHTML = btn.innerHTML; diff --git a/js/events.js b/js/events.js index 8d071d6..3ac3b3e 100644 --- a/js/events.js +++ b/js/events.js @@ -361,7 +361,7 @@ export async function handleTrackAction(action, item, player, api, lyricsManager showNotification(`Playing next: ${item.title}`); } else if (action === 'track-mix') { if (item.mixes && item.mixes.TRACK_MIX) { - window.location.hash = `#mix/${item.mixes.TRACK_MIX}?type=track&name=${encodeURIComponent(item.title)}`; + window.location.hash = `#mix/${item.mixes.TRACK_MIX}`; } } else if (action === 'play-card') { try { diff --git a/js/ui.js b/js/ui.js index 953e207..985d396 100644 --- a/js/ui.js +++ b/js/ui.js @@ -933,7 +933,7 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) { const mixBtn = document.getElementById('album-mix-btn'); if (mixBtn && artistData.mixes && artistData.mixes.ARTIST_MIX) { mixBtn.style.display = 'flex'; - mixBtn.onclick = () => window.location.hash = `#mix/${artistData.mixes.ARTIST_MIX}?type=artist&name=${encodeURIComponent(artistData.name)}`; + mixBtn.onclick = () => window.location.hash = `#mix/${artistData.mixes.ARTIST_MIX}`; } const renderSection = (items, container, section, titleEl, titleText) => { @@ -1171,12 +1171,8 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) { } } - async renderMixPage(param) { + async renderMixPage(mixId) { this.showPage('mix'); - const [mixId, query] = param.split('?'); - const urlParams = new URLSearchParams(query); - const type = urlParams.get('type'); - const name = urlParams.get('name'); const imageEl = document.getElementById('mix-detail-image'); const titleEl = document.getElementById('mix-detail-title'); @@ -1206,16 +1202,10 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) { try { const { mix, tracks } = await this.api.getMix(mixId); - // Mixes usually have covers from Tidal resources, similar to playlists - const imageId = mix.images?.medium?.source || mix.image || mix.id; - // Fallback for cover: if mix.id matches a pattern or we can just try generic mix cover - // Often mix ID isn't directly an image ID. - // If API returns explicit image URL/ID use it. - // For now assume standard playlist-like cover or placeholder. - if (imageId && imageId !== mix.id) { - imageEl.src = this.api.getCoverUrl(imageId); - this.setPageBackground(imageEl.src); - this.extractAndApplyColor(this.api.getCoverUrl(imageId, '160')); + if (mix.cover) { + imageEl.src = mix.cover; + this.setPageBackground(mix.cover); + this.extractAndApplyColor(mix.cover); } else { // Try to get cover from first track album if (tracks.length > 0 && tracks[0].album?.cover) { @@ -1231,26 +1221,14 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) { imageEl.style.backgroundColor = ''; - let displayTitle; - if (type === 'artist' && name) { - const decodedName = decodeURIComponent(name); - titleEl.innerHTML = `Mix for artist ${decodedName}`; - this.adjustTitleFontSize(titleEl, `Mix for artist ${decodedName}`); - } else if (type === 'track' && name) { - const decodedName = decodeURIComponent(name); - titleEl.innerHTML = `Mix for track ${decodedName}`; - this.adjustTitleFontSize(titleEl, `Mix for track ${decodedName}`); - } else { - const firstTrackArtist = tracks.length > 0 ? tracks[0].artist?.name : ''; - displayTitle = mix.title || (firstTrackArtist ? `${firstTrackArtist} Mix` : 'Mix'); - titleEl.textContent = displayTitle; - this.adjustTitleFontSize(titleEl, displayTitle); - } + // Use title and subtitle from API directly + const displayTitle = mix.title || 'Mix'; + titleEl.textContent = displayTitle; + this.adjustTitleFontSize(titleEl, displayTitle); const totalDuration = calculateTotalDuration(tracks); - metaEl.textContent = `${tracks.length} tracks • ${formatDuration(totalDuration)}`; - descEl.textContent = mix.subTitle || mix.description || ''; + descEl.innerHTML = `${mix.subTitle}`; tracklistContainer.innerHTML = `