IMP: mix, similar stuff
This commit is contained in:
parent
57f3e42dbe
commit
34dba30d6b
4 changed files with 77 additions and 7 deletions
|
|
@ -24,6 +24,7 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li data-action="toggle-like">Like</li>
|
<li data-action="toggle-like">Like</li>
|
||||||
<li data-action="add-to-playlist">Add to Playlist</li>
|
<li data-action="add-to-playlist">Add to Playlist</li>
|
||||||
|
<li data-action="track-mix" style="display: none;">Track Mix</li>
|
||||||
<li data-action="play-next">Play Next</li>
|
<li data-action="play-next">Play Next</li>
|
||||||
<li data-action="add-to-queue">Add to Queue</li>
|
<li data-action="add-to-queue">Add to Queue</li>
|
||||||
<li data-action="download">Download</li>
|
<li data-action="download">Download</li>
|
||||||
|
|
@ -342,7 +343,7 @@
|
||||||
<div class="card-grid" id="artist-detail-eps"></div>
|
<div class="card-grid" id="artist-detail-eps"></div>
|
||||||
</section>
|
</section>
|
||||||
<section class="content-section" id="artist-section-similar" style="display: none;">
|
<section class="content-section" id="artist-section-similar" style="display: none;">
|
||||||
<h2 class="section-title">Fans Also Like</h2>
|
<h2 class="section-title">Similar Artists</h2>
|
||||||
<div class="card-grid" id="artist-detail-similar"></div>
|
<div class="card-grid" id="artist-detail-similar"></div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
20
js/api.js
20
js/api.js
|
|
@ -630,6 +630,26 @@ export class LosslessAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getSimilarAlbums(albumId) {
|
||||||
|
const cached = await this.cache.get('similar_albums', albumId);
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.fetchWithRetry(`/album/similar/?id=${albumId}`, { type: 'api' });
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
const items = data.items || data.albums || data.data || (Array.isArray(data) ? data : []);
|
||||||
|
|
||||||
|
const result = items.map(album => this.prepareAlbum(album));
|
||||||
|
|
||||||
|
await this.cache.set('similar_albums', albumId, result);
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to fetch similar albums:', e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
normalizeTrackResponse(apiResponse) {
|
normalizeTrackResponse(apiResponse) {
|
||||||
if (!apiResponse || typeof apiResponse !== 'object') {
|
if (!apiResponse || typeof apiResponse !== 'object') {
|
||||||
return apiResponse;
|
return apiResponse;
|
||||||
|
|
|
||||||
13
js/events.js
13
js/events.js
|
|
@ -359,6 +359,10 @@ export async function handleTrackAction(action, item, player, api, lyricsManager
|
||||||
player.addNextToQueue(item);
|
player.addNextToQueue(item);
|
||||||
renderQueue(player);
|
renderQueue(player);
|
||||||
showNotification(`Playing next: ${item.title}`);
|
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)}`;
|
||||||
|
}
|
||||||
} else if (action === 'play-card') {
|
} else if (action === 'play-card') {
|
||||||
try {
|
try {
|
||||||
let tracks = [];
|
let tracks = [];
|
||||||
|
|
@ -676,6 +680,15 @@ async function updateContextMenuLikeState(menu, track) {
|
||||||
const isLiked = await db.isFavorite('track', track.id);
|
const isLiked = await db.isFavorite('track', track.id);
|
||||||
likeItem.textContent = isLiked ? 'Remove from Favorites' : 'Add to Favorites';
|
likeItem.textContent = isLiked ? 'Remove from Favorites' : 'Add to Favorites';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mixItem = menu.querySelector('[data-action="track-mix"]');
|
||||||
|
if (mixItem) {
|
||||||
|
if (track.mixes && track.mixes.TRACK_MIX) {
|
||||||
|
mixItem.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
mixItem.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function positionMenu(menu, x, y, anchorRect = null) {
|
function positionMenu(menu, x, y, anchorRect = null) {
|
||||||
|
|
|
||||||
48
js/ui.js
48
js/ui.js
|
|
@ -908,7 +908,7 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
||||||
const mixBtn = document.getElementById('album-mix-btn');
|
const mixBtn = document.getElementById('album-mix-btn');
|
||||||
if (mixBtn && artistData.mixes && artistData.mixes.ARTIST_MIX) {
|
if (mixBtn && artistData.mixes && artistData.mixes.ARTIST_MIX) {
|
||||||
mixBtn.style.display = 'flex';
|
mixBtn.style.display = 'flex';
|
||||||
mixBtn.onclick = () => window.location.hash = `#mix/${artistData.mixes.ARTIST_MIX}`;
|
mixBtn.onclick = () => window.location.hash = `#mix/${artistData.mixes.ARTIST_MIX}?type=artist&name=${encodeURIComponent(artistData.name)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove placeholder
|
// Remove placeholder
|
||||||
|
|
@ -954,7 +954,7 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
||||||
section.className = 'content-section album-more-section';
|
section.className = 'content-section album-more-section';
|
||||||
section.style.marginTop = '3rem';
|
section.style.marginTop = '3rem';
|
||||||
section.innerHTML = `
|
section.innerHTML = `
|
||||||
<h2 class="section-title">Fans Also Like</h2>
|
<h2 class="section-title">Similar Artists</h2>
|
||||||
<div class="card-grid">
|
<div class="card-grid">
|
||||||
${similar.map(a => this.createArtistCardHTML(a)).join('')}
|
${similar.map(a => this.createArtistCardHTML(a)).join('')}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -963,6 +963,30 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
||||||
}
|
}
|
||||||
}).catch(e => console.warn('Failed to load similar artists:', e));
|
}).catch(e => console.warn('Failed to load similar artists:', e));
|
||||||
|
|
||||||
|
// Similar Albums
|
||||||
|
this.api.getSimilarAlbums(albumId).then(similar => {
|
||||||
|
if (similar && similar.length > 0) {
|
||||||
|
const section = document.createElement('section');
|
||||||
|
section.className = 'content-section album-more-section';
|
||||||
|
section.style.marginTop = '3rem';
|
||||||
|
section.innerHTML = `
|
||||||
|
<h2 class="section-title">Similar Albums</h2>
|
||||||
|
<div class="card-grid">
|
||||||
|
${similar.map(a => this.createAlbumCardHTML(a)).join('')}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.getElementById('page-album').appendChild(section);
|
||||||
|
|
||||||
|
similar.forEach(a => {
|
||||||
|
const el = section.querySelector(`[data-album-id="${a.id}"]`);
|
||||||
|
if (el) {
|
||||||
|
trackDataStore.set(el, a);
|
||||||
|
this.updateLikeState(el, 'album', a.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(e => console.warn('Failed to load similar albums:', e));
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('Failed to load "More from artist":', err);
|
console.warn('Failed to load "More from artist":', err);
|
||||||
document.querySelectorAll('.album-more-section').forEach(el => el.remove());
|
document.querySelectorAll('.album-more-section').forEach(el => el.remove());
|
||||||
|
|
@ -1114,8 +1138,13 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderMixPage(mixId) {
|
async renderMixPage(param) {
|
||||||
this.showPage('mix');
|
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 imageEl = document.getElementById('mix-detail-image');
|
||||||
const titleEl = document.getElementById('mix-detail-title');
|
const titleEl = document.getElementById('mix-detail-title');
|
||||||
const metaEl = document.getElementById('mix-detail-meta');
|
const metaEl = document.getElementById('mix-detail-meta');
|
||||||
|
|
@ -1161,8 +1190,15 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
||||||
|
|
||||||
imageEl.style.backgroundColor = '';
|
imageEl.style.backgroundColor = '';
|
||||||
|
|
||||||
const firstTrackArtist = tracks.length > 0 ? tracks[0].artist?.name : '';
|
let displayTitle;
|
||||||
const displayTitle = firstTrackArtist ? `${firstTrackArtist} Mix` : 'Mix';
|
if (type === 'artist' && name) {
|
||||||
|
displayTitle = `Mix for artist ${decodeURIComponent(name)}`;
|
||||||
|
} else if (type === 'track' && name) {
|
||||||
|
displayTitle = `Mix for track ${decodeURIComponent(name)}`;
|
||||||
|
} else {
|
||||||
|
const firstTrackArtist = tracks.length > 0 ? tracks[0].artist?.name : '';
|
||||||
|
displayTitle = mix.title || (firstTrackArtist ? `${firstTrackArtist} Mix` : 'Mix');
|
||||||
|
}
|
||||||
|
|
||||||
titleEl.textContent = displayTitle;
|
titleEl.textContent = displayTitle;
|
||||||
this.adjustTitleFontSize(titleEl, displayTitle);
|
this.adjustTitleFontSize(titleEl, displayTitle);
|
||||||
|
|
@ -1228,7 +1264,7 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
|
||||||
if (mixBtn) {
|
if (mixBtn) {
|
||||||
if (artist.mixes && artist.mixes.ARTIST_MIX) {
|
if (artist.mixes && artist.mixes.ARTIST_MIX) {
|
||||||
mixBtn.style.display = 'flex';
|
mixBtn.style.display = 'flex';
|
||||||
mixBtn.onclick = () => window.location.hash = `#mix/${artist.mixes.ARTIST_MIX}`;
|
mixBtn.onclick = () => window.location.hash = `#mix/${artist.mixes.ARTIST_MIX}?type=artist&name=${encodeURIComponent(artist.name)}`;
|
||||||
} else {
|
} else {
|
||||||
mixBtn.style.display = 'none';
|
mixBtn.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue