Base sync support.

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

120
Cargo.lock generated
View File

@ -32,12 +32,12 @@ checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c"
[[package]]
name = "atty"
version = "0.2.14"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
checksum = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
dependencies = [
"hermit-abi",
"libc",
"termion",
"winapi 0.3.8",
]
@ -217,7 +217,7 @@ dependencies = [
"autocfg 0.1.7",
"cfg-if",
"crossbeam-utils 0.7.0",
"lazy_static",
"lazy_static 1.4.0",
"memoffset",
"scopeguard",
]
@ -238,7 +238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
dependencies = [
"cfg-if",
"lazy_static",
"lazy_static 1.4.0",
]
[[package]]
@ -249,7 +249,7 @@ checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
dependencies = [
"autocfg 0.1.7",
"cfg-if",
"lazy_static",
"lazy_static 1.4.0",
]
[[package]]
@ -598,6 +598,12 @@ dependencies = [
"winapi-build",
]
[[package]]
name = "lazy_static"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -781,6 +787,12 @@ dependencies = [
"libc",
]
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "parking_lot"
version = "0.9.0"
@ -825,7 +837,10 @@ version = "0.1.0"
dependencies = [
"google-photoslibrary1",
"google_api_auth",
"log 0.4.8",
"regex",
"serde_json",
"stderrlog",
"structopt",
"yup-oauth2",
]
@ -873,7 +888,7 @@ checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b"
dependencies = [
"error-chain",
"idna 0.2.0",
"lazy_static",
"lazy_static 1.4.0",
"regex",
"url 2.1.1",
]
@ -1008,6 +1023,15 @@ version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
[[package]]
name = "redox_termios"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
dependencies = [
"redox_syscall",
]
[[package]]
name = "regex"
version = "1.3.4"
@ -1017,7 +1041,7 @@ dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
"thread_local 1.0.1",
]
[[package]]
@ -1069,7 +1093,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "113f53b644c5442e20ff3a299be3d6c61ba143737af5bd2ab298e248a7575b2d"
dependencies = [
"cc",
"lazy_static",
"lazy_static 1.4.0",
"libc",
"spin",
"untrusted",
@ -1240,6 +1264,19 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "stderrlog"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e5ee9b90a5452c570a0b0ac1c99ae9498db7e56e33d74366de7f2a7add7f25"
dependencies = [
"atty",
"chrono",
"log 0.4.8",
"termcolor",
"thread_local 0.3.4",
]
[[package]]
name = "string"
version = "0.2.1"
@ -1262,7 +1299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98"
dependencies = [
"clap",
"lazy_static",
"lazy_static 1.4.0",
"structopt-derive",
]
@ -1313,6 +1350,27 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
dependencies = [
"winapi-util",
]
[[package]]
name = "termion"
version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905"
dependencies = [
"libc",
"numtoa",
"redox_syscall",
"redox_termios",
]
[[package]]
name = "textnonce"
version = "0.6.5"
@ -1336,13 +1394,23 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thread_local"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14"
dependencies = [
"lazy_static 0.2.11",
"unreachable",
]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
"lazy_static 1.4.0",
]
[[package]]
@ -1452,7 +1520,7 @@ checksum = "6732fe6b53c8d11178dcb77ac6d9682af27fc6d4cb87789449152e5377377146"
dependencies = [
"crossbeam-utils 0.6.6",
"futures",
"lazy_static",
"lazy_static 1.4.0",
"log 0.4.8",
"mio",
"num_cpus",
@ -1511,7 +1579,7 @@ dependencies = [
"crossbeam-queue",
"crossbeam-utils 0.6.6",
"futures",
"lazy_static",
"lazy_static 1.4.0",
"log 0.4.8",
"num_cpus",
"slab",
@ -1623,6 +1691,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "unreachable"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
dependencies = [
"void",
]
[[package]]
name = "untrusted"
version = "0.7.0"
@ -1678,6 +1755,12 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "want"
version = "0.2.0"
@ -1706,7 +1789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45"
dependencies = [
"bumpalo",
"lazy_static",
"lazy_static 1.4.0",
"log 0.4.8",
"proc-macro2",
"quote",
@ -1828,6 +1911,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View File

@ -13,3 +13,6 @@ google_api_auth = { git = "https://github.com/google-apis-rs/generator", feature
google-photoslibrary1 = { path = "../google-api-photoslibrary" }
structopt = "0.3.9"
regex = "1.3.4"
log = "0.4.8"
stderrlog = "0.4.3"
serde_json = "1.0.46"

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),
}
}