Player: runtime source override and play-a-URL on /api/player

The player now owns the effective mode (config or override) and passes it
down: spotify_apply(enable) and hls_set_url(url) instead of both reading
source.mode from the config. /api/player/source forces spotify|hls|off
(config returns to the saved mode), /api/player/url plays an m3u8 now;
neither is saved. GET reports it in `forced`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 23:15:38 +10:00
parent fd88e02e96
commit e5c3c0ad8a
6 changed files with 105 additions and 31 deletions
+6 -5
View File
@@ -25,13 +25,14 @@ typedef void (*spotify_event_cb_t)(spotify_event_t ev, int value);
// played out so far. Used to tell Spotify when a new track actually becomes audible.
typedef uint32_t (*spotify_pos_cb_t)(void);
// Registers the zeroconf endpoints (/spotify_info), starts the session task and applies the config.
// Registers the zeroconf endpoints (/spotify_info) and starts the session task (disabled until
// spotify_apply(true)).
esp_err_t spotify_init(spotify_pcm_cb_t pcm, spotify_event_cb_t event, spotify_pos_cb_t written,
spotify_pos_cb_t played);
// Re-read the "source" config: enabled (advertised, sessions accepted) when mode is spotify/auto and
// the client credentials are set; otherwise not advertised and a running session is ended.
// A new device name ends the session and re-advertises.
void spotify_apply(void);
// Enable (advertised, sessions accepted) when the player's mode needs Spotify and the client
// credentials are set; otherwise not advertised and a running session is ended. Re-reads name and
// credentials from the "source" config: a new device name ends the session and re-advertises.
void spotify_apply(bool enable);
typedef enum {
SPOTIFY_CMD_PLAY, SPOTIFY_CMD_PAUSE, SPOTIFY_CMD_NEXT, SPOTIFY_CMD_PREV,
SPOTIFY_CMD_SEEK, // arg: position in ms
+7 -7
View File
@@ -33,6 +33,7 @@ static std::shared_ptr<cspot::LoginBlob> s_blob; // guarded by s_mutex
static std::mutex s_mutex;
static std::string s_name; // advertised device name ("" = not advertised)
static std::atomic<bool> s_enabled{false};
static std::atomic<bool> s_want{false}; // last spotify_apply(): the player needs Spotify
static std::atomic<bool> s_stop{false}; // end the running session
static std::atomic<bool> s_got_blob{false};
static std::atomic<bool> s_in_session{false}; // session() running (its tasks may exist)
@@ -107,7 +108,7 @@ static void check_playback(const std::shared_ptr<cspot::SpircHandler> &handler)
extern "C" void spotify_suspend(bool suspend)
{
if (!suspend) {
spotify_apply();
spotify_apply(s_want);
return;
}
s_enabled = false;
@@ -500,15 +501,15 @@ static void spotify_task(void *arg)
}
}
extern "C" void spotify_apply(void)
extern "C" void spotify_apply(bool enable)
{
s_want = enable;
cJSON *src = cfg_get("source");
std::string mode = cJSON_GetObjectItemCaseSensitive(src, "mode")->valuestring;
std::string name = cJSON_GetObjectItemCaseSensitive(src, "spotify_name")->valuestring;
bool creds = cJSON_GetObjectItemCaseSensitive(src, "spotify_client_id")->valuestring[0] &&
cJSON_GetObjectItemCaseSensitive(src, "spotify_client_secret")->valuestring[0];
cJSON_Delete(src);
bool want = (mode == "spotify" || mode == "auto") && creds;
bool want = enable && creds;
if (!want || name != s_name) {
// Disable, or re-advertise under a new name: end any session, drop the old entry.
@@ -523,7 +524,7 @@ extern "C" void spotify_apply(void)
}
if (!want) {
s_state = !creds ? "no client credentials" : "disabled (source mode)";
if (!creds && (mode == "spotify" || mode == "auto")) {
if (!creds && enable) {
ESP_LOGW(TAG, "not started: set the Spotify client ID and secret (web UI, Source)");
}
return;
@@ -558,6 +559,5 @@ extern "C" esp_err_t spotify_init(spotify_pcm_cb_t pcm, spotify_event_cb_t event
if (xTaskCreate(spotify_task, "spotify", 32 * 1024, nullptr, 5, nullptr) != pdPASS) {
return ESP_ERR_NO_MEM;
}
spotify_apply();
return ESP_OK;
return ESP_OK; // the player enables it (spotify_apply)
}