aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/src/lib.rs26
-rw-r--r--remuxer/src/import/mod.rs29
-rw-r--r--remuxer/src/lib.rs9
-rw-r--r--server/src/database.rs6
-rw-r--r--server/src/import.rs68
-rw-r--r--server/src/library.rs195
-rw-r--r--server/src/main.rs7
-rw-r--r--server/src/routes/api/mod.rs73
-rw-r--r--server/src/routes/mod.rs19
-rw-r--r--server/src/routes/stream.rs44
-rw-r--r--server/src/routes/ui/assets.rs44
-rw-r--r--server/src/routes/ui/browser.rs31
-rw-r--r--server/src/routes/ui/home.rs8
-rw-r--r--server/src/routes/ui/mod.rs1
-rw-r--r--server/src/routes/ui/node.rs172
-rw-r--r--server/src/routes/ui/player.rs49
-rw-r--r--tools/src/bin/import.rs216
17 files changed, 396 insertions, 601 deletions
diff --git a/common/src/lib.rs b/common/src/lib.rs
index bab1d79..7392c87 100644
--- a/common/src/lib.rs
+++ b/common/src/lib.rs
@@ -1,3 +1,8 @@
+/*
+ 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) 2023 metamuffin <metamuffin.org>
+*/
pub mod r#impl;
use bincode::{Decode, Encode};
@@ -5,14 +10,14 @@ use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Deserialize, Serialize)]
-pub struct Item {
- pub public: ItemPublic,
- pub private: ItemPrivate,
+pub struct Node {
+ pub public: NodePublic,
+ pub private: NodePrivate,
}
#[rustfmt::skip]
#[derive(Debug, Clone, Deserialize, Serialize)]
-pub struct ItemPrivate {
+pub struct NodePrivate {
#[serde(default)] pub poster: Option<PathBuf>,
#[serde(default)] pub backdrop: Option<PathBuf>,
#[serde(default)] pub source: Option<MediaSource>,
@@ -20,24 +25,25 @@ pub struct ItemPrivate {
#[rustfmt::skip]
#[derive(Debug, Clone, Deserialize, Serialize)]
-pub struct ItemPublic {
- pub kind: ItemKind,
+pub struct NodePublic {
+ pub kind: NodeKind,
pub title: String,
+ #[serde(default)] pub children: Vec<String>,
#[serde(default)] pub tagline: Option<String>,
#[serde(default)] pub description: Option<String>,
- #[serde(default)] pub poster: Option<PathBuf>,
- #[serde(default)] pub backdrop: Option<PathBuf>,
#[serde(default)] pub index: Option<usize>,
#[serde(default)] pub media: Option<MediaInfo>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
-pub enum ItemKind {
+pub enum NodeKind {
+ Movie,
Collection,
- Series,
Show,
+ Series,
Season,
+ Episode,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
diff --git a/remuxer/src/import/mod.rs b/remuxer/src/import/mod.rs
index 4ccbd1c..1af74f6 100644
--- a/remuxer/src/import/mod.rs
+++ b/remuxer/src/import/mod.rs
@@ -12,11 +12,15 @@ use jellymatroska::{
unflatten::{IterWithPos, Unflat, Unflatten},
};
use log::{debug, error, info, trace, warn};
-use std::{collections::HashMap, fs::File, path::PathBuf};
+use std::{collections::HashMap, path::PathBuf};
-pub fn import_read(path: &PathBuf, input: &mut EbmlReader) -> Result<()> {
+pub fn import_read(
+ path: &PathBuf,
+ input: &mut EbmlReader,
+) -> Result<(Vec<SourceTrack>, Vec<LocalTrack>, Vec<SeekIndex>)> {
let mut iteminfo = Vec::new();
let mut private = Vec::new();
+ let mut seek_index = Vec::new();
while let Some(item) = input.next() {
let item = match item {
Ok(item) => item,
@@ -45,14 +49,20 @@ pub fn import_read(path: &PathBuf, input: &mut EbmlReader) -> Result<()> {
MatroskaTag::Segment(_) => {
info!("segment start");
let mut children = Unflatten::new_with_end(input, item);
- import_read_segment(path, &mut children, &mut iteminfo, &mut private)?;
+ import_read_segment(
+ path,
+ &mut children,
+ &mut iteminfo,
+ &mut private,
+ &mut seek_index,
+ )?;
info!("segment end");
}
_ => debug!("(r) tag ignored: {item:?}"),
}
}
- Ok(())
+ Ok((iteminfo, private, seek_index))
}
fn import_read_segment(
@@ -60,6 +70,7 @@ fn import_read_segment(
segment: &mut Unflatten,
iteminfo: &mut Vec<SourceTrack>,
private: &mut Vec<LocalTrack>,
+ seek_index_out: &mut Vec<SeekIndex>,
) -> Result<Option<f64>> {
let (mut timestamp_scale, mut duration) = (None, None);
let mut seek_index = HashMap::new();
@@ -246,14 +257,10 @@ fn import_read_segment(
};
}
- for (tn, index) in seek_index {
- info!("writing index {tn} with {} blocks", index.blocks.len());
- bincode::encode_into_std_write(
- index,
- &mut File::create(path.with_extension(&format!("si.{tn}")))?,
- bincode::config::standard(),
- )?;
+ for i in 0..iteminfo.len() {
+ seek_index_out.push(seek_index.get(&(i as u64)).unwrap().clone())
}
+
Ok(if let Some(duration) = duration {
Some((duration * timestamp_scale.unwrap_or(1_000_000) as f64) / 1_000_000_000_f64)
} else {
diff --git a/remuxer/src/lib.rs b/remuxer/src/lib.rs
index 5fc35f8..4a73edf 100644
--- a/remuxer/src/lib.rs
+++ b/remuxer/src/lib.rs
@@ -9,7 +9,7 @@ pub mod trim_writer;
use crate::{segment_extractor::SegmentExtractIter, trim_writer::TrimWriter};
use anyhow::{anyhow, Context};
-use jellycommon::{BlockIndex, ItemPublic, LocalTrack, SeekIndex, SourceTrack, SourceTrackKind};
+use jellycommon::{BlockIndex, NodePublic, LocalTrack, SeekIndex, SourceTrack, SourceTrackKind};
use jellymatroska::{
block::Block,
read::EbmlReader,
@@ -39,7 +39,7 @@ impl RemuxerContext {
writer: impl Write + 'static,
range: Range<usize>,
path_base: PathBuf,
- item: ItemPublic,
+ item: NodePublic,
track_sources: Vec<LocalTrack>,
selection: Vec<usize>,
webm: bool,
@@ -65,7 +65,8 @@ impl RemuxerContext {
.enumerate()
.map(|(index, sel)| {
let info = item
- .media.as_ref()
+ .media
+ .as_ref()
.unwrap()
.tracks
.get(*sel)
@@ -228,7 +229,7 @@ impl RemuxerContext {
+ 1 // ccp id
+ 1 // ccp len
+ 8 // ccp content offset
- )
+ )
)
) * inputs.len()
) * segment_layout.len()
diff --git a/server/src/database.rs b/server/src/database.rs
index bfb5d47..eb88bda 100644
--- a/server/src/database.rs
+++ b/server/src/database.rs
@@ -5,7 +5,7 @@
*/
use crate::{routes::ui::account::hash_password, CONF};
use anyhow::Context;
-use jellycommon::SeekIndex;
+use jellycommon::{SeekIndex, Node};
use log::info;
use serde::{Deserialize, Serialize};
use std::path::Path;
@@ -16,7 +16,7 @@ pub struct Database {
pub users: Tree<String, User>,
pub invites: Tree<String, ()>,
- pub items: Tree<String, Item>,
+ pub node: Tree<String, Node>,
pub seek_index: Tree<(String, usize), SeekIndex>,
}
@@ -36,7 +36,7 @@ impl Database {
let r = Ok(Self {
users: Tree::open(&db, "users"),
invites: Tree::open(&db, "invites"),
- items: Tree::open(&db, "items"),
+ node: Tree::open(&db, "items"),
seek_index: Tree::open(&db, "seek_index"),
db,
});
diff --git a/server/src/import.rs b/server/src/import.rs
new file mode 100644
index 0000000..06d32c3
--- /dev/null
+++ b/server/src/import.rs
@@ -0,0 +1,68 @@
+/*
+ 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) 2023 metamuffin <metamuffin.org>
+*/
+use crate::{database::Database, CONF};
+use anyhow::{bail, Context, Ok};
+use jellycommon::Node;
+use log::info;
+use std::{ffi::OsStr, fs::File, os::unix::prelude::OsStrExt, path::PathBuf};
+
+pub fn import(db: &Database) -> anyhow::Result<()> {
+ info!("clearing node tree");
+ db.node.clear()?;
+ info!("importing...");
+ import_path(CONF.library_path.clone(), db).context("indexing")?;
+ Ok(())
+}
+
+pub fn import_path(path: PathBuf, db: &Database) -> anyhow::Result<Vec<String>> {
+ if path.is_dir() {
+ let mpath = path.join("directory.json");
+ let children = path.read_dir()?.filter_map(|e| {
+ let e = e.unwrap();
+ if e.path().extension() == Some(&OsStr::from_bytes(b"jelly"))
+ || e.metadata().unwrap().is_dir()
+ {
+ Some(e.path())
+ } else {
+ None
+ }
+ });
+ let children = children
+ .map(|e| import_path(e, db))
+ .collect::<anyhow::Result<Vec<_>>>()?
+ .into_iter()
+ .flatten()
+ .collect();
+ if mpath.exists() {
+ let data: Node =
+ serde_json::from_reader(File::open(mpath).context("metadata missing")?)?;
+
+ let identifier = path.file_name().unwrap().to_str().unwrap().to_string();
+
+ db.node.insert(&identifier, &data)?;
+
+ Ok(vec![identifier])
+ } else {
+ Ok(children)
+ }
+ } else if path.is_file() {
+ info!("loading item {path:?}");
+ let datafile = File::open(path.clone()).context("cant load metadata")?;
+ let data: Node = serde_json::from_reader(datafile).context("invalid metadata")?;
+ let identifier = path
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .strip_suffix(".jelly")
+ .unwrap()
+ .to_string();
+ db.node.insert(&identifier, &data)?;
+ Ok(vec![identifier])
+ } else {
+ bail!("did somebody really put a fifo or socket in the library?!")
+ }
+}
diff --git a/server/src/library.rs b/server/src/library.rs
deleted file mode 100644
index 8606a6e..0000000
--- a/server/src/library.rs
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- 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) 2023 metamuffin <metamuffin.org>
-*/
-use anyhow::{anyhow, bail, Context, Ok};
-use jellycommon::{CommmonInfo, DirectoryInfo, ItemInfo};
-use log::info;
-use std::{
- ffi::OsStr,
- fs::File,
- os::unix::prelude::OsStrExt,
- path::{Path, PathBuf},
- sync::Arc,
-};
-
-use crate::{routes::ui::node::AssetRole, CONF};
-
-impl Library {
- pub fn open(path: &Path) -> anyhow::Result<Self> {
- Ok(Self {
- root_path: path.to_path_buf(),
- root: Node::from_path(path.to_path_buf(), PathBuf::new(), true)
- .context("indexing root")?
- .into_iter()
- .next()
- .ok_or(anyhow!("root need directory.json"))?,
- })
- }
- pub fn nested_path(&self, path: &Path) -> anyhow::Result<Arc<Node>> {
- self.nested(path.to_str().unwrap())
- }
- pub fn nested(&self, path: &str) -> anyhow::Result<Arc<Node>> {
- let mut n = self.root.clone();
- if path.is_empty() {
- return Ok(n);
- }
- for seg in path.split('/') {
- n = n
- .get_directory()?
- .child_by_ident(seg)
- .ok_or(anyhow!("does not exist"))?;
- }
- Ok(n)
- }
-}
-
-impl Node {
- pub fn get_directory(&self) -> anyhow::Result<Arc<Directory>> {
- match self {
- 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 common(&self) -> &CommmonInfo {
- match self {
- Node::Directory(d) => &d.info.common,
- Node::Item(i) => &i.info.common,
- }
- }
- pub fn identifier(&self) -> &str {
- match self {
- Node::Directory(d) => &d.identifier,
- Node::Item(i) => &i.identifier,
- }
- }
- pub fn lib_path(&self) -> &PathBuf {
- match self {
- Node::Directory(d) => &d.lib_path,
- Node::Item(i) => &i.lib_path,
- }
- }
-
- pub fn from_path(
- path: PathBuf,
- mut lib_path: PathBuf,
- root: bool,
- ) -> anyhow::Result<Vec<Arc<Node>>> {
- if path.is_dir() {
- let mpath = path.join("directory.json");
- let children = path.read_dir()?.filter_map(|e| {
- let e = e.unwrap();
- if e.path().extension() == Some(&OsStr::from_bytes(b"jelly"))
- || e.metadata().unwrap().is_dir()
- {
- Some(e.path())
- } else {
- None
- }
- });
- if !mpath.exists() {
- info!("flattening {path:?}");
- Ok(children
- .map(|e| Node::from_path(e, lib_path.clone(), false))
- .collect::<Result<Vec<_>, _>>()?
- .into_iter()
- .flatten()
- .collect())
- } else {
- let data: DirectoryInfo =
- serde_json::from_reader(File::open(mpath).context("metadata missing")?)?;
-
- let identifier = path.file_name().unwrap().to_str().unwrap().to_string();
- if !root {
- lib_path = lib_path.join(identifier.clone());
- }
-
- info!("scanning directory {path:?}");
-
- let children = children
- .map(|e| {
- Node::from_path(e.clone(), lib_path.clone(), false)
- .context(format!("loading {e:?}"))
- })
- .collect::<anyhow::Result<Vec<_>>>()?
- .into_iter()
- .flatten()
- .collect();
-
- Ok(Vec::from_iter(Some(
- Node::Directory(Arc::new(Directory {
- lib_path,
- children,
- info: data,
- identifier,
- }))
- .into(),
- )))
- }
- } else if path.is_file() {
- info!("loading item {path:?}");
- let datafile = File::open(path.clone()).context("cant load metadata")?;
- let data: ItemInfo = serde_json::from_reader(datafile).context("invalid metadata")?;
- let identifier = path
- .file_name()
- .unwrap()
- .to_str()
- .unwrap()
- .strip_suffix(".jelly")
- .unwrap()
- .to_string();
- Ok(Vec::from_iter(Some(
- Node::Item(Arc::new(Item {
- fs_path: path,
- lib_path: lib_path.join(identifier.clone()),
- info: data,
- identifier,
- }))
- .into(),
- )))
- } else {
- bail!("did somebody really put a fifo or socket in the library?!")
- }
- }
-
- pub fn get_asset(&self, library: &Library, role: AssetRole) -> PathBuf {
- let path = match role {
- AssetRole::Backdrop => self
- .common()
- .backdrop
- .clone()
- .or_else(|| self.common().poster.clone()),
- AssetRole::Poster => self.common().poster.clone(),
- };
- if let Some(p) = path {
- library.root_path.join(p)
- } else {
- CONF.asset_path.join("fallback.jpeg")
- }
- }
-}
-
-impl Item {
- pub fn path(&self) -> String {
- self.lib_path.to_str().unwrap().to_string()
- }
-}
-
-impl Directory {
- pub fn path(&self) -> String {
- self.lib_path.to_str().unwrap().to_string()
- }
- pub fn child_by_ident(&self, i: &str) -> Option<Arc<Node>> {
- self.children
- .iter()
- .find(|e| e.identifier() == i)
- .map(|e| e.to_owned())
- }
-}
diff --git a/server/src/main.rs b/server/src/main.rs
index 6d7f812..a9a22cf 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -6,14 +6,13 @@
use config::{load_global_config, GlobalConfig};
use database::Database;
use jellyremuxer::RemuxerContext;
-use library::Library;
use once_cell::sync::Lazy;
use rocket::launch;
use routes::build_rocket;
pub mod config;
pub mod database;
-pub mod library;
+pub mod import;
pub mod routes;
pub static CONF: Lazy<GlobalConfig> = Lazy::new(load_global_config);
@@ -29,8 +28,8 @@ fn rocket() -> _ {
log::warn!("authentification bypass enabled");
let remuxer = RemuxerContext::new();
- let library = Library::open(&CONF.library_path).unwrap();
let database = Database::open(&CONF.database_path).unwrap();
+ import::import(&database).unwrap();
database.create_admin();
- build_rocket(remuxer, library, database)
+ build_rocket(remuxer, database)
}
diff --git a/server/src/routes/api/mod.rs b/server/src/routes/api/mod.rs
index af795f1..d49ecec 100644
--- a/server/src/routes/api/mod.rs
+++ b/server/src/routes/api/mod.rs
@@ -5,31 +5,10 @@
*/
pub mod error;
-use std::path::PathBuf;
-
-use super::ui::{
- account::{login_logic, LoginForm},
- node::AssetRole,
- CacheControlFile,
-};
-use crate::{
- database::Database,
- library::{Library, Node},
- routes::{api::error::ApiResult, ui::account::session::Session},
-};
-use anyhow::Context;
-use jellycommon::api::ApiNode;
-use log::info;
-use rocket::{
- get,
- http::{ContentType, CookieJar},
- post,
- response::Redirect,
- serde::json::Json,
- State,
-};
+use super::ui::account::{login_logic, LoginForm};
+use crate::{database::Database, routes::api::error::ApiResult};
+use rocket::{get, http::CookieJar, post, response::Redirect, serde::json::Json, State};
use serde_json::{json, Value};
-use tokio::fs::File;
#[get("/api")]
pub fn r_api_root() -> Redirect {
@@ -50,49 +29,3 @@ pub fn r_api_account_login(
login_logic(jar, database, &data.username, &data.password)?;
Ok(json!({ "ok": true }))
}
-
-#[get("/api/assets/node/<path..>?<role>")]
-pub async fn r_api_assets_node(
- _sess: Session,
- path: PathBuf,
- role: AssetRole,
- library: &State<Library>,
-) -> ApiResult<(ContentType, CacheControlFile)> {
- let node = library
- .nested_path(&path)
- .context("retrieving library node")?;
- let path = node.get_asset(library, role);
- info!("loading asset from {path:?}");
- let ext = path.extension().unwrap().to_str().unwrap();
- Ok((
- ContentType::from_extension(ext).unwrap(),
- CacheControlFile::new(File::open(path).await?).await,
- ))
-}
-
-#[get("/api/node/<path..>")]
-pub fn r_api_node(
- _sess: Session,
- path: PathBuf,
- library: &State<Library>,
-) -> ApiResult<Json<ApiNode>> {
- let node = library
- .nested_path(&path)
- .context("retrieving library node")?;
-
- Ok(Json(match node.as_ref() {
- Node::Directory(d) => ApiNode::Directory {
- identifier: d.identifier.clone(),
- info: d.info.clone(),
- children: d
- .children
- .iter()
- .map(|c| c.identifier().to_string())
- .collect::<Vec<_>>(),
- },
- Node::Item(i) => ApiNode::Item {
- identifier: i.identifier.clone(),
- info: i.info.clone(),
- },
- }))
-}
diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs
index 8d50c2e..52918d9 100644
--- a/server/src/routes/mod.rs
+++ b/server/src/routes/mod.rs
@@ -3,11 +3,8 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2023 metamuffin <metamuffin.org>
*/
-use crate::{database::Database, library::Library, routes::ui::error::MyResult, CONF};
-use api::{
- error::r_api_catch, r_api_account_login, r_api_assets_node, r_api_node, r_api_root,
- r_api_version,
-};
+use crate::{database::Database, routes::ui::error::MyResult, CONF};
+use api::{error::r_api_catch, r_api_account_login, r_api_root, r_api_version};
use jellyremuxer::RemuxerContext;
use rocket::{
catchers, config::SecretKey, fairing::AdHoc, fs::FileServer, get, http::Header, routes, Build,
@@ -25,10 +22,11 @@ use ui::{
r_account_register, r_account_register_post,
settings::{r_account_settings, r_account_settings_post},
},
+ assets::r_item_assets,
browser::r_all_items,
error::r_catch,
home::{r_home, r_home_unpriv},
- node::{r_item_assets, r_library_node},
+ node::r_library_node,
player::r_player,
style::{r_assets_font, r_assets_js, r_assets_style},
};
@@ -44,18 +42,13 @@ macro_rules! uri {
};
}
-pub fn build_rocket(
- remuxer: RemuxerContext,
- library: Library,
- database: Database,
-) -> Rocket<Build> {
+pub fn build_rocket(remuxer: RemuxerContext, database: Database) -> Rocket<Build> {
rocket::build()
.configure(Config {
secret_key: SecretKey::derive_from(CONF.cookie_key.as_bytes()),
..Default::default()
})
.manage(remuxer)
- .manage(library)
.manage(database)
.attach(AdHoc::on_response("set server header", |_req, res| {
res.set_header(Header::new("server", "jellything"));
@@ -92,8 +85,6 @@ pub fn build_rocket(
r_account_settings_post,
r_api_version,
r_api_account_login,
- r_api_node,
- r_api_assets_node,
r_api_root,
],
)
diff --git a/server/src/routes/stream.rs b/server/src/routes/stream.rs
index b2b708b..2583cb1 100644
--- a/server/src/routes/stream.rs
+++ b/server/src/routes/stream.rs
@@ -4,8 +4,9 @@
Copyright (C) 2023 metamuffin <metamuffin.org>
*/
use super::ui::{account::session::Session, error::MyError};
-use crate::library::Library;
-use anyhow::{anyhow, Context, Result};
+use crate::{database::Database, CONF};
+use anyhow::{anyhow, Result};
+use jellycommon::MediaSource;
use jellyremuxer::RemuxerContext;
use log::{debug, info, warn};
use rocket::{
@@ -15,17 +16,14 @@ use rocket::{
response::{self, Responder},
Request, Response, State,
};
-use std::{
- ops::{Deref, Range},
- path::{Path, PathBuf},
-};
+use std::ops::{Deref, Range};
use tokio::io::{duplex, DuplexStream};
use tokio_util::io::SyncIoBridge;
-pub fn stream_uri(path: &Path, tracks: &[u64], webm: bool) -> String {
+pub fn stream_uri(id: &str, tracks: &[u64], webm: bool) -> String {
format!(
"/stream/{}?tracks={}&webm={}",
- path.to_str().unwrap(),
+ id,
tracks
.iter()
.map(|v| format!("{v}"))
@@ -35,16 +33,27 @@ pub fn stream_uri(path: &Path, tracks: &[u64], webm: bool) -> String {
)
}
-#[get("/stream/<path..>?<tracks>&<webm>")]
+#[get("/stream/<id>?<tracks>&<webm>")]
pub fn r_stream(
_sess: Session,
- path: PathBuf,
+ id: String,
webm: Option<bool>,
tracks: String,
remuxer: &State<RemuxerContext>,
- library: &State<Library>,
+ db: &State<Database>,
range: Option<RequestRange>,
) -> Result<StreamResponse, MyError> {
+ let node = db.node.get(&id)?.ok_or(anyhow!("node does not exist"))?;
+ let source = node
+ .private
+ .source
+ .ok_or(anyhow!("item does not contain media"))?;
+
+ let source_tracks = match source {
+ MediaSource::Local { tracks } => tracks,
+ _ => Err(anyhow!("todo"))?,
+ };
+
info!(
"stream request (range={})",
range
@@ -52,12 +61,9 @@ pub fn r_stream(
.map(|r| r.to_cr_hv())
.unwrap_or(format!("none"))
);
+
let (a, b) = duplex(4096);
- let path = path.to_str().unwrap().to_string();
- let item = library
- .nested(&path)
- .context("retrieving library node")?
- .get_item()?;
+
let remuxer = remuxer.deref().clone();
let tracks = tracks
.split(',')
@@ -74,13 +80,13 @@ pub fn r_stream(
None => 0..(isize::MAX as usize),
};
- let path_base = library.root_path.clone();
tokio::task::spawn_blocking(move || {
if let Err(e) = remuxer.generate_into(
b,
urange,
- path_base,
- item.info.clone(),
+ CONF.library_path.clone(),
+ node.public,
+ source_tracks,
tracks,
webm.unwrap_or(false),
) {
diff --git a/server/src/routes/ui/assets.rs b/server/src/routes/ui/assets.rs
new file mode 100644
index 0000000..2c0b85a
--- /dev/null
+++ b/server/src/routes/ui/assets.rs
@@ -0,0 +1,44 @@
+/*
+ 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) 2023 metamuffin <metamuffin.org>
+*/
+use crate::{
+ database::Database,
+ routes::ui::{account::session::Session, error::MyError, CacheControlFile},
+ CONF,
+};
+use anyhow::anyhow;
+use log::info;
+use rocket::{get, http::ContentType, FromFormField, State, UriDisplayQuery};
+use std::{path::PathBuf, str::FromStr};
+use tokio::fs::File;
+
+#[derive(FromFormField, UriDisplayQuery)]
+pub enum AssetRole {
+ Poster,
+ Backdrop,
+}
+
+#[get("/item_assets/<id>?<role>")]
+pub async fn r_item_assets(
+ _sess: Session,
+ id: String,
+ role: AssetRole,
+ db: &State<Database>,
+) -> Result<(ContentType, CacheControlFile), MyError> {
+ let node = db.node.get(&id)?.ok_or(anyhow!("node does not exist"))?;
+ let path = CONF.asset_path.join(
+ match role {
+ AssetRole::Backdrop => node.private.backdrop,
+ AssetRole::Poster => node.private.poster,
+ }
+ .unwrap_or_else(|| PathBuf::from_str("fallback.jpeg").unwrap()),
+ );
+ info!("loading asset from {path:?}");
+ let ext = path.extension().unwrap().to_str().unwrap();
+ Ok((
+ ContentType::from_extension(ext).unwrap(),
+ CacheControlFile::new(File::open(path).await?).await,
+ ))
+}
diff --git a/server/src/routes/ui/browser.rs b/server/src/routes/ui/browser.rs
index 30eb3f2..767e411 100644
--- a/server/src/routes/ui/browser.rs
+++ b/server/src/routes/ui/browser.rs
@@ -3,35 +3,24 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2023 metamuffin <metamuffin.org>
*/
-use super::{account::session::Session, error::MyError, layout::DynLayoutPage, node::PosterCard};
-use crate::library::{Library, Node};
+use super::{account::session::Session, error::MyError, layout::DynLayoutPage};
+use crate::database::Database;
use rocket::{get, State};
-use std::collections::VecDeque;
#[get("/items")]
-pub fn r_all_items(_sess: Session, library: &State<Library>) -> Result<DynLayoutPage<'_>, MyError> {
- let mut dirs = VecDeque::from_iter(Some(library.root.get_directory().unwrap()));
- let mut items = Vec::new();
- while let Some(d) = dirs.pop_front() {
- for e in &d.children {
- match e.as_ref() {
- Node::Directory(d) => dirs.push_back(d.clone()),
- Node::Item(i) => items.push(i.clone()),
- }
- }
- }
+pub fn r_all_items(_sess: Session, db: &State<Database>) -> Result<DynLayoutPage<'_>, MyError> {
Ok(super::layout::LayoutPage {
title: "All Items".to_owned(),
content: markup::new! {
.page.dir {
h1 { "All Items" }
- ul.directorylisting { @for item in &items {
- li {@PosterCard {
- wide: false, dir: false,
- path: item.lib_path.clone(),
- title: &item.info.title
- }}
- }}
+ // ul.directorylisting { @for item in &items {
+ // li {@PosterCard {
+ // wide: false, dir: false,
+ // path: item.lib_path.clone(),
+ // title: &item.info.title
+ // }}
+ // }}
}
},
..Default::default()
diff --git a/server/src/routes/ui/home.rs b/server/src/routes/ui/home.rs
index cdde478..0b85e89 100644
--- a/server/src/routes/ui/home.rs
+++ b/server/src/routes/ui/home.rs
@@ -5,20 +5,20 @@
*/
use super::{account::session::Session, layout::LayoutPage};
use crate::{
- library::Library,
- routes::ui::{error::MyResult, layout::DynLayoutPage, node::NodePage},
+ database::Database,
+ routes::ui::{error::MyResult, layout::DynLayoutPage},
CONF,
};
use rocket::{get, State};
use tokio::fs::read_to_string;
#[get("/")]
-pub fn r_home(_sess: Session, library: &State<Library>) -> DynLayoutPage {
+pub fn r_home(_sess: Session, _db: &State<Database>) -> DynLayoutPage {
LayoutPage {
title: "Home".to_string(),
content: markup::new! {
p { "Welcome to " @CONF.brand }
- @NodePage { node: &library.root }
+ // @NodePage { node: &db }
},
..Default::default()
}
diff --git a/server/src/routes/ui/mod.rs b/server/src/routes/ui/mod.rs
index f566f6d..5fad2b7 100644
--- a/server/src/routes/ui/mod.rs
+++ b/server/src/routes/ui/mod.rs
@@ -29,6 +29,7 @@ pub mod layout;
pub mod node;
pub mod player;
pub mod style;
+pub mod assets;
pub struct HtmlTemplate<'a>(pub markup::DynRender<'a>);
diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs
index 4d599dc..0ae0d9e 100644
--- a/server/src/routes/ui/node.rs
+++ b/server/src/routes/ui/node.rs
@@ -1,163 +1,141 @@
/*
-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) 2023 metamuffin <metamuffin.org>
+ 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) 2023 metamuffin <metamuffin.org>
*/
-use super::error::MyError;
-use super::player::player_uri;
-use super::CacheControlFile;
-use crate::uri;
+use super::{assets::rocket_uri_macro_r_item_assets, error::MyError, player::player_uri};
use crate::{
- library::{Directory, Item, Library, Node},
+ database::Database,
routes::ui::{
account::session::Session,
+ assets::AssetRole,
layout::{DynLayoutPage, LayoutPage},
},
+ uri,
};
-use anyhow::Context;
-use jellycommon::DirectoryKind;
-use log::info;
-use rocket::{get, http::ContentType, State};
-use rocket::{FromFormField, UriDisplayQuery};
-use std::{path::PathBuf, sync::Arc};
-use tokio::fs::File;
+use anyhow::{anyhow, Context};
+use jellycommon::{Node, NodeKind};
+use rocket::{get, State};
-#[get("/library/<path..>")]
+#[get("/library/<id>")]
pub async fn r_library_node(
_sess: Session,
- path: PathBuf,
- library: &State<Library>,
+ id: String,
+ db: &State<Database>,
) -> Result<DynLayoutPage<'_>, MyError> {
- let node = library
- .nested_path(&path)
- .context("retrieving library node")?;
+ let node = db
+ .node
+ .get(&id)
+ .context("retrieving library node")?
+ .ok_or(anyhow!("node does not exist"))?;
+
+ let children = node
+ .public
+ .children
+ .iter()
+ .map(|c| {
+ Ok((
+ c.to_owned(),
+ db.node.get(c)?.ok_or(anyhow!("child does not exist"))?,
+ ))
+ })
+ .collect::<anyhow::Result<Vec<_>>>()?
+ .into_iter()
+ .collect();
+
Ok(LayoutPage {
- title: node.common().title.to_string(),
- show_back: node.get_item().is_ok(),
+ title: node.public.title.to_string(),
+ show_back: matches!(node.public.kind, NodeKind::Movie | NodeKind::Episode),
content: markup::new! {
- @NodePage { node: &node }
+ @NodePage { node: &node, id: &id, children: &children }
},
..Default::default()
})
}
markup::define! {
- NodePage<'a>(node: &'a Arc<Node>) {
- @match node.as_ref() {
- Node::Directory(dir) => { @match dir.info.kind {
- DirectoryKind::Series => { @SeriesPage { dir } }
- _ => { @DirectoryPage { dir } }
- } }
- Node::Item(item) => { @ItemPage { item } }
+ NodePage<'a>(id: &'a str, node: &'a Node, children: &'a Vec<(String,Node)>) {
+ @match node.public.kind {
+ NodeKind::Collection | NodeKind::Show | NodeKind::Season => { @DirectoryPage { node, id, children } }
+ NodeKind::Series => { @SeriesPage { node, children, id } }
+ NodeKind::Movie | NodeKind::Episode => { @ItemPage { node, id } }
}
}
- NodeCard<'a>(node: &'a Arc<Node>) {
- @match node.as_ref() {
- Node::Directory(dir) => {@PosterCard {
- wide: !matches!(dir.info.kind, DirectoryKind::Series | DirectoryKind::Season),
- dir: true,
- path: dir.lib_path.clone(),
- title: &dir.info.title
- }}
- Node::Item(item) => {@PosterCard {
- wide: false, dir: false,
- path: item.lib_path.clone(),
- title: &item.info.title
- }}
+ NodeCard<'a>(id: &'a str, node: &'a Node) {
+ @PosterCard {
+ wide: matches!(node.public.kind, NodeKind::Collection),
+ dir: !matches!(node.public.kind, NodeKind::Movie | NodeKind::Episode),
+ id,
+ title: &node.public.title
}
}
- DirectoryPage<'a>(dir: &'a Arc<Directory>) {
+ DirectoryPage<'a>(id: &'a str, node: &'a Node, children: &'a Vec<(String,Node)>) {
div.page.dir {
- h1 { @dir.info.title }
- @if let Some(parent) = dir.lib_path.parent() {
- a.dirup[href=uri!(r_library_node(&parent))] { "Go up" }
- }
+ h1 { @node.public.title }
+ // @if let Some(parent) = node.lib_path.parent() {
+ // a.dirup[href=uri!(r_library_node(&parent))] { "Go up" }
+ // }
ul.directorylisting {
- @for node in &dir.children {
- li { @NodeCard { node } }
+ @for (id, node) in children.iter() {
+ li { @NodeCard { id, node } }
}
}
}
}
- PosterCard<'a>(path: PathBuf, title: &'a str, wide: bool, dir: bool) {
+ PosterCard<'a>(id: &'a str, title: &'a str, wide: bool, dir: bool) {
div[class=if *wide {"card wide poster"} else {"card poster"}] {
div.banner {
- a[href=uri!(r_library_node(path))] {
- img[src=uri!(r_item_assets(path, AssetRole::Poster))];
+ a[href=uri!(r_library_node(id))] {
+ img[src=uri!(r_item_assets(id, AssetRole::Poster))];
}
@if *dir {
- div.hoverdir { a[href=&uri!(r_library_node(path))] { "Open" } }
+ div.hoverdir { a[href=&uri!(r_library_node(id))] { "Open" } }
} else {
- div.hoveritem { a[href=&player_uri(path)] { "▶" } }
+ div.hoveritem { a[href=&player_uri(id)] { "▶" } }
}
}
p.title {
- a[href=uri!(r_library_node(path))] {
+ a[href=uri!(r_library_node(id))] {
@title
}
}
}
}
- ItemPage<'a>(item: &'a Arc<Item>) {
+ ItemPage<'a>(id: &'a str, node: &'a Node) {
// TODO different image here
- img.backdrop[src=uri!(r_item_assets(&item.lib_path, AssetRole::Backdrop))];
+ img.backdrop[src=uri!(r_item_assets(id, AssetRole::Backdrop))];
div.page.item {
div.banner {
- img[src=uri!(r_item_assets(&item.lib_path, AssetRole::Poster))];
+ img[src=uri!(r_item_assets(id, AssetRole::Poster))];
}
div.title {
- h1 { @item.info.title }
+ h1 { @node.public.title }
// TODO release date, duration, ratings
- a.play[href=&player_uri(&item.lib_path)] { "Watch now" }
+ a.play[href=&player_uri(id)] { "Watch now" }
}
div.details {
- h3 { @item.info.tagline }
- p { @item.info.description }
+ h3 { @node.public.tagline }
+ p { @node.public.description }
}
}
}
- SeriesPage<'a>(dir: &'a Arc<Directory>) {
+ SeriesPage<'a>(id: &'a str, node: &'a Node, children: &'a Vec<(String,Node)>) {
// TODO different image here
- img.backdrop[src=uri!(r_item_assets(&dir.lib_path, AssetRole::Backdrop))];
+ img.backdrop[src=uri!(r_item_assets(id, AssetRole::Backdrop))];
div.page.item {
div.banner {
- img[src=uri!(r_item_assets(&dir.lib_path, AssetRole::Poster))];
+ img[src=uri!(r_item_assets(id, AssetRole::Poster))];
}
div.title {
- h1 { @dir.info.title }
+ h1 { @node.public.title }
}
div.details {
- h3 { @dir.info.tagline }
- p { @dir.info.description }
+ h3 { @node.public.tagline }
+ p { @node.public.description }
}
- ol { @for ep in &dir.children {
- li { a[href=uri!(r_library_node(ep.lib_path()))] { @ep.common().title } }
+ ol { @for (id, c) in children.iter() {
+ li { a[href=uri!(r_library_node(id))] { @c.public.title } }
} }
}
}
}
-
-#[derive(FromFormField, UriDisplayQuery)]
-pub enum AssetRole {
- Poster,
- Backdrop,
-}
-
-#[get("/item_assets/<path..>?<role>")]
-pub async fn r_item_assets(
- _sess: Session,
- path: PathBuf,
- role: AssetRole,
- library: &State<Library>,
-) -> Result<(ContentType, CacheControlFile), MyError> {
- let node = library
- .nested_path(&path)
- .context("retrieving library node")?;
- let path = node.get_asset(library, role);
- info!("loading asset from {path:?}");
- let ext = path.extension().unwrap().to_str().unwrap();
- Ok((
- ContentType::from_extension(ext).unwrap(),
- CacheControlFile::new(File::open(path).await?).await,
- ))
-}
diff --git a/server/src/routes/ui/player.rs b/server/src/routes/ui/player.rs
index d5cb685..0e87749 100644
--- a/server/src/routes/ui/player.rs
+++ b/server/src/routes/ui/player.rs
@@ -5,27 +5,24 @@
*/
use super::{account::session::Session, layout::LayoutPage};
use crate::{
- library::{Item, Library},
+ database::Database,
routes::{
stream::stream_uri,
ui::{
+ assets::{rocket_uri_macro_r_item_assets, AssetRole},
error::MyResult,
layout::DynLayoutPage,
- node::{rocket_uri_macro_r_item_assets, AssetRole},
},
},
uri,
};
-use jellycommon::SourceTrackKind;
+use anyhow::anyhow;
+use jellycommon::{Node, SourceTrackKind};
use markup::DynRender;
use rocket::{get, FromForm, State};
-use std::{
- path::{Path, PathBuf},
- sync::Arc,
-};
-pub fn player_uri(path: &Path) -> String {
- format!("/player/{}", path.to_str().unwrap())
+pub fn player_uri(id: &str) -> String {
+ format!("/player/{}", id)
}
#[derive(FromForm, Default, Clone, Debug)]
@@ -36,14 +33,14 @@ pub struct PlayerConfig {
pub webm: bool,
}
-#[get("/player/<path..>?<conf..>", rank = 4)]
+#[get("/player/<id>?<conf..>", rank = 4)]
pub fn r_player(
_sess: Session,
- library: &State<Library>,
- path: PathBuf,
+ db: &State<Database>,
+ id: String,
conf: PlayerConfig,
) -> MyResult<DynLayoutPage<'_>> {
- let item = library.nested_path(&path)?.get_item()?;
+ let item = db.node.get(&id)?.ok_or(anyhow!("node does not exist"))?;
let tracks = None
.into_iter()
.chain(conf.v.into_iter())
@@ -51,27 +48,35 @@ pub fn r_player(
.chain(conf.s.into_iter())
.collect::<Vec<_>>();
+ let conf = player_conf(item.clone(), !tracks.is_empty())?;
Ok(LayoutPage {
- title: item.info.title.to_owned(),
+ title: item.public.title.to_owned(),
class: Some("player"),
content: markup::new! {
@if tracks.is_empty() {
- img.backdrop[src=uri!(r_item_assets(&item.lib_path, AssetRole::Backdrop)).to_string()];
+ img.backdrop[src=uri!(r_item_assets(&id, AssetRole::Backdrop)).to_string()];
} else {
- video[src=stream_uri(&item.lib_path, &tracks, true), controls]{}
+ video[src=stream_uri(&id, &tracks, true), controls]{}
}
- @player_conf(item.clone(), !tracks.is_empty())
+ @conf
},
show_back: true,
..Default::default()
})
}
-pub fn player_conf<'a>(item: Arc<Item>, playing: bool) -> DynRender<'a> {
+pub fn player_conf<'a>(item: Node, playing: bool) -> anyhow::Result<DynRender<'a>> {
let mut audio_tracks = vec![];
let mut video_tracks = vec![];
let mut sub_tracks = vec![];
- for (tid, track) in item.info.tracks.clone() {
+ let tracks = item
+ .public
+ .media
+ .clone()
+ .ok_or(anyhow!("node does not have media"))?
+ .tracks
+ .clone();
+ for (tid, track) in tracks.into_iter().enumerate() {
match &track.kind {
SourceTrackKind::Audio { .. } => audio_tracks.push((tid, track)),
SourceTrackKind::Video { .. } => video_tracks.push((tid, track)),
@@ -79,9 +84,9 @@ pub fn player_conf<'a>(item: Arc<Item>, playing: bool) -> DynRender<'a> {
}
}
- markup::new! {
+ Ok(markup::new! {
form.playerconf[method = "GET", action = ""] {
- h2 { "Select tracks for " @item.info.title }
+ h2 { "Select tracks for " @item.public.title }
fieldset.video {
legend { "Video" }
@@ -115,5 +120,5 @@ pub fn player_conf<'a>(item: Arc<Item>, playing: bool) -> DynRender<'a> {
input[type="submit", value=if playing { "Change tracks" } else { "Start playback" }];
}
- }
+ })
}
diff --git a/tools/src/bin/import.rs b/tools/src/bin/import.rs
index 3050f71..f740fec 100644
--- a/tools/src/bin/import.rs
+++ b/tools/src/bin/import.rs
@@ -5,17 +5,12 @@ Copyright (C) 2023 metamuffin <metamuffin.org>
*/
use anyhow::Context;
use clap::{Parser, Subcommand};
-use jellycommon::{CommmonInfo, DirectoryInfo, ItemInfo};
+use jellycommon::{MediaInfo, Node, NodeKind, NodePrivate, NodePublic};
use jellymatroska::read::EbmlReader;
use jellyremuxer::import::import_read;
use jellytools::tmdb::{tmdb_details, tmdb_image, tmdb_search};
-use log::{info, warn};
-use std::{
- fs::File,
- io::{stdin, Write},
- path::{Path, PathBuf},
- process::exit,
-};
+use log::info;
+use std::{fs::File, io::stdin, path::PathBuf, process::exit};
#[derive(Parser)]
struct Args {
@@ -37,12 +32,6 @@ enum Action {
#[arg(short, long)]
series: bool,
},
- Episode {
- path: PathBuf,
- index: usize,
- title: String,
- input: PathBuf,
- },
Set {
#[arg(short = 'I', long)]
item: PathBuf,
@@ -131,89 +120,68 @@ fn main() -> anyhow::Result<()> {
})
.transpose()?;
- let common = CommmonInfo {
- poster,
- backdrop,
- description: Some(details.overview),
- tagline: details.tagline,
- title: details.title.clone().or(details.name.clone()).unwrap(),
- index: None,
- };
-
info!("is this correct? [y/n]");
if stdin().lines().next().unwrap().unwrap() != "y" {
exit(0)
}
- let k = if let Some(input) = input {
- let mut iteminfo = ItemInfo {
- common,
- duration: Default::default(),
- tracks: Default::default(),
- };
- info!("{iteminfo:#?}");
+ let kind;
+ let media;
+ let source;
+
+ if let Some(input) = input {
let source_path = path.join(format!("source.mkv"));
// std::fs::rename(&input, &source_path)?;
// std::os::unix::fs::symlink(&input, &source_path)?;
std::fs::copy(&input, &source_path)?;
- import_source(&mut iteminfo, &source_path)?;
- serde_json::to_string_pretty(&iteminfo)?
+ let input = File::open(&source_path).unwrap();
+ let mut input = EbmlReader::new(input);
+ let (tracks, local_tracks, _seek_index) =
+ import_read(&source_path.to_path_buf(), &mut input)?;
+ kind = NodeKind::Movie;
+ media = Some(MediaInfo {
+ tracks,
+ duration: 0.,
+ });
+ source = Some(jellycommon::MediaSource::Local {
+ tracks: local_tracks,
+ });
} else {
- serde_json::to_string_pretty(&DirectoryInfo {
- common,
- kind: jellycommon::DirectoryKind::Series,
- })?
+ kind = NodeKind::Series;
+ media = None;
+ source = None;
+ };
+
+ let node = Node {
+ private: NodePrivate {
+ backdrop: backdrop.clone(),
+ poster: poster.clone(),
+ source,
+ },
+ public: NodePublic {
+ description: Some(details.overview),
+ tagline: details.tagline,
+ title: details.title.clone().or(details.name.clone()).unwrap(),
+ index: None,
+ kind,
+ children: Vec::new(),
+ media,
+ },
};
if args.dry {
- println!("{k}")
+ println!("{node:?}")
} else {
- let mut f = File::create(path.join(if series {
+ let f = File::create(path.join(if series {
"directory.json".to_string()
} else {
format!("{ident}.jelly",)
}))?;
- f.write_all(k.as_bytes())?;
+ serde_json::to_writer_pretty(f, &node)?;
}
Ok(())
}
- Action::Episode {
- path,
- index,
- title,
- input,
- } => {
- let ident = make_ident(&title);
- let common = CommmonInfo {
- poster: None,
- backdrop: None,
- description: None,
- tagline: None,
- title,
- index: Some(index),
- };
- let mut iteminfo = ItemInfo {
- common,
- duration: Default::default(),
- tracks: Default::default(),
- };
- let path = path.join(&ident);
- std::fs::create_dir_all(&path)?;
- let source_path = path.join(format!("source.mkv"));
- // std::fs::rename(&input, &source_path)?;
- // std::os::unix::fs::symlink(&input, &source_path)?;
- std::fs::copy(&input, &source_path)?;
- import_source(&mut iteminfo, &source_path)?;
- let k = serde_json::to_string_pretty(&iteminfo)?;
- if args.dry {
- println!("{k}")
- } else {
- let mut f = File::create(path.join(format!("{ident}.jelly",)))?;
- f.write_all(k.as_bytes())?;
- }
- Ok(())
- }
Action::Set {
poster,
clear_inputs,
@@ -223,55 +191,55 @@ fn main() -> anyhow::Result<()> {
item,
title,
} => {
- let mut iteminfo: ItemInfo = match File::open(item.clone()) {
- Ok(f) => serde_json::from_reader(f)?,
- Err(e) => {
- warn!("could not load item info: {e}");
- warn!("using the default instead");
- ItemInfo {
- common: CommmonInfo {
- poster: None,
- backdrop: None,
- tagline: None,
- description: None,
- title: item.to_str().unwrap().to_string(),
- index: None,
- },
- duration: 0.0,
- tracks: Default::default(),
- }
- }
- };
+ // let mut iteminfo: ItemInfo = match File::open(item.clone()) {
+ // Ok(f) => serde_json::from_reader(f)?,
+ // Err(e) => {
+ // warn!("could not load item info: {e}");
+ // warn!("using the default instead");
+ // ItemInfo {
+ // common: CommmonInfo {
+ // poster: None,
+ // backdrop: None,
+ // tagline: None,
+ // description: None,
+ // title: item.to_str().unwrap().to_string(),
+ // index: None,
+ // },
+ // duration: 0.0,
+ // tracks: Default::default(),
+ // }
+ // }
+ // };
- if let Some(title) = title {
- iteminfo.title = title;
- }
- if let Some(poster) = poster {
- iteminfo.poster = Some(poster);
- }
- if let Some(d) = description {
- iteminfo.description = Some(d);
- }
- if let Some(d) = tagline {
- iteminfo.tagline = Some(d);
- }
- if clear_inputs {
- iteminfo.tracks = Default::default()
- }
+ // if let Some(title) = title {
+ // iteminfo.title = title;
+ // }
+ // if let Some(poster) = poster {
+ // iteminfo.poster = Some(poster);
+ // }
+ // if let Some(d) = description {
+ // iteminfo.description = Some(d);
+ // }
+ // if let Some(d) = tagline {
+ // iteminfo.tagline = Some(d);
+ // }
+ // if clear_inputs {
+ // iteminfo.tracks = Default::default()
+ // }
- for input_path in input {
- let input = File::open(input_path.clone()).unwrap();
- let mut input = EbmlReader::new(input);
- import_read(&input_path, &mut input, &mut iteminfo)?;
- }
+ // // for input_path in input {
+ // // let input = File::open(input_path.clone()).unwrap();
+ // // let mut input = EbmlReader::new(input);
+ // // import_read(&input_path, &mut input, &mut iteminfo)?;
+ // // }
- let k = serde_json::to_string_pretty(&iteminfo)?;
- if args.dry {
- println!("{k}")
- } else {
- let mut f = File::create(item)?;
- f.write_all(k.as_bytes())?;
- }
+ // let k = serde_json::to_string_pretty(&iteminfo)?;
+ // if args.dry {
+ // println!("{k}")
+ // } else {
+ // let mut f = File::create(item)?;
+ // f.write_all(k.as_bytes())?;
+ // }
Ok(())
}
}
@@ -289,9 +257,3 @@ fn make_ident(s: &str) -> String {
}
out
}
-fn import_source(iteminfo: &mut ItemInfo, source_path: &Path) -> anyhow::Result<()> {
- let input = File::open(&source_path).unwrap();
- let mut input = EbmlReader::new(input);
- import_read(&source_path.to_path_buf(), &mut input, iteminfo)?;
- Ok(())
-}