this repo has no description
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}