Base sync support.
Refactored list_albums to be useful in sync and list-albums.
This commit is contained in:
92
src/main.rs
92
src/main.rs
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user