aboutsummaryrefslogtreecommitdiff
path: root/stream/types/src/lib.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-03-07 04:02:48 +0100
committermetamuffin <metamuffin@disroot.org>2026-03-07 04:02:48 +0100
commit4ce6d64648634bd8d22e8ed0676e0e5b22947dc3 (patch)
treec1df9c9f623603651157006b9fd249de6d63fc7b /stream/types/src/lib.rs
parentd3ed810656a563fc733771e760b2abbb05bd98cb (diff)
downloadjellything-4ce6d64648634bd8d22e8ed0676e0e5b22947dc3.tar
jellything-4ce6d64648634bd8d22e8ed0676e0e5b22947dc3.tar.bz2
jellything-4ce6d64648634bd8d22e8ed0676e0e5b22947dc3.tar.zst
new media path format
Diffstat (limited to 'stream/types/src/lib.rs')
-rw-r--r--stream/types/src/lib.rs189
1 files changed, 46 insertions, 143 deletions
diff --git a/stream/types/src/lib.rs b/stream/types/src/lib.rs
index 6a4fc79..cbf5dcb 100644
--- a/stream/types/src/lib.rs
+++ b/stream/types/src/lib.rs
@@ -4,7 +4,9 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
use serde::{Deserialize, Serialize};
-use std::{collections::BTreeMap, fmt::Display, fmt::Write, str::FromStr};
+use std::{fmt::Display, fmt::Write, str::FromStr};
+
+pub mod path;
pub type TrackNum = usize;
pub type FormatNum = usize;
@@ -12,41 +14,41 @@ pub type IndexNum = usize;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum StreamSpec {
- // Whep {
- // track: TrackNum,
- // seek: u64,
- // },
- // WhepControl {
- // token: String,
- // },
- Remux {
- tracks: Vec<usize>,
- container: StreamContainer,
- },
- Original {
- track: TrackNum,
- },
+ /// stream.m3u8
HlsMultiVariant,
+ /// stream.mpd
+ Dash,
+ /// stream.json
+ Info,
+ /// <track>/<format>/variant.m3u8
HlsVariant {
track: TrackNum,
format: FormatNum,
},
- Dash,
- Info,
+ /// <track>/fragindex.json
FragmentIndex {
track: TrackNum,
},
+ /// <track>/<format>/init.<cont>
FragmentInit {
track: TrackNum,
container: StreamContainer,
format: FormatNum,
},
+ /// <track>/<format>/frag<i>.<cont>
Fragment {
track: TrackNum,
index: IndexNum,
container: StreamContainer,
format: FormatNum,
},
+ Remux {
+ tracks: Vec<usize>,
+ container: StreamContainer,
+ },
+ Original {
+ track: TrackNum,
+ },
// Track {
// segment: SegmentNum,
// track: TrackNum,
@@ -85,10 +87,15 @@ pub struct StreamFormatInfo {
pub remux: bool,
pub containers: Vec<StreamContainer>,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub width: Option<u64>,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub height: Option<u64>,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub samplerate: Option<f64>,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub channels: Option<usize>,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub bit_depth: Option<u8>,
}
@@ -151,6 +158,28 @@ impl StreamContainer {
(Self::JVTT, _) => "application/jellything-vtt+json",
}
}
+ pub fn from_file_ext(ext: &str) -> Option<Self> {
+ Some(match ext {
+ "weba" | "webm" => Self::WebM,
+ "mkv" | "mka" | "mks" => Self::Matroska,
+ "mp4" | "m4a" | "m4s" => Self::MP4,
+ "vtt" => Self::WebVTT,
+ "json" => Self::JVTT,
+ _ => return None,
+ })
+ }
+ pub fn file_ext(&self, kind: TrackKind) -> &'static str {
+ match (self, kind) {
+ (Self::WebM, TrackKind::Audio) => "weba",
+ (Self::WebM, _) => "webm",
+ (Self::Matroska, TrackKind::Audio) => "mka",
+ (Self::Matroska, _) => "mkv",
+ (Self::MP4, TrackKind::Audio) => "m4a",
+ (Self::MP4, _) => "mp4",
+ (Self::WebVTT, _) => "vtt",
+ (Self::JVTT, _) => "json",
+ }
+ }
}
impl Display for TrackKind {
@@ -163,132 +192,6 @@ impl Display for TrackKind {
}
}
-impl StreamSpec {
- pub fn to_query(&self) -> String {
- match self {
- 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::HlsMultiVariant => {
- format!("?hlsmultivariant")
- }
- StreamSpec::HlsVariant { track, format } => {
- format!("?hlsvariant&track={track}&format={format}")
- }
- StreamSpec::Info => "?info".to_string(),
- StreamSpec::Dash => "?info".to_string(),
- StreamSpec::FragmentIndex { track } => {
- format!("?fragmentindex&track={track}")
- }
- StreamSpec::FragmentInit {
- track,
- container,
- format,
- } => format!("?fragmentinit&track={track}&container={container}&format={format}"),
- StreamSpec::Fragment {
- track,
- index,
- container,
- format,
- } => format!(
- "?fragment&track={track}&index={index}&container={container}&format={format}"
- ),
- }
- }
- pub fn to_query_short(&self) -> String {
- match self {
- StreamSpec::Remux { tracks, container } => {
- format!(
- "?remux&ts={}&c={container}",
- tracks
- .iter()
- .map(|t| t.to_string())
- .collect::<Vec<String>>()
- .join(",")
- )
- }
- StreamSpec::Original { track } => format!("?original&t={track}"),
- StreamSpec::HlsMultiVariant => {
- format!("?hlsmultivariant")
- }
- StreamSpec::HlsVariant { track, format } => {
- format!("?hlsvariant&t={track}&f={format}")
- }
- StreamSpec::Info => "?info".to_string(),
- StreamSpec::Dash => "?dash".to_string(),
- StreamSpec::FragmentIndex { track } => {
- format!("?fragmentindex&t={track}")
- }
- StreamSpec::FragmentInit {
- track,
- container,
- format,
- } => format!("?fragmentinit&t={track}&c={container}&f={format}"),
- StreamSpec::Fragment {
- track,
- index,
- container,
- format,
- } => format!("?fragment&t={track}&i={index}&c={container}&f={format}"),
- }
- }
- pub fn from_query_kv(query: &BTreeMap<String, String>) -> Result<Self, &'static str> {
- let get_num = |k: &'static str, ks: &'static str| {
- query
- .get(k)
- .or(query.get(ks))
- .ok_or(k)
- .and_then(|a| a.parse::<usize>().map_err(|_| "invalid number"))
- };
- let get_container = || {
- query
- .get("container")
- .or(query.get("c"))
- .ok_or("container")
- .and_then(|s| s.parse().map_err(|()| "unknown container"))
- };
- if query.contains_key("info") {
- Ok(Self::Info)
- } else if query.contains_key("dash") {
- Ok(Self::Dash)
- } else if query.contains_key("hlsmultivariant") {
- Ok(Self::HlsMultiVariant)
- } else if query.contains_key("hlsvariant") {
- Ok(Self::HlsVariant {
- track: get_num("track", "t")? as TrackNum,
- format: get_num("format", "f")? as FormatNum,
- })
- } else if query.contains_key("fragmentinit") {
- Ok(Self::FragmentInit {
- track: get_num("track", "t")? as TrackNum,
- format: get_num("format", "f")? as FormatNum,
- container: get_container()?,
- })
- } else if query.contains_key("fragment") {
- Ok(Self::Fragment {
- track: get_num("track", "t")? as TrackNum,
- format: get_num("format", "f")? as FormatNum,
- index: get_num("index", "i")? as IndexNum,
- container: get_container()?,
- })
- } else if query.contains_key("fragmentindex") {
- Ok(Self::FragmentIndex {
- track: get_num("track", "t")? as TrackNum,
- })
- } else {
- Err("invalid stream spec")
- }
- }
-}
-
impl Display for StreamContainer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {