This repository has been archived on 2020-04-11. You can view files and clone it, but cannot push or open issues or pull requests.
music-sync/src/main.rs

174 lines
5.5 KiB
Rust

#![allow(unknown_lints)]
#![warn(clippy)]
extern crate clap;
extern crate cursive;
extern crate failure;
#[macro_use]
extern crate lazy_static;
extern crate walkdir;
use clap::{App, Arg};
use cursive::traits::*;
use cursive::views::{Checkbox, Dialog, ListView, ProgressBar, TextView};
use cursive::Cursive;
use failure::Error;
use std::ffi::OsStr;
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::Command;
use std::sync::{Arc, Mutex};
use registry::Registry;
mod registry;
lazy_static! {
static ref REGISTRY: Arc<Mutex<Registry>> = Arc::new(Mutex::new(Registry::default()));
}
fn main() -> Result<(), Error> {
let matches = App::new("Music Sync")
.version("0.1.0")
.author("Paul van Tilburg <paul@luon.net>")
.about("Syncs selected music (albums) to a remote location using rsync")
.arg(Arg::with_name("device").index(1).required(true))
.arg(Arg::with_name("directory").index(2).required(true))
.get_matches();
let device = matches.value_of("device").unwrap();
let base_path = matches.value_of("directory").unwrap();
{
let mut registry = REGISTRY.lock().unwrap();
registry.device = String::from(device);
registry.base_path = PathBuf::from(base_path);
}
let mut ui = Cursive::default();
load_albums(&mut ui);
ui.set_fps(10);
ui.run();
Ok(())
}
fn load_albums(ui: &mut Cursive) {
let mut registry = REGISTRY.lock().unwrap();
if let Err(error) = registry.load_albums_file() {
eprintln!("Something went wrong: {}", error);
quit(ui);
}
let cb = ui.cb_sink().clone();
ui.add_layer(
Dialog::around(
ProgressBar::new()
.range(0, registry.albums_list.len())
.with_task(move |counter| {
let mut registry = REGISTRY.lock().unwrap();
if let Err(error) = registry.load_albums_fs(&counter) {
eprintln!("Something went wrong: {}", error);
cb.send(Box::new(quit));
} else {
cb.send(Box::new(select_albums));
}
}),
).title("Loading list of albums…"),
);
}
fn album_name(album_path: &PathBuf) -> &str {
album_path
.file_name()
.unwrap_or_else(|| OsStr::new("<Couldn't determine filename>"))
.to_str()
.unwrap_or("<Non-UTF-8 characters found>")
}
fn select_albums(ui: &mut Cursive) {
let registry = REGISTRY.lock().unwrap();
let added_albums = registry.added_albums();
let existing_albums = registry.existing_albums();
let removed_albums = registry.removed_albums();
// FIXME: Use a custom view for this.
let list_view = ListView::new()
.with(|list_view| {
list_view.add_child("--- New", Checkbox::new().disabled());
for album_path in added_albums {
let album_name = album_name(&album_path);
let album_path = album_path.clone();
let mut checkbox = Checkbox::new().on_change(move |_, checked| {
let mut registry = REGISTRY.lock().unwrap();
registry.select_album(&album_path, checked);
});
list_view.add_child(album_name, checkbox);
}
list_view.add_child("--- Existing ", Checkbox::new().disabled());
for album_path in existing_albums {
let album_name = album_name(&album_path);
let album_path = album_path.clone();
let selected = registry.selected_albums.contains(&album_path);
let mut checkbox = Checkbox::new().on_change(move |_, checked| {
let mut registry = REGISTRY.lock().unwrap();
registry.select_album(&album_path, checked);
});
if selected {
checkbox.check();
}
list_view.add_child(album_name, checkbox)
}
list_view.add_child("--- Gone ", Checkbox::new().disabled());
for album_path in removed_albums {
let mut checkbox = Checkbox::new().disabled();
if registry.selected_albums.contains(album_path) {
checkbox.check();
}
list_view.add_child(album_name(album_path), checkbox);
}
}).scrollable();
ui.pop_layer();
ui.add_layer(
Dialog::new()
.title("Select or unselect albums")
.button("Quit", |ui| quit(ui))
.button("Apply", |ui| save_albums(ui))
.content(list_view),
);
}
fn save_albums(ui: &mut Cursive) {
let registry = REGISTRY.lock().unwrap();
if let Err(error) = registry.save_albums_file() {
eprintln!("Something went wrong: {}", error);
quit(ui);
}
ui.pop_layer();
ui.add_layer(
Dialog::around(TextView::new("All albums have been saved!\nSynchronise?"))
.button("Yes", |ui| sync_albums(ui))
.button("No", |ui| ui.quit()),
);
}
fn sync_albums(ui: &mut Cursive) {
let registry = REGISTRY.lock().unwrap();
ui.quit();
Command::new("echo")
.arg("rsync")
.arg("-vaP")
.arg(&format!("--include-from={}.list.new", registry.device))
.arg("--exclude=\"*\"")
.arg("--delete")
.arg(&registry.base_path)
.arg("target_path")
.exec();
}
fn quit(ui: &mut Cursive) {
ui.quit();
}