diff options
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | tool/Cargo.toml (renamed from import/Cargo.toml) | 2 | ||||
-rw-r--r-- | tool/src/import/infojson.rs (renamed from import/src/infojson.rs) | 0 | ||||
-rw-r--r-- | tool/src/import/mod.rs (renamed from import/src/main.rs) | 158 | ||||
-rw-r--r-- | tool/src/import/tmdb.rs (renamed from import/src/tmdb.rs) | 0 | ||||
-rw-r--r-- | tool/src/main.rs | 150 |
7 files changed, 163 insertions, 151 deletions
@@ -1492,7 +1492,7 @@ dependencies = [ ] [[package]] -name = "jellything-import" +name = "jellytool" version = "0.1.0" dependencies = [ "anyhow", @@ -3,7 +3,7 @@ members = [ "server", "remuxer", "common", - "import", + "tool", "matroska", "ebml_derive", "client", diff --git a/import/Cargo.toml b/tool/Cargo.toml index 9f0912a..c9d46f1 100644 --- a/import/Cargo.toml +++ b/tool/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "jellything-import" +name = "jellytool" version = "0.1.0" edition = "2021" diff --git a/import/src/infojson.rs b/tool/src/import/infojson.rs index dd2151b..dd2151b 100644 --- a/import/src/infojson.rs +++ b/tool/src/import/infojson.rs diff --git a/import/src/main.rs b/tool/src/import/mod.rs index c274e54..e148c98 100644 --- a/import/src/main.rs +++ b/tool/src/import/mod.rs @@ -1,141 +1,25 @@ -/* - 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> -*/ -#![feature(file_create_new)] - pub mod infojson; pub mod tmdb; +use crate::{make_ident, ok_or_warn, Action}; use anyhow::Context; -use base64::Engine; -use clap::{Parser, Subcommand}; use infojson::{parse_upload_date, YVideo}; use jellycommon::{ - config::GlobalConfig, AssetLocation, LocalTrack, MediaInfo, MediaSource, Node, NodeKind, - NodePrivate, NodePublic, Rating, + AssetLocation, LocalTrack, MediaInfo, MediaSource, Node, NodeKind, NodePrivate, NodePublic, + Rating, }; use jellymatroska::read::EbmlReader; use jellyremuxer::import::import_metadata; use log::{info, warn}; -use rand::random; use std::{ collections::BTreeMap, - fmt::Debug, fs::{remove_file, File}, io::{stdin, BufReader, Write}, - path::PathBuf, }; -use tmdb::{tmdb_details, tmdb_image, tmdb_search}; - -#[derive(Parser)] -struct Args { - #[arg(short = 'N', long)] - dry: bool, - #[clap(subcommand)] - action: Action, -} - -#[derive(Subcommand)] -enum Action { - /// Initialize a new jellything instance - Init { - /// Base path of the instance, must either be absolute or relative to the servers pwd - base_path: PathBuf, - #[arg(short, long)] - brand: String, - }, - /// Imports a movie, video or series given media and metadata sources - New { - /// Relative path to the node's parent(!). - path: PathBuf, - /// Search the node by title on TMDB - #[arg(short = 't', long)] - tmdb_search: Option<String>, - /// Search the node by id on TMDB - #[arg(short = 'T', long)] - tmdb_id: Option<String>, - #[arg(long)] - /// Prefix the inferred id with something to avoid collisions - ident_prefix: Option<String>, - /// Copies media into the library - #[arg(long)] - copy: bool, - /// Moves media into the library (potentially destructive operation) - #[arg(long)] - r#move: bool, - /// Marks node as a video - #[arg(long)] - video: bool, - /// Path to the media of the node, required for non-series - #[arg(short, long)] - input: Option<PathBuf>, - /// Marks node as a series - #[arg(short, long)] - series: bool, - }, -} - -fn main() -> anyhow::Result<()> { - env_logger::builder() - .filter_level(log::LevelFilter::Info) - .parse_env("LOG") - .init(); - let args = Args::parse(); +use tmdb::{tmdb_details, tmdb_image}; - match args.action { - Action::Init { - base_path: path, - brand, - } => { - info!("creating new instance..."); - std::fs::create_dir_all(path.join("library"))?; - std::fs::create_dir_all(path.join("cache"))?; - std::fs::create_dir_all(path.join("assets"))?; - File::create_new(path.join("assets/front.htm"))? - .write_fmt(format_args!("<h1>My very own jellything instance</h1>"))?; - serde_yaml::to_writer( - File::create_new(path.join("config.yaml"))?, - &GlobalConfig { - brand: brand.clone(), - slogan: "Creative slogan here".to_string(), - asset_path: path.join("assets"), - cache_path: path.join("cache"), - library_path: path.join("library"), - database_path: path.join("database"), - temp_path: "/tmp".into(), - cookie_key: Some( - base64::engine::general_purpose::STANDARD - .encode([(); 32].map(|_| random())), - ), - session_key: Some( - base64::engine::general_purpose::STANDARD - .encode([(); 32].map(|_| random())), - ), - admin_username: "admin".to_string(), - admin_password: "hackme".to_string(), - login_expire: 10, - ..Default::default() - }, - )?; - serde_json::to_writer( - File::create_new(path.join("library/directory.json"))?, - &Node { - public: NodePublic { - kind: NodeKind::Collection, - title: "My Library".to_string(), - ..Default::default() - }, - private: NodePrivate { - ..Default::default() - }, - }, - )?; - info!("{brand:?} is ready!"); - warn!("please change the admin password."); - Ok(()) - } +pub(crate) fn import(action: Action, dry: bool) -> anyhow::Result<()> { + match action { Action::New { path, tmdb_id, @@ -156,7 +40,7 @@ fn main() -> anyhow::Result<()> { Some(id.parse().unwrap()) } else if let Some(title) = tmdb_search { let tmdb_key = std::env::var("TMDB_API_KEY").context("tmdb api key required")?; - let results = crate::tmdb_search(tmdb_kind, &title, &tmdb_key)?; + let results = tmdb::tmdb_search(tmdb_kind, &title, &tmdb_key)?; info!("results:"); for (i, r) in results.results.iter().enumerate() { info!( @@ -237,7 +121,7 @@ fn main() -> anyhow::Result<()> { let source_path = input.as_ref().map(|_| path.join(format!("source.mkv"))); let (mut poster, mut backdrop) = (None, None); - if !args.dry { + if !dry { std::fs::create_dir_all(&path)?; poster = file_meta @@ -362,7 +246,7 @@ fn main() -> anyhow::Result<()> { }, }; - if args.dry { + if dry { println!("{}", serde_json::to_string_pretty(&node)?); } else { if let Some(source_path) = source_path { @@ -388,28 +272,6 @@ fn main() -> anyhow::Result<()> { Ok(()) } - } -} - -fn make_ident(s: &str) -> String { - let mut out = String::new(); - for s in s.chars() { - match s { - 'a'..='z' | '0'..='9' => out.push(s), - 'A'..='Z' => out.push(s.to_ascii_lowercase()), - '-' | ' ' | '_' | ':' => out.push('-'), - _ => (), - } - } - out -} - -fn ok_or_warn<T, E: Debug>(r: Result<T, E>) -> Option<T> { - match r { - Ok(t) => Some(t), - Err(e) => { - warn!("{e:?}"); - None - } + _ => unreachable!(), } } diff --git a/import/src/tmdb.rs b/tool/src/import/tmdb.rs index 5f21afd..5f21afd 100644 --- a/import/src/tmdb.rs +++ b/tool/src/import/tmdb.rs diff --git a/tool/src/main.rs b/tool/src/main.rs new file mode 100644 index 0000000..1e84bda --- /dev/null +++ b/tool/src/main.rs @@ -0,0 +1,150 @@ +/* + 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> +*/ +#![feature(file_create_new)] + +pub mod import; + +use base64::Engine; +use clap::{Parser, Subcommand}; +use import::import; +use jellycommon::{config::GlobalConfig, Node, NodeKind, NodePrivate, NodePublic}; +use log::{info, warn}; +use rand::random; +use std::{fmt::Debug, fs::File, io::Write, path::PathBuf}; + +#[derive(Parser)] +struct Args { + #[arg(short = 'N', long)] + dry: bool, + #[clap(subcommand)] + action: Action, +} + +#[derive(Subcommand)] +enum Action { + /// Initialize a new jellything instance + Init { + /// Base path of the instance, must either be absolute or relative to the servers pwd + base_path: PathBuf, + #[arg(short, long)] + brand: String, + }, + /// Imports a movie, video or series given media and metadata sources + New { + /// Relative path to the node's parent(!). + path: PathBuf, + /// Search the node by title on TMDB + #[arg(short = 't', long)] + tmdb_search: Option<String>, + /// Search the node by id on TMDB + #[arg(short = 'T', long)] + tmdb_id: Option<String>, + #[arg(long)] + /// Prefix the inferred id with something to avoid collisions + ident_prefix: Option<String>, + /// Copies media into the library + #[arg(long)] + copy: bool, + /// Moves media into the library (potentially destructive operation) + #[arg(long)] + r#move: bool, + /// Marks node as a video + #[arg(long)] + video: bool, + /// Path to the media of the node, required for non-series + #[arg(short, long)] + input: Option<PathBuf>, + /// Marks node as a series + #[arg(short, long)] + series: bool, + }, +} + +fn main() -> anyhow::Result<()> { + env_logger::builder() + .filter_level(log::LevelFilter::Info) + .parse_env("LOG") + .init(); + let args = Args::parse(); + + match args.action { + Action::Init { + base_path: path, + brand, + } => { + info!("creating new instance..."); + std::fs::create_dir_all(path.join("library"))?; + std::fs::create_dir_all(path.join("cache"))?; + std::fs::create_dir_all(path.join("assets"))?; + File::create_new(path.join("assets/front.htm"))? + .write_fmt(format_args!("<h1>My very own jellything instance</h1>"))?; + serde_yaml::to_writer( + File::create_new(path.join("config.yaml"))?, + &GlobalConfig { + brand: brand.clone(), + slogan: "Creative slogan here".to_string(), + asset_path: path.join("assets"), + cache_path: path.join("cache"), + library_path: path.join("library"), + database_path: path.join("database"), + temp_path: "/tmp".into(), + cookie_key: Some( + base64::engine::general_purpose::STANDARD + .encode([(); 32].map(|_| random())), + ), + session_key: Some( + base64::engine::general_purpose::STANDARD + .encode([(); 32].map(|_| random())), + ), + admin_username: "admin".to_string(), + admin_password: "hackme".to_string(), + login_expire: 10, + ..Default::default() + }, + )?; + serde_json::to_writer( + File::create_new(path.join("library/directory.json"))?, + &Node { + public: NodePublic { + kind: NodeKind::Collection, + title: "My Library".to_string(), + ..Default::default() + }, + private: NodePrivate { + ..Default::default() + }, + }, + )?; + info!("{brand:?} is ready!"); + warn!("please change the admin password."); + Ok(()) + } + a @ Action::New { .. } => import(a, args.dry), + } +} + +fn make_ident(s: &str) -> String { + let mut out = String::new(); + for s in s.chars() { + match s { + 'a'..='z' | '0'..='9' => out.push(s), + 'A'..='Z' => out.push(s.to_ascii_lowercase()), + '-' | ' ' | '_' | ':' => out.push('-'), + _ => (), + } + } + out +} + +fn ok_or_warn<T, E: Debug>(r: Result<T, E>) -> Option<T> { + match r { + Ok(t) => Some(t), + Err(e) => { + warn!("{e:?}"); + None + } + } +} |