feat(player): allow playing a Library folder (queue it) so next/prev work (refs #501)

DLNA STORED_MUSIC playback stopped after one track and next/previous did nothing:
the Library UI only offered a play button on individual tracks and always
selected with type "track", so the speaker had no queue to advance through
(next/prev send the NEXT_TRACK/PREV_TRACK key, which needs a queue).

- playEntry now passes the entry's own type, so selecting a folder uses the
  container type ("dir") instead of "track" — letting the speaker queue the
  folder for next/previous + auto-advance.
- show the play button on folders too (title "Play folder"), in addition to
  navigating into them.

Server-side needs no change: HandlePlayLibrary already forwards the type to the
speaker's /select. Whether a given firmware queues a container select is to be
confirmed on hardware (testable with cmd/example-dlna-server).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tobias Gesellchen
2026-06-14 21:48:02 +02:00
co-authored by Claude Opus 4.8
parent 0cdf8deb3b
commit b8de0f90f0
@@ -96,10 +96,13 @@ export function Library({ devices }) {
}
async function playEntry(entry) {
// Pass the entry's own type so a folder selects as a container ("dir")
// rather than a single track — that lets the speaker queue the folder so
// next/previous and auto-advance work, instead of stopping after one item.
await api.libraryPlay(deviceId, {
account: server.account,
location: entry.location,
type: 'track',
type: entry.type || 'track',
name: entry.name,
});
setPlayingName(entry.name);
@@ -251,10 +254,10 @@ export function Library({ devices }) {
<span class="tunein-item-name">${entry.name}</span>
${entry.type ? html`<span class="tunein-item-desc">${entry.type}</span>` : null}
</div>
${entry.playable && !entry.isDir ? html`
${entry.playable || entry.isDir ? html`
<button
class="tunein-play-btn"
title="Play on ${devices[deviceId]?.info?.name || deviceId}"
title="${entry.isDir ? 'Play folder' : 'Play'} on ${devices[deviceId]?.info?.name || deviceId}"
onClick=${(e) => { e.stopPropagation(); playEntry(entry); }}
>▶</button>
` : null}