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]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" checksum = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
dependencies = [ dependencies = [
"hermit-abi",
"libc", "libc",
"termion",
"winapi 0.3.8", "winapi 0.3.8",
] ]
@ -217,7 +217,7 @@ dependencies = [
"autocfg 0.1.7", "autocfg 0.1.7",
"cfg-if", "cfg-if",
"crossbeam-utils 0.7.0", "crossbeam-utils 0.7.0",
"lazy_static", "lazy_static 1.4.0",
"memoffset", "memoffset",
"scopeguard", "scopeguard",
] ]
@ -238,7 +238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"lazy_static", "lazy_static 1.4.0",
] ]
[[package]] [[package]]
@ -249,7 +249,7 @@ checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
dependencies = [ dependencies = [
"autocfg 0.1.7", "autocfg 0.1.7",
"cfg-if", "cfg-if",
"lazy_static", "lazy_static 1.4.0",
] ]
[[package]] [[package]]
@ -598,6 +598,12 @@ dependencies = [
"winapi-build", "winapi-build",
] ]
[[package]]
name = "lazy_static"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -781,6 +787,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.9.0" version = "0.9.0"
@ -825,7 +837,10 @@ version = "0.1.0"
dependencies = [ dependencies = [
"google-photoslibrary1", "google-photoslibrary1",
"google_api_auth", "google_api_auth",
"log 0.4.8",
"regex", "regex",
"serde_json",
"stderrlog",
"structopt", "structopt",
"yup-oauth2", "yup-oauth2",
] ]
@ -873,7 +888,7 @@ checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b"
dependencies = [ dependencies = [
"error-chain", "error-chain",
"idna 0.2.0", "idna 0.2.0",
"lazy_static", "lazy_static 1.4.0",
"regex", "regex",
"url 2.1.1", "url 2.1.1",
] ]
@ -1008,6 +1023,15 @@ version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 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]] [[package]]
name = "regex" name = "regex"
version = "1.3.4" version = "1.3.4"
@ -1017,7 +1041,7 @@ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-syntax", "regex-syntax",
"thread_local", "thread_local 1.0.1",
] ]
[[package]] [[package]]
@ -1069,7 +1093,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "113f53b644c5442e20ff3a299be3d6c61ba143737af5bd2ab298e248a7575b2d" checksum = "113f53b644c5442e20ff3a299be3d6c61ba143737af5bd2ab298e248a7575b2d"
dependencies = [ dependencies = [
"cc", "cc",
"lazy_static", "lazy_static 1.4.0",
"libc", "libc",
"spin", "spin",
"untrusted", "untrusted",
@ -1240,6 +1264,19 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 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]] [[package]]
name = "string" name = "string"
version = "0.2.1" version = "0.2.1"
@ -1262,7 +1299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98" checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98"
dependencies = [ dependencies = [
"clap", "clap",
"lazy_static", "lazy_static 1.4.0",
"structopt-derive", "structopt-derive",
] ]
@ -1313,6 +1350,27 @@ dependencies = [
"unicode-xid", "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]] [[package]]
name = "textnonce" name = "textnonce"
version = "0.6.5" version = "0.6.5"
@ -1336,13 +1394,23 @@ dependencies = [
"unicode-width", "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]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [ dependencies = [
"lazy_static", "lazy_static 1.4.0",
] ]
[[package]] [[package]]
@ -1452,7 +1520,7 @@ checksum = "6732fe6b53c8d11178dcb77ac6d9682af27fc6d4cb87789449152e5377377146"
dependencies = [ dependencies = [
"crossbeam-utils 0.6.6", "crossbeam-utils 0.6.6",
"futures", "futures",
"lazy_static", "lazy_static 1.4.0",
"log 0.4.8", "log 0.4.8",
"mio", "mio",
"num_cpus", "num_cpus",
@ -1511,7 +1579,7 @@ dependencies = [
"crossbeam-queue", "crossbeam-queue",
"crossbeam-utils 0.6.6", "crossbeam-utils 0.6.6",
"futures", "futures",
"lazy_static", "lazy_static 1.4.0",
"log 0.4.8", "log 0.4.8",
"num_cpus", "num_cpus",
"slab", "slab",
@ -1623,6 +1691,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "unreachable"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
dependencies = [
"void",
]
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.7.0" version = "0.7.0"
@ -1678,6 +1755,12 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]] [[package]]
name = "want" name = "want"
version = "0.2.0" version = "0.2.0"
@ -1706,7 +1789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45" checksum = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static 1.4.0",
"log 0.4.8", "log 0.4.8",
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1828,6 +1911,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 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]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" 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" } google-photoslibrary1 = { path = "../google-api-photoslibrary" }
structopt = "0.3.9" structopt = "0.3.9"
regex = "1.3.4" 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::error::Error;
use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use google_api_auth; use google_api_auth;
use google_photoslibrary1; use google_photoslibrary1;
use google_photoslibrary1::schemas::Album;
use google_photoslibrary1::schemas::SearchMediaItemsRequest; use google_photoslibrary1::schemas::SearchMediaItemsRequest;
use log::debug;
use log::info;
use regex::Regex; use regex::Regex;
use structopt::StructOpt; use structopt::StructOpt;
use yup_oauth2::{Authenticator, InstalledFlow}; use yup_oauth2::{Authenticator, InstalledFlow};
@ -17,6 +21,13 @@ enum Command {
SearchMediaItems { SearchMediaItems {
album_id: String, 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)] #[derive(Debug, StructOpt)]
@ -27,7 +38,7 @@ enum Command {
struct Opt { struct Opt {
/// Activate debug mode /// Activate debug mode
#[structopt(short, parse(from_occurrences))] #[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. /// Path to json file containing Google client ID and secrets for out of band auth flow.
#[structopt(long)] #[structopt(long)]
@ -58,7 +69,7 @@ fn new_client(
.build() .build()
.unwrap(); .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); 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, client: google_photoslibrary1::Client,
title_filter: Option<Regex>, title_filter: Option<Regex>,
output_dir: PathBuf,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
for album in client let albums = list_albums(client, title_filter)?;
.shared_albums() for a in &albums {
.list() let album_dir = output_dir.join(a.id.as_ref().expect("missing album id"));
.iter_shared_albums_with_all_fields() if !album_dir.exists() {
{ info!("making album directory {}", album_dir.to_string_lossy());
let a = album?; fs::create_dir_all(album_dir)?;
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
_ => {}
} }
}
// 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!( println!(
"album: {} {} ({} items)", "album: {} {} ({} items)",
a.id.unwrap_or("NO ID".to_string()), a.id.unwrap_or("NO ID".to_string()),
@ -128,15 +143,50 @@ fn list_albums(
a.media_items_count.unwrap_or(0) 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>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let opt = Opt::from_args(); 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)?; let client = new_client(&opt.credentials, &opt.token_cache)?;
match opt.cmd { 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::SearchMediaItems { album_id } => search_media_items(client, album_id),
Command::Sync {
title_filter,
output,
} => sync_albums(client, title_filter, output),
} }
} }