aboutsummaryrefslogtreecommitdiff
path: root/tool/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tool/src/main.rs')
-rw-r--r--tool/src/main.rs164
1 files changed, 164 insertions, 0 deletions
diff --git a/tool/src/main.rs b/tool/src/main.rs
new file mode 100644
index 0000000..3a670f5
--- /dev/null
+++ b/tool/src/main.rs
@@ -0,0 +1,164 @@
+/*
+ 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;
+pub mod migrate;
+
+use base64::Engine;
+use clap::{Parser, Subcommand, ValueEnum};
+use import::import;
+use jellycommon::{config::GlobalConfig, Node, NodeKind, NodePrivate, NodePublic};
+use log::{info, warn};
+use migrate::migrate;
+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,
+ },
+ Migrate {
+ database: PathBuf,
+ mode: MigrateMode,
+ save_location: PathBuf,
+ },
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, ValueEnum)]
+enum MigrateMode {
+ Import,
+ Export,
+}
+
+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),
+ a @ Action::Migrate { .. } => migrate(a),
+ }
+}
+
+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
+ }
+ }
+}