Base sync support.

Refactored list_albums to be useful in sync and list-albums.
This commit is contained in:
2020-02-06 18:27:37 -08:00
parent 3952f9e1fb
commit b0a10364b0
3 changed files with 180 additions and 35 deletions

View File

@@ -1,9 +1,13 @@
use std::error::Error;
use std::fs;
use std::path::PathBuf;
use google_api_auth;
use google_photoslibrary1;
use google_photoslibrary1::schemas::Album;
use google_photoslibrary1::schemas::SearchMediaItemsRequest;
use log::debug;
use log::info;
use regex::Regex;
use structopt::StructOpt;
use yup_oauth2::{Authenticator, InstalledFlow};
@@ -17,6 +21,13 @@ enum Command {
SearchMediaItems {
album_id: String,
},
Sync {
/// Optional album title to filter. Default will mirror all albums.
#[structopt(short, long)]
title_filter: Option<Regex>,
/// Directory to store sync.
output: PathBuf,
},
}
#[derive(Debug, StructOpt)]
@@ -27,7 +38,7 @@ enum Command {
struct Opt {
/// Activate debug mode
#[structopt(short, parse(from_occurrences))]
verbose: u32,
verbose: usize,
/// Path to json file containing Google client ID and secrets for out of band auth flow.
#[structopt(long)]
@@ -58,7 +69,7 @@ fn new_client(
.build()
.unwrap();
let scopes = vec!["https://www.googleapis.com/auth/photoslibrary.readonly".to_string()];
let scopes = vec![google_photoslibrary1::scopes::PHOTOSLIBRARY_READONLY];
let auth = google_api_auth::yup_oauth2::from_authenticator(auth, scopes);
@@ -101,26 +112,30 @@ fn search_media_items(
}
}
fn list_albums(
fn sync_albums(
client: google_photoslibrary1::Client,
title_filter: Option<Regex>,
output_dir: PathBuf,
) -> Result<(), Box<dyn Error>> {
for album in client
.shared_albums()
.list()
.iter_shared_albums_with_all_fields()
{
let a = album?;
match (&title_filter, &a.title) {
// Print everything when no filter or title.
(None, None) => {}
// skip when filter given but the media item doesn't have a title (it can't match)
(_, None) => continue,
// skip when the media item doesn't match the filter
(Some(title_filter), Some(title)) if !title_filter.is_match(&title) => continue,
// handle everything else
_ => {}
let albums = list_albums(client, title_filter)?;
for a in &albums {
let album_dir = output_dir.join(a.id.as_ref().expect("missing album id"));
if !album_dir.exists() {
info!("making album directory {}", album_dir.to_string_lossy());
fs::create_dir_all(album_dir)?;
}
}
// Serialize it to a JSON string.
let j = serde_json::to_string(&albums)?;
let path = output_dir.join("albums.json");
info!("saving {}", path.to_string_lossy());
fs::write(path, j)?;
Ok(())
}
fn print_albums(albums: Vec<Album>) {
for a in albums {
println!(
"album: {} {} ({} items)",
a.id.unwrap_or("NO ID".to_string()),
@@ -128,15 +143,50 @@ fn list_albums(
a.media_items_count.unwrap_or(0)
);
}
Ok(())
}
fn list_albums(
client: google_photoslibrary1::Client,
title_filter: Option<Regex>,
) -> Result<Vec<Album>, Box<dyn Error>> {
Ok(client
.shared_albums()
.list()
.iter_shared_albums_with_all_fields()
.filter_map(|a| a.ok())
.filter(|a| {
match (&title_filter, &a.title) {
// keep everything when no filter or title.
(None, None) => true,
// skip when filter given but the media item doesn't have a title (it can't match)
(_, None) => false,
// skip when the media item doesn't match the filter
(Some(title_filter), Some(title)) if !title_filter.is_match(&title) => false,
// keep everything else
_ => true,
}
})
.collect())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let opt = Opt::from_args();
println!("opt: {:?}", opt);
stderrlog::new()
.module(module_path!())
.verbosity(opt.verbose)
.init()
.unwrap();
debug!("opt: {:?}", opt);
let client = new_client(&opt.credentials, &opt.token_cache)?;
match opt.cmd {
Command::ListAlbums { title_filter } => list_albums(client, title_filter),
Command::ListAlbums { title_filter } => {
print_albums(list_albums(client, title_filter)?);
Ok(())
}
Command::SearchMediaItems { album_id } => search_media_items(client, album_id),
Command::Sync {
title_filter,
output,
} => sync_albums(client, title_filter, output),
}
}