Merge pull request 'push-zplyprxpvxxm' (#51) from push-zplyprxpvxxm into main
All checks were successful
Build and Push Docker Image / build (push) Successful in 28m7s

Reviewed-on: https://gargoton.petite-maison-orange.fr/eric/pmomusic/pulls/51
This commit was merged in pull request #51.
This commit is contained in:
2026-01-11 08:16:46 +01:00
5 changed files with 52 additions and 47 deletions

2
Cargo.lock generated
View File

@@ -3713,7 +3713,7 @@ dependencies = [
[[package]] [[package]]
name = "pmocontrol" name = "pmocontrol"
version = "0.1.0" version = "0.3.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-std", "async-std",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "pmocontrol" name = "pmocontrol"
version = "0.1.0" version = "0.3.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]

View File

@@ -542,6 +542,9 @@ impl ControlPoint {
"Sleep timer expired, stopping playback" "Sleep timer expired, stopping playback"
); );
// Mark this as a user-requested stop to prevent auto-advance
renderer.mark_user_stop_requested();
// Stop playback // Stop playback
if let Err(err) = renderer.stop() { if let Err(err) = renderer.stop() {
warn!( warn!(
@@ -1516,10 +1519,22 @@ impl ControlPoint {
#[cfg(feature = "pmoserver")] #[cfg(feature = "pmoserver")]
fn convert_runtime_position(position: Option<&PlaybackPositionInfo>) -> (Option<u64>, Option<u64>) { fn convert_runtime_position(position: Option<&PlaybackPositionInfo>) -> (Option<u64>, Option<u64>) {
match position { match position {
Some(info) => ( Some(info) => {
parse_hms_to_ms(info.rel_time.as_deref()), let position_ms = parse_hms_to_ms(info.rel_time.as_deref());
parse_hms_to_ms(info.track_duration.as_deref()), let duration_ms = parse_hms_to_ms(info.track_duration.as_deref());
),
// Validate that position doesn't exceed duration
// If position > duration, the renderer is reporting invalid data
// (common during track initialization on some UPNP renderers)
match (position_ms, duration_ms) {
(Some(pos), Some(dur)) if pos > dur => {
// Position exceeds duration - invalid state during initialization
// Return None for position to avoid showing bogus timestamps
(None, duration_ms)
}
_ => (position_ms, duration_ms),
}
}
None => (None, None), None => (None, None),
} }
} }

View File

@@ -672,47 +672,37 @@ async fn seek_queue_index(
let control_point = Arc::clone(&state.control_point); let control_point = Arc::clone(&state.control_point);
let rid_for_task = rid.clone(); let rid_for_task = rid.clone();
let index = payload.index; let index = payload.index;
let seek_task =
tokio::task::spawn_blocking(move || control_point.play_queue_index(&rid_for_task, index));
time::timeout(TRANSPORT_COMMAND_TIMEOUT, seek_task) // Launch the command in background and return immediately
.await // The UI will be updated via SSE events when the state changes
.map_err(|_| { tokio::task::spawn(async move {
warn!( let rid_for_log = rid_for_task.clone();
"Seek queue command for renderer {} exceeded {:?}", let result = tokio::task::spawn_blocking(move || {
renderer_id, TRANSPORT_COMMAND_TIMEOUT control_point.play_queue_index(&rid_for_task, index)
); })
( .await;
StatusCode::GATEWAY_TIMEOUT,
Json(ErrorResponse { match result {
error: format!( Ok(Ok(())) => {
"Seek command timed out after {}s", debug!(
TRANSPORT_COMMAND_TIMEOUT.as_secs() "Successfully started playback at index {} for renderer {}",
), index, rid_for_log.0
}), );
) }
})? Ok(Err(e)) => {
.map_err(|e| { warn!(
warn!("Task join error during queue seek: {}", e); "Failed to seek to index {} for renderer {}: {}",
( index, rid_for_log.0, e
StatusCode::INTERNAL_SERVER_ERROR, );
Json(ErrorResponse { }
error: format!("Internal task error: {}", e), Err(e) => {
}), warn!(
) "Task join error during queue seek for renderer {}: {}",
})? rid_for_log.0, e
.map_err(|e| { );
warn!( }
"Failed to seek to index {} for renderer {}: {}", }
index, renderer_id, e });
);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: format!("Failed to seek to index {}: {}", index, e),
}),
)
})?;
Ok(Json(SuccessResponse { Ok(Json(SuccessResponse {
message: format!("Playing item at index {}", index), message: format!("Playing item at index {}", index),

View File

@@ -1 +1 @@
0.20 0.3.0