fix(qconnect): align queue load payload and inbound diagnostics

cx
This commit is contained in:
vicrodh committed 2026-03-12 19:31:27 -06:00
1 parent 2d643a9bfa
commit d3c2d4c6ca
3 files changed
+91 -8

No files matched your search

+33 -3
View File
@@ -187,8 +187,7 @@ fn map_queue_command(command: &QueueCommand) -> Result<QConnectMessage, Protocol
let shuffle_mode = optional_bool(&command.payload, "shuffle_mode", false); let shuffle_mode = optional_bool(&command.payload, "shuffle_mode", false);
let context_uuid = optional_uuid_bytes(&command.payload, &["context_uuid"])?; let context_uuid = optional_uuid_bytes(&command.payload, &["context_uuid"])?;
let autoplay_reset = optional_bool(&command.payload, "autoplay_reset", false); let autoplay_reset = optional_bool(&command.payload, "autoplay_reset", false);
let autoplay_loading = let autoplay_loading = optional_bool(&command.payload, "autoplay_loading", false);
optional_bool(&command.payload, "autoplay_loading", autoplay_reset);
Ok(QConnectMessage { Ok(QConnectMessage {
message_type: Some(QConnectMessageType::MessageTypeCtrlSrvrQueueLoadTracks as i32), message_type: Some(QConnectMessageType::MessageTypeCtrlSrvrQueueLoadTracks as i32),
@@ -1084,7 +1083,7 @@ mod tests {
assert_eq!(load.shuffle_seed, Some(3344)); assert_eq!(load.shuffle_seed, Some(3344));
assert_eq!(load.shuffle_pivot_index, Some(1)); assert_eq!(load.shuffle_pivot_index, Some(1));
assert_eq!(load.autoplay_reset, Some(true)); assert_eq!(load.autoplay_reset, Some(true));
assert_eq!(load.autoplay_loading, Some(true)); assert_eq!(load.autoplay_loading, Some(false));
assert_eq!( assert_eq!(
load.queue_version_ref load.queue_version_ref
.as_ref() .as_ref()
@@ -1100,6 +1099,37 @@ mod tests {
assert_eq!(load.context_uuid.as_ref().map(|uuid| uuid.len()), Some(16)); assert_eq!(load.context_uuid.as_ref().map(|uuid| uuid.len()), Some(16));
} }
#[test]
fn defaults_queue_load_tracks_autoplay_loading_to_false_when_omitted() {
let command = QueueCommand::new(
QueueCommandType::CtrlSrvrQueueLoadTracks,
"f997cd72-d2ce-4121-a89b-75bf2be251ec",
QueueVersion::new(3, 9),
json!({
"track_ids": [2001, 2002, 2003],
"queue_position": 2,
"shuffle_mode": false,
"shuffle_pivot_index": 2,
"context_uuid": "a3f7be0f-3854-44c6-9194-c93cedec322a",
"autoplay_reset": true
}),
);
let payload = encode_queue_command_batch(&command).expect("batch payload");
let decoded = QConnectMessages::decode(payload.as_slice()).expect("decode batch");
let load = decoded.messages[0]
.ctrl_srvr_queue_load_tracks
.as_ref()
.expect("queue load payload");
assert_eq!(load.queue_position, Some(2));
assert_eq!(load.shuffle_mode, Some(false));
assert_eq!(load.shuffle_pivot_index, Some(2));
assert_eq!(load.autoplay_reset, Some(true));
assert_eq!(load.autoplay_loading, Some(false));
assert_eq!(load.context_uuid.as_ref().map(|uuid| uuid.len()), Some(16));
}
#[test] #[test]
fn encodes_renderer_state_updated_report_with_queue_version() { fn encodes_renderer_state_updated_report_with_queue_version() {
let report = RendererReport::new( let report = RendererReport::new(
+48 -1
View File
@@ -1,6 +1,7 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
sync::{Arc, OnceLock}, sync::{Arc, OnceLock},
time::{SystemTime, UNIX_EPOCH},
}; };
use async_trait::async_trait; use async_trait::async_trait;
@@ -12,7 +13,7 @@ use qconnect_app::{
}; };
use qconnect_transport_ws::{NativeWsTransport, WsTransportConfig}; use qconnect_transport_ws::{NativeWsTransport, WsTransportConfig};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::{json, Value};
use tauri::async_runtime::JoinHandle; use tauri::async_runtime::JoinHandle;
use tauri::{AppHandle, Emitter, State}; use tauri::{AppHandle, Emitter, State};
use tokio::sync::{Mutex, RwLock}; use tokio::sync::{Mutex, RwLock};
@@ -308,6 +309,38 @@ struct QconnectRemoteSyncState {
session: QconnectSessionState, session: QconnectSessionState,
} }
fn queue_payload_track_preview(payload: &Value, key: &str) -> Vec<i64> {
payload
.get(key)
.and_then(Value::as_array)
.map(|tracks| {
tracks
.iter()
.filter_map(|track| track.get("track_id").and_then(Value::as_i64))
.take(8)
.collect::<Vec<_>>()
})
.unwrap_or_default()
}
fn emit_qconnect_diagnostic(app_handle: &AppHandle, channel: &str, level: &str, payload: Value) {
let ts = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_millis() as u64)
.unwrap_or(0);
if let Err(err) = app_handle.emit(
"qconnect:diagnostic",
json!({
"ts": ts,
"channel": channel,
"level": level,
"payload": payload,
}),
) {
log::warn!("[QConnect] Failed to emit diagnostic {channel}: {err}");
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct QconnectSessionState { pub struct QconnectSessionState {
pub session_uuid: Option<String>, pub session_uuid: Option<String>,
@@ -587,6 +620,20 @@ impl QconnectServiceState {
} }
qconnect_transport_ws::TransportEvent::InboundQueueServerEvent(evt) => { qconnect_transport_ws::TransportEvent::InboundQueueServerEvent(evt) => {
log::info!("[QConnect] <-- Inbound queue event: {} payload={}", evt.message_type(), evt.payload); log::info!("[QConnect] <-- Inbound queue event: {} payload={}", evt.message_type(), evt.payload);
emit_qconnect_diagnostic(
&app_for_errors,
"qconnect:inbound_queue_event",
"info",
json!({
"message_type": evt.message_type(),
"action_uuid": evt.action_uuid.clone(),
"queue_version": evt.queue_version,
"track_count": evt.payload.get("tracks").and_then(|value| value.as_array()).map(|tracks| tracks.len()).unwrap_or(0),
"autoplay_track_count": evt.payload.get("autoplay_tracks").and_then(|value| value.as_array()).map(|tracks| tracks.len()).unwrap_or(0),
"preview_track_ids": queue_payload_track_preview(&evt.payload, "tracks"),
"preview_autoplay_track_ids": queue_payload_track_preview(&evt.payload, "autoplay_tracks"),
}),
);
} }
qconnect_transport_ws::TransportEvent::InboundRendererServerCommand(cmd) => { qconnect_transport_ws::TransportEvent::InboundRendererServerCommand(cmd) => {
log::info!("[QConnect] <-- Inbound renderer command: {} payload={}", cmd.message_type(), cmd.payload); log::info!("[QConnect] <-- Inbound renderer command: {} payload={}", cmd.message_type(), cmd.payload);
+10 -4
View File
@@ -530,17 +530,23 @@ export async function loadQconnectQueue(
} }
try { try {
const normalizedStartIndex = Math.max(0, Math.min(startIndex, Math.max(trackIds.length - 1, 0)));
const shuffleSeed = shuffleMode
? Math.floor(Math.random() * 0x1_0000_0000)
: undefined;
const payload = { const payload = {
track_ids: trackIds, track_ids: trackIds,
queue_position: startIndex, queue_position: shuffleMode ? 0 : normalizedStartIndex,
shuffle_seed: shuffleSeed,
shuffle_pivot_index: normalizedStartIndex,
shuffle_mode: shuffleMode, shuffle_mode: shuffleMode,
context_uuid: crypto.randomUUID(), context_uuid: crypto.randomUUID(),
autoplay_reset: true, autoplay_reset: true,
autoplay_loading: true autoplay_loading: false
}; };
await emitQconnectDiagnostic('qconnect:queue_load_tracks', 'info', { await emitQconnectDiagnostic('qconnect:queue_load_tracks', 'info', {
track_count: trackIds.length, track_count: trackIds.length,
start_index: startIndex, start_index: normalizedStartIndex,
shuffle_mode: shuffleMode, shuffle_mode: shuffleMode,
preview_track_ids: trackIds.slice(0, 8), preview_track_ids: trackIds.slice(0, 8),
payload payload
@@ -550,7 +556,7 @@ export async function loadQconnectQueue(
console.log('[QConnect/LoadQueue] SUCCESS'); console.log('[QConnect/LoadQueue] SUCCESS');
await emitQconnectDiagnostic('qconnect:queue_load_sent', 'info', { await emitQconnectDiagnostic('qconnect:queue_load_sent', 'info', {
track_count: trackIds.length, track_count: trackIds.length,
start_index: startIndex, start_index: normalizedStartIndex,
shuffle_mode: shuffleMode, shuffle_mode: shuffleMode,
preview_track_ids: trackIds.slice(0, 8) preview_track_ids: trackIds.slice(0, 8)
}); });