feat: load settings from a configuration file

Co-authored-by: aider (gemini/gemini-2.5-pro-preview-05-06) <aider@aider.chat>
This commit is contained in:
Chaos Rogers 2025-08-17 14:16:24 +01:00
parent 8d58e50847
commit c740e0df0e
2 changed files with 41 additions and 16 deletions

View file

@ -8,5 +8,8 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.82" anyhow = "1.0.82"
clap = { version = "4.5.4", features = ["derive"] } clap = { version = "4.5.4", features = ["derive"] }
config = "0.14.0"
dirs = "5.0.1"
fs_extra = "1.3.0" fs_extra = "1.3.0"
serde = { version = "1.0", features = ["derive"] }
tempfile = "3.10.1" tempfile = "3.10.1"

View file

@ -1,15 +1,18 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use clap::Parser; use clap::Parser;
use serde::Deserialize;
use std::fs; use std::fs;
use std::path::Path; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use tempfile::TempDir; use tempfile::TempDir;
// ==== CONFIGURATION ==== #[derive(Debug, Deserialize)]
const NAS_HOST: &str = "(localaddress)"; struct Settings {
const NAS_USER: &str = "ssh_user"; nas_host: String,
const NAS_PATH: &str = "/path/to/files/on/nas"; nas_user: String,
const COOKIES_FILE: &str = "/location/of/apple-music-cookies-file.txt"; nas_path: String,
cookies_file: String,
}
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -22,6 +25,22 @@ struct Args {
fn main() -> Result<()> { fn main() -> Result<()> {
let args = Args::parse(); let args = Args::parse();
let config_path = dirs::config_dir()
.context("Could not find config directory")?
.join("jamdl/config.toml");
if !config_path.exists() {
anyhow::bail!(
"Config file not found. Please create it at: {}",
config_path.display()
);
}
let settings = config::Config::builder()
.add_source(config::File::from(config_path))
.build()?
.try_deserialize::<Settings>()?;
// Create temp directory // Create temp directory
let temp_dir = TempDir::new().context("Failed to create temporary directory")?; let temp_dir = TempDir::new().context("Failed to create temporary directory")?;
let temp_path = temp_dir.path(); let temp_path = temp_dir.path();
@ -30,26 +49,26 @@ fn main() -> Result<()> {
temp_path.display() temp_path.display()
); );
download_media(&args.url, temp_path)?; download_media(&args.url, temp_path, &settings)?;
transfer_files(temp_path)?; transfer_files(temp_path, &settings)?;
// Cleanup is handled by TempDir's Drop trait // Cleanup is handled by TempDir's Drop trait
println!("[INFO] Cleaning up..."); println!("[INFO] Cleaning up...");
Ok(()) Ok(())
} }
fn download_media(video_url: &str, download_path: &Path) -> Result<()> { fn download_media(video_url: &str, download_path: &Path, settings: &Settings) -> Result<()> {
let mut cmd: Command; let mut cmd: Command;
if video_url.contains("music.apple.com") { if video_url.contains("music.apple.com") {
println!("[INFO] Apple Music link detected. Using gamdl..."); println!("[INFO] Apple Music link detected. Using gamdl...");
cmd = Command::new("gamdl"); cmd = Command::new("gamdl");
if Path::new(COOKIES_FILE).exists() { if Path::new(&settings.cookies_file).exists() {
cmd.args(["--cookies-path", COOKIES_FILE, video_url]); cmd.args(["--cookies-path", &settings.cookies_file, video_url]);
} else { } else {
println!( println!(
"[WARN] cookies.txt not found at {} — running gamdl without it", "[WARN] cookies.txt not found at {} — running gamdl without it",
COOKIES_FILE settings.cookies_file
); );
cmd.arg(video_url); cmd.arg(video_url);
} }
@ -75,15 +94,15 @@ fn download_media(video_url: &str, download_path: &Path) -> Result<()> {
Ok(()) Ok(())
} }
fn transfer_files(source_path: &Path) -> Result<()> { fn transfer_files(source_path: &Path, settings: &Settings) -> Result<()> {
let is_empty = fs::read_dir(source_path)?.next().is_none(); let is_empty = fs::read_dir(source_path)?.next().is_none();
if !is_empty { if !is_empty {
if NAS_HOST == "localhost" { if settings.nas_host == "localhost" {
println!("[INFO] NAS_HOST is localhost, copying files locally..."); println!("[INFO] NAS_HOST is localhost, copying files locally...");
let mut options = fs_extra::dir::CopyOptions::new(); let mut options = fs_extra::dir::CopyOptions::new();
options.content_only = true; options.content_only = true;
fs_extra::dir::copy(source_path, NAS_PATH, &options) fs_extra::dir::copy(source_path, &settings.nas_path, &options)
.context("Failed to copy files locally")?; .context("Failed to copy files locally")?;
} else { } else {
println!("[INFO] Transferring files to NAS via scp..."); println!("[INFO] Transferring files to NAS via scp...");
@ -92,7 +111,10 @@ fn transfer_files(source_path: &Path) -> Result<()> {
"{}/.", "{}/.",
source_path.to_str().context("Invalid source path")? source_path.to_str().context("Invalid source path")?
); );
let destination = format!("{}@{}:{}", NAS_USER, NAS_HOST, NAS_PATH); let destination = format!(
"{}@{}:{}",
settings.nas_user, settings.nas_host, settings.nas_path
);
scp_cmd.args(["-r", &source, &destination]); scp_cmd.args(["-r", &source, &destination]);
let status = scp_cmd.status().context("Failed to execute scp")?; let status = scp_cmd.status().context("Failed to execute scp")?;