Player: GET /api/player with Spotify now-playing info
Source, state, artist/title/album, position and duration (from cspot's playback state, so it matches the app), volume and `can`. cspot patch 0004 adds read getters for the playback state and context. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+60
-3
@@ -323,18 +323,70 @@ static void on_ota(bool starting)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *source_state(player_src_t src)
|
||||
{
|
||||
return src == PLAYER_SRC_TONE ? "playing" : src == PLAYER_SRC_OFF ? "idle" :
|
||||
src == PLAYER_SRC_SPOTIFY && !spotify_session_active() ? "idle" :
|
||||
src == PLAYER_SRC_SPOTIFY && s_paused ? "paused" : s_playing ? "playing" : "buffering";
|
||||
}
|
||||
|
||||
static void player_status(cJSON *st)
|
||||
{
|
||||
player_src_t src = s_src;
|
||||
const char *state = src == PLAYER_SRC_TONE ? "playing" : src == PLAYER_SRC_OFF ? "idle" :
|
||||
src == PLAYER_SRC_SPOTIFY && !spotify_session_active() ? "idle" :
|
||||
src == PLAYER_SRC_SPOTIFY && s_paused ? "paused" : s_playing ? "playing" : "buffering";
|
||||
const char *state = source_state(src);
|
||||
cJSON_AddStringToObject(st, "active_source", SRC_NAME[src]);
|
||||
cJSON_AddStringToObject(st, "source_state", state);
|
||||
cJSON_AddStringToObject(st, "spotify_state", spotify_state());
|
||||
cJSON_AddNumberToObject(st, "buffer_ms", (double)(audio_ring_level() * 1000 / RATE));
|
||||
}
|
||||
|
||||
/* ----- Player API ----- */
|
||||
|
||||
static void add_str_or_null(cJSON *o, const char *key, const char *s)
|
||||
{
|
||||
if (s && s[0]) {
|
||||
cJSON_AddStringToObject(o, key, s);
|
||||
} else {
|
||||
cJSON_AddNullToObject(o, key);
|
||||
}
|
||||
}
|
||||
|
||||
// Same JSON for GET /api/player and every POST.
|
||||
static esp_err_t send_player(httpd_req_t *req)
|
||||
{
|
||||
player_src_t src = s_src;
|
||||
spotify_track_t t;
|
||||
bool sp = src == PLAYER_SRC_SPOTIFY && spotify_now_playing(&t);
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(o, "source", SRC_NAME[src]);
|
||||
cJSON_AddNullToObject(o, "forced");
|
||||
cJSON_AddStringToObject(o, "state", src == PLAYER_SRC_OFF ? "stopped" : source_state(src));
|
||||
add_str_or_null(o, "artist", sp ? t.artist : NULL);
|
||||
add_str_or_null(o, "title", sp ? t.title : NULL);
|
||||
add_str_or_null(o, "album", sp ? t.album : NULL);
|
||||
if (sp && t.duration_ms) {
|
||||
cJSON_AddNumberToObject(o, "position_ms", t.position_ms);
|
||||
cJSON_AddNumberToObject(o, "duration_ms", t.duration_ms);
|
||||
} else {
|
||||
cJSON_AddNullToObject(o, "position_ms");
|
||||
cJSON_AddNullToObject(o, "duration_ms");
|
||||
}
|
||||
cJSON_AddNumberToObject(o, "volume", s_volume_pct);
|
||||
cJSON *can = cJSON_AddObjectToObject(o, "can");
|
||||
cJSON_AddBoolToObject(can, "pause", sp);
|
||||
cJSON_AddBoolToObject(can, "next", sp);
|
||||
cJSON_AddBoolToObject(can, "prev", sp);
|
||||
cJSON_AddBoolToObject(can, "seek", sp);
|
||||
esp_err_t err = web_send_json(req, o);
|
||||
cJSON_Delete(o);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t player_get(httpd_req_t *req)
|
||||
{
|
||||
return send_player(req);
|
||||
}
|
||||
|
||||
esp_err_t player_init(void)
|
||||
{
|
||||
esp_err_t err = audio_ring_init(RING_FRAMES, CHANNELS);
|
||||
@@ -364,5 +416,10 @@ esp_err_t player_init(void)
|
||||
if (xTaskCreate(control_task, "player_ctl", 3072, NULL, 4, NULL) != pdPASS) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
static const httpd_uri_t get = { .uri = "/api/player", .method = HTTP_GET, .handler = player_get };
|
||||
err = web_register_uri(&get);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
return status_register(player_status);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user