mirror of
https://github.com/crunchy-labs/crunchy-cli.git
synced 2026-01-21 12:12:00 -06:00
Use config file to store sessions
This commit is contained in:
parent
9ad27102fc
commit
850aa7a969
8 changed files with 177 additions and 47 deletions
82
Cargo.lock
generated
82
Cargo.lock
generated
|
|
@ -397,6 +397,7 @@ dependencies = [
|
||||||
"sys-locale",
|
"sys-locale",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -578,6 +579,12 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
|
@ -730,7 +737,7 @@ dependencies = [
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"indexmap",
|
"indexmap 1.9.3",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
|
@ -743,6 +750,12 @@ version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
|
@ -900,10 +913,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown 0.12.3",
|
||||||
"serde",
|
"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]]
|
[[package]]
|
||||||
name = "indicatif"
|
name = "indicatif"
|
||||||
version = "0.17.5"
|
version = "0.17.5"
|
||||||
|
|
@ -1459,6 +1482,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
|
@ -1480,7 +1512,7 @@ dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"chrono",
|
"chrono",
|
||||||
"hex",
|
"hex",
|
||||||
"indexmap",
|
"indexmap 1.9.3",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with_macros",
|
"serde_with_macros",
|
||||||
|
|
@ -1732,6 +1764,41 @@ dependencies = [
|
||||||
"tracing",
|
"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]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
|
@ -2114,6 +2181,15 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ serde_json = "1.0"
|
||||||
serde_plain = "1.0"
|
serde_plain = "1.0"
|
||||||
shlex = "1.1"
|
shlex = "1.1"
|
||||||
tempfile = "3.6"
|
tempfile = "3.6"
|
||||||
|
toml = { version = "0.7", features = ["display", "parse", "preserve_order"] }
|
||||||
tokio = { version = "1.29", features = ["macros", "rt-multi-thread", "time"] }
|
tokio = { version = "1.29", features = ["macros", "rt-multi-thread", "time"] }
|
||||||
sys-locale = "0.3"
|
sys-locale = "0.3"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ mod login;
|
||||||
mod search;
|
mod search;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
use crate::utils::config::{Auth, Config};
|
||||||
pub use archive::Archive;
|
pub use archive::Archive;
|
||||||
use dialoguer::console::Term;
|
use dialoguer::console::Term;
|
||||||
pub use download::Download;
|
pub use download::Download;
|
||||||
|
|
@ -136,8 +137,20 @@ pub async fn cli_entrypoint() {
|
||||||
}
|
}
|
||||||
Command::Login(login) => {
|
Command::Login(login) => {
|
||||||
if login.remove {
|
if login.remove {
|
||||||
if let Some(session_file) = login::session_file_path() {
|
match Config::load() {
|
||||||
let _ = fs::remove_file(session_file);
|
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;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -226,11 +239,12 @@ async fn execute_executor(executor: impl Execute, ctx: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_ctx(cli: &mut Cli) -> Result<Context> {
|
async fn create_ctx(cli: &mut Cli) -> Result<Context> {
|
||||||
let crunchy = crunchyroll_session(cli).await?;
|
let mut config = Config::load()?.unwrap_or_default();
|
||||||
Ok(Context { crunchy })
|
let crunchy = crunchyroll_session(cli, &mut config).await?;
|
||||||
|
Ok(Context { crunchy, config })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn crunchyroll_session(cli: &mut Cli) -> Result<Crunchyroll> {
|
async fn crunchyroll_session(cli: &mut Cli, config: &mut Config) -> Result<Crunchyroll> {
|
||||||
let supported_langs = vec![
|
let supported_langs = vec![
|
||||||
Locale::ar_ME,
|
Locale::ar_ME,
|
||||||
Locale::de_DE,
|
Locale::de_DE,
|
||||||
|
|
@ -293,20 +307,12 @@ async fn crunchyroll_session(cli: &mut Cli) -> Result<Crunchyroll> {
|
||||||
|
|
||||||
let progress_handler = progress!("Logging in");
|
let progress_handler = progress!("Logging in");
|
||||||
if root_login_methods_count + login_login_methods_count == 0 {
|
if root_login_methods_count + login_login_methods_count == 0 {
|
||||||
if let Some(login_file_path) = login::session_file_path() {
|
if let Some(auth) = &config.auth {
|
||||||
if login_file_path.exists() {
|
return match auth {
|
||||||
let session = fs::read_to_string(login_file_path)?;
|
Auth::RefreshToken { token } => Ok(builder.login_with_refresh_token(token).await?),
|
||||||
if let Some((token_type, token)) = session.split_once(':') {
|
Auth::EtpRt { token } => Ok(builder.login_with_etp_rt(token).await?),
|
||||||
match token_type {
|
Auth::Anonymous => Ok(builder.login_anonymously().await?),
|
||||||
"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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bail!("Please use a login method ('--credentials', '--etp-rt' or '--anonymous')")
|
bail!("Please use a login method ('--credentials', '--etp-rt' or '--anonymous')")
|
||||||
} else if root_login_methods_count + login_login_methods_count > 1 {
|
} else if root_login_methods_count + login_login_methods_count > 1 {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
|
use crate::utils::config::Auth;
|
||||||
use crate::utils::context::Context;
|
use crate::utils::context::Context;
|
||||||
use crate::Execute;
|
use crate::Execute;
|
||||||
use anyhow::bail;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use crunchyroll_rs::crunchyroll::SessionToken;
|
use crunchyroll_rs::crunchyroll::SessionToken;
|
||||||
use std::fs;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[derive(Debug, clap::Parser)]
|
#[derive(Debug, clap::Parser)]
|
||||||
#[clap(about = "Save your login credentials persistent on disk")]
|
#[clap(about = "Save your login credentials persistent on disk")]
|
||||||
|
|
@ -19,23 +17,14 @@ pub struct Login {
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Execute for Login {
|
impl Execute for Login {
|
||||||
async fn execute(self, ctx: Context) -> Result<()> {
|
async fn execute(self, mut ctx: Context) -> Result<()> {
|
||||||
if let Some(login_file_path) = session_file_path() {
|
let auth = match ctx.crunchy.session_token().await {
|
||||||
fs::create_dir_all(login_file_path.parent().unwrap())?;
|
SessionToken::RefreshToken(token) => Auth::RefreshToken { token },
|
||||||
|
SessionToken::EtpRt(token) => Auth::EtpRt { token },
|
||||||
match ctx.crunchy.session_token().await {
|
SessionToken::Anonymous => Auth::Anonymous,
|
||||||
SessionToken::RefreshToken(refresh_token) => Ok(fs::write(
|
};
|
||||||
login_file_path,
|
ctx.config.auth = Some(auth);
|
||||||
format!("refresh_token:{}", refresh_token),
|
Ok(ctx.config.write()?)
|
||||||
)?),
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,7 +45,3 @@ pub struct LoginMethod {
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub anonymous: bool,
|
pub anonymous: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn session_file_path() -> Option<PathBuf> {
|
|
||||||
dirs::config_dir().map(|config_dir| config_dir.join("crunchy-cli").join("session"))
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
mod command;
|
mod command;
|
||||||
|
|
||||||
pub use command::{session_file_path, Login, LoginMethod};
|
pub use command::{Login, LoginMethod};
|
||||||
|
|
|
||||||
59
crunchy-cli-core/src/utils/config.rs
Normal file
59
crunchy-cli-core/src/utils/config.rs
Normal file
|
|
@ -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<Auth>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn load() -> Result<Option<Self>> {
|
||||||
|
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<PathBuf> {
|
||||||
|
dirs::config_dir().map(|config_dir| config_dir.join("crunchy-cli.conf"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_config_file_path(ignore_non_existing_config_dir: bool) -> Result<Option<PathBuf>> {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
use crate::utils::config::Config;
|
||||||
use crunchyroll_rs::Crunchyroll;
|
use crunchyroll_rs::Crunchyroll;
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub crunchy: Crunchyroll,
|
pub crunchy: Crunchyroll,
|
||||||
|
pub config: Config,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod clap;
|
pub mod clap;
|
||||||
|
pub mod config;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod download;
|
pub mod download;
|
||||||
pub mod ffmpeg;
|
pub mod ffmpeg;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue