Skip to content

Qobuz Connect: apply a settings change without restarting the daemon - #795

Open
Gjuju wants to merge 4 commits into
moode-player:developfrom
Gjuju:contrib/qobuz-settings-no-restart
Open

Qobuz Connect: apply a settings change without restarting the daemon#795
Gjuju wants to merge 4 commits into
moode-player:developfrom
Gjuju:contrib/qobuz-settings-no-restart

Conversation

@Gjuju

@Gjuju Gjuju commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Depends on #793. This branch is stacked on it, so the diff above also
shows its three one-word commits. Only 374e6a10 belongs to this PR
(24 additions, 8 deletions). Once #793 is merged I will rebase and the diff
will narrow to that commit alone.

Saving anything in Qobuz Config queues a qobuzsvc job that runs
stopQobuz(); startQobuz();. That ends the Qobuz Connect session: playback
stops, the app has to be pointed at the player again, and the stopped event
on the way out truncates qbzmeta.json, so the renderer screen goes blank too.

Why the restart is not needed

qbzd's CLI writes the daemon's own stores directly and then nudges a running
daemon with POST /api/settings/reload, which reloads in place. Its key table
classifies the 24 settings startQobuz() sends as:

class count what it costs
None 11 nothing
Reload 7 "struct refresh only, no audible gap"
Reinit 6 reopens the output device

Nothing there asks for the process to be killed. On a running daemon,
qbzd settings set audio.device … — the heaviest class — answers
(daemon reinitialized the output device) and the pid does not change.

What it does

startQobuz() is split into cfgQobuz() + startQobuz(), the shape
cfgSqueezelite()/startSqueezelite() already use, so its eight callers are
unaffected. The qobuzsvc job applies settings in place when it carries
apply_settings and the daemon is up. The service toggle and the manual
restart button still stop and start it, and a save while the service is off or
the daemon is down falls back to the old path.

Validation

x86_64 and a Raspberry Pi running stock 10.3.4, both playing over Qobuz
Connect. Saving a setting produced a Job qobuzsvc with no startQobuz()
behind it, the daemon pid spanned the save, and playback did not stop:

14:43:51  DEBUG: startQobuz()      <- last launch
14:49:32  worker: Job qobuzsvc     <- settings saved, nothing behind it
daemon uptime 6:17, playing, ALSA RUNNING

The saved value reached the daemon on the same pass: moOde had always_skip,
qbzd settings show reported always_skip.

Reversibility

Reverting restores the unconditional stop/start. cfgQobuz() is additive and
startQobuz() keeps its previous behaviour for every existing caller.

Gjuju and others added 4 commits September 10, 2026 14:52
The startup reset that clears every renderer flag lists param='qbzctive',
which matches nothing, so qbzactive is the only flag left set when the
worker restarts. A player whose worker restarts while Qobuz Connect is
active then shows the Renderer Active overlay with nothing playing, and
it stays there until the flag is cleared by hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
isQobuzUpgradable() reads the installed version of librespot and compares
it against the qobuz-connect entry in cfg_plugin. The two can never match,
so the function always reports that an upgrade is available.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The setting was read as `quality_fallback_behaviour`. Every other reference
spells it `quality_fallback_behavior` -- the schema row, qbz-config.php, the
template and autocfg.php -- so the lookup returned nothing and the command sent
to the daemon carried no value:

    qbzd settings set audio.quality_fallback_behavior
    error: the following required arguments were not provided: <VALUE>

sysCmd() discards that, so the daemon silently kept its own default and the
Quality fallback behavior selector had no effect.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Saving anything in Qobuz Config queues a qobuzsvc job that runs
stopQobuz(); startQobuz();. That ends the Qobuz Connect session: playback
stops, the app has to be pointed at the player again, and the stopped event
on the way out truncates the metadata cache, so the renderer screen goes
blank until a track starts.

None of it is needed. qbzd's CLI writes the daemon's stores directly and then
nudges a running daemon with POST /api/settings/reload, which reloads in
place. Its own key table classifies every setting startQobuz() sends as None
(11 of them), Reload (7 -- "struct refresh only, no audible gap") or Reinit
(6 -- reopens the output device). Nothing there asks for the process to be
killed; measured on a running daemon, `qbzd settings set audio.device ...`
answers "(daemon reinitialized the output device)" and the pid does not change.

startQobuz() is split into cfgQobuz() + startQobuz(), the shape
cfgSqueezelite()/startSqueezelite() already use, so its eight callers are
unaffected. The job applies settings in place when the daemon is up; the
service toggle and the manual restart button still stop and start it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant