use koan_core::config;
use owo_colors::OwoColorize;
use super::open_db;
pub fn cmd_remote_login(url: &str, username: &str) {
if !url.starts_with("https://") && !url.contains("localhost") && !url.contains("127.0.0.1") {
eprintln!("warning: server URL does not use HTTPS — credentials will be sent in plaintext");
}
let password = rpassword::prompt_password("password: ").unwrap_or_else(|e| {
eprintln!("{} {}", "error:".red().bold(), e);
std::process::exit(1);
});
let client = koan_core::remote::client::SubsonicClient::new(url, username, &password);
match client.ping() {
Ok(()) => println!("{} {}", "connected".green(), url),
Err(e) => {
eprintln!("{} {}", "connection failed:".red().bold(), e);
std::process::exit(1);
}
}
let mut remote_vals = toml::map::Map::new();
remote_vals.insert("enabled".into(), toml::Value::Boolean(true));
remote_vals.insert("url".into(), toml::Value::String(url.to_string()));
remote_vals.insert("username".into(), toml::Value::String(username.to_string()));
remote_vals.insert("password".into(), toml::Value::String(password));
if let Err(e) = config::Config::patch_local("remote", &remote_vals) {
eprintln!("{} {}", "config error:".red().bold(), e);
std::process::exit(1);
}
println!("{}", "credentials saved to config.local.toml".green());
}
pub fn cmd_remote_sync(full: bool) {
let cfg = config::Config::load().unwrap_or_default();
let client = match koan_core::helpers::subsonic_client(&cfg) {
Some(c) => c,
None => {
eprintln!(
"{} no remote server configured — run {} first",
"error:".red().bold(),
"koan remote login".bold()
);
std::process::exit(1);
}
};
let db = open_db();
let start = std::time::Instant::now();
match koan_core::remote::sync::sync_library(
&db,
&client,
full,
&cfg.remote.url,
&cfg.remote.username,
) {
Ok(result) => {
let elapsed = start.elapsed();
let headline = if result.is_complete() {
"sync complete".green().bold().to_string()
} else {
"sync incomplete".yellow().bold().to_string()
};
println!(
"{} {} {} artists, {} albums, {} tracks",
headline,
format!("({:.1}s)", elapsed.as_secs_f64()).dimmed(),
result.artists_synced.to_string().bold(),
result.albums_synced.to_string().bold(),
result.tracks_synced.to_string().bold(),
);
if !result.is_complete() {
eprintln!(
"{} {} album(s) could not be fetched — the sync watermark was left \
unchanged, so the next sync will retry them",
"warning:".yellow().bold(),
result.albums_failed.to_string().bold(),
);
}
}
Err(e) => {
eprintln!("{} {}", "sync failed:".red().bold(), e);
std::process::exit(1);
}
}
print!("{}", "syncing favourites...".dimmed());
use std::io::Write;
std::io::stdout().flush().ok();
let local_favs =
koan_core::db::queries::favourites_with_remote_id(&db.conn).unwrap_or_default();
let mut starred = 0;
for (_path, remote_id) in &local_favs {
if client.star(remote_id).is_ok() {
starred += 1;
}
}
let imported = match client.get_starred() {
Ok(songs) => {
let remote_ids: Vec<String> = songs.into_iter().map(|s| s.id).collect();
match koan_core::db::queries::import_remote_favourites(&db.conn, &remote_ids) {
Ok(n) => n,
Err(e) => {
eprintln!("\n{} importing favourites: {}", "error".red(), e);
0
}
}
}
Err(e) => {
eprintln!("\n{} fetching starred: {}", "error".red(), e);
0
}
};
println!(
"\r{} {} pushed, {} imported",
"favourites synced:".green().bold(),
starred.to_string().bold(),
imported.to_string().bold(),
);
}
pub fn cmd_remote_status() {
let cfg = config::Config::load().unwrap_or_default();
if !cfg.remote.enabled || cfg.remote.url.is_empty() {
println!("no remote server configured");
return;
}
println!("{} {}", "server:".cyan(), cfg.remote.url);
println!("{} {}", "username:".cyan(), cfg.remote.username);
let has_password = !cfg.remote.password.is_empty();
println!(
"{} {}",
"password:".cyan(),
if has_password {
"configured".green().to_string()
} else {
"not set".red().to_string()
}
);
if has_password && let Some(client) = koan_core::helpers::subsonic_client(&cfg) {
match client.ping() {
Ok(()) => println!("{} {}", "status:".cyan(), "connected".green()),
Err(e) => println!(
"{} {} {}",
"status:".cyan(),
"error".red(),
format!("\u{2014} {}", e).dimmed()
),
}
}
}