diff --git a/src/main.rs b/src/main.rs index cd0c95d..7aa547e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,14 @@ use std::collections::HashMap; use std::error::Error; use std::net::SocketAddr; use std::path::PathBuf; +use std::thread; +use std::time; use google_api_auth; use google_photoslibrary1 as photos; use hexihasher; use lazy_static::lazy_static; -use log::{debug, info}; +use log::{debug, error, info}; use photos::schemas::{Album, MediaItem, SearchMediaItemsRequest}; use regex::Regex; use structopt::StructOpt; @@ -16,6 +18,31 @@ use yup_oauth2::{Authenticator, InstalledFlow}; use photosync::library::Library; use photosync::web; +fn parse_duration(src: &str) -> Result { + let secs = str::parse::(src)?; + Ok(time::Duration::from_secs(secs)) +} + +#[derive(Debug, StructOpt)] +struct Sync { + #[structopt(flatten)] + auth: Auth, + /// Optional album title to filter. Default will mirror all albums. + #[structopt(short, long)] + title_filter: Option, + /// Directory to store sync. + root: PathBuf, +} + +#[derive(Debug, StructOpt)] +struct Serve { + /// Directory of data fetched by `sync`. + root: PathBuf, + /// HTTP address to listen for web requests. + #[structopt(long = "addr", default_value = "0.0.0.0:0")] + addr: SocketAddr, +} + #[derive(Debug, StructOpt)] enum Command { /// List albums for the user of the given credentials. Optionally title filter. @@ -31,16 +58,19 @@ enum Command { }, Sync { #[structopt(flatten)] - auth: Auth, - /// Optional album title to filter. Default will mirror all albums. - #[structopt(short, long)] - title_filter: Option, - /// Directory to store sync. - output: PathBuf, + sync: Sync, }, Serve { - /// Directory of data fetched by `sync`. - root: PathBuf, + #[structopt(flatten)] + serve: Serve, + }, + ServeAndSync { + /// Sync albums at given interval. + #[structopt(parse(try_from_str = parse_duration))] + interval: time::Duration, + + #[structopt(flatten)] + sync: Sync, /// HTTP address to listen for web requests. #[structopt(long = "addr", default_value = "0.0.0.0:0")] addr: SocketAddr, @@ -192,10 +222,9 @@ lazy_static! { fn sync_albums( client: &photos::Client, - title_filter: Option, - output_dir: PathBuf, + title_filter: &Option, + lib: &Library, ) -> Result<(), Box> { - let lib = Library::new(output_dir)?; let albums = list_albums(client, title_filter)?; info!("albums {:?}", albums); lib.create_album_index(&albums)?; @@ -236,12 +265,18 @@ fn print_albums(albums: Vec) { fn list_albums( client: &photos::Client, - title_filter: Option, + title_filter: &Option, ) -> Result, Box> { Ok(client - .shared_albums() + .albums() .list() - .iter_shared_albums_with_all_fields() + .iter_albums_with_all_fields() + .chain( + client + .shared_albums() + .list() + .iter_shared_albums_with_all_fields(), + ) .filter_map(|a| a.ok()) .filter(|a| { match (&title_filter, &a.title) { @@ -258,8 +293,23 @@ fn list_albums( .collect()) } -pub fn serve(addr: SocketAddr, root: PathBuf) -> Result<(), Box> { - web::run(addr, root) +fn background_sync( + client: photos::Client, + interval: time::Duration, + title_filter: Option, + lib: Library, +) -> Result<(), Box> { + thread::spawn(move || loop { + if let Err(err) = sync_albums(&client, &title_filter, &lib) { + error!("Error syncing: {}", err); + } + thread::sleep(interval); + }); + Ok(()) +} + +pub fn serve(addr: SocketAddr, lib: Library) -> Result<(), Box> { + web::run(addr, lib) } fn main() -> Result<(), Box> { @@ -273,7 +323,7 @@ fn main() -> Result<(), Box> { match opt.cmd { Command::ListAlbums { auth, title_filter } => { let client = new_client(&auth.credentials, &auth.token_cache)?; - print_albums(list_albums(&client, title_filter)?); + print_albums(list_albums(&client, &title_filter)?); Ok(()) } Command::SearchMediaItems { auth, album_id } => { @@ -282,14 +332,39 @@ fn main() -> Result<(), Box> { Ok(()) } Command::Sync { - auth, - title_filter, - output, + sync: + Sync { + auth, + title_filter, + root, + }, } => { let client = new_client(&auth.credentials, &auth.token_cache)?; - sync_albums(&client, title_filter, output)?; + let lib = Library::new(root)?; + sync_albums(&client, &title_filter, &lib)?; + Ok(()) + } + Command::Serve { + serve: Serve { addr, root }, + } => { + let lib = Library::new(root)?; + serve(addr, lib) + } + Command::ServeAndSync { + interval, + sync: + Sync { + auth, + title_filter, + root, + }, + addr, + } => { + let client = new_client(&auth.credentials, &auth.token_cache)?; + let lib = Library::new(root)?; + background_sync(client, interval, title_filter, lib.clone())?; + serve(addr, lib)?; Ok(()) } - Command::Serve { addr, root } => serve(addr, root), } } diff --git a/src/web.rs b/src/web.rs index ffb600e..385e922 100644 --- a/src/web.rs +++ b/src/web.rs @@ -1,6 +1,5 @@ use std::error::Error; use std::net::SocketAddr; -use std::path::PathBuf; use log::warn; use prometheus::Encoder; @@ -98,11 +97,10 @@ fn image( } #[derive(RustEmbed)] -#[folder = "react-debug/build/"] +#[folder = "react-slideshow/build/"] struct Asset; -pub fn run(addr: SocketAddr, root: PathBuf) -> Result<(), Box> { - let lib = Library::new(root)?; +pub fn run(addr: SocketAddr, lib: Library) -> Result<(), Box> { let lib = warp::any().map(move || lib.clone()); let index = warp::get2().and(warp::path::full()).and_then(index);