diff --git a/Cargo.lock b/Cargo.lock index 4310d61..b3c201d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" + [[package]] name = "aho-corasick" version = "0.7.8" @@ -111,6 +117,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytemuck" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431" + [[package]] name = "byteorder" version = "0.5.3" @@ -197,6 +209,12 @@ dependencies = [ "bitflags", ] +[[package]] +name = "color_quant" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" + [[package]] name = "core-foundation" version = "0.6.4" @@ -213,6 +231,15 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-deque" version = "0.7.2" @@ -267,6 +294,16 @@ dependencies = [ "sct", ] +[[package]] +name = "deflate" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" +dependencies = [ + "adler32", + "byteorder 1.3.4", +] + [[package]] name = "digest" version = "0.8.1" @@ -450,6 +487,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "gif" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471d90201b3b223f3451cd4ad53e34295f16a1df17b1edf3736d47761c3981af" +dependencies = [ + "color_quant", + "lzw", +] + [[package]] name = "google-photoslibrary1" version = "0.1.0-20200203" @@ -757,6 +804,24 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4e336ec01a678e7ab692914c641181528e8656451e6252f8f9e33728882eaf" +dependencies = [ + "bytemuck", + "byteorder 1.3.4", + "gif", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + [[package]] name = "indexmap" version = "1.3.2" @@ -766,6 +831,15 @@ dependencies = [ "autocfg 1.0.0", ] +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +dependencies = [ + "adler32", +] + [[package]] name = "input_buffer" version = "0.2.0" @@ -799,6 +873,16 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +[[package]] +name = "jpeg-decoder" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0256f0aec7352539102a9efbcb75543227b7ab1117e0f95450023af730128451" +dependencies = [ + "byteorder 1.3.4", + "rayon", +] + [[package]] name = "js-sys" version = "0.3.35" @@ -863,6 +947,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lzw" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" + [[package]] name = "matches" version = "0.1.8" @@ -927,6 +1017,15 @@ dependencies = [ "unicase 2.6.0", ] +[[package]] +name = "miniz_oxide" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" +dependencies = [ + "adler32", +] + [[package]] name = "mio" version = "0.6.21" @@ -1042,6 +1141,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" +dependencies = [ + "autocfg 1.0.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" +dependencies = [ + "autocfg 1.0.0", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.11" @@ -1190,6 +1311,7 @@ dependencies = [ "google-photoslibrary1", "google_api_auth", "hexihasher", + "image", "lazy_static 1.4.0", "log 0.4.8", "prometheus", @@ -1241,6 +1363,18 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +[[package]] +name = "png" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef859a23054bbfee7811284275ae522f0434a3c8e7f4b74bd4a35ae7e1c4a283" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "inflate", +] + [[package]] name = "ppv-lite86" version = "0.2.6" @@ -1495,6 +1629,30 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rayon" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +dependencies = [ + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +dependencies = [ + "crossbeam-deque", + "crossbeam-queue", + "crossbeam-utils", + "lazy_static 1.4.0", + "num_cpus", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -1683,6 +1841,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + [[package]] name = "scopeguard" version = "1.0.0" @@ -2009,6 +2173,17 @@ dependencies = [ "lazy_static 1.4.0", ] +[[package]] +name = "tiff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "002351e428db1eb1d8656d4ca61947c3519ac3191e1c804d4600cd32093b77ad" +dependencies = [ + "byteorder 1.3.4", + "lzw", + "miniz_oxide", +] + [[package]] name = "time" version = "0.1.42" diff --git a/Cargo.toml b/Cargo.toml index 24e09db..a4f011d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ structopt = "0.3.9" yup-oauth2 = "^3.1" warp = "0.1" serde = { version = "1.0.104", features = ["derive"] } +image = "0.23.0" [dependencies.prometheus] features = ["process"] diff --git a/src/library.rs b/src/library.rs index ca48cfb..6d4456c 100644 --- a/src/library.rs +++ b/src/library.rs @@ -53,6 +53,12 @@ impl Library { info!("saving {}", path.to_string_lossy()); fs::write(path, j) } + pub fn albums(&self) -> Result, Box> { + let albums_path = self.root.join("albums.json"); + info!("loading {}", albums_path.to_string_lossy()); + let bytes = fs::read(albums_path)?; + Ok(serde_json::from_slice(&bytes)?) + } pub fn album(&self, album_id: &str) -> Result, Box> { let album_path = self.root.join(album_id).join("album.json"); let bytes = fs::read(album_path)?; @@ -88,4 +94,12 @@ impl Library { } Ok(image_path) } + pub fn original(&self, media_items_id: &str) -> Option { + let path = self.originals_dir.join(media_items_id); + if path.exists() { + Some(path) + } else { + None + } + } } diff --git a/src/main.rs b/src/main.rs index c8ae486..cd0c95d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -197,6 +197,7 @@ fn sync_albums( ) -> Result<(), Box> { let lib = Library::new(output_dir)?; let albums = list_albums(client, title_filter)?; + info!("albums {:?}", albums); lib.create_album_index(&albums)?; for a in &albums { let album_id = a.id.as_ref().expect("unset album id").to_string(); diff --git a/src/web.rs b/src/web.rs index 8846fe0..22e0932 100644 --- a/src/web.rs +++ b/src/web.rs @@ -2,6 +2,7 @@ use std::error::Error; use std::net::SocketAddr; use std::path::PathBuf; +use log::info; use log::warn; use prometheus::Encoder; use serde::Deserialize; @@ -34,6 +35,14 @@ fn index() -> Result { Ok("Hello world") } +fn albums(lib: Library) -> Result { + let albums = lib.albums().map_err(|e| { + warn!("Couldn't find albums: {}", e); + warp::reject::not_found() + })?; + Ok(warp::reply::json(&albums)) +} + fn album(lib: Library, id: String) -> Result { let album = lib.album(&id).map_err(|e| { warn!("Couldn't find album {}: {}", id, e); @@ -44,17 +53,53 @@ fn album(lib: Library, id: String) -> Result #[derive(Debug, Deserialize)] struct ImageParams { - w: Option, - h: Option, + w: Option, + h: Option, c: Option, } fn image( lib: Library, - image_id: String, + media_items_id: String, params: ImageParams, ) -> Result { - Ok(format!("Hello world: {} {:?}", image_id, params)) + info!("image_id {} params {:?}", media_items_id, params); + + match lib.original(&media_items_id) { + None => { + warn!("Couldn't find original {}", &media_items_id); + Err(warp::reject::not_found()) + } + Some(path) => { + let orig_img = image::io::Reader::open(&path) + .map_err(|e| { + warn!("Couldn't open {}: {}", path.to_string_lossy(), e); + warp::reject::not_found() + })? + .with_guessed_format() + .map_err(|e| { + warn!("Couldn't guess format {}: {}", path.to_string_lossy(), e); + warp::reject::not_found() + })? + .decode() + .map_err(|e| { + warn!("Couldn't decode {}: {}", path.to_string_lossy(), e); + warp::reject::not_found() + })?; + let img = orig_img.resize( + params.w.unwrap_or(0), + params.h.unwrap_or(0), + image::imageops::FilterType::CatmullRom, + ); + let mut buf = Vec::new(); + img.write_to(&mut buf, image::ImageFormat::Jpeg) + .map_err(|e| { + warn!("Couldn't write_to {}: {}", path.to_string_lossy(), e); + warp::reject::not_found() + })?; + Ok(buf) + } + } } pub fn run(addr: SocketAddr, root: PathBuf) -> Result<(), Box> { @@ -62,18 +107,20 @@ pub fn run(addr: SocketAddr, root: PathBuf) -> Result<(), Box> { let lib = warp::any().map(move || lib.clone()); let index = warp::path::end().and_then(index); - // TODO(wathiede): implement album index + let albums = warp::path("albums").and(lib.clone()).and_then(albums); + let album = warp::path("album") .and(lib.clone()) .and(warp::path::param()) .and_then(album); + let image = warp::path("image") .and(lib.clone()) .and(warp::path::param()) .and(warp::query::()) .and_then(image); - let api = album.or(image); + let api = albums.or(album).or(image); let api = warp::path("api").and(api); // Fallback, always keep this last.