From 850aa7a969768992820d8a9142ed01ccb27adec8 Mon Sep 17 00:00:00 2001 From: bytedream Date: Thu, 13 Jul 2023 13:51:02 +0200 Subject: [PATCH] Use config file to store sessions --- Cargo.lock | 82 ++++++++++++++++++++++++++- crunchy-cli-core/Cargo.toml | 1 + crunchy-cli-core/src/lib.rs | 44 +++++++------- crunchy-cli-core/src/login/command.rs | 33 +++-------- crunchy-cli-core/src/login/mod.rs | 2 +- crunchy-cli-core/src/utils/config.rs | 59 +++++++++++++++++++ crunchy-cli-core/src/utils/context.rs | 2 + crunchy-cli-core/src/utils/mod.rs | 1 + 8 files changed, 177 insertions(+), 47 deletions(-) create mode 100644 crunchy-cli-core/src/utils/config.rs diff --git a/Cargo.lock b/Cargo.lock index cc02df5..7468e73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -397,6 +397,7 @@ dependencies = [ "sys-locale", "tempfile", "tokio", + "toml", ] [[package]] @@ -578,6 +579,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + [[package]] name = "errno" version = "0.3.1" @@ -730,7 +737,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -743,6 +750,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.4.1" @@ -900,10 +913,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "serde", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + [[package]] name = "indicatif" version = "0.17.5" @@ -1459,6 +1482,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1480,7 +1512,7 @@ dependencies = [ "base64", "chrono", "hex", - "indexmap", + "indexmap 1.9.3", "serde", "serde_json", "serde_with_macros", @@ -1732,6 +1764,41 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -2114,6 +2181,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" diff --git a/crunchy-cli-core/Cargo.toml b/crunchy-cli-core/Cargo.toml index 2ba5322..32c2ec4 100644 --- a/crunchy-cli-core/Cargo.toml +++ b/crunchy-cli-core/Cargo.toml @@ -27,6 +27,7 @@ serde_json = "1.0" serde_plain = "1.0" shlex = "1.1" tempfile = "3.6" +toml = { version = "0.7", features = ["display", "parse", "preserve_order"] } tokio = { version = "1.29", features = ["macros", "rt-multi-thread", "time"] } sys-locale = "0.3" diff --git a/crunchy-cli-core/src/lib.rs b/crunchy-cli-core/src/lib.rs index fbfb409..303d38e 100644 --- a/crunchy-cli-core/src/lib.rs +++ b/crunchy-cli-core/src/lib.rs @@ -17,6 +17,7 @@ mod login; mod search; mod utils; +use crate::utils::config::{Auth, Config}; pub use archive::Archive; use dialoguer::console::Term; pub use download::Download; @@ -136,8 +137,20 @@ pub async fn cli_entrypoint() { } Command::Login(login) => { if login.remove { - if let Some(session_file) = login::session_file_path() { - let _ = fs::remove_file(session_file); + match Config::load() { + Ok(config) => { + if let Some(mut c) = config { + c.auth = None; + if let Err(e) = c.write() { + error!("{}", e); + std::process::exit(1) + } + } + } + Err(e) => { + error!("{}", e); + std::process::exit(1) + } } return; } else { @@ -226,11 +239,12 @@ async fn execute_executor(executor: impl Execute, ctx: Context) { } async fn create_ctx(cli: &mut Cli) -> Result { - let crunchy = crunchyroll_session(cli).await?; - Ok(Context { crunchy }) + let mut config = Config::load()?.unwrap_or_default(); + let crunchy = crunchyroll_session(cli, &mut config).await?; + Ok(Context { crunchy, config }) } -async fn crunchyroll_session(cli: &mut Cli) -> Result { +async fn crunchyroll_session(cli: &mut Cli, config: &mut Config) -> Result { let supported_langs = vec![ Locale::ar_ME, Locale::de_DE, @@ -293,20 +307,12 @@ async fn crunchyroll_session(cli: &mut Cli) -> Result { let progress_handler = progress!("Logging in"); if root_login_methods_count + login_login_methods_count == 0 { - if let Some(login_file_path) = login::session_file_path() { - if login_file_path.exists() { - let session = fs::read_to_string(login_file_path)?; - if let Some((token_type, token)) = session.split_once(':') { - match token_type { - "refresh_token" => { - return Ok(builder.login_with_refresh_token(token).await?) - } - "etp_rt" => return Ok(builder.login_with_etp_rt(token).await?), - _ => (), - } - } - bail!("Could not read stored session ('{}')", session) - } + if let Some(auth) = &config.auth { + return match auth { + Auth::RefreshToken { token } => Ok(builder.login_with_refresh_token(token).await?), + Auth::EtpRt { token } => Ok(builder.login_with_etp_rt(token).await?), + Auth::Anonymous => Ok(builder.login_anonymously().await?), + }; } bail!("Please use a login method ('--credentials', '--etp-rt' or '--anonymous')") } else if root_login_methods_count + login_login_methods_count > 1 { diff --git a/crunchy-cli-core/src/login/command.rs b/crunchy-cli-core/src/login/command.rs index ab16e06..076e1b7 100644 --- a/crunchy-cli-core/src/login/command.rs +++ b/crunchy-cli-core/src/login/command.rs @@ -1,11 +1,9 @@ +use crate::utils::config::Auth; use crate::utils::context::Context; use crate::Execute; -use anyhow::bail; use anyhow::Result; use clap::Parser; use crunchyroll_rs::crunchyroll::SessionToken; -use std::fs; -use std::path::PathBuf; #[derive(Debug, clap::Parser)] #[clap(about = "Save your login credentials persistent on disk")] @@ -19,23 +17,14 @@ pub struct Login { #[async_trait::async_trait(?Send)] impl Execute for Login { - async fn execute(self, ctx: Context) -> Result<()> { - if let Some(login_file_path) = session_file_path() { - fs::create_dir_all(login_file_path.parent().unwrap())?; - - match ctx.crunchy.session_token().await { - SessionToken::RefreshToken(refresh_token) => Ok(fs::write( - login_file_path, - format!("refresh_token:{}", refresh_token), - )?), - SessionToken::EtpRt(etp_rt) => { - Ok(fs::write(login_file_path, format!("etp_rt:{}", etp_rt))?) - } - SessionToken::Anonymous => bail!("Anonymous login cannot be saved"), - } - } else { - bail!("Cannot find config path") - } + async fn execute(self, mut ctx: Context) -> Result<()> { + let auth = match ctx.crunchy.session_token().await { + SessionToken::RefreshToken(token) => Auth::RefreshToken { token }, + SessionToken::EtpRt(token) => Auth::EtpRt { token }, + SessionToken::Anonymous => Auth::Anonymous, + }; + ctx.config.auth = Some(auth); + Ok(ctx.config.write()?) } } @@ -56,7 +45,3 @@ pub struct LoginMethod { #[arg(long, default_value_t = false)] pub anonymous: bool, } - -pub fn session_file_path() -> Option { - dirs::config_dir().map(|config_dir| config_dir.join("crunchy-cli").join("session")) -} diff --git a/crunchy-cli-core/src/login/mod.rs b/crunchy-cli-core/src/login/mod.rs index 8c1220a..8c79d61 100644 --- a/crunchy-cli-core/src/login/mod.rs +++ b/crunchy-cli-core/src/login/mod.rs @@ -1,3 +1,3 @@ mod command; -pub use command::{session_file_path, Login, LoginMethod}; +pub use command::{Login, LoginMethod}; diff --git a/crunchy-cli-core/src/utils/config.rs b/crunchy-cli-core/src/utils/config.rs new file mode 100644 index 0000000..3206ca8 --- /dev/null +++ b/crunchy-cli-core/src/utils/config.rs @@ -0,0 +1,59 @@ +use anyhow::{bail, Result}; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +#[serde(tag = "method")] +pub enum Auth { + RefreshToken { token: String }, + EtpRt { token: String }, + Anonymous, +} + +#[derive(Default, Deserialize, Serialize)] +pub struct Config { + pub auth: Option, +} + +impl Config { + pub fn load() -> Result> { + let path = Config::assert_config_file_path(true)?; + + if let Some(p) = path { + if p.exists() { + let content = fs::read_to_string(p)?; + return Ok(Some(toml::from_str(&content)?)); + } + } + Ok(None) + } + + pub fn write(&self) -> Result<()> { + let path = Config::assert_config_file_path(false)?.unwrap(); + Ok(fs::write(path, toml::to_string(self)?)?) + } + + pub fn config_file_path() -> Option { + dirs::config_dir().map(|config_dir| config_dir.join("crunchy-cli.conf")) + } + + fn assert_config_file_path(ignore_non_existing_config_dir: bool) -> Result> { + let Some(path) = Config::config_file_path() else { + if ignore_non_existing_config_dir { + return Ok(None) + } + bail!("Cannot find config directory") + }; + + if path.exists() && path.is_dir() { + bail!( + "Config path ({}) is a directory (must be a normal file)", + path.to_string_lossy() + ) + } + + Ok(Some(path)) + } +} diff --git a/crunchy-cli-core/src/utils/context.rs b/crunchy-cli-core/src/utils/context.rs index f8df024..3e9af73 100644 --- a/crunchy-cli-core/src/utils/context.rs +++ b/crunchy-cli-core/src/utils/context.rs @@ -1,5 +1,7 @@ +use crate::utils::config::Config; use crunchyroll_rs::Crunchyroll; pub struct Context { pub crunchy: Crunchyroll, + pub config: Config, } diff --git a/crunchy-cli-core/src/utils/mod.rs b/crunchy-cli-core/src/utils/mod.rs index d46cc33..510eeec 100644 --- a/crunchy-cli-core/src/utils/mod.rs +++ b/crunchy-cli-core/src/utils/mod.rs @@ -1,4 +1,5 @@ pub mod clap; +pub mod config; pub mod context; pub mod download; pub mod ffmpeg;