From edd352c3d0441f6eb837e7c89686286849cb2855 Mon Sep 17 00:00:00 2001 From: Julien Gainza Date: Wed, 9 Sep 2026 22:28:53 +0200 Subject: [PATCH 1/4] Reset the Qobuz Connect active flag on worker startup 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 --- www/daemon/worker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/daemon/worker.php b/www/daemon/worker.php index 1a8d1a800..89128e8b5 100755 --- a/www/daemon/worker.php +++ b/www/daemon/worker.php @@ -1603,7 +1603,7 @@ phpSession('write', 'volknob', '0'); sysCmd('/var/www/util/vol.sh 0'); $result = sqlQuery("UPDATE cfg_system SET value='0' WHERE param='btactive' OR param='aplactive' OR - param='spotactive' OR param='qbzctive' OR param='slactive' OR param='paactive' OR param='rbactive' OR + param='spotactive' OR param='qbzactive' OR param='slactive' OR param='paactive' OR param='rbactive' OR param='inpactive'", $dbh); workerLog('worker: Active flags: at least one true'); workerLog('worker: Reset flags: all reset to false'); From febee4fa29be2a74daeff6b53e60fdf8590fae8c Mon Sep 17 00:00:00 2001 From: Julien Gainza Date: Wed, 9 Sep 2026 22:28:59 +0200 Subject: [PATCH 2/4] Query the qbzd package when checking for a Qobuz Connect upgrade 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 --- www/inc/renderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/inc/renderer.php b/www/inc/renderer.php index 9c5f3d957..9f428abf4 100644 --- a/www/inc/renderer.php +++ b/www/inc/renderer.php @@ -357,7 +357,7 @@ function isQobuzInstalled() { return empty($result) ? false : true; } function isQobuzUpgradable() { - $installedVersion = sysCmd('dpkg-query --showformat=\'${Version}\n\' --show librespot | grep moode')[0]; + $installedVersion = sysCmd('dpkg-query --showformat=\'${Version}\n\' --show qbzd | grep moode')[0]; $availableVersion = sqlQuery("SELECT version FROM cfg_plugin WHERE component='renderer' AND type='qobuz-connect'", sqlConnect())[0]['version']; return ($installedVersion == $availableVersion ? false : true); } From b8ce9c9649efcfe1120e50c062c0a97fae45e57f Mon Sep 17 00:00:00 2001 From: Julien Gainza Date: Thu, 10 Sep 2026 11:37:22 +0200 Subject: [PATCH 3/4] Pass the configured quality fallback behavior to qbzd 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: 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 --- www/inc/renderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/inc/renderer.php b/www/inc/renderer.php index 9f428abf4..0e1e9d134 100644 --- a/www/inc/renderer.php +++ b/www/inc/renderer.php @@ -299,7 +299,7 @@ function startQobuz() { sysCmd('qbzd settings set audio.normalization_enabled ' . $cfgQobuz['normalization_enabled']); sysCmd('qbzd settings set audio.allow_quality_fallback true'); sysCmd('qbzd settings set audio.gapless_enabled ' . $cfgQobuz['gapless_enabled']); - sysCmd('qbzd settings set audio.quality_fallback_behavior ' . $cfgQobuz['quality_fallback_behaviour']); + sysCmd('qbzd settings set audio.quality_fallback_behavior ' . $cfgQobuz['quality_fallback_behavior']); sysCmd('qbzd settings set audio.streaming_only ' . $cfgQobuz['streaming_only']); sysCmd('qbzd settings set audio.stream_first_track ' . $cfgQobuz['stream_first_track']); sysCmd('qbzd settings set audio.cache_to_disk ' . $cfgQobuz['cache_to_disk']); From cbbe9e312e7346bb4acc7667953392cd0fa6056c Mon Sep 17 00:00:00 2001 From: Julien Gainza Date: Thu, 10 Sep 2026 11:56:36 +0200 Subject: [PATCH 4/4] Qobuz Connect: apply a settings change without restarting the daemon 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 --- www/daemon/worker.php | 17 ++++++++++++++--- www/inc/constants.php | 1 + www/inc/renderer.php | 10 +++++++--- www/qbz-config.php | 4 ++-- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/www/daemon/worker.php b/www/daemon/worker.php index 89128e8b5..d5be03c1a 100755 --- a/www/daemon/worker.php +++ b/www/daemon/worker.php @@ -3301,9 +3301,20 @@ function runQueuedJob() { break; // Qobuz Connect case 'qobuzsvc': - stopQobuz(); - if ($_SESSION['qobuzsvc'] == 1) { - startQobuz(); + // A settings save does not need the daemon taken down: qbzd writes + // its own stores and nudges a running daemon through + // POST /api/settings/reload, which reloads in place. Restarting + // ends the Qobuz Connect session instead, and the app has to be + // pointed at the player again before anything plays. The service + // toggle and the manual restart button still go the long way. + if ($_SESSION['w_queueargs'] == 'apply_settings' && $_SESSION['qobuzsvc'] == 1 && + !empty(sysCmd('pgrep -x qbzd'))) { + cfgQobuz(); + } else { + stopQobuz(); + if ($_SESSION['qobuzsvc'] == 1) { + startQobuz(); + } } if ($_SESSION['w_queueargs'] == 'disconnect_renderer' && $_SESSION['rsmafterqbz'] == 'Yes') { sysCmd('mpc play'); diff --git a/www/inc/constants.php b/www/inc/constants.php index 9dac80352..6cc37d7f7 100755 --- a/www/inc/constants.php +++ b/www/inc/constants.php @@ -149,6 +149,7 @@ const NOTIFY_DURATION_INFINITE = 8640000; // 100 days const NOTIFY_MSG_SYSTEM_RESTART_REQD = 'Restart the system for the changes to take effect.'; const NOTIFY_MSG_SVC_RESTARTED = ' has been restarted to make the changes effective.'; +const NOTIFY_MSG_SVC_SETTINGS_APPLIED = ' settings have been applied.'; const NOTIFY_MSG_SVC_MANUAL_RESTART = ' has been restarted.'; const NOTIFY_MSG_LOCALDISPLAY_STARTING = 'Local display is starting...'; const NOTIFY_MSG_PEPPYDISPLAY_STARTING = 'Peppy display is starting...'; diff --git a/www/inc/renderer.php b/www/inc/renderer.php index 0e1e9d134..241ecbc02 100644 --- a/www/inc/renderer.php +++ b/www/inc/renderer.php @@ -264,9 +264,7 @@ function isSpotifyUpgradable() { // Qobuz Connect // Copyright 2026 @PhilipVinc qbz fork of moode / https://github.com/PhilipVinc/moode -function startQobuz() { - // Logging - $logging = $_SESSION['debuglog'] == '1' ? ' > ' . QBZD_LOG : ' > /dev/null'; +function cfgQobuz() { // Settings $result = sqlRead('cfg_qobuz', sqlConnect()); $cfgQobuz = array(); @@ -307,6 +305,12 @@ function startQobuz() { sysCmd('qbzd settings set audio.alsa_buffer_ms ' . $cfgQobuz['alsa_buffer_ms']); // Event script sysCmd('qbzd settings set hooks.script /var/local/www/commandw/qbzevent.sh'); +} +function startQobuz() { + // Logging + $logging = $_SESSION['debuglog'] == '1' ? ' > ' . QBZD_LOG : ' > /dev/null'; + + cfgQobuz(); // Start the daemon $cmd = 'qbzd run' . $logging . ' 2>&1 &'; diff --git a/www/qbz-config.php b/www/qbz-config.php index b5811c362..93e5de20e 100644 --- a/www/qbz-config.php +++ b/www/qbz-config.php @@ -27,8 +27,8 @@ sqlUpdate('cfg_qobuz', $dbh, $key, $value); } if ($_SESSION['qobuzsvc'] == '1') { - $notify = array('title' => NOTIFY_TITLE_INFO, 'msg' => NAME_QOBUZ . NOTIFY_MSG_SVC_RESTARTED); - submitJob('qobuzsvc', '', $notify['title'], $notify['msg']); + $notify = array('title' => NOTIFY_TITLE_INFO, 'msg' => NAME_QOBUZ . NOTIFY_MSG_SVC_SETTINGS_APPLIED); + submitJob('qobuzsvc', 'apply_settings', $notify['title'], $notify['msg']); } }