Use config file to store sessions

This commit is contained in:
bytedream 2023-07-13 13:51:02 +02:00
parent 9ad27102fc
commit 850aa7a969
8 changed files with 177 additions and 47 deletions

View file

@ -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"

View file

@ -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<Context> {
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<Crunchyroll> {
async fn crunchyroll_session(cli: &mut Cli, config: &mut Config) -> Result<Crunchyroll> {
let supported_langs = vec![
Locale::ar_ME,
Locale::de_DE,
@ -293,20 +307,12 @@ async fn crunchyroll_session(cli: &mut Cli) -> Result<Crunchyroll> {
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 {

View file

@ -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<PathBuf> {
dirs::config_dir().map(|config_dir| config_dir.join("crunchy-cli").join("session"))
}

View file

@ -1,3 +1,3 @@
mod command;
pub use command::{session_file_path, Login, LoginMethod};
pub use command::{Login, LoginMethod};

View 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))
}
}

View file

@ -1,5 +1,7 @@
use crate::utils::config::Config;
use crunchyroll_rs::Crunchyroll;
pub struct Context {
pub crunchy: Crunchyroll,
pub config: Config,
}

View file

@ -1,4 +1,5 @@
pub mod clap;
pub mod config;
pub mod context;
pub mod download;
pub mod ffmpeg;