Implement album index and image resize.

This commit is contained in:
Bill Thiede 2020-02-15 08:12:13 -08:00
parent 8abeef8859
commit 4bb2354fdf
5 changed files with 244 additions and 6 deletions

175
Cargo.lock generated
View File

@ -1,5 +1,11 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # 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]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.8" version = "0.7.8"
@ -111,6 +117,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "bytemuck"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "0.5.3" version = "0.5.3"
@ -197,6 +209,12 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "color_quant"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd"
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.6.4" version = "0.6.4"
@ -213,6 +231,15 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" 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]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.7.2" version = "0.7.2"
@ -267,6 +294,16 @@ dependencies = [
"sct", "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]] [[package]]
name = "digest" name = "digest"
version = "0.8.1" version = "0.8.1"
@ -450,6 +487,16 @@ dependencies = [
"wasi", "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]] [[package]]
name = "google-photoslibrary1" name = "google-photoslibrary1"
version = "0.1.0-20200203" version = "0.1.0-20200203"
@ -757,6 +804,24 @@ dependencies = [
"unicode-normalization", "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]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.3.2" version = "1.3.2"
@ -766,6 +831,15 @@ dependencies = [
"autocfg 1.0.0", "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]] [[package]]
name = "input_buffer" name = "input_buffer"
version = "0.2.0" version = "0.2.0"
@ -799,6 +873,16 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" 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]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.35" version = "0.3.35"
@ -863,6 +947,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "lzw"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
[[package]] [[package]]
name = "matches" name = "matches"
version = "0.1.8" version = "0.1.8"
@ -927,6 +1017,15 @@ dependencies = [
"unicase 2.6.0", "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]] [[package]]
name = "mio" name = "mio"
version = "0.6.21" version = "0.6.21"
@ -1042,6 +1141,28 @@ dependencies = [
"num-traits", "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]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.11" version = "0.2.11"
@ -1190,6 +1311,7 @@ dependencies = [
"google-photoslibrary1", "google-photoslibrary1",
"google_api_auth", "google_api_auth",
"hexihasher", "hexihasher",
"image",
"lazy_static 1.4.0", "lazy_static 1.4.0",
"log 0.4.8", "log 0.4.8",
"prometheus", "prometheus",
@ -1241,6 +1363,18 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" 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]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.6" version = "0.2.6"
@ -1495,6 +1629,30 @@ dependencies = [
"rand_core 0.3.1", "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]] [[package]]
name = "rdrand" name = "rdrand"
version = "0.4.0" version = "0.4.0"
@ -1683,6 +1841,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "scoped_threadpool"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.0.0" version = "1.0.0"
@ -2009,6 +2173,17 @@ dependencies = [
"lazy_static 1.4.0", "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]] [[package]]
name = "time" name = "time"
version = "0.1.42" version = "0.1.42"

View File

@ -21,6 +21,7 @@ structopt = "0.3.9"
yup-oauth2 = "^3.1" yup-oauth2 = "^3.1"
warp = "0.1" warp = "0.1"
serde = { version = "1.0.104", features = ["derive"] } serde = { version = "1.0.104", features = ["derive"] }
image = "0.23.0"
[dependencies.prometheus] [dependencies.prometheus]
features = ["process"] features = ["process"]

View File

@ -53,6 +53,12 @@ impl Library {
info!("saving {}", path.to_string_lossy()); info!("saving {}", path.to_string_lossy());
fs::write(path, j) fs::write(path, j)
} }
pub fn albums(&self) -> Result<Vec<Album>, Box<dyn std::error::Error>> {
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<Vec<MediaItem>, Box<dyn std::error::Error>> { pub fn album(&self, album_id: &str) -> Result<Vec<MediaItem>, Box<dyn std::error::Error>> {
let album_path = self.root.join(album_id).join("album.json"); let album_path = self.root.join(album_id).join("album.json");
let bytes = fs::read(album_path)?; let bytes = fs::read(album_path)?;
@ -88,4 +94,12 @@ impl Library {
} }
Ok(image_path) Ok(image_path)
} }
pub fn original(&self, media_items_id: &str) -> Option<PathBuf> {
let path = self.originals_dir.join(media_items_id);
if path.exists() {
Some(path)
} else {
None
}
}
} }

View File

@ -197,6 +197,7 @@ fn sync_albums(
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let lib = Library::new(output_dir)?; let lib = Library::new(output_dir)?;
let albums = list_albums(client, title_filter)?; let albums = list_albums(client, title_filter)?;
info!("albums {:?}", albums);
lib.create_album_index(&albums)?; lib.create_album_index(&albums)?;
for a in &albums { for a in &albums {
let album_id = a.id.as_ref().expect("unset album id").to_string(); let album_id = a.id.as_ref().expect("unset album id").to_string();

View File

@ -2,6 +2,7 @@ use std::error::Error;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::path::PathBuf; use std::path::PathBuf;
use log::info;
use log::warn; use log::warn;
use prometheus::Encoder; use prometheus::Encoder;
use serde::Deserialize; use serde::Deserialize;
@ -34,6 +35,14 @@ fn index() -> Result<impl warp::Reply, warp::Rejection> {
Ok("Hello world") Ok("Hello world")
} }
fn albums(lib: Library) -> Result<impl warp::Reply, warp::Rejection> {
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<impl warp::Reply, warp::Rejection> { fn album(lib: Library, id: String) -> Result<impl warp::Reply, warp::Rejection> {
let album = lib.album(&id).map_err(|e| { let album = lib.album(&id).map_err(|e| {
warn!("Couldn't find album {}: {}", id, e); warn!("Couldn't find album {}: {}", id, e);
@ -44,17 +53,53 @@ fn album(lib: Library, id: String) -> Result<impl warp::Reply, warp::Rejection>
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct ImageParams { struct ImageParams {
w: Option<usize>, w: Option<u32>,
h: Option<usize>, h: Option<u32>,
c: Option<bool>, c: Option<bool>,
} }
fn image( fn image(
lib: Library, lib: Library,
image_id: String, media_items_id: String,
params: ImageParams, params: ImageParams,
) -> Result<impl warp::Reply, warp::Rejection> { ) -> Result<impl warp::Reply, warp::Rejection> {
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<dyn Error>> { pub fn run(addr: SocketAddr, root: PathBuf) -> Result<(), Box<dyn Error>> {
@ -62,18 +107,20 @@ pub fn run(addr: SocketAddr, root: PathBuf) -> Result<(), Box<dyn Error>> {
let lib = warp::any().map(move || lib.clone()); let lib = warp::any().map(move || lib.clone());
let index = warp::path::end().and_then(index); 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") let album = warp::path("album")
.and(lib.clone()) .and(lib.clone())
.and(warp::path::param()) .and(warp::path::param())
.and_then(album); .and_then(album);
let image = warp::path("image") let image = warp::path("image")
.and(lib.clone()) .and(lib.clone())
.and(warp::path::param()) .and(warp::path::param())
.and(warp::query::<ImageParams>()) .and(warp::query::<ImageParams>())
.and_then(image); .and_then(image);
let api = album.or(image); let api = albums.or(album).or(image);
let api = warp::path("api").and(api); let api = warp::path("api").and(api);
// Fallback, always keep this last. // Fallback, always keep this last.