fix: omit queue_item_ids from renderer state reports to fix seekbar sync

The server rejects renderer state reports with "Current track not found
in queue nor autoplay" after QBZ-initiated queue loads, because it assigns
non-standard queue_item_ids (first track = track_id, rest = 1..N).

Omit current_queue_item_id and next_queue_item_id from all outbound
renderer reports. The server already knows the current track from
SET_STATE commands and adds the correct queue_item_id when forwarding
to controllers.

Also add info-level diagnostic logging to both report paths (periodic
frontend reports and command-response reports) and the protobuf encoder
for verifying wire content.
This commit is contained in:
vicrodh committed 2026-02-22 15:18:35 -06:00
1 parent 155897a5b1
commit 40a2fa618e
4 files changed
+48 -10

No files matched your search

+1
View File
@@ -15,6 +15,7 @@ serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
uuid = { version = "1", features = ["v4"] }
log = { workspace = true }
[dev-dependencies]
tokio = { workspace = true, features = ["test-util"] }
+17 -2
View File
@@ -416,10 +416,25 @@ where
) -> Result<(), QconnectAppError> {
match command {
RendererCommand::SetState { .. } => {
log::info!(
"[QConnect/Report] SetState report: playing={:?} pos={:?} track={:?} next={:?} qv={}.{}",
renderer.playing_state,
renderer.current_position_ms,
renderer.current_track.as_ref().map(|t| (t.track_id, t.queue_item_id)),
renderer.next_track.as_ref().map(|t| (t.track_id, t.queue_item_id)),
queue_version_ref.major,
queue_version_ref.minor
);
let report = RendererReport::new(
RendererReportType::RndrSrvrStateUpdated,
self.next_action_uuid(),
queue_version_ref,
// NOTE: We intentionally OMIT current_queue_item_id and next_queue_item_id.
// The server validates these IDs against its queue state and rejects reports
// when IDs don't match ("Current track not found in queue nor autoplay").
// This happens after QBZ-initiated queue loads where the server assigns
// non-standard queue_item_ids (first track = track_id, rest = 1..N).
// The server already knows the current track from SET_STATE commands.
serde_json::json!({
"playing_state": renderer.playing_state,
"buffer_state": infer_buffer_state(renderer.playing_state),
@@ -429,8 +444,8 @@ where
"major": queue_version_ref.major,
"minor": queue_version_ref.minor
},
"current_queue_item_id": renderer.current_track.as_ref().map(|item| item.queue_item_id),
"next_queue_item_id": renderer.next_track.as_ref().map(|item| item.queue_item_id)
"current_queue_item_id": Option::<i32>::None,
"next_queue_item_id": Option::<i32>::None
}),
);
self.send_renderer_report(report).await?;
+13 -6
View File
@@ -424,25 +424,32 @@ fn map_renderer_report(report: &RendererReport) -> Result<QConnectMessage, Proto
.unwrap_or(report.queue_version_ref);
let current_position = optional_i32(&report.payload, "current_position")?;
let duration = optional_i32(&report.payload, "duration")?;
let current_qid = optional_i32(&report.payload, "current_queue_item_id")?;
let next_qid = optional_i32(&report.payload, "next_queue_item_id")?;
let playing_state = optional_i32(&report.payload, "playing_state")?;
let playback_position = current_position.map(|value| PlaybackPositionMessage {
timestamp: Some(now_ms()),
value: Some(value),
});
log::info!(
"[QConnect/Proto] Encoding StateUpdated: playing={:?} pos={:?} dur={:?} qid={:?} next_qid={:?} qv={}.{}",
playing_state, current_position, duration,
current_qid, next_qid,
queue_version.major, queue_version.minor
);
Ok(QConnectMessage {
message_type: Some(QConnectMessageType::MessageTypeRndrSrvrStateUpdated as i32),
rndr_srvr_state_updated: Some(RendererStateUpdatedMessage {
state: Some(RendererStateMessage {
playing_state: optional_i32(&report.payload, "playing_state")?,
playing_state,
buffer_state: optional_i32(&report.payload, "buffer_state")?,
current_position: playback_position,
duration,
queue_version: Some(to_proto_queue_version(queue_version)?),
current_queue_item_id: optional_i32(
&report.payload,
"current_queue_item_id",
)?,
next_queue_item_id: optional_i32(&report.payload, "next_queue_item_id")?,
current_queue_item_id: current_qid,
next_queue_item_id: next_qid,
}),
}),
..Default::default()