diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-08-23 13:11:09 +0200 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-08-23 13:14:13 +0200 |
commit | 94c656ad40a7aae570e5a5fb61ad32632acc6d46 (patch) | |
tree | 269614af20caf10d76643c302e0115bd36fd2378 | |
parent | refactor(yt_dlp): Also move the `crates` subdirectory (diff) | |
download | yt-94c656ad40a7aae570e5a5fb61ad32632acc6d46.tar.gz yt-94c656ad40a7aae570e5a5fb61ad32632acc6d46.zip |
feat(treewide): Use a configuration file
This allows use to avoid duplication of default values in the codebase and obviously also facilitates changing these without having to re-compile.
-rw-r--r-- | Cargo.lock | 53 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/app.rs | 14 | ||||
-rw-r--r-- | src/cli.rs | 23 | ||||
-rw-r--r-- | src/config/default.rs | 100 | ||||
-rw-r--r-- | src/config/file_system.rs | 123 | ||||
-rw-r--r-- | src/config/mod.rs | 52 | ||||
-rw-r--r-- | src/constants.rs | 74 | ||||
-rw-r--r-- | src/download/download_options.rs | 9 | ||||
-rw-r--r-- | src/download/mod.rs | 15 | ||||
-rw-r--r-- | src/main.rs | 5 | ||||
-rw-r--r-- | src/select/mod.rs | 9 | ||||
-rw-r--r-- | src/select/selection_file/display.rs | 2 | ||||
-rw-r--r-- | src/storage/video_database/mod.rs | 11 | ||||
-rw-r--r-- | src/storage/video_database/setters.rs | 9 | ||||
-rw-r--r-- | src/watch/events.rs | 3 | ||||
-rw-r--r-- | src/watch/mod.rs | 5 |
17 files changed, 387 insertions, 121 deletions
diff --git a/Cargo.lock b/Cargo.lock index 587c33a..ed5f824 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1432,6 +1432,15 @@ dependencies = [ ] [[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + +[[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1896,6 +1905,40 @@ dependencies = [ ] [[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2286,6 +2329,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] name = "xdg" version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2311,6 +2363,7 @@ dependencies = [ "stderrlog", "tempfile", "tokio", + "toml", "trinitry", "url", "xdg", diff --git a/Cargo.toml b/Cargo.toml index c722845..8b77b16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ yt_dlp = { path = "./crates/yt_dlp/" } libmpv2 = { path = "./crates/libmpv2" } bytes = { path = "./crates/bytes" } trinitry = { version = "0.2.2" } +toml = "0.8.19" [[bin]] name = "yt" diff --git a/src/app.rs b/src/app.rs index f956251..b7d136e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -8,19 +8,20 @@ // You should have received a copy of the License along with this program. // If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. -use std::path::PathBuf; - use anyhow::{Context, Result}; use sqlx::{query, sqlite::SqliteConnectOptions, SqlitePool}; +use crate::config::Config; + pub struct App { pub database: SqlitePool, + pub config: Config, } impl App { - pub async fn new(db_name: PathBuf) -> Result<Self> { + pub async fn new(config: Config) -> Result<Self> { let options = SqliteConnectOptions::new() - .filename(db_name) + .filename(&config.paths.database_path) .optimize_on_close(true, None) .create_if_missing(true); @@ -32,6 +33,9 @@ impl App { .execute(&pool) .await?; - Ok(App { database: pool }) + Ok(App { + database: pool, + config, + }) } } diff --git a/src/cli.rs b/src/cli.rs index c567828..05c78de 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -10,14 +10,17 @@ use std::path::PathBuf; -use anyhow::{bail, Error}; +use anyhow::Context; use chrono::NaiveDate; use clap::{ArgAction, Args, Parser, Subcommand}; use url::Url; use bytes::Bytes; use crate::{ - constants, select::selection_file::duration::Duration, + config::{ + default::{download, select, update}, + }, + select::selection_file::duration::Duration, storage::video_database::extractor_hash::LazyExtractorHash, }; @@ -33,10 +36,15 @@ pub struct CliArgs { #[arg(long="verbose", short = 'v', action = ArgAction::Count)] pub verbosity: u8, - /// Set the path to the videos.db. Otherwise use the default location + /// Set the path to the videos.db. This overrides the default and the config file. #[arg(long, short)] pub db_path: Option<PathBuf>, + /// Set the path to the config.toml. + /// This overrides the default. + #[arg(long, short)] + pub config_path: Option<PathBuf>, + /// Silence all output #[arg(long, short = 'q')] pub quiet: bool, @@ -52,7 +60,7 @@ pub enum Command { /// The maximum size the download dir should have. Beware that the value must be given in /// bytes. - #[arg(short, long, default_value = "3 GiB", value_parser = byte_parser)] + #[arg(short, long, default_value = download::max_cache_size(), value_parser = byte_parser)] max_cache_size: u64, }, @@ -88,7 +96,7 @@ pub enum Command { /// Update the video database Update { - #[arg(short, long, default_value = "20")] + #[arg(short, long, default_value_t = update::max_backlog() as u32)] /// The number of videos to updating max_backlog: u32, @@ -192,12 +200,11 @@ pub enum SelectCommand { shared: SharedSelectionCommandArgs, /// The subtitles to download (e.g. 'en,de,sv') - #[arg(short = 'l', long, default_value = constants::DEFAULT_SUBTITLE_LANGS)] + #[arg(short = 'l', long, default_value = select::subtitle_langs())] subtitle_langs: String, /// The speed to set mpv to - // NOTE: KEEP THIS IN SYNC WITH THE `DEFAULT_MPV_PLAYBACK_SPEED` in `constants.rs` <2024-08-20> - #[arg(short, long, default_value = "2.7")] + #[arg(short, long, default_value_t = select::playback_speed())] speed: f64, }, diff --git a/src/config/default.rs b/src/config/default.rs new file mode 100644 index 0000000..131c289 --- /dev/null +++ b/src/config/default.rs @@ -0,0 +1,100 @@ +// yt - A fully featured command line YouTube client +// +// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Yt. +// +// You should have received a copy of the License along with this program. +// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. + +use std::path::PathBuf; + +use anyhow::{Context, Result}; + +fn get_runtime_path(name: &'static str) -> Result<PathBuf> { + let xdg_dirs = xdg::BaseDirectories::with_prefix(PREFIX)?; + xdg_dirs + .place_runtime_file(name) + .with_context(|| format!("Failed to place runtime file: '{}'", name)) +} +fn get_data_path(name: &'static str) -> Result<PathBuf> { + let xdg_dirs = xdg::BaseDirectories::with_prefix(PREFIX)?; + xdg_dirs + .place_data_file(name) + .with_context(|| format!("Failed to place data file: '{}'", name)) +} +fn get_config_path(name: &'static str) -> Result<PathBuf> { + let xdg_dirs = xdg::BaseDirectories::with_prefix(PREFIX)?; + xdg_dirs + .place_config_file(name) + .with_context(|| format!("Failed to place config file: '{}'", name)) +} + +pub(super) fn create_path(path: PathBuf) -> Result<PathBuf> { + if !path.exists() { + std::fs::create_dir_all(&path) + .with_context(|| format!("Failed to create the '{}' directory", path.display()))? + } + + Ok(path) +} + +pub const PREFIX: &str = "yt"; + +pub mod select { + pub fn playback_speed() -> f64 { + 2.7 + } + pub fn subtitle_langs() -> &'static str { + "" + } +} + +pub mod watch { + pub fn local_comments_length() -> i64 { + 1000 + } +} + +pub mod update { + pub fn max_backlog() -> i64 { + 20 + } +} + +pub mod paths { + use std::{env::temp_dir, path::PathBuf}; + + use anyhow::Result; + + use super::{create_path, get_config_path, get_data_path, get_runtime_path, PREFIX}; + + // We download to the temp dir to avoid taxing the disk + pub fn download_dir() -> Result<PathBuf> { + let temp_dir = temp_dir(); + + create_path(temp_dir.join(PREFIX)) + } + pub fn mpv_config_path() -> Result<PathBuf> { + get_config_path("mpv.conf") + } + pub fn mpv_input_path() -> Result<PathBuf> { + get_config_path("mpv.input.conf") + } + pub fn database_path() -> Result<PathBuf> { + get_data_path("videos.sqlite") + } + pub fn config_path() -> Result<PathBuf> { + get_config_path("config.toml") + } + pub fn last_selection_path() -> Result<PathBuf> { + get_runtime_path("selected.yts") + } +} + +pub mod download { + pub fn max_cache_size() -> &'static str { + "3 GiB" + } +} diff --git a/src/config/file_system.rs b/src/config/file_system.rs new file mode 100644 index 0000000..8528130 --- /dev/null +++ b/src/config/file_system.rs @@ -0,0 +1,123 @@ +// yt - A fully featured command line YouTube client +// +// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Yt. +// +// You should have received a copy of the License along with this program. +// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. + +use crate::config::{DownloadConfig, PathsConfig, SelectConfig, WatchConfig}; + +use super::{ + default::{create_path, download, paths, select, update, watch}, + Config, UpdateConfig, +}; + +use std::{fs::read_to_string, path::PathBuf}; + +use anyhow::{Context, Result}; +use toml::Table; +use bytes::Bytes; + +macro_rules! get { + ($default:path, $config:expr, $get_fn:ident, $key_one:expr, $($keys:expr),*) => { + try_get!{@default $default, $config, $get_fn, $key_one, $($keys),*} + .with_context(|| format!("Failed to parse '{}' as a '{}'", stringify!($key_one), stringify!($get_fn)))? + }; + (@path_if_none $config:expr, $option_default:expr, $default:path, $key_one:expr, $($keys:expr),*) => { + { + let maybe_download_dir = + try_get! {@option $config, as_str, $key_one, $($keys),*}; + + let down_dir = if let Some(dir) = maybe_download_dir { + PathBuf::from(dir) + } else { + if let Some(path) = $option_default { + path + } else { + $default() + .with_context(|| format!("Failed to get default path for: '{}.{}'", stringify!($key_one), stringify!($($keys),*)))? + } + }; + create_path(down_dir)? + } + }; + (@path $config:expr, $default:path, $key_one:expr, $($keys:expr),*) => { + get! {@path_if_none $config, None, $default, $key_one, $($keys),*} + }; +} +macro_rules! try_get { + (@option $config:expr, $get_fn:ident, $key_one:expr, $($keys:expr),*) => { + $config.get($key_one).map(|val| { + try_get! {@option val, $get_fn, $($keys),*} + }).flatten().flatten() + }; + (@option $config:expr, $get_fn:ident, $key_one:expr) => { + $config.get($key_one).map(|val| val.$get_fn()) + }; + + (@default $default:path, $config:expr, $get_fn:ident, $key_one:expr, $($keys:expr),*) => { + if let Some(a) = $config.get($key_one) { + try_get! {@default $default, a, $get_fn, $($keys),*} + } else { + Some($default()) + } + }; + (@default $default:path, $config:expr, $get_fn:ident, $key_one:expr) => { + if let Some(a) = $config.get($key_one) { + a.$get_fn() + } else { + Some($default()) + } + }; +} + +impl Config { + pub fn from_config_file( + db_path: Option<PathBuf>, + config_path: Option<PathBuf>, + ) -> Result<Self> { + let config_file_path = config_path + .map(|val| Ok(val)) + .unwrap_or_else(|| -> Result<_> { paths::config_path() })?; + + let config: Table = read_to_string(config_file_path)? + .parse() + .context("Failed to parse the config file as toml")?; + + Ok(Self { + select: SelectConfig { + playback_speed: get! {select::playback_speed, config, as_float, "select", "playback_speed"}, + subtitle_langs: + get! {select::subtitle_langs, config, as_str, "select", "subtitle_langs"} + .to_owned(), + }, + watch: WatchConfig { + local_comments_length: get! {watch::local_comments_length, config, as_integer, "watch", "local_comments_length"} + as usize, + }, + update: UpdateConfig { + max_backlog: get! {update::max_backlog, config, as_integer, "update", "max_backlog"} + as u32, + }, + paths: PathsConfig { + download_dir: get! {@path config, paths::download_dir, "paths", "download_dir"}, + mpv_config_path: get! {@path config, paths::mpv_config_path, "paths", "mpv_config_path"}, + mpv_input_path: get! {@path config, paths::mpv_input_path, "paths", "mpv_input_path"}, + database_path: get! {@path_if_none config, db_path, paths::database_path, "paths", "database_path"}, + last_selection_path: get! {@path config, paths::last_selection_path, "paths", "last_selection_path"}, + }, + download: DownloadConfig { + max_cache_size: { + let bytes_str = get! {download::max_cache_size, config, as_str, "download", "max_cache_path"}; + let number: Bytes = bytes_str + .parse() + .context("Failed to parse max_cache_size")?; + number.as_u64() + }, + }, + }) + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..8ee7cc7 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,52 @@ +// yt - A fully featured command line YouTube client +// +// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Yt. +// +// You should have received a copy of the License along with this program. +// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. + +use std::path::PathBuf; + +pub mod default; +pub mod file_system; + +pub struct Config { + pub select: SelectConfig, + pub watch: WatchConfig, + pub paths: PathsConfig, + pub download: DownloadConfig, + pub update: UpdateConfig, +} +pub struct UpdateConfig { + pub max_backlog: u32, +} +pub struct DownloadConfig { + pub max_cache_size: u64, +} +pub struct SelectConfig { + pub playback_speed: f64, + pub subtitle_langs: String, +} +pub struct WatchConfig { + pub local_comments_length: usize, +} +pub struct PathsConfig { + pub download_dir: PathBuf, + pub mpv_config_path: PathBuf, + pub mpv_input_path: PathBuf, + pub database_path: PathBuf, + pub last_selection_path: PathBuf, +} + +// pub fn status_path() -> anyhow::Result<PathBuf> { +// const STATUS_PATH: &str = "running.info.json"; +// get_runtime_path(STATUS_PATH) +// } + +// pub fn subscriptions() -> anyhow::Result<PathBuf> { +// const SUBSCRIPTIONS: &str = "subscriptions.json"; +// get_data_path(SUBSCRIPTIONS) +// } diff --git a/src/constants.rs b/src/constants.rs index cb8388d..54cae89 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -8,78 +8,4 @@ // You should have received a copy of the License along with this program. // If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. -use std::{env::temp_dir, fs, path::PathBuf}; - -use anyhow::{Context, Result}; - pub const HELP_STR: &str = include_str!("./select/selection_file/help.str"); -pub const LOCAL_COMMENTS_LENGTH: usize = 1000; - -// NOTE: KEEP THIS IN SYNC WITH THE `mpv_playback_speed` in `cli.rs` <2024-08-20> -pub const DEFAULT_MPV_PLAYBACK_SPEED: f64 = 2.7; -pub const DEFAULT_SUBTITLE_LANGS: &str = ""; - -pub const CONCURRENT_DOWNLOADS: u32 = 5; -// We download to the temp dir to avoid taxing the disk -pub fn download_dir(create: bool) -> Result<PathBuf> { - const DOWNLOAD_DIR: &str = "/tmp/yt"; - let dir = PathBuf::from(DOWNLOAD_DIR); - - if !dir.exists() && create { - fs::create_dir_all(&dir).context("Failed to create the download directory")? - } - - Ok(dir) -} - -const PREFIX: &str = "yt"; -fn get_runtime_path(name: &'static str) -> anyhow::Result<PathBuf> { - let xdg_dirs = xdg::BaseDirectories::with_prefix(PREFIX)?; - xdg_dirs - .place_runtime_file(name) - .with_context(|| format!("Failed to place runtime file: '{}'", name)) -} -fn get_data_path(name: &'static str) -> anyhow::Result<PathBuf> { - let xdg_dirs = xdg::BaseDirectories::with_prefix(PREFIX)?; - xdg_dirs - .place_data_file(name) - .with_context(|| format!("Failed to place data file: '{}'", name)) -} -fn get_config_path(name: &'static str) -> anyhow::Result<PathBuf> { - let xdg_dirs = xdg::BaseDirectories::with_prefix(PREFIX)?; - xdg_dirs - .place_config_file(name) - .with_context(|| format!("Failed to place config file: '{}'", name)) -} - -pub fn mpv_config_path() -> anyhow::Result<PathBuf> { - const MPV_CONFIG_PATH: &str = "mpv.conf"; - get_config_path(MPV_CONFIG_PATH) -} -pub fn mpv_input_path() -> anyhow::Result<PathBuf> { - const MPV_INPUT_CONFIG_PATH: &str = "mpv.input.conf"; - get_config_path(MPV_INPUT_CONFIG_PATH) -} - -pub fn status_path() -> anyhow::Result<PathBuf> { - const STATUS_PATH: &str = "running.info.json"; - get_runtime_path(STATUS_PATH) -} -pub fn last_select() -> anyhow::Result<PathBuf> { - const LAST_SELECT: &str = "selected.yts"; - get_runtime_path(LAST_SELECT) -} - -pub fn database() -> anyhow::Result<PathBuf> { - const DATABASE: &str = "videos.sqlite"; - get_data_path(DATABASE) -} -pub fn subscriptions() -> anyhow::Result<PathBuf> { - const SUBSCRIPTIONS: &str = "subscriptions.json"; - get_data_path(SUBSCRIPTIONS) -} - -pub fn cache_path() -> PathBuf { - let temp_dir = temp_dir(); - temp_dir.join("ytc") -} diff --git a/src/download/download_options.rs b/src/download/download_options.rs index 04c1600..e93170a 100644 --- a/src/download/download_options.rs +++ b/src/download/download_options.rs @@ -10,7 +10,7 @@ use serde_json::{json, Value}; -use crate::{constants, storage::video_database::YtDlpOptions}; +use crate::{app::App, storage::video_database::YtDlpOptions}; // { // "ratelimit": conf.ratelimit if conf.ratelimit > 0 else None, @@ -22,7 +22,10 @@ use crate::{constants, storage::video_database::YtDlpOptions}; // "logger": _ytdl_logger // } -pub fn download_opts(additional_opts: YtDlpOptions) -> serde_json::Map<String, serde_json::Value> { +pub fn download_opts( + app: &App, + additional_opts: YtDlpOptions, +) -> serde_json::Map<String, serde_json::Value> { match json!({ "extract_flat": false, "extractor_args": { @@ -50,7 +53,7 @@ pub fn download_opts(additional_opts: YtDlpOptions) -> serde_json::Map<String, s "writeautomaticsub": true, "outtmpl": { - "default": constants::download_dir(false).expect("We're not creating this dir, thus this function can't error").join("%(channel)s/%(title)s.%(ext)s"), + "default": app.config.paths.download_dir.join("%(channel)s/%(title)s.%(ext)s"), "chapter": "%(title)s - %(section_number)03d %(section_title)s [%(id)s].%(ext)s" }, "compat_opts": {}, diff --git a/src/download/mod.rs b/src/download/mod.rs index c3d79b7..707f281 100644 --- a/src/download/mod.rs +++ b/src/download/mod.rs @@ -12,7 +12,6 @@ use std::{collections::HashMap, sync::Arc, time::Duration}; use crate::{ app::App, - constants::download_dir, download::download_options::download_opts, storage::video_database::{ downloader::{get_next_uncached_video, set_video_cache_path}, @@ -72,8 +71,8 @@ impl Downloader { /// This will run, until the database doesn't contain any watchable videos pub async fn consume(&mut self, app: Arc<App>, max_cache_size: u64) -> Result<()> { while let Some(next_video) = get_next_uncached_video(&app).await? { - if Self::get_current_cache_allocation().await? - + self.get_approx_video_size(&next_video).await? + if Self::get_current_cache_allocation(&app).await? + + self.get_approx_video_size(&app, &next_video).await? >= max_cache_size { warn!( @@ -134,7 +133,7 @@ impl Downloader { Ok(()) } - async fn get_current_cache_allocation() -> Result<u64> { + async fn get_current_cache_allocation(app: &App) -> Result<u64> { fn dir_size(mut dir: fs::ReadDir) -> BoxFuture<'static, Result<u64>> { async move { let mut acc = 0; @@ -155,14 +154,14 @@ impl Downloader { .boxed() } - let val = dir_size(fs::read_dir(download_dir(true)?).await?).await; + let val = dir_size(fs::read_dir(&app.config.paths.download_dir).await?).await; if let Ok(val) = val.as_ref() { info!("Cache dir has a size of '{}'", val); } val } - async fn get_approx_video_size(&mut self, video: &Video) -> Result<u64> { + async fn get_approx_video_size(&mut self, app: &App, video: &Video) -> Result<u64> { if let Some(value) = self.video_size_cache.get(&video.extractor_hash) { Ok(*value) } else { @@ -170,7 +169,7 @@ impl Downloader { let add_opts = YtDlpOptions { subtitle_langs: "".to_owned(), }; - let opts = &download_opts(add_opts); + let opts = &download_opts(&app, add_opts); let result = yt_dlp::extract_info(&opts, &video.url, false, true) .await @@ -201,7 +200,7 @@ impl Downloader { let addional_opts = get_video_yt_dlp_opts(&app, &video.extractor_hash).await?; - let result = yt_dlp::download(&[video.url.clone()], &download_opts(addional_opts)) + let result = yt_dlp::download(&[video.url.clone()], &download_opts(&app, addional_opts)) .await .with_context(|| format!("Failed to download video: '{}'", video.title))?; diff --git a/src/main.rs b/src/main.rs index 7852aa0..a6766f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ use app::App; use cache::invalidate; use clap::Parser; use cli::{CacheCommand, CheckCommand, SelectCommand, SubscriptionCommand}; +use config::Config; use log::info; use select::cmds::handle_select_cmd; use tokio::{ @@ -31,6 +32,7 @@ pub mod cli; pub mod cache; pub mod comments; +pub mod config; pub mod constants; pub mod download; pub mod select; @@ -54,7 +56,8 @@ async fn main() -> Result<()> { .init() .expect("Let's just hope that this does not panic"); - let app = App::new(args.db_path.unwrap_or(constants::database()?)).await?; + let config = Config::from_config_file(args.db_path, args.config_path)?; + let app = App::new(config).await?; match args.command.unwrap_or(Command::default()) { Command::Download { diff --git a/src/select/mod.rs b/src/select/mod.rs index 6774ce6..2288e1a 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -18,7 +18,7 @@ use std::{ use crate::{ app::App, cli::CliArgs, - constants::{last_select, HELP_STR}, + constants::HELP_STR, storage::video_database::{getters::get_videos, VideoStatus}, }; @@ -111,11 +111,8 @@ pub async fn select(app: &App, done: bool) -> Result<()> { } let read_file = temp_file.reopen()?; - fs::copy( - temp_file.path(), - last_select().context("Failed to get the persistent selection file path")?, - ) - .context("Failed to persist selection file")?; + fs::copy(temp_file.path(), &app.config.paths.last_selection_path) + .context("Failed to persist selection file")?; let reader = BufReader::new(&read_file); diff --git a/src/select/selection_file/display.rs b/src/select/selection_file/display.rs index 12d128c..0a0ce96 100644 --- a/src/select/selection_file/display.rs +++ b/src/select/selection_file/display.rs @@ -32,7 +32,7 @@ impl Video { let opts = get_video_opts(app, &self.extractor_hash) .await? - .to_cli_flags(); + .to_cli_flags(&app); let opts_white = if !opts.is_empty() { " " } else { "" }; let publish_date = if let Some(date) = self.publish_date { diff --git a/src/storage/video_database/mod.rs b/src/storage/video_database/mod.rs index 28263ca..da08f8f 100644 --- a/src/storage/video_database/mod.rs +++ b/src/storage/video_database/mod.rs @@ -12,10 +12,7 @@ use std::{fmt::Write, path::PathBuf}; use url::Url; -use crate::{ - constants::{DEFAULT_MPV_PLAYBACK_SPEED, DEFAULT_SUBTITLE_LANGS}, - storage::video_database::extractor_hash::ExtractorHash, -}; +use crate::{app::App, storage::video_database::extractor_hash::ExtractorHash}; pub mod downloader; pub mod extractor_hash; @@ -55,13 +52,13 @@ impl VideoOptions { /// This will write out the options that are different from the defaults. /// Beware, that this does not set the priority. - pub fn to_cli_flags(self) -> String { + pub fn to_cli_flags(self, app: &App) -> String { let mut f = String::new(); - if self.mpv.playback_speed != DEFAULT_MPV_PLAYBACK_SPEED { + if self.mpv.playback_speed != app.config.select.playback_speed { write!(f, " --speed '{}'", self.mpv.playback_speed).expect("Works"); } - if self.yt_dlp.subtitle_langs != DEFAULT_SUBTITLE_LANGS { + if self.yt_dlp.subtitle_langs != app.config.select.subtitle_langs { write!(f, " --subtitle-langs '{}'", self.yt_dlp.subtitle_langs).expect("Works"); } diff --git a/src/storage/video_database/setters.rs b/src/storage/video_database/setters.rs index 42875ce..e2b38e6 100644 --- a/src/storage/video_database/setters.rs +++ b/src/storage/video_database/setters.rs @@ -16,7 +16,10 @@ use log::debug; use sqlx::query; use tokio::fs; -use crate::{app::App, constants, storage::video_database::extractor_hash::ExtractorHash}; +use crate::{ + app::App, + storage::video_database::extractor_hash::ExtractorHash, +}; use super::{Video, VideoOptions, VideoStatus}; @@ -213,8 +216,8 @@ pub async fn add_video(app: &App, video: Video) -> Result<()> { let url = video.url.to_string(); let extractor_hash = video.extractor_hash.hash().to_string(); - let default_subtitle_langs = constants::DEFAULT_SUBTITLE_LANGS; - let default_mpv_playback_speed = constants::DEFAULT_MPV_PLAYBACK_SPEED; + let default_subtitle_langs = &app.config.select.subtitle_langs; + let default_mpv_playback_speed = app.config.select.playback_speed; query!( r#" diff --git a/src/watch/events.rs b/src/watch/events.rs index adb35e5..df414ff 100644 --- a/src/watch/events.rs +++ b/src/watch/events.rs @@ -18,7 +18,6 @@ use tokio::process::Command; use crate::{ app::App, comments::get_comments, - constants::LOCAL_COMMENTS_LENGTH, storage::video_database::{ extractor_hash::ExtractorHash, getters::{get_video_by_hash, get_video_mpv_opts, get_videos}, @@ -213,7 +212,7 @@ impl MpvEventHandler { .replace("\"", "") .replace("'", "") .chars() - .take(LOCAL_COMMENTS_LENGTH) + .take(app.config.watch.local_comments_length) .collect(); mpv.execute("show-text", &[&format!("'{}'", comments), "6000"])?; diff --git a/src/watch/mod.rs b/src/watch/mod.rs index 374c1d7..815e208 100644 --- a/src/watch/mod.rs +++ b/src/watch/mod.rs @@ -16,7 +16,6 @@ use log::{debug, info, warn}; use crate::{ app::App, cache::maintain, - constants::{mpv_config_path, mpv_input_path}, storage::video_database::{extractor_hash::ExtractorHash, getters::get_videos, VideoStatus}, }; @@ -41,7 +40,7 @@ pub async fn watch(app: &App) -> Result<()> { Ok(()) })?; - let config_path = mpv_config_path()?; + let config_path = &app.config.paths.mpv_config_path; if config_path.try_exists()? { info!("Found mpv.conf at '{}'!", config_path.display()); mpv.execute( @@ -55,7 +54,7 @@ pub async fn watch(app: &App) -> Result<()> { ); } - let input_path = mpv_input_path()?; + let input_path = &app.config.paths.mpv_input_path; if input_path.try_exists()? { info!("Found mpv.input.conf at '{}'!", input_path.display()); mpv.execute( |