diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b54fc74..a778970 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -7,31 +7,11 @@ edition = "2021" default = ["rustls-tls-native-roots"] # Reqwest TLS options -native-tls = [ - "reqwest/native-tls", - "rustypipe/native-tls", - "rustypipe-downloader/native-tls", -] -native-tls-alpn = [ - "reqwest/native-tls-alpn", - "rustypipe/native-tls-alpn", - "rustypipe-downloader/native-tls-alpn", -] -native-tls-vendored = [ - "reqwest/native-tls-vendored", - "rustypipe/native-tls-vendored", - "rustypipe-downloader/native-tls-vendored", -] -rustls-tls-webpki-roots = [ - "reqwest/rustls-tls-webpki-roots", - "rustypipe/rustls-tls-webpki-roots", - "rustypipe-downloader/rustls-tls-webpki-roots", -] -rustls-tls-native-roots = [ - "reqwest/rustls-tls-native-roots", - "rustypipe/rustls-tls-native-roots", - "rustypipe-downloader/rustls-tls-native-roots", -] +native-tls = ["reqwest/native-tls", "rustypipe/native-tls", "rustypipe-downloader/native-tls"] +native-tls-alpn = ["reqwest/native-tls-alpn", "rustypipe/native-tls-alpn", "rustypipe-downloader/native-tls-alpn"] +native-tls-vendored = ["reqwest/native-tls-vendored", "rustypipe/native-tls-vendored", "rustypipe-downloader/native-tls-vendored"] +rustls-tls-webpki-roots = ["reqwest/rustls-tls-webpki-roots", "rustypipe/rustls-tls-webpki-roots", "rustypipe-downloader/rustls-tls-webpki-roots"] +rustls-tls-native-roots = ["reqwest/rustls-tls-native-roots", "rustypipe/rustls-tls-native-roots", "rustypipe-downloader/rustls-tls-native-roots"] [dependencies] rustypipe = { path = "../", default-features = false } @@ -46,3 +26,4 @@ env_logger = "0.10.0" serde = "1.0" serde_json = "1.0.82" serde_yaml = "0.9.19" +dirs = "5.0.0" diff --git a/cli/src/main.rs b/cli/src/main.rs index 5595e71..41b60d8 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -384,7 +384,11 @@ async fn main() { let cli = Cli::parse(); - let rp = RustyPipe::new(); + let mut storage_dir = dirs::data_dir().expect("no data dir"); + storage_dir.push("rustypipe"); + _ = std::fs::create_dir(&storage_dir); + + let rp = RustyPipe::builder().storage_dir(storage_dir).build(); match cli.command { Commands::Download { diff --git a/src/cache.rs b/src/cache.rs index 9060759..fb69a5a 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -7,6 +7,8 @@ use std::{ use log::error; +pub(crate) const DEFAULT_CACHE_FILE: &str = "rustypipe_cache.json"; + /// RustyPipe has to cache some information fetched from YouTube: specifically /// the client versions and the JavaScript code used to deobfuscate the stream URLs. /// @@ -42,7 +44,7 @@ impl FileStorage { impl Default for FileStorage { fn default() -> Self { Self { - path: Path::new("rustypipe_cache.json").into(), + path: Path::new(DEFAULT_CACHE_FILE).into(), } } } diff --git a/src/client/mod.rs b/src/client/mod.rs index 743f6d4..251157e 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -22,6 +22,7 @@ mod video_details; #[cfg_attr(docsrs, doc(cfg(feature = "rss")))] mod channel_rss; +use std::path::PathBuf; use std::sync::Arc; use std::{borrow::Cow, fmt::Debug, time::Duration}; @@ -34,11 +35,11 @@ use time::OffsetDateTime; use tokio::sync::RwLock; use crate::{ - cache::{CacheStorage, FileStorage}, + cache::{CacheStorage, FileStorage, DEFAULT_CACHE_FILE}, deobfuscate::DeobfData, error::{Error, ExtractionError}, param::{Country, Language}, - report::{FileReporter, Level, Report, Reporter}, + report::{FileReporter, Level, Report, Reporter, DEFAULT_REPORT_DIR}, serializer::MapResult, util, }; @@ -247,6 +248,7 @@ pub struct RustyPipeBuilder { timeout: DefaultOpt, user_agent: Option, default_opts: RustyPipeOpts, + storage_dir: Option, } enum DefaultOpt { @@ -361,6 +363,7 @@ impl RustyPipeBuilder { timeout: DefaultOpt::Default, n_http_retries: 2, user_agent: None, + storage_dir: None, } } @@ -378,7 +381,15 @@ impl RustyPipeBuilder { let http = client_builder.build().unwrap(); - let cdata = if let DefaultOpt::Some(storage) = &self.storage { + let storage_dir = self.storage_dir.unwrap_or_default(); + + let storage = self.storage.or_default(|| { + let mut cache_file = storage_dir.clone(); + cache_file.push(DEFAULT_CACHE_FILE); + Box::new(FileStorage::new(cache_file)) + }); + + let cdata = if let Some(storage) = &storage { if let Some(data) = storage.read() { match serde_json::from_str::(&data) { Ok(data) => data, @@ -397,8 +408,12 @@ impl RustyPipeBuilder { RustyPipe { inner: Arc::new(RustyPipeRef { http, - storage: self.storage.or_default(|| Box::::default()), - reporter: self.reporter.or_default(|| Box::::default()), + storage, + reporter: self.reporter.or_default(|| { + let mut report_dir = storage_dir; + report_dir.push(DEFAULT_REPORT_DIR); + Box::new(FileReporter::new(report_dir)) + }), n_http_retries: self.n_http_retries, consent_cookie: format!( "{}={}{}", @@ -416,6 +431,16 @@ impl RustyPipeBuilder { } } + /// Set the default directory to store the cachefile and reports. + /// + /// This option has no effect if the storage backend or reporter are manually set or disabled. + /// + /// **Default value**: current working directory + pub fn storage_dir>(mut self, path: P) -> Self { + self.storage_dir = Some(path.into()); + self + } + /// Add a [`CacheStorage`] backend for persisting cached information /// (YouTube client versions, deobfuscation code) between /// program executions. diff --git a/src/report.rs b/src/report.rs index e2263af..5fd99f4 100644 --- a/src/report.rs +++ b/src/report.rs @@ -29,6 +29,8 @@ use time::{macros::format_description, OffsetDateTime}; use crate::{deobfuscate::DeobfData, util}; +pub(crate) const DEFAULT_REPORT_DIR: &str = "rustypipe_reports"; + const FILENAME_FORMAT: &[time::format_description::FormatItem] = format_description!("[year]-[month]-[day]_[hour]-[minute]-[second]"); @@ -137,7 +139,7 @@ impl FileReporter { impl Default for FileReporter { fn default() -> Self { Self { - path: Path::new("rustypipe_reports").to_path_buf(), + path: Path::new(DEFAULT_REPORT_DIR).to_path_buf(), } } }