Player: 'Play next' action + accent-correct queue buttons

- Added playNext(track): inserts a track right after the current one (Spotify
  'Play next'), vs addToQueue which appends to the end. Falls back to
  addToQueue when nothing is playing.
- Artist-detail track rows now show BOTH a Play-next (⇥) and Add-to-queue (+)
  button; the delegated handler builds one shared library-track payload and
  routes to playNext / addToQueue. (Add-to-queue was already wired; play-next
  + the second button are new.)
- Fixed the queue button's hardcoded 29,185,84 to var(--accent-rgb) so it
  follows the settings accent (kettui UI-consistency), and styled the new
  play-next button to match.

Note: deliberately NOT adding queue buttons to SEARCH results — those are
stream/download (non-library) tracks the queue's auto-advance can't reliably
play. JS syntax clean on both files.
This commit is contained in:
BoulderBadgeDad 2026-05-30 14:54:14 -07:00
parent 65f49ccecd
commit 1e514693f1
3 changed files with 39 additions and 12 deletions

View file

@ -4085,6 +4085,13 @@ function _buildTrackRow(track, album, admin) {
const queueTd = document.createElement('td');
queueTd.className = 'col-queue';
if (!track._missingExpected && track.file_path) {
const playNextBtn = document.createElement('button');
playNextBtn.className = 'enhanced-playnext-btn';
// Play-next glyph (queue-with-arrow feel)
playNextBtn.innerHTML = '⇥'; // ⇥
playNextBtn.title = 'Play next';
queueTd.appendChild(playNextBtn);
const queueBtn = document.createElement('button');
queueBtn.className = 'enhanced-queue-btn';
queueBtn.innerHTML = '+';
@ -4270,8 +4277,10 @@ function _attachTableDelegation(table, album) {
}
}
// Queue button
if (target.closest('.enhanced-queue-btn')) {
// Queue / Play-next buttons (share the same track payload)
const isQueueBtn = target.closest('.enhanced-queue-btn');
const isPlayNextBtn = target.closest('.enhanced-playnext-btn');
if (isQueueBtn || isPlayNextBtn) {
e.stopPropagation();
if (track.file_path) {
const artistName = artistDetailPageState.enhancedData ? artistDetailPageState.enhancedData.artist.name : '';
@ -4279,7 +4288,7 @@ function _attachTableDelegation(table, album) {
if (!albumArt && artistDetailPageState.enhancedData) {
albumArt = artistDetailPageState.enhancedData.artist?.thumb_url;
}
addToQueue({
const payload = {
title: track.title || 'Unknown Track',
artist: artistName || 'Unknown Artist',
album: album.title || 'Unknown Album',
@ -4292,7 +4301,9 @@ function _attachTableDelegation(table, album) {
album_id: album.id,
bitrate: track.bitrate,
sample_rate: track.sample_rate
});
};
if (isPlayNextBtn && typeof playNext === 'function') playNext(payload);
else addToQueue(payload);
}
return;
}

View file

@ -2104,6 +2104,19 @@ function addToQueue(track) {
}
}
// Insert a track to play right after the current one (Spotify "Play next").
function playNext(track) {
if (npQueue.length === 0 || npQueueIndex < 0) {
// Nothing queued / playing — same as add-to-queue (which auto-plays).
addToQueue(track);
return;
}
npQueue.splice(npQueueIndex + 1, 0, track);
showToast('Playing next', 'success');
renderNpQueue();
updateNpPrevNextButtons();
}
function removeFromQueue(index) {
if (index < 0 || index >= npQueue.length) return;
const wasCurrentTrack = (index === npQueueIndex);

View file

@ -48808,26 +48808,29 @@ textarea.enhanced-meta-field-input {
width: 36px;
text-align: center;
}
.enhanced-queue-btn {
.enhanced-queue-btn,
.enhanced-playnext-btn {
background: none;
border: 1px solid rgba(29, 185, 84, 0.2);
color: rgba(29, 185, 84, 0.5);
border: 1px solid rgba(var(--accent-rgb), 0.2);
color: rgba(var(--accent-rgb), 0.5);
border-radius: 50%;
width: 24px;
height: 24px;
font-size: 14px;
font-weight: bold;
cursor: pointer;
display: flex;
display: inline-flex;
align-items: center;
justify-content: center;
transition: all 0.15s ease;
padding: 0;
margin: 0 2px;
}
.enhanced-queue-btn:hover {
background: rgba(29, 185, 84, 0.12);
color: rgba(29, 185, 84, 0.9);
border-color: rgba(29, 185, 84, 0.4);
.enhanced-queue-btn:hover,
.enhanced-playnext-btn:hover {
background: rgba(var(--accent-rgb), 0.12);
color: rgba(var(--accent-rgb), 0.9);
border-color: rgba(var(--accent-rgb), 0.4);
transform: scale(1.1);
}