IMP: mixes using new API

This commit is contained in:
Julien Maille 2026-01-04 18:48:49 +01:00
parent 4fc36f63e4
commit 6ee3c57bc5
4 changed files with 29 additions and 56 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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 {

View file

@ -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 = `<span style="color: var(--muted-foreground)">Mix for artist</span> ${decodedName}`;
this.adjustTitleFontSize(titleEl, `Mix for artist ${decodedName}`);
} else if (type === 'track' && name) {
const decodedName = decodeURIComponent(name);
titleEl.innerHTML = `<span style="color: var(--muted-foreground)">Mix for track</span> ${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 = `
<div class="track-list-header">
@ -1309,7 +1287,7 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
if (mixBtn) {
if (artist.mixes && artist.mixes.ARTIST_MIX) {
mixBtn.style.display = 'flex';
mixBtn.onclick = () => window.location.hash = `#mix/${artist.mixes.ARTIST_MIX}?type=artist&name=${encodeURIComponent(artist.name)}`;
mixBtn.onclick = () => window.location.hash = `#mix/${artist.mixes.ARTIST_MIX}`;
} else {
mixBtn.style.display = 'none';
}