Small change that modifies the `scrobble` controller method to always limit sending a
`scrobble` payload to the subsonic backend to cases where a scrobble is being submitted
or a new song is being started.
This is the current behavior for subsonic backends which support the `reportPlayback` backend
(e.g., Navidrome). This change only modifies the behavior for other backends, which would receive
`scrobble` notifications whenever a song changed for both the stopped song and the newly-started song.
After this change, all subsonic backends now only receive a `scrobble` when a new song is started or
when feishin is attempting to submit a scrobble.
* feat(player): add right-click audio output device menu to volume icon
Right-clicking the volume/speaker icon in the player bar now opens a
context menu listing available audio output devices, with the active
device checkmarked and a System default reset option.
Selecting a device writes to the existing audioDeviceId (web) or
mpvAudioDeviceId (mpv) playback settings, so the choice is applied live
and persisted across restarts via the settings store. Unlike the
settings-page selector, the menu is not disabled during playback.
* refactor(player): drop redundant audio menu label, document wrapper div
Address review feedback: remove the self-explanatory ContextMenu.Label
from the audio output menu, and add a comment explaining why the volume
icon is wrapped in a div (Mantine Tooltip does not forward Radix's
asChild onContextMenu/ref to the button).
applyStartupSeek() compared the armed target song against
getQueue().items[player.index], but player.index is a position in the
shuffled order while getQueue().items is always the default order. With
shuffle enabled the lookup resolves to the wrong song, the uniqueId
check fails, and the startup resume-seek is silently cancelled — the
track then plays from 0:00 and the progress poller overwrites the
persisted timestamp.
Use getCurrentSong() instead, which maps the index through
queue.shuffled, matching how the seek target is armed.
Fixes#2202
Co-authored-by: Maurits <WhoCarrot@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
- Romaji conversion joined all synced lyric lines into one string. Because the block contained kana somewhere, hasKana passed for the entire array of lyrics.