Compare commits

..

No commits in common. "main" and "rust" have entirely different histories.
main ... rust

5 changed files with 223 additions and 204 deletions

2
.gitignore vendored
View file

@ -16,5 +16,3 @@ target
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
Cargo.lock
.DS_Store

257
Cargo.lock generated
View file

@ -2,6 +2,24 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "anstream"
version = "0.6.20"
@ -153,21 +171,20 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "config"
version = "0.15.15"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0faa974509d38b33ff89282db9c3295707ccf031727c0de9772038ec526852ba"
checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf"
dependencies = [
"async-trait",
"convert_case",
"json5",
"nom",
"pathdiff",
"ron",
"rust-ini",
"serde",
"serde-untagged",
"serde_json",
"toml",
"winnow",
"yaml-rust2",
]
@ -237,23 +254,23 @@ dependencies = [
[[package]]
name = "dirs"
version = "6.0.0"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.60.2",
"windows-sys 0.48.0",
]
[[package]]
@ -275,14 +292,10 @@ dependencies = [
]
[[package]]
name = "erased-serde"
version = "0.4.6"
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7"
dependencies = [
"serde",
"typeid",
]
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
@ -300,12 +313,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "fs_extra"
version = "1.3.0"
@ -350,23 +357,24 @@ name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "hashlink"
version = "0.10.0"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
dependencies = [
"hashbrown 0.15.5",
"hashbrown 0.14.5",
]
[[package]]
@ -375,6 +383,16 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "indexmap"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [
"equivalent",
"hashbrown 0.15.5",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
@ -439,6 +457,22 @@ version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "once_cell"
version = "1.21.3"
@ -480,7 +514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
dependencies = [
"memchr",
"thiserror",
"thiserror 2.0.15",
"ucd-trie",
]
@ -543,13 +577,13 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "redox_users"
version = "0.5.2"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom 0.2.16",
"libredox",
"thiserror",
"thiserror 1.0.69",
]
[[package]]
@ -566,9 +600,9 @@ dependencies = [
[[package]]
name = "rust-ini"
version = "0.21.3"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7"
checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a"
dependencies = [
"cfg-if",
"ordered-multimap",
@ -602,17 +636,6 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-untagged"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3"
dependencies = [
"erased-serde",
"serde",
"typeid",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
@ -638,9 +661,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "1.0.0"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
dependencies = [
"serde",
]
@ -686,13 +709,33 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850"
dependencies = [
"thiserror-impl",
"thiserror-impl 2.0.15",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
@ -717,40 +760,44 @@ dependencies = [
[[package]]
name = "toml"
version = "0.9.5"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_parser",
"winnow",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.7.0"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
dependencies = [
"serde",
]
[[package]]
name = "toml_parser"
version = "1.0.2"
name = "toml_edit"
version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"toml_write",
"winnow",
]
[[package]]
name = "typeid"
version = "1.0.3"
name = "toml_write"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "typenum"
@ -809,6 +856,15 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
@ -827,6 +883,21 @@ dependencies = [
"windows-targets 0.53.3",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
@ -860,6 +931,12 @@ dependencies = [
"windows_x86_64_msvc 0.53.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
@ -872,6 +949,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
@ -884,6 +967,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@ -908,6 +997,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
@ -920,6 +1015,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
@ -932,6 +1033,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
@ -944,6 +1051,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
@ -976,11 +1089,31 @@ dependencies = [
[[package]]
name = "yaml-rust2"
version = "0.10.3"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7"
checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8"
dependencies = [
"arraydeque",
"encoding_rs",
"hashlink",
]
[[package]]
name = "zerocopy"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View file

@ -1,6 +1,6 @@
[package]
name = "jamdl"
version = "0.2.0"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -8,8 +8,8 @@ edition = "2021"
[dependencies]
anyhow = "1.0.82"
clap = { version = "4.5.4", features = ["derive"] }
config = "0.15.15"
dirs = "6.0.0"
config = "0.14.0"
dirs = "5.0.1"
fs_extra = "1.3.0"
serde = { version = "1.0", features = ["derive"] }
tempfile = "3.10.1"

View file

@ -4,7 +4,7 @@ A command-line tool to download music from various sources like Apple Music, Sou
## Description
`jamdl` is a wrapper around popular downloaders (`gamdl`, `scdl`, `yt-dlp`) that automates the process of downloading media and transferring it to a specified location, such as a local directory or a remote NAS via rsync over SSH.
`jamdl` is a wrapper around popular downloaders (`gamdl`, `scdl`, `yt-dlp`) that automates the process of downloading media and transferring it to a specified location, such as a local directory or a remote NAS via SCP.
## Prerequisites
@ -12,7 +12,6 @@ You must have the following command-line tools installed and available in your s
- `gamdl` (for Apple Music)
- `scdl` (for SoundCloud)
- `yt-dlp` (for other sources)
- `rsync` (for file transfers; remote copies use rsync over SSH)
## Installation
@ -29,7 +28,7 @@ cargo install --path .
You can also install it directly from a git repository:
```bash
cargo install --git https://git.shed.gay/chaos/jamdl
cargo install --git <repository_url>
```
## Configuration
@ -54,39 +53,6 @@ cookies_file = "/path/to/your/apple-music-cookies-file.txt"
- `nas_path`: The destination path on your NAS or local filesystem.
- `cookies_file`: The path to your Apple Music cookies file for `gamdl`.
## Profiles
`jamdl` supports multiple profiles so you can target different destinations (NAS paths, hosts, or local folders) without editing the config each time. Profiles live under a top-level `[profiles]` table. The profile named `default` is used when no `--profile` is specified.
Example multi-profile configuration:
```toml
# Multi-profile configuration for jamdl
[profiles.default]
nas_host = "localhost"
nas_user = "your_ssh_user"
nas_path = "/path/on/nas"
cookies_file = "/path/to/your/apple-music-cookies-file.txt"
[profiles.work]
nas_host = "work-nas"
nas_user = "alice"
nas_path = "/srv/media/music"
cookies_file = "/home/alice/apple-cookies.txt"
[profiles.laptop]
nas_host = "localhost"
nas_user = "your_ssh_user"
nas_path = "/Users/you/Music"
cookies_file = "/Users/you/cookies.txt"
```
- Choose a profile when running `jamdl`:
- `jamdl --profile work "https://music.apple.com/us/album/some-album/123456789"`
- Short form: `jamdl -p work "https://music.apple.com/us/album/some-album/123456789"`
- If the requested profile is missing, `jamdl` falls back to `default` and lists available profiles.
- Backwards compatibility: legacy single-profile configs (top-level keys without `[profiles]`) are still supported.
## Usage
Once configured, you can download media by passing a URL to `jamdl`:

View file

@ -1,13 +1,12 @@
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use clap::Parser;
use serde::Deserialize;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::process::Command;
use tempfile::TempDir;
#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize)]
struct Settings {
nas_host: String,
nas_user: String,
@ -15,48 +14,22 @@ struct Settings {
cookies_file: String,
}
#[derive(Debug, Deserialize)]
struct AppConfig {
profiles: HashMap<String, Settings>,
}
#[derive(Subcommand, Debug)]
enum Commands {
/// Show the path to the configuration file and exit
ConfigPath,
}
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// The URL of the video/music to download
#[arg(name = "URL")]
url: Option<String>,
/// Profile name to use from config (falls back to 'default')
#[arg(short, long, default_value = "default")]
profile: String,
/// Command to run
#[command(subcommand)]
cmd: Option<Commands>,
url: String,
}
fn main() -> Result<()> {
let args = Args::parse();
let config_dir = dirs::home_dir()
.context("Could not find home directory")?
.join(".config")
let config_dir = dirs::config_dir()
.context("Could not find config directory")?
.join("jamdl");
let config_path = config_dir.join("config.toml");
// Handle 'config-path' subcommand early without creating or reading config
if matches!(args.cmd, Some(Commands::ConfigPath)) {
println!("{}", config_path.display());
return Ok(());
}
if !config_path.exists() {
println!("[INFO] No config file found, creating a default one...");
fs::create_dir_all(&config_dir).context("Failed to create config directory")?;
@ -72,39 +45,10 @@ fn main() -> Result<()> {
return Ok(());
}
println!("[INFO] Using config file at: {}", config_path.display());
let cfg = config::Config::builder()
.add_source(config::File::from(config_path.clone()))
.build()?;
let selected_profile = args.profile.clone();
let settings: Settings = match cfg.clone().try_deserialize::<AppConfig>() {
Ok(app_cfg) => {
if let Some(s) = app_cfg.profiles.get(&selected_profile).cloned() {
println!("[INFO] Using profile: {}", selected_profile);
s
} else if let Some(s) = app_cfg.profiles.get("default").cloned() {
if selected_profile != "default" {
println!("[WARN] Profile '{}' not found. Falling back to 'default'.", selected_profile);
let available = app_cfg.profiles.keys().cloned().collect::<Vec<_>>().join(", ");
println!("[INFO] Available profiles: {}", available);
} else {
println!("[INFO] Using profile: default");
}
s
} else {
anyhow::bail!("No profile '{}' found and no 'default' profile defined in config.", selected_profile);
}
}
Err(_) => {
if selected_profile != "default" {
println!("[WARN] Requested profile '{}' but config file uses single-profile format. Using that single profile.", selected_profile);
}
println!("[INFO] Using legacy single-profile configuration.");
cfg.try_deserialize::<Settings>()?
}
};
let settings = config::Config::builder()
.add_source(config::File::from(config_path))
.build()?
.try_deserialize::<Settings>()?;
// Create temp directory
let temp_dir = TempDir::new().context("Failed to create temporary directory")?;
@ -114,8 +58,7 @@ fn main() -> Result<()> {
temp_path.display()
);
let url = args.url.clone().context("A URL is required when not using a subcommand. Use '--help' for usage.")?;
download_media(&url, temp_path, &settings)?;
download_media(&args.url, temp_path, &settings)?;
transfer_files(temp_path, &settings)?;
// Cleanup is handled by TempDir's Drop trait
@ -140,26 +83,12 @@ fn download_media(video_url: &str, download_path: &Path, settings: &Settings) ->
}
} else if video_url.contains("soundcloud.com") {
println!("[INFO] Soundcloud link detected. Using scdl...");
let clean_url = video_url.split('?').next().unwrap();
cmd = Command::new("scdl");
cmd.args([
"-l",
clean_url,
"-c",
"--onlymp3",
"--yt-dlp-args",
"--embed-thumbnail --write-thumbnail",
]);
cmd.args(["-l", video_url]);
} else {
println!("[INFO] Non-Apple Music link. Using yt-dlp...");
cmd = Command::new("yt-dlp");
cmd.args([
"-o",
"%(title)s.%(ext)s",
"--write-thumbnail",
"--embed-thumbnail",
video_url,
]);
cmd.args(["-o", "%(title)s.%(ext)s", video_url]);
}
cmd.current_dir(download_path);
@ -179,34 +108,27 @@ fn transfer_files(source_path: &Path, settings: &Settings) -> Result<()> {
if !is_empty {
if settings.nas_host == "localhost" {
println!("[INFO] NAS_HOST is localhost, copying files locally via rsync...");
let mut rsync_cmd = Command::new("rsync");
let source = format!(
"{}/",
source_path.to_str().context("Invalid source path")?
);
rsync_cmd.args(["-r", &source, &settings.nas_path]);
let status = rsync_cmd.status().context("Failed to execute rsync")?;
if !status.success() {
anyhow::bail!("rsync failed with status: {}", status);
}
println!("[INFO] NAS_HOST is localhost, copying files locally...");
let mut options = fs_extra::dir::CopyOptions::new();
options.content_only = true;
fs_extra::dir::copy(source_path, &settings.nas_path, &options)
.context("Failed to copy files locally")?;
} else {
println!("[INFO] Transferring files to NAS via rsync over SSH...");
let mut rsync_cmd = Command::new("rsync");
println!("[INFO] Transferring files to NAS via scp...");
let mut scp_cmd = Command::new("scp");
let source = format!(
"{}/",
"{}/.",
source_path.to_str().context("Invalid source path")?
);
let destination = format!(
"{}@{}:{}",
settings.nas_user, settings.nas_host, settings.nas_path
);
rsync_cmd.args(["-r", "-e", "ssh", &source, &destination]);
scp_cmd.args(["-r", &source, &destination]);
let status = rsync_cmd.status().context("Failed to execute rsync")?;
let status = scp_cmd.status().context("Failed to execute scp")?;
if !status.success() {
anyhow::bail!("rsync failed with status: {}", status);
anyhow::bail!("scp failed with status: {}", status);
}
}
} else {