fix(qconnect): correct track_ids wire type to fixed32 packed in outbound queue commands

track_ids in QueueLoadTracks, QueueInsertTracks, QueueAddTracks, and
AutoplayLoadTracks was int32 packed=false but the server expects
fixed32 (packed by default). This silently broke all outbound queue
manipulation commands.
This commit is contained in:
vicrodh committed 2026-02-21 23:12:09 -06:00
1 parent 9e8dfd91e5
commit a9121ef427
2 files changed
+37 -12

No files matched your search

+29 -4
View File
@@ -158,7 +158,7 @@ fn map_queue_command(command: &QueueCommand) -> Result<QConnectMessage, Protocol
..Default::default()
}),
QueueCommandType::CtrlSrvrQueueAddTracks => {
let track_ids = required_i32_list(&command.payload, "track_ids")?;
let track_ids = required_u32_list(&command.payload, "track_ids")?;
let shuffle_seed = optional_i32(&command.payload, "shuffle_seed")?.map(|v| v as u32);
let context_uuid = optional_uuid_bytes(&command.payload, &["context_uuid"])?;
let autoplay_reset = optional_bool(&command.payload, "autoplay_reset", false);
@@ -180,7 +180,7 @@ fn map_queue_command(command: &QueueCommand) -> Result<QConnectMessage, Protocol
})
}
QueueCommandType::CtrlSrvrQueueLoadTracks => {
let track_ids = required_i32_list(&command.payload, "track_ids")?;
let track_ids = required_u32_list(&command.payload, "track_ids")?;
let queue_position = optional_i32(&command.payload, "queue_position")?;
let shuffle_seed = optional_i32(&command.payload, "shuffle_seed")?.map(|v| v as u32);
let shuffle_pivot_index = optional_i32(&command.payload, "shuffle_pivot_index")?;
@@ -208,7 +208,7 @@ fn map_queue_command(command: &QueueCommand) -> Result<QConnectMessage, Protocol
})
}
QueueCommandType::CtrlSrvrQueueInsertTracks => {
let track_ids = required_i32_list(&command.payload, "track_ids")?;
let track_ids = required_u32_list(&command.payload, "track_ids")?;
let insert_after = optional_i32(&command.payload, "insert_after")?;
let shuffle_seed = optional_i32(&command.payload, "shuffle_seed")?.map(|v| v as u32);
let context_uuid = optional_uuid_bytes(&command.payload, &["context_uuid"])?;
@@ -325,7 +325,7 @@ fn map_queue_command(command: &QueueCommand) -> Result<QConnectMessage, Protocol
})
}
QueueCommandType::CtrlSrvrAutoplayLoadTracks => {
let track_ids = required_i32_list(&command.payload, "track_ids")?;
let track_ids = required_u32_list(&command.payload, "track_ids")?;
let context_uuid = optional_uuid_bytes(&command.payload, &["context_uuid"])?;
Ok(QConnectMessage {
@@ -536,6 +536,31 @@ fn required_i32_list(payload: &Value, key: &str) -> Result<Vec<i32>, ProtocolErr
.collect()
}
fn required_u32_list(payload: &Value, key: &str) -> Result<Vec<u32>, ProtocolError> {
let values = payload
.get(key)
.and_then(Value::as_array)
.ok_or_else(|| ProtocolError::InvalidPayload(format!("missing array field '{key}'")))?;
values
.iter()
.enumerate()
.map(|(idx, value)| {
let raw = value
.as_i64()
.or_else(|| value.as_u64().map(|v| v as i64))
.ok_or_else(|| {
ProtocolError::InvalidPayload(format!("field '{key}[{idx}]' is not numeric"))
})?;
u32::try_from(raw).map_err(|_| {
ProtocolError::InvalidPayload(format!(
"field '{key}[{idx}]' value {raw} out of u32 range"
))
})
})
.collect()
}
fn optional_i32_list(payload: &Value, key: &str) -> Result<Vec<i32>, ProtocolError> {
let Some(values) = payload.get(key).and_then(Value::as_array) else {
return Ok(Vec::new());
@@ -229,8 +229,8 @@ pub struct QueueLoadTracksMessage {
pub queue_version_ref: Option<QueueVersionRef>,
#[prost(bytes = "vec", optional, tag = "2")]
pub action_uuid: Option<Vec<u8>>,
#[prost(int32, repeated, packed = "false", tag = "3")]
pub track_ids: Vec<i32>,
#[prost(fixed32, repeated, tag = "3")]
pub track_ids: Vec<u32>,
#[prost(int32, optional, tag = "4")]
pub queue_position: Option<i32>,
#[prost(fixed32, optional, tag = "5")]
@@ -253,8 +253,8 @@ pub struct QueueInsertTracksMessage {
pub queue_version_ref: Option<QueueVersionRef>,
#[prost(bytes = "vec", optional, tag = "2")]
pub action_uuid: Option<Vec<u8>>,
#[prost(int32, repeated, packed = "false", tag = "3")]
pub track_ids: Vec<i32>,
#[prost(fixed32, repeated, tag = "3")]
pub track_ids: Vec<u32>,
#[prost(int32, optional, tag = "4")]
pub insert_after: Option<i32>,
#[prost(fixed32, optional, tag = "5")]
@@ -273,8 +273,8 @@ pub struct QueueAddTracksMessage {
pub queue_version_ref: Option<QueueVersionRef>,
#[prost(bytes = "vec", optional, tag = "2")]
pub action_uuid: Option<Vec<u8>>,
#[prost(int32, repeated, packed = "false", tag = "3")]
pub track_ids: Vec<i32>,
#[prost(fixed32, repeated, tag = "3")]
pub track_ids: Vec<u32>,
#[prost(fixed32, optional, tag = "4")]
pub shuffle_seed: Option<u32>,
#[prost(bytes = "vec", optional, tag = "5")]
@@ -353,8 +353,8 @@ pub struct AutoplayLoadTracksMessage {
pub queue_version_ref: Option<QueueVersionRef>,
#[prost(bytes = "vec", optional, tag = "2")]
pub action_uuid: Option<Vec<u8>>,
#[prost(int32, repeated, packed = "false", tag = "3")]
pub track_ids: Vec<i32>,
#[prost(fixed32, repeated, tag = "3")]
pub track_ids: Vec<u32>,
#[prost(bytes = "vec", optional, tag = "4")]
pub context_uuid: Option<Vec<u8>>,
}