FEAT: add track mix button to player bar and display release year

This commit is contained in:
Julien Maille 2026-01-03 13:00:12 +01:00
parent 0791c59f15
commit 3e228a0d46
5 changed files with 54 additions and 7 deletions

View file

@ -711,6 +711,11 @@
<div class="player-actions-row">
<button id="now-playing-like-btn" class="like-btn" data-action="toggle-like" title="Save to Favorites" style="display: none;">
</button>
<button id="now-playing-mix-btn" class="mix-btn" data-action="track-mix" title="Track Mix" style="display: none;">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/>
</svg>
</button>
<button id="toggle-lyrics-btn" title="Lyrics">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mic"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/><line x1="8" y1="22" x2="16" y2="22"/></svg>
</button>

View file

@ -667,6 +667,16 @@ export function initializeTrackInteractions(player, api, mainContent, contextMen
}
});
}
const nowPlayingMixBtn = document.getElementById('now-playing-mix-btn');
if (nowPlayingMixBtn) {
nowPlayingMixBtn.addEventListener('click', async (e) => {
e.stopPropagation();
if (player.currentTrack) {
await handleTrackAction('track-mix', player.currentTrack, player, api, lyricsManager, 'track', ui, scrobbler);
}
});
}
}
function renderQueue(player) {

View file

@ -44,6 +44,15 @@ export class Player {
const track = this.currentTrack;
const trackTitle = getTrackTitle(track);
const trackArtistsHTML = getTrackArtistsHTML(track);
let yearDisplay = '';
const releaseDate = track.album?.releaseDate || track.streamStartDate;
if (releaseDate) {
const date = new Date(releaseDate);
if (!isNaN(date.getTime())) {
yearDisplay = `${date.getFullYear()}`;
}
}
const coverEl = document.querySelector('.now-playing-bar .cover');
const titleEl = document.querySelector('.now-playing-bar .title');
@ -51,7 +60,12 @@ export class Player {
if (coverEl) coverEl.src = this.api.getCoverUrl(track.album?.cover, '1280');
if (titleEl) titleEl.textContent = trackTitle;
if (artistEl) artistEl.innerHTML = trackArtistsHTML;
if (artistEl) artistEl.innerHTML = trackArtistsHTML + yearDisplay;
const mixBtn = document.getElementById('now-playing-mix-btn');
if (mixBtn) {
mixBtn.style.display = (track.mixes && track.mixes.TRACK_MIX) ? 'flex' : 'none';
}
const totalDurationEl = document.getElementById('total-duration');
if (totalDurationEl) totalDurationEl.textContent = formatTime(track.duration);
document.title = `${trackTitle}${track.artist?.name || 'Unknown'}`;
@ -169,11 +183,25 @@ export class Player {
const trackTitle = getTrackTitle(track);
const trackArtistsHTML = getTrackArtistsHTML(track);
let yearDisplay = '';
const releaseDate = track.album?.releaseDate || track.streamStartDate;
if (releaseDate) {
const date = new Date(releaseDate);
if (!isNaN(date.getTime())) {
yearDisplay = `${date.getFullYear()}`;
}
}
document.querySelector('.now-playing-bar .cover').src =
this.api.getCoverUrl(track.album?.cover, '1280');
document.querySelector('.now-playing-bar .title').textContent = trackTitle;
document.querySelector('.now-playing-bar .artist').innerHTML = trackArtistsHTML;
document.querySelector('.now-playing-bar .artist').innerHTML = trackArtistsHTML + yearDisplay;
const mixBtn = document.getElementById('now-playing-mix-btn');
if (mixBtn) {
mixBtn.style.display = (track.mixes && track.mixes.TRACK_MIX) ? 'flex' : 'none';
}
document.title = `${trackTitle}${track.artist?.name || 'Unknown'}`;
this.updatePlayingTrackIndicator();

View file

@ -1189,17 +1189,20 @@ async showFullscreenCover(track, nextTrack, lyricsManager, audioPlayer) {
let displayTitle;
if (type === 'artist' && name) {
displayTitle = `Mix for artist ${decodeURIComponent(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) {
displayTitle = `Mix for track ${decodeURIComponent(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);
}
titleEl.textContent = displayTitle;
this.adjustTitleFontSize(titleEl, displayTitle);
const totalDuration = calculateTotalDuration(tracks);
metaEl.textContent = `${tracks.length} tracks • ${formatDuration(totalDuration)}`;

View file

@ -35,6 +35,7 @@ export const SVG_MENU = '<svg xmlns="http://www.w3.org/2000/svg" width="24" heig
export const SVG_HEART = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="heart-icon"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>';
export const SVG_CLOSE = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>';
export const SVG_BIN = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>';
export const SVG_MIX = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/></svg>';
export const formatTime = (seconds) => {
if (isNaN(seconds)) return '0:00';