From 1435d8a2eab8b85eb69065553e82ff84487da346 Mon Sep 17 00:00:00 2001 From: Marcus Hanestad Date: Tue, 24 Jun 2025 13:17:52 +0000 Subject: [PATCH] Rust terminal sender fixes --- senders/terminal/README.md | 2 +- senders/terminal/src/fcastsession.rs | 7 +++--- senders/terminal/src/main.rs | 17 ++++++++++++-- senders/terminal/src/models/v3.rs | 34 +++++++++++++++++++++------- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/senders/terminal/README.md b/senders/terminal/README.md index c2b81d0..5d9174f 100644 --- a/senders/terminal/README.md +++ b/senders/terminal/README.md @@ -58,5 +58,5 @@ cat dash.mpd | ./fcast -h localhost play --mime_type application/dash+xml cat image_playlist_example.json | ./fcast -h localhost play --mime_type application/json # Play from video playlist -cat image_playlist_example.json | ./fcast -h localhost play --mime_type application/json +cat video_playlist_example.json | ./fcast -h localhost play --mime_type application/json ``` diff --git a/senders/terminal/src/fcastsession.rs b/senders/terminal/src/fcastsession.rs index 7e74503..5726df2 100644 --- a/senders/terminal/src/fcastsession.rs +++ b/senders/terminal/src/fcastsession.rs @@ -123,9 +123,9 @@ impl<'a> FCastSession<'a> { if opcode != Opcode::Initial { return Err(format!("Expected Opcode::Initial, got {opcode:?}").into()); } - let inital_receiver: v3::InitialReceiverMessage = + let initial_receiver: v3::InitialReceiverMessage = serde_json::from_str(&body.ok_or("InitialReceiverMessage requires body")?)?; - println!("Got inital message from sender: {inital_receiver:?}"); + println!("Got initial message from sender: {initial_receiver:?}"); session.state = SessionState::Connected(ProtoVersion::V3); } else { session.state = SessionState::Connected(ProtoVersion::V2); @@ -259,6 +259,7 @@ impl<'a> FCastSession<'a> { time: Option, speed: Option, headers: Option>, + volume: Option, ) -> Result<(), Box> { match self.state { SessionState::Connected(ProtoVersion::V2) => { @@ -278,7 +279,7 @@ impl<'a> FCastSession<'a> { url, content, time, - volume: Some(1.0), + volume, speed, headers, metadata: None, diff --git a/senders/terminal/src/main.rs b/senders/terminal/src/main.rs index 8b26f19..8820e70 100644 --- a/senders/terminal/src/main.rs +++ b/senders/terminal/src/main.rs @@ -135,6 +135,14 @@ fn run() -> Result<(), Box> { .help("Custom request headers in key:value format") .required(false) .multiple_occurrences(true), + ) + .arg( + Arg::with_name("volume") + .short('v') + .long("volume") + .value_name("VOLUME") + .help("The desired volume") + .takes_value(true) ), ) .subcommand( @@ -301,6 +309,11 @@ fn run() -> Result<(), Box> { .collect::>() }); + let volume = match play_matches.value_of("volume") { + Some(v) => v.parse::().ok(), + _ => None, + }; + #[allow(unused_assignments)] let mut url = None; let mut content = None; @@ -313,7 +326,7 @@ fn run() -> Result<(), Box> { ctrlc::set_handler(move || { println!( - "Ctrl+C triggered, server will stop when onging request finishes..." + "Ctrl+C triggered, server will stop when ongoing request finishes..." ); r.store(false, Ordering::SeqCst); }) @@ -340,7 +353,7 @@ fn run() -> Result<(), Box> { content = Some(buffer); } - session.send_play_message(mime_type, url, content, time, speed, headers)?; + session.send_play_message(mime_type, url, content, time, speed, headers, volume)?; } else if let Some(seek_matches) = matches.subcommand_matches("seek") { let seek_message = SeekMessage::new(match seek_matches.value_of("timestamp") { Some(s) => s.parse::()?, diff --git a/senders/terminal/src/models/v3.rs b/senders/terminal/src/models/v3.rs index c2432a6..74aca0e 100644 --- a/senders/terminal/src/models/v3.rs +++ b/senders/terminal/src/models/v3.rs @@ -15,7 +15,7 @@ pub enum MetadataObject { Generic { title: Option, thumbnail_url: Option, - custom: Value, + custom: Option, }, } @@ -46,7 +46,9 @@ impl Serialize for MetadataObject { None => Value::Null, }, ); - map.insert("custom".to_owned(), custom.clone()); + if let Some(custom) = custom { + map.insert("custom".to_owned(), custom.clone()); + } map.serialize(serializer) } } @@ -90,8 +92,7 @@ impl<'de> Deserialize<'de> for MetadataObject { thumbnail_url, custom: rest .get("custom") - .ok_or(de::Error::missing_field("custom"))? - .clone(), + .cloned(), }) } _ => Err(de::Error::custom(format!("Unknown metadata type {type_}"))), @@ -476,7 +477,7 @@ mod tests { &serde_json::to_string(&MetadataObject::Generic { title: Some(s!("abc")), thumbnail_url: Some(s!("def")), - custom: serde_json::Value::Null, + custom: Some(serde_json::Value::Null), }) .unwrap(), r#"{"custom":null,"thumbnailUrl":"def","title":"abc","type":0}"# @@ -485,11 +486,20 @@ mod tests { &serde_json::to_string(&MetadataObject::Generic { title: None, thumbnail_url: None, - custom: serde_json::Value::Null, + custom: Some(serde_json::Value::Null), }) .unwrap(), r#"{"custom":null,"thumbnailUrl":null,"title":null,"type":0}"# ); + assert_eq!( + &serde_json::to_string(&MetadataObject::Generic { + title: Some(s!("abc")), + thumbnail_url: Some(s!("def")), + custom: None, + }) + .unwrap(), + r#"{"thumbnailUrl":"def","title":"abc","type":0}"# + ); } #[test] @@ -502,7 +512,7 @@ mod tests { MetadataObject::Generic { title: Some(s!("abc")), thumbnail_url: Some(s!("def")), - custom: serde_json::Value::Null, + custom: Some(serde_json::Value::Null), } ); assert_eq!( @@ -510,7 +520,15 @@ mod tests { MetadataObject::Generic { title: None, thumbnail_url: None, - custom: serde_json::Value::Null, + custom: Some(serde_json::Value::Null), + } + ); + assert_eq!( + serde_json::from_str::(r#"{"type":0}"#).unwrap(), + MetadataObject::Generic { + title: None, + thumbnail_url: None, + custom: None, } ); assert!(serde_json::from_str::(r#"{"type":1"#).is_err());