1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
/*
This file is part of jellything (https://codeberg.org/metamuffin/jellything)
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use serde::{Deserialize, Serialize};
use std::fmt::Display;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum StreamSpec {
Whep {
track: usize,
seek: u64,
},
WhepControl {
token: String,
},
Remux {
tracks: Vec<usize>,
container: StreamContainer,
},
Original {
track: usize,
},
HlsSuperMultiVariant {
container: StreamContainer,
},
HlsMultiVariant {
segment: u64,
container: StreamContainer,
},
HlsVariant {
segment: u64,
track: usize,
container: StreamContainer,
format: usize,
},
Info {
segment: Option<u64>,
},
FragmentIndex {
segment: u64,
track: usize,
},
Fragment {
segment: u64,
track: usize,
index: u64,
container: StreamContainer,
format: usize,
},
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum StreamContainer {
WebM,
Matroska,
WebVTT,
JVTT,
}
impl StreamSpec {
pub fn to_query(&self) -> String {
match self {
StreamSpec::Whep { track, seek } => format!("?whep&track={track}&seek={seek}"),
StreamSpec::WhepControl { token } => format!("?whepcontrol&token={token}"),
StreamSpec::Remux { tracks, container } => {
format!(
"?remux&tracks={}&container={container}",
tracks
.iter()
.map(|t| t.to_string())
.collect::<Vec<String>>()
.join(",")
)
}
StreamSpec::Original { track } => format!("?original&track={track}"),
StreamSpec::HlsSuperMultiVariant { container } => {
format!("?hlssupermultivariant&container={container}")
}
StreamSpec::HlsMultiVariant { segment, container } => {
format!("?hlsmultivariant&segment={segment}&container={container}")
}
StreamSpec::HlsVariant {
segment,
track,
container,
format,
} => format!(
"?hlsvariant&segment={segment}&track={track}&container={container}&format={format}"
),
StreamSpec::Info {
segment: Some(segment),
} => format!("?info&segment={segment}"),
StreamSpec::Info { segment: None } => format!("?info"),
StreamSpec::FragmentIndex { segment, track } => {
format!("?fragmentindex&segment={segment}&track={track}")
}
StreamSpec::Fragment {
segment,
track,
index,
container,
format,
} => format!("?fragment&segment={segment}&track={track}&index={index}&container={container}&format={format}"),
}
}
}
impl Display for StreamContainer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
StreamContainer::WebM => "webm",
StreamContainer::Matroska => "matroska",
StreamContainer::WebVTT => "webvtt",
StreamContainer::JVTT => "jvtt",
})
}
}
|