From dfad96df7b0debb7eaaeedcfc88ca6b2a4c925f4 Mon Sep 17 00:00:00 2001 From: Scott Simon Date: Wed, 1 Jul 2026 10:56:07 -0500 Subject: [PATCH 1/9] Add queue_next MQTT remote command to enqueue a track by persistent ID Sends ctrl-int/1/cue?command=add&query='dmap.persistentid:0x'&mode=3 to the DACP source, where is the hex track_id string taken directly from the MQTT payload after "queue_next ". Co-Authored-By: Claude Sonnet 5 --- mqtt.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/mqtt.c b/mqtt.c index 3acb8865..b0753f58 100644 --- a/mqtt.c +++ b/mqtt.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -70,7 +71,8 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq, char *commands[] = {"command", "beginff", "beginrew", "mutetoggle", "nextitem", "previtem", "pause", "playpause", "play", "stop", "playresume", "shuffle_songs", - "volumedown", "volumeup", "disconnect", NULL}; + "volumedown", "volumeup", "disconnect", "queue_next", + NULL}; int it = 0; @@ -81,7 +83,26 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq, debug(2, "[MQTT]: Received Recognized Command: %s\n", commands[it]); if (strcmp(commands[it], "disconnect") == 0) { debug(2, "[MQTT]: Disconnect Command: %s\n", commands[it]); - stop_play(); // stop any current session and don't replace it + release_play_lock(NULL); // stop any current session and don't replace it + } else if (strcmp(commands[it], "queue_next") == 0) { + // payload is "queue_next ", where is the hex track_id + // string as published by shairport-sync itself (see the "mper"/"track_id" handling + // above), e.g. "queue_next 1A2B3C4D5E6F7" + char *track_id = payload + strlen(commands[it]); + while (*track_id == ' ' || *track_id == '\t') + track_id++; + size_t track_id_len = strlen(track_id); + while (track_id_len > 0 && isspace((unsigned char)track_id[track_id_len - 1])) + track_id[--track_id_len] = '\0'; + if (track_id_len == 0) { + warn("[MQTT]: queue_next command received with no track_id -- ignoring."); + } else { + char dacp_command[256]; + snprintf(dacp_command, sizeof(dacp_command), + "cue?command=add&query='dmap.persistentid:0x%s'&mode=3", track_id); + debug(2, "[MQTT]: Queue Next Command: %s\n", dacp_command); + send_simple_dacp_command(dacp_command); + } } else { debug(2, "[MQTT]: DACP Command: %s\n", commands[it]); #ifdef CONFIG_DACP_CLIENT From bac7a7b4c8d68c873b18c25c39ce4e2608e01796 Mon Sep 17 00:00:00 2001 From: Scott Simon Date: Wed, 1 Jul 2026 12:57:08 -0500 Subject: [PATCH 2/9] Update MQTT command for queue_next functionality --- mqtt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mqtt.c b/mqtt.c index b0753f58..edb7c785 100644 --- a/mqtt.c +++ b/mqtt.c @@ -99,7 +99,7 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq, } else { char dacp_command[256]; snprintf(dacp_command, sizeof(dacp_command), - "cue?command=add&query='dmap.persistentid:0x%s'&mode=3", track_id); + "ctrl-int/1/cue?command=add&query='dmap.persistentid:0x%s'&mode=3", track_id); debug(2, "[MQTT]: Queue Next Command: %s\n", dacp_command); send_simple_dacp_command(dacp_command); } From 790752499c3383fb90bef4a3d798d279d4932b9c Mon Sep 17 00:00:00 2001 From: Scott Simon Date: Wed, 1 Jul 2026 13:20:49 -0500 Subject: [PATCH 3/9] Fix command format in MQTT queue_next function --- mqtt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mqtt.c b/mqtt.c index edb7c785..b0753f58 100644 --- a/mqtt.c +++ b/mqtt.c @@ -99,7 +99,7 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq, } else { char dacp_command[256]; snprintf(dacp_command, sizeof(dacp_command), - "ctrl-int/1/cue?command=add&query='dmap.persistentid:0x%s'&mode=3", track_id); + "cue?command=add&query='dmap.persistentid:0x%s'&mode=3", track_id); debug(2, "[MQTT]: Queue Next Command: %s\n", dacp_command); send_simple_dacp_command(dacp_command); } From b6a5a07df5294b8193ea8ddbbd6b8a9a739392bf Mon Sep 17 00:00:00 2001 From: Scott Simon Date: Thu, 2 Jul 2026 23:20:28 -0500 Subject: [PATCH 4/9] Add test track_id reference notes to mqtt.c Co-Authored-By: Claude Sonnet 5 --- mqtt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mqtt.c b/mqtt.c index b0753f58..7c6e861b 100644 --- a/mqtt.c +++ b/mqtt.c @@ -30,6 +30,11 @@ pc_queue metadata_mqtt_queue; metadata_package metadata_mqtt_queue_items[metadata_mqtt_queue_size]; pthread_t metadata_mqtt_thread; +/* +CDA9038D27ABF88 Kiss me +710E8D864C9E2392 Elizabeth +*/ + // this holds the mosquitto client struct mosquitto *global_mosq = NULL; int connected = 0; From 1fbb6312f60f4c9812f2d6ff2dc06508bac54ccd Mon Sep 17 00:00:00 2001 From: Scott Simon Date: Thu, 2 Jul 2026 23:50:23 -0500 Subject: [PATCH 5/9] Fix byte order of track_id published over MQTT The 'mper' handler cast the raw big-endian persistent-id bytes directly to a host-native uint64_t, which reverses the value on little-endian hosts (e.g. Raspberry Pi). metadata_hub.c already reconstructs the same field correctly by applying ntohl() to each 32-bit half; mqtt.c now does the same, so the published track_id matches the real dmap.persistentid and can be used directly with the queue_next command. Co-Authored-By: Claude Sonnet 5 --- mqtt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mqtt.c b/mqtt.c index 7c6e861b..6fb1fd36 100644 --- a/mqtt.c +++ b/mqtt.c @@ -347,7 +347,9 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le mqtt_publish("title", data, length); break; case 'mper': - trackid = *(uint64_t *)(data); + trackid = ntohl(*(uint32_t *)data); + trackid = trackid << 32; + trackid += ntohl(*(uint32_t *)(data + sizeof(uint32_t))); r = snprintf(trackidstring, sizeof(trackidstring), "%" PRIX64 "", trackid); mqtt_publish("track_id", trackidstring, r); } From f6e2c6f2529a6ff89f637733d03e17fdde16fd0f Mon Sep 17 00:00:00 2001 From: Scott Simon Date: Fri, 3 Jul 2026 15:30:34 -0500 Subject: [PATCH 6/9] Remove commented-out code from mqtt.c --- mqtt.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mqtt.c b/mqtt.c index 6fb1fd36..61be4520 100644 --- a/mqtt.c +++ b/mqtt.c @@ -30,11 +30,6 @@ pc_queue metadata_mqtt_queue; metadata_package metadata_mqtt_queue_items[metadata_mqtt_queue_size]; pthread_t metadata_mqtt_thread; -/* -CDA9038D27ABF88 Kiss me -710E8D864C9E2392 Elizabeth -*/ - // this holds the mosquitto client struct mosquitto *global_mosq = NULL; int connected = 0; From 6531f0ef5dc936f9fea8d5081173c35a46b09971 Mon Sep 17 00:00:00 2001 From: zutroy97 Date: Fri, 3 Jul 2026 21:57:25 -0500 Subject: [PATCH 7/9] Fix build: restore stop_play() for MQTT disconnect command An unrelated change in a prior commit swapped stop_play() for release_play_lock(NULL), which isn't declared in rtsp.h and broke the build with an implicit-declaration error. Co-Authored-By: Claude Sonnet 5 --- mqtt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mqtt.c b/mqtt.c index 61be4520..3c1d097a 100644 --- a/mqtt.c +++ b/mqtt.c @@ -83,7 +83,7 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq, debug(2, "[MQTT]: Received Recognized Command: %s\n", commands[it]); if (strcmp(commands[it], "disconnect") == 0) { debug(2, "[MQTT]: Disconnect Command: %s\n", commands[it]); - release_play_lock(NULL); // stop any current session and don't replace it + stop_play(); // stop any current session and don't replace it } else if (strcmp(commands[it], "queue_next") == 0) { // payload is "queue_next ", where is the hex track_id // string as published by shairport-sync itself (see the "mper"/"track_id" handling From 0c946ea040dfea389c6793549aa07375accf0813 Mon Sep 17 00:00:00 2001 From: zutroy97 Date: Sat, 4 Jul 2026 07:55:01 -0500 Subject: [PATCH 8/9] Publish and accept track_id as decimal instead of hex Changes the mper-derived track_id published over MQTT from hex to decimal, and updates queue_next to embed the value in the DACP persistentid query without a 0x prefix, matching the new format. Queueing behavior (command=add&mode=3) is unchanged. Co-Authored-By: Claude Sonnet 5 --- mqtt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mqtt.c b/mqtt.c index 3c1d097a..9c6c429d 100644 --- a/mqtt.c +++ b/mqtt.c @@ -85,9 +85,9 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq, debug(2, "[MQTT]: Disconnect Command: %s\n", commands[it]); stop_play(); // stop any current session and don't replace it } else if (strcmp(commands[it], "queue_next") == 0) { - // payload is "queue_next ", where is the hex track_id + // payload is "queue_next ", where is the decimal track_id // string as published by shairport-sync itself (see the "mper"/"track_id" handling - // above), e.g. "queue_next 1A2B3C4D5E6F7" + // above), e.g. "queue_next 123456789012345" char *track_id = payload + strlen(commands[it]); while (*track_id == ' ' || *track_id == '\t') track_id++; @@ -99,7 +99,7 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq, } else { char dacp_command[256]; snprintf(dacp_command, sizeof(dacp_command), - "cue?command=add&query='dmap.persistentid:0x%s'&mode=3", track_id); + "cue?command=add&query='dmap.persistentid:%s'&mode=3", track_id); debug(2, "[MQTT]: Queue Next Command: %s\n", dacp_command); send_simple_dacp_command(dacp_command); } @@ -345,7 +345,7 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le trackid = ntohl(*(uint32_t *)data); trackid = trackid << 32; trackid += ntohl(*(uint32_t *)(data + sizeof(uint32_t))); - r = snprintf(trackidstring, sizeof(trackidstring), "%" PRIX64 "", trackid); + r = snprintf(trackidstring, sizeof(trackidstring), "%" PRIu64 "", trackid); mqtt_publish("track_id", trackidstring, r); } } else if (type == 'ssnc') { From 0e37b5adcf400f338defa2b3d1675b0194d049c5 Mon Sep 17 00:00:00 2001 From: zutroy97 Date: Sun, 5 Jul 2026 17:45:24 -0500 Subject: [PATCH 9/9] Reverted back track_id as hex string --- mqtt.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mqtt.c b/mqtt.c index 9c6c429d..f9936cb9 100644 --- a/mqtt.c +++ b/mqtt.c @@ -85,9 +85,9 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq, debug(2, "[MQTT]: Disconnect Command: %s\n", commands[it]); stop_play(); // stop any current session and don't replace it } else if (strcmp(commands[it], "queue_next") == 0) { - // payload is "queue_next ", where is the decimal track_id + // payload is "queue_next ", where is the hex track_id // string as published by shairport-sync itself (see the "mper"/"track_id" handling - // above), e.g. "queue_next 123456789012345" + // above), e.g. "queue_next 1A2B3C4D5E6F7" char *track_id = payload + strlen(commands[it]); while (*track_id == ' ' || *track_id == '\t') track_id++; @@ -99,7 +99,7 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq, } else { char dacp_command[256]; snprintf(dacp_command, sizeof(dacp_command), - "cue?command=add&query='dmap.persistentid:%s'&mode=3", track_id); + "cue?command=add&query='dmap.persistentid:0x%s'&mode=3", track_id); debug(2, "[MQTT]: Queue Next Command: %s\n", dacp_command); send_simple_dacp_command(dacp_command); } @@ -322,7 +322,6 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le if (config.mqtt_publish_parsed) { if (type == 'core') { int32_t r; - uint64_t trackid; char trackidstring[32]; switch (code) { @@ -342,10 +341,12 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le mqtt_publish("title", data, length); break; case 'mper': - trackid = ntohl(*(uint32_t *)data); - trackid = trackid << 32; - trackid += ntohl(*(uint32_t *)(data + sizeof(uint32_t))); - r = snprintf(trackidstring, sizeof(trackidstring), "%" PRIu64 "", trackid); + // publish the raw persistent-id bytes as hex, in the order received -- + // no byte-order or integer conversion, so this can't reverse the value. + r = 0; + for (uint32_t i = 0; i < length && r < (int32_t)sizeof(trackidstring) - 2; i++) + r += snprintf(trackidstring + r, sizeof(trackidstring) - (size_t)r, "%02X", + (unsigned char)data[i]); mqtt_publish("track_id", trackidstring, r); } } else if (type == 'ssnc') {