diff options
author | metamuffin <metamuffin@disroot.org> | 2023-01-11 23:16:10 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-01-11 23:16:10 +0100 |
commit | 43e986b5e626b339095c118fa79ba6cd011aeb9f (patch) | |
tree | aeb70c1233d6bade2d04e86086f48e88b72f3815 | |
parent | 0f944c90380b28e010211f3c8d57423e38bb2c11 (diff) | |
download | jellything-43e986b5e626b339095c118fa79ba6cd011aeb9f.tar jellything-43e986b5e626b339095c118fa79ba6cd011aeb9f.tar.bz2 jellything-43e986b5e626b339095c118fa79ba6cd011aeb9f.tar.zst |
doesnt work but should
-rw-r--r-- | Cargo.lock | 3 | ||||
-rw-r--r-- | common/src/lib.rs | 9 | ||||
-rw-r--r-- | remuxer/Cargo.toml | 1 | ||||
-rw-r--r-- | remuxer/src/lib.rs | 54 | ||||
-rw-r--r-- | server/Cargo.toml | 4 | ||||
-rw-r--r-- | server/src/library.rs | 19 | ||||
-rw-r--r-- | server/src/main.rs | 50 | ||||
-rw-r--r-- | tools/src/bin/gen_meta.rs | 132 |
8 files changed, 187 insertions, 85 deletions
@@ -828,6 +828,7 @@ dependencies = [ "anyhow", "jellycommon", "log", + "tokio", "webm-iterable", ] @@ -839,6 +840,7 @@ dependencies = [ "chashmap", "env_logger", "jellycommon", + "jellyremuxer", "log", "markup", "rocket", @@ -846,6 +848,7 @@ dependencies = [ "serde_json", "sled", "tokio", + "tokio-util", "typed-sled", ] diff --git a/common/src/lib.rs b/common/src/lib.rs index 77e90a5..0e736b7 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -10,12 +10,12 @@ pub struct DirectoryInfo { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct ItemInfo { pub title: String, - pub source: Vec<Source>, + pub source: Source, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Source { - pub file: PathBuf, + pub path: PathBuf, pub tracks: BTreeMap<u64, SourceTrack>, } @@ -25,8 +25,8 @@ pub enum SourceTrack { Video { language: String, codec: String, - width: usize, - height: usize, + width: u64, + height: u64, }, Audio { channels: usize, @@ -35,6 +35,7 @@ pub enum SourceTrack { language: String, }, Subtitles { + codec: String, language: String, }, } diff --git a/remuxer/Cargo.toml b/remuxer/Cargo.toml index 4870c25..e6a4b48 100644 --- a/remuxer/Cargo.toml +++ b/remuxer/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +tokio = { version = "1.24.1", features = ["io-util"] } jellycommon = { path = "../common" } webm-iterable = "0.4.2" diff --git a/remuxer/src/lib.rs b/remuxer/src/lib.rs index 69a6f4b..8b4e2ed 100644 --- a/remuxer/src/lib.rs +++ b/remuxer/src/lib.rs @@ -1,9 +1,55 @@ -use std::path::PathBuf; - -use jellycommon::Source; +use jellycommon::ItemInfo; +use log::{debug, info}; +use std::{fs::File, io::Write, path::PathBuf, sync::Arc}; +use tokio::sync::mpsc::Sender; +use webm_iterable::{ + matroska_spec::{Master, MatroskaSpec}, + WebmIterator, WebmWriter, +}; pub struct RemuxerContext {} impl RemuxerContext { - pub fn generate(&self, path_base: PathBuf, source: Source) {} + pub fn new() -> Arc<Self> { + Arc::new(Self {}) + } + + pub fn generate_into( + &self, + writer: impl Write, + path_base: PathBuf, + item: ItemInfo, + selection: Vec<u64>, + ) -> anyhow::Result<()> { + let source_path = path_base.join(item.source.path); + info!("remuxing {source_path:?} to have tracks {selection:?}"); + let mut input = File::open(source_path)?; + + let tags = WebmIterator::new(&mut input, &[MatroskaSpec::TrackEntry(Master::Start)]); + let mut output = WebmWriter::new(writer); + + for tag in tags { + match tag.unwrap() { + x => { + // debug!("tag"); + output.write(&x)?; + } + } + } + Ok(()) + } +} + +pub struct SendWriter(pub Sender<Vec<u8>>); + +impl Write for SendWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { + debug!("write {buf:?}"); + self.0.blocking_send(buf.to_owned()).unwrap(); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) // TODO should we actually do something here? + } } diff --git a/server/Cargo.toml b/server/Cargo.toml index 4128b40..b8a57f2 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] jellycommon = { path = "../common" } +jellyremuxer = { path = "../remuxer" } serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.91" @@ -14,7 +15,8 @@ env_logger = "0.10.0" anyhow = "1.0.68" rocket = "0.5.0-rc.2" -tokio = "1.24.1" +tokio = { version = "1.24.1", features = ["io-util"] } +tokio-util = { version = "0.7.4", features = ["io", "io-util"] } markup = "0.13.1" chashmap = "2.2.2" diff --git a/server/src/library.rs b/server/src/library.rs index c46937a..4bded8a 100644 --- a/server/src/library.rs +++ b/server/src/library.rs @@ -22,6 +22,7 @@ pub struct Directory { #[derive(Debug, Clone)] pub struct Item { + pub fs_path: PathBuf, pub lib_path: PathBuf, pub identifier: String, pub data: ItemInfo, @@ -49,12 +50,18 @@ impl Library { } impl Node { - pub fn get_directory(&self) -> anyhow::Result<&Directory> { + pub fn get_directory(&self) -> anyhow::Result<Arc<Directory>> { match self { - Node::Directory(d) => Ok(d), + Node::Directory(d) => Ok(d.clone()), Node::Item(_) => bail!("not a directory"), } } + pub fn get_item(&self) -> anyhow::Result<Arc<Item>> { + match self { + Node::Item(i) => Ok(i.clone()), + Node::Directory(_) => bail!("not an item"), + } + } pub fn title(&self) -> &str { match self { Node::Directory(d) => &d.data.name, @@ -86,9 +93,10 @@ impl Node { .read_dir()? .filter_map(|e| { let e = e.unwrap(); - eprintln!("{:?}",e.path()); - eprintln!("{:?}",e.path().extension()); - if e.path().extension() == Some(OsStr::new("json")) + eprintln!("{:?}", e.path()); + eprintln!("{:?}", e.path().extension()); + if (e.path().extension() == Some(OsStr::new("json")) + && e.path().file_name() != Some(OsStr::new("directory.json"))) || e.metadata().unwrap().is_dir() { Some(e.path()) @@ -121,6 +129,7 @@ impl Node { .unwrap() .to_string(); Ok(Node::Item(Arc::new(Item { + fs_path: path.clone(), lib_path: lib_path.join(identifier.clone()), data, identifier, diff --git a/server/src/main.rs b/server/src/main.rs index 36571f0..3f8d241 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,11 +1,14 @@ #![feature(box_syntax)] -use crate::frontend::style::CSS_BUNDLE; +use crate::frontend::{pages::MyError, style::CSS_BUNDLE}; use database::Database; use frontend::pages::{home::page_home, node::page_library_node}; +use jellyremuxer::{RemuxerContext, SendWriter}; use library::Library; -use rocket::{get, http::ContentType, launch, routes}; -use std::fs::read_to_string; +use log::debug; +use rocket::{get, http::ContentType, launch, response::stream::ByteStream, routes, State}; +use std::{fs::read_to_string, sync::Arc}; +use tokio::sync::mpsc; pub mod database; pub mod frontend; @@ -23,9 +26,42 @@ async fn assets_style() -> (ContentType, String) { ) } +#[get("/stream?<selection>")] +fn stream( + selection: String, + state: &State<AppState>, +) -> Result<(ContentType, ByteStream![Vec<u8>]), MyError> { + let (tx, mut rx) = mpsc::channel(16); + let item = state.library.nested("mili-bento-box-bivouac")?.get_item()?; + debug!("generating matroska"); + let remuxer = state.remuxer.clone(); + tokio::task::spawn_blocking(move || { + remuxer + .generate_into( + SendWriter(tx), + // SyncIoBridge() + item.fs_path.parent().unwrap().to_path_buf(), + item.data.clone(), + selection.split(",").map(|e| e.parse().unwrap()).collect(), + ) + .unwrap(); + }); + debug!("starting stream"); + Ok(( + ContentType::WEBM, + ByteStream! { + while let Some(x) = rx.recv().await { + debug!("yield {x:?}"); + yield x + } + }, + )) +} + pub struct AppState { pub database: Database, pub library: Library, + pub remuxer: Arc<RemuxerContext>, } #[launch] @@ -34,11 +70,13 @@ fn rocket() -> _ { let db_path = std::env::var("DB_PATH").unwrap_or("data/db".to_string()); let lib_path = std::env::var("LIB_PATH").unwrap_or("data/library".to_string()); let state = AppState { + remuxer: RemuxerContext::new(), library: Library::open(&lib_path).unwrap(), database: Database::open(&db_path).unwrap(), }; - rocket::build() - .manage(state) - .mount("/", routes![page_home, page_library_node, assets_style]) + rocket::build().manage(state).mount( + "/", + routes![page_home, page_library_node, assets_style, stream], + ) } diff --git a/tools/src/bin/gen_meta.rs b/tools/src/bin/gen_meta.rs index b09f4a1..e7aa559 100644 --- a/tools/src/bin/gen_meta.rs +++ b/tools/src/bin/gen_meta.rs @@ -16,89 +16,91 @@ struct Args { #[clap(short, long)] title: String, #[clap(short = 'i', long)] - inputs: Vec<PathBuf>, + input: PathBuf, } fn main() -> anyhow::Result<()> { let args = Args::parse(); - let mut source = vec![]; - for fname in args.inputs { - let mut tracks = BTreeMap::new(); - let mut input = File::open(fname.clone()).unwrap(); - for tag in WebmIterator::new(&mut input, &[MatroskaSpec::TrackEntry(Master::Start)]) { - let tag = tag?; - match tag { - MatroskaSpec::TrackEntry(master) => { - let ( - mut index, - mut language, - mut codec, - mut kind, - mut sample_rate, - mut channels, - mut width, - mut height, - ) = (None, None, None, None, None, None, None, None); - for c in master.get_children() { - match c { - MatroskaSpec::CodecID(b) => codec = Some(b), - MatroskaSpec::Language(v) => language = Some(v), - MatroskaSpec::TrackNumber(v) => index = Some(v), - MatroskaSpec::TrackType(v) => kind = Some(v), - MatroskaSpec::Audio(master) => { - for c in master.get_children() { - match c { - MatroskaSpec::Channels(v) => channels = Some(v as usize), - MatroskaSpec::SamplingFrequency(v) => sample_rate = Some(v), - _ => (), - } + let mut tracks = BTreeMap::new(); + let mut input = File::open(args.input.clone()).unwrap(); + for tag in WebmIterator::new(&mut input, &[MatroskaSpec::TrackEntry(Master::Start)]) { + let tag = tag?; + match tag { + MatroskaSpec::TrackEntry(master) => { + let ( + mut index, + mut language, + mut codec, + mut kind, + mut sample_rate, + mut channels, + mut width, + mut height, + mut name, + ) = (None, None, None, None, None, None, None, None, None); + for c in master.get_children() { + match c { + MatroskaSpec::CodecID(b) => codec = Some(b), + MatroskaSpec::Language(v) => language = Some(v), + MatroskaSpec::TrackNumber(v) => index = Some(v), + MatroskaSpec::TrackType(v) => kind = Some(v), + MatroskaSpec::Audio(master) => { + for c in master.get_children() { + match c { + MatroskaSpec::Channels(v) => channels = Some(v as usize), + MatroskaSpec::SamplingFrequency(v) => sample_rate = Some(v), + _ => (), } } - MatroskaSpec::Video(master) => { - for c in master.get_children() { - match c { - MatroskaSpec::PixelWidth(v) => width = Some(v as usize), - MatroskaSpec::PixelHeight(v) => height = Some(v as usize), - _ => (), - } + } + MatroskaSpec::Video(master) => { + for c in master.get_children() { + match c { + MatroskaSpec::PixelWidth(v) => width = Some(v), + MatroskaSpec::PixelHeight(v) => height = Some(v), + _ => (), } } - _ => (), } + MatroskaSpec::Name(v) => name = Some(v), + _ => (), } - tracks.insert( - index.unwrap(), - match kind.ok_or(anyhow!("track type required"))? { - 1 => SourceTrack::Video { - language: language.unwrap(), - codec: codec.unwrap(), - width: width.unwrap(), - height: height.unwrap(), - }, - 2 => SourceTrack::Audio { - channels: channels.unwrap(), - codec: codec.unwrap(), - sample_rate: sample_rate.unwrap(), - language: language.unwrap(), - }, - _ => bail!("invalid track type"), - }, - ); } - MatroskaSpec::Tags(Master::End) => break, - _ => (), + tracks.insert( + index.unwrap(), + match kind.ok_or(anyhow!("track type required"))? { + 1 => SourceTrack::Video { + language: language.unwrap_or("none".to_string()), + codec: codec.unwrap(), + width: width.unwrap(), + height: height.unwrap(), + }, + 2 => SourceTrack::Audio { + channels: channels.unwrap(), + codec: codec.unwrap(), + sample_rate: sample_rate.unwrap(), + language: language.unwrap(), + }, + 17 => SourceTrack::Subtitles { + language: name.unwrap(), + codec: codec.unwrap(), + }, + _ => bail!("invalid track type"), + }, + ); } + MatroskaSpec::Tags(Master::End) => break, + _ => (), } - source.push(Source { - file: fname.clone(), - tracks, - }) } let k = serde_json::to_string_pretty(&ItemInfo { title: args.title, - source, + source: Source { + path: args.input.clone(), + tracks, + }, })?; if args.write_json { |