aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-01-11 23:16:10 +0100
committermetamuffin <metamuffin@disroot.org>2023-01-11 23:16:10 +0100
commit43e986b5e626b339095c118fa79ba6cd011aeb9f (patch)
treeaeb70c1233d6bade2d04e86086f48e88b72f3815
parent0f944c90380b28e010211f3c8d57423e38bb2c11 (diff)
downloadjellything-43e986b5e626b339095c118fa79ba6cd011aeb9f.tar
jellything-43e986b5e626b339095c118fa79ba6cd011aeb9f.tar.bz2
jellything-43e986b5e626b339095c118fa79ba6cd011aeb9f.tar.zst
doesnt work but should
-rw-r--r--Cargo.lock3
-rw-r--r--common/src/lib.rs9
-rw-r--r--remuxer/Cargo.toml1
-rw-r--r--remuxer/src/lib.rs54
-rw-r--r--server/Cargo.toml4
-rw-r--r--server/src/library.rs19
-rw-r--r--server/src/main.rs50
-rw-r--r--tools/src/bin/gen_meta.rs132
8 files changed, 187 insertions, 85 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c6776fb..97d59a0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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 {