this repo has no description
at main 54 lines 1.7 kB view raw
1use thiserror::Error; 2use url::Url; 3 4#[derive(Debug, Error, PartialEq)] 5pub enum RtmpError { 6 #[error("provide either `--output` or both `--rtmp-url` and `--stream-key`")] 7 MissingDestination, 8 #[error("stream key cannot be empty")] 9 EmptyStreamKey, 10 #[error("invalid RTMP output URL `{0}`")] 11 InvalidOutputUrl(String), 12 #[error("RTMP URL scheme must be `rtmp` or `rtmps`, got `{0}`")] 13 InvalidScheme(String), 14} 15 16pub fn build_output( 17 output: Option<String>, 18 rtmp_url: Option<String>, 19 stream_key: Option<String>, 20) -> Result<String, RtmpError> { 21 if let Some(full_output) = output { 22 let trimmed = full_output.trim(); 23 validate_output_url(trimmed)?; 24 return Ok(trimmed.to_string()); 25 } 26 27 match (rtmp_url, stream_key) { 28 (Some(base), Some(key)) => { 29 let normalized_key = normalize_stream_key(&key)?; 30 let merged = format!("{}/{}", base.trim_end_matches('/'), normalized_key); 31 validate_output_url(&merged)?; 32 Ok(merged) 33 } 34 _ => Err(RtmpError::MissingDestination), 35 } 36} 37 38fn normalize_stream_key(raw: &str) -> Result<String, RtmpError> { 39 let key = raw.trim().trim_start_matches('/').trim(); 40 if key.is_empty() { 41 return Err(RtmpError::EmptyStreamKey); 42 } 43 44 Ok(key.to_string()) 45} 46 47fn validate_output_url(candidate: &str) -> Result<(), RtmpError> { 48 let parsed = 49 Url::parse(candidate).map_err(|_| RtmpError::InvalidOutputUrl(candidate.to_string()))?; 50 match parsed.scheme() { 51 "rtmp" | "rtmps" => Ok(()), 52 other => Err(RtmpError::InvalidScheme(other.to_string())), 53 } 54}