Compare commits

...

14 Commits

11 changed files with 1005 additions and 13561 deletions

559
Cargo.lock generated
View File

@ -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"
@ -53,7 +59,7 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
dependencies = [
"byteorder",
"byteorder 1.3.4",
]
[[package]]
@ -76,7 +82,7 @@ checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"byteorder 1.3.4",
"generic-array",
]
@ -89,6 +95,16 @@ dependencies = [
"byte-tools",
]
[[package]]
name = "buf_redux"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
dependencies = [
"memchr",
"safemem",
]
[[package]]
name = "bumpalo"
version = "3.2.0"
@ -101,6 +117,18 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
[[package]]
name = "byteorder"
version = "1.3.4"
@ -113,7 +141,7 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
dependencies = [
"byteorder",
"byteorder 1.3.4",
"either",
"iovec",
]
@ -181,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"
@ -197,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"
@ -251,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"
@ -434,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"
@ -442,7 +505,7 @@ dependencies = [
"chrono",
"google_api_auth",
"google_field_selector",
"mime",
"mime 0.3.16",
"percent-encoding 2.1.0",
"reqwest",
"serde",
@ -484,7 +547,7 @@ version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462"
dependencies = [
"byteorder",
"byteorder 1.3.4",
"bytes 0.4.12",
"fnv",
"futures",
@ -515,6 +578,32 @@ dependencies = [
"tokio-util",
]
[[package]]
name = "headers"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "882ca7d8722f33ce2c2db44f95425d6267ed59ca96ce02acbe58320054ceb642"
dependencies = [
"base64 0.10.1",
"bitflags",
"bytes 0.4.12",
"headers-core",
"http 0.1.21",
"mime 0.3.16",
"sha-1",
"time",
]
[[package]]
name = "headers-core"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "967131279aaa9f7c20c7205b45a391638a83ab118e6509b2d0ccbe08de044237"
dependencies = [
"bytes 0.4.12",
"http 0.1.21",
]
[[package]]
name = "heck"
version = "0.3.1"
@ -609,7 +698,7 @@ dependencies = [
"itoa",
"log 0.4.8",
"net2",
"rustc_version",
"rustc_version 0.2.3",
"time",
"tokio 0.1.22",
"tokio-buf",
@ -715,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"
@ -724,6 +831,24 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1b822cc844905551931d6f81608ed5f50a79c1078a4e2b4d42dbc7c1eedfbf"
dependencies = [
"bytes 0.4.12",
]
[[package]]
name = "iovec"
version = "0.1.4"
@ -748,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"
@ -812,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"
@ -836,7 +977,16 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
dependencies = [
"rustc_version",
"rustc_version 0.2.3",
]
[[package]]
name = "mime"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
dependencies = [
"log 0.3.9",
]
[[package]]
@ -845,14 +995,35 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "1.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d977de9ee851a0b16e932979515c0f3da82403183879811bc97d50bd9cc50f7"
dependencies = [
"mime 0.2.6",
"phf",
"phf_codegen",
"unicase 1.4.2",
]
[[package]]
name = "mime_guess"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599"
dependencies = [
"mime",
"unicase",
"mime 0.3.16",
"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]]
@ -897,6 +1068,24 @@ dependencies = [
"ws2_32-sys",
]
[[package]]
name = "multipart"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "136eed74cadb9edd2651ffba732b19a450316b680e4f48d6c79e905799e19d01"
dependencies = [
"buf_redux",
"httparse",
"log 0.4.8",
"mime 0.2.6",
"mime_guess 1.8.7",
"quick-error",
"rand 0.6.5",
"safemem",
"tempfile",
"twoway",
]
[[package]]
name = "native-tls"
version = "0.2.3"
@ -926,6 +1115,12 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "nom"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
[[package]]
name = "nom"
version = "4.2.3"
@ -946,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"
@ -1018,7 +1235,7 @@ checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
dependencies = [
"lock_api",
"parking_lot_core",
"rustc_version",
"rustc_version 0.2.3",
]
[[package]]
@ -1031,7 +1248,7 @@ dependencies = [
"cloudabi",
"libc",
"redox_syscall",
"rustc_version",
"rustc_version 0.2.3",
"smallvec 0.6.13",
"winapi 0.3.8",
]
@ -1048,6 +1265,45 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "phf"
version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662"
dependencies = [
"phf_shared",
"rand 0.6.5",
]
[[package]]
name = "phf_shared"
version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
dependencies = [
"siphasher",
"unicase 1.4.2",
]
[[package]]
name = "photosync"
version = "0.1.0"
@ -1055,13 +1311,19 @@ dependencies = [
"google-photoslibrary1",
"google_api_auth",
"hexihasher",
"image",
"lazy_static 1.4.0",
"log 0.4.8",
"mime_guess 2.0.1",
"prometheus",
"regex",
"reqwest",
"rust-embed",
"serde",
"serde_json",
"stderrlog",
"structopt",
"warp",
"yup-oauth2",
]
@ -1103,6 +1365,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"
@ -1161,6 +1435,46 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "procinfo"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f42e8578852a3306838981aedad8c5642ba794929aa12af0c9eb6c072b77af6c"
dependencies = [
"byteorder 0.5.3",
"libc",
"nom 1.2.4",
"rustc_version 0.1.7",
]
[[package]]
name = "prometheus"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5567486d5778e2c6455b1b90ff1c558f29e751fc018130fa182e15828e728af1"
dependencies = [
"cfg-if",
"fnv",
"lazy_static 1.4.0",
"libc",
"procinfo",
"protobuf",
"quick-error",
"spin",
]
[[package]]
name = "protobuf"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6686ddd96a8dbe2687b5f2a687b2cfb520854010ec480f2d74c32e7c9873d3c5"
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.2"
@ -1317,6 +1631,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"
@ -1387,8 +1725,8 @@ dependencies = [
"js-sys",
"lazy_static 1.4.0",
"log 0.4.8",
"mime",
"mime_guess",
"mime 0.3.16",
"mime_guess 2.0.1",
"native-tls",
"percent-encoding 2.1.0",
"pin-project-lite",
@ -1423,13 +1761,54 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "rust-embed"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "412cddb9905679491f2829ef7fea79179b9a76fa942e06ab52b420dbab94a406"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50212d0e652f580e6d297537c31237d4b2f4497e5912eebe25fde97ac06a51df"
dependencies = [
"quote",
"rust-embed-utils",
"syn",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97655158074ccb2d2cfb1ccb4c956ef0f4054e43a2c1e71146d4991e6961e105"
dependencies = [
"walkdir",
]
[[package]]
name = "rustc_version"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
dependencies = [
"semver 0.1.20",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
"semver 0.9.0",
]
[[package]]
@ -1474,6 +1853,21 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
[[package]]
name = "safemem"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.17"
@ -1484,6 +1878,18 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "scoped-tls"
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"
@ -1521,6 +1927,12 @@ dependencies = [
"core-foundation-sys",
]
[[package]]
name = "semver"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
[[package]]
name = "semver"
version = "0.9.0"
@ -1590,6 +2002,18 @@ dependencies = [
"url 2.1.1",
]
[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
"block-buffer",
"digest",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "sha2"
version = "0.8.1"
@ -1602,6 +2026,12 @@ dependencies = [
"opaque-debug",
]
[[package]]
name = "siphasher"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
[[package]]
name = "slab"
version = "0.4.2"
@ -1751,7 +2181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dafb35214e317d6c0a72b16d1aa667bbc0fea57e302798e7bc520e0f39988006"
dependencies = [
"base64 0.10.1",
"byteorder",
"byteorder 1.3.4",
"chrono",
"rand 0.6.5",
"serde",
@ -1786,6 +2216,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"
@ -2069,12 +2510,49 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
[[package]]
name = "tungstenite"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0c2bd5aeb7dcd2bb32e472c8872759308495e5eccc942e929a513cd8d36110"
dependencies = [
"base64 0.11.0",
"byteorder 1.3.4",
"bytes 0.4.12",
"http 0.1.21",
"httparse",
"input_buffer",
"log 0.4.8",
"rand 0.7.3",
"sha-1",
"url 2.1.1",
"utf-8",
]
[[package]]
name = "twoway"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
dependencies = [
"memchr",
]
[[package]]
name = "typenum"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
[[package]]
name = "unicase"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
dependencies = [
"version_check 0.1.5",
]
[[package]]
name = "unicase"
version = "2.6.0"
@ -2157,6 +2635,18 @@ dependencies = [
"percent-encoding 2.1.0",
]
[[package]]
name = "urlencoding"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df3561629a8bb4c57e5a2e4c43348d9e29c7c29d9b1c4c1f47166deca8f37ed"
[[package]]
name = "utf-8"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
[[package]]
name = "vcpkg"
version = "0.2.8"
@ -2187,6 +2677,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "walkdir"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
dependencies = [
"same-file",
"winapi 0.3.8",
"winapi-util",
]
[[package]]
name = "want"
version = "0.2.0"
@ -2208,6 +2709,32 @@ dependencies = [
"try-lock",
]
[[package]]
name = "warp"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3921463c44f680d24f1273ea55efd985f31206a22a02dee207a2ec72684285ca"
dependencies = [
"bytes 0.4.12",
"futures",
"headers",
"http 0.1.21",
"hyper 0.12.35",
"log 0.4.8",
"mime 0.3.16",
"mime_guess 2.0.1",
"multipart",
"scoped-tls",
"serde",
"serde_json",
"serde_urlencoded",
"tokio 0.1.22",
"tokio-io",
"tokio-threadpool",
"tungstenite",
"urlencoding",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
@ -2336,7 +2863,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164"
dependencies = [
"nom",
"nom 4.2.3",
]
[[package]]

View File

@ -19,3 +19,12 @@ serde_json = "1.0.46"
stderrlog = "0.4.3"
structopt = "0.3.9"
yup-oauth2 = "^3.1"
warp = "0.1"
serde = { version = "1.0.104", features = ["derive"] }
image = "0.23.0"
rust-embed = "5.2.0"
mime_guess = "2.0.1"
[dependencies.prometheus]
features = ["process"]
version = "0.7.0"

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
{
"name": "react-debug",
"version": "0.1.0",
"proxy": "http://localhost:4000",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
@ -8,6 +9,7 @@
"@testing-library/user-event": "^7.1.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.3.1"
},
"scripts": {

View File

@ -1 +0,0 @@
/tmp/photosync

View File

@ -1,4 +1,10 @@
import React from 'react';
import {
HashRouter as Router,
Switch,
Route,
useParams
} from "react-router-dom";
import './App.css';
@ -12,7 +18,7 @@ class Album extends React.Component {
}
componentDidMount() {
let {album} = this.props;
fetch(process.env.PUBLIC_URL + `/photosync/${album}/album.json`)
fetch(process.env.PUBLIC_URL + `/api/album/${album}`)
.then(res => res.json())
.then(
(result) => this.setState({media_items: result}),
@ -20,7 +26,6 @@ class Album extends React.Component {
);
}
render() {
let {album} = this.props;
let {error, media_items} = this.state;
console.log(this.state);
if (error !== null) {
@ -31,7 +36,7 @@ class Album extends React.Component {
// TODO(wathiede): use coverPhotoMediaItemId and fetch from a
// locally cached image.
return <figure key={ mi.id } className="figure">
<img height="256" width="256" src={ `/photosync/images/${mi.id}/${mi.filename}?thumb` } className="mr-3" alt={ mi.filename }/>
<img height="256" width="256" src={ `/api/image/${mi.id}?w=256&h=256` } className="mr-3" alt={ mi.filename }/>
<figcaption className="figure-caption">
<a key={ mi.id } href={ mi.productUrl }>
<p className="text-truncate">{ mi.filename}</p>
@ -54,7 +59,7 @@ class AlbumIndex extends React.Component {
};
}
componentDidMount() {
fetch(process.env.PUBLIC_URL + "/photosync/albums.json")
fetch(process.env.PUBLIC_URL + "/api/albums")
.then(res => res.json())
.then(
(result) => this.setState({albums: result}),
@ -69,8 +74,8 @@ class AlbumIndex extends React.Component {
console.log(albums);
return albums.map((a) => {
let img = <img src="https://via.placeholder.com/256x128" className="mr-3" alt="unset"/>;
if (a.coverPhotoBaseUrl !== undefined) {
img = <img src={ a.coverPhotoBaseUrl + "=w256" } className="mr-3" alt={ a.title }/>;
if (a.coverPhotoMediaItemId !== undefined) {
img = <img height="256" width="256" src={ `/api/image/${a.coverPhotoMediaItemId}?w=256&h=256` } className="mr-3" alt={ a.title }/>
}
let figure = <figure key={ a.id } className="figure">
@ -87,29 +92,26 @@ class AlbumIndex extends React.Component {
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
curAlbum: window.location.hash,
};
}
render() {
let {curAlbum} = this.state;
console.log(this.state);
let content;
if (curAlbum) {
content = <Album album={ curAlbum.slice(1) } />
} else {
content = <AlbumIndex />;
}
const AlbumRoute = () => {
// We can use the `useParams` hook here to access
// the dynamic pieces of the URL.
let { album_id } = useParams();
return <Album album={album_id} />;
}
return (
<div className="container">
{ content }
</div>
);
}
const App = () => {
return <div className="container">
<Router>
<Switch>
<Route exact path="/">
<AlbumIndex />
</Route>
<Route exact path="/:album_id">
<AlbumRoute />
</Route>
</Switch>
</Router>
</div>
}
export default App;

View File

@ -849,7 +849,7 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.2"
"@babel/runtime@7.8.4", "@babel/runtime@^7.0.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.1", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6":
"@babel/runtime@7.8.4", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.1", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==
@ -4609,6 +4609,11 @@ growly@^1.3.0:
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
gud@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==
gzip-size@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274"
@ -4726,6 +4731,18 @@ hex-color-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
history@^4.9.0:
version "4.10.1"
resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
dependencies:
"@babel/runtime" "^7.1.2"
loose-envify "^1.2.0"
resolve-pathname "^3.0.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
value-equal "^1.0.1"
hmac-drbg@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@ -4735,6 +4752,13 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
hoist-non-react-statics@^3.1.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
dependencies:
react-is "^16.7.0"
hosted-git-info@^2.1.4:
version "2.8.5"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
@ -5376,6 +5400,11 @@ is-wsl@^1.1.0:
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@ -6219,7 +6248,7 @@ loglevel@^1.6.6:
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312"
integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@ -6421,6 +6450,15 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256"
integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY=
mini-create-react-context@^0.3.0:
version "0.3.2"
resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz#79fc598f283dd623da8e088b05db8cddab250189"
integrity sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==
dependencies:
"@babel/runtime" "^7.4.0"
gud "^1.0.0"
tiny-warning "^1.0.2"
mini-css-extract-plugin@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e"
@ -7187,6 +7225,13 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
path-to-regexp@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
dependencies:
isarray "0.0.1"
path-type@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
@ -8268,11 +8313,40 @@ react-error-overlay@^6.0.5:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.5.tgz#55d59c2a3810e8b41922e0b4e5f85dcf239bd533"
integrity sha512-+DMR2k5c6BqMDSMF8hLH0vYKtKTeikiFW+fj0LClN+XZg4N9b8QUAdHC62CGWNLTi/gnuuemNcNcTFrCvK1f+A==
react-is@^16.8.1, react-is@^16.8.4:
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
react-router-dom@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18"
integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
loose-envify "^1.3.1"
prop-types "^15.6.2"
react-router "5.1.2"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router@5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418"
integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
hoist-non-react-statics "^3.1.0"
loose-envify "^1.3.1"
mini-create-react-context "^0.3.0"
path-to-regexp "^1.7.0"
prop-types "^15.6.2"
react-is "^16.6.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-scripts@3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.3.1.tgz#dee7962045dbee5b02b1d47569815e62f7a546b5"
@ -8630,6 +8704,11 @@ resolve-from@^4.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve-pathname@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
resolve-url-loader@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.1.tgz#28931895fa1eab9be0647d3b2958c100ae3c0bf0"
@ -9622,6 +9701,16 @@ timsort@^0.3.0:
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
tiny-invariant@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
tiny-warning@^1.0.0, tiny-warning@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@ -9970,6 +10059,11 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
value-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"

2
src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod library;
pub mod web;

105
src/library.rs Normal file
View File

@ -0,0 +1,105 @@
use std::fs;
use std::fs::File;
use std::io;
use std::path::Path;
use std::path::PathBuf;
use google_photoslibrary1 as photos;
use log::info;
use photos::schemas::Album;
use photos::schemas::MediaItem;
#[derive(Clone)]
pub struct Library {
root: PathBuf,
originals_dir: PathBuf,
}
impl Library {
pub fn new(root: PathBuf) -> io::Result<Library> {
let lib = Library {
originals_dir: root.join("images").join("originals"),
root,
};
if !lib.originals_dir.exists() {
info!(
"create originals dir {}",
&lib.originals_dir.to_string_lossy()
);
fs::create_dir_all(&lib.originals_dir)?;
}
Ok(lib)
}
pub fn create_album_index(&self, albums: &Vec<Album>) -> io::Result<()> {
// Serialize it to a JSON string.
let j = serde_json::to_string(albums)?;
let path = self.root.join("albums.json");
info!("saving {}", path.to_string_lossy());
fs::write(path, j)
}
pub fn create_album<P: AsRef<Path>>(
&self,
album_id: P,
media_items: &Vec<MediaItem>,
) -> io::Result<()> {
let album_dir = self.root.join(album_id);
if !album_dir.exists() {
info!("making album directory {}", album_dir.to_string_lossy());
fs::create_dir_all(&album_dir)?;
}
let j = serde_json::to_string(&media_items)?;
let path = album_dir.join("album.json");
info!("saving {}", path.to_string_lossy());
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>> {
let album_path = self.root.join(album_id).join("album.json");
let bytes = fs::read(album_path)?;
Ok(serde_json::from_slice(&bytes)?)
}
pub fn download_image(
&self,
filename: &str,
media_items_id: &str,
base_url: &str,
) -> Result<PathBuf, Box<dyn std::error::Error>> {
// Put images from all albums in common directory.
let image_path = self.originals_dir.join(media_items_id);
if image_path.exists() {
info!(
"Skipping already downloaded {} @ {}",
&filename,
image_path.to_string_lossy()
);
} else {
let download_path = image_path.with_extension("download");
let url = format!("{}=d", base_url);
let mut r = reqwest::blocking::get(&url)?;
let mut w = File::create(&download_path)?;
info!("Downloading {}", &url);
let _n = io::copy(&mut r, &mut w)?;
info!(
"Rename {} -> {}",
download_path.to_string_lossy(),
image_path.to_string_lossy()
);
fs::rename(download_path, &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

@ -1,8 +1,6 @@
use std::collections::HashMap;
use std::error::Error;
use std::fs;
use std::fs::File;
use std::io;
use std::net::SocketAddr;
use std::path::PathBuf;
use google_api_auth;
@ -12,26 +10,51 @@ use lazy_static::lazy_static;
use log::{debug, info};
use photos::schemas::{Album, MediaItem, SearchMediaItemsRequest};
use regex::Regex;
use reqwest;
use structopt::StructOpt;
use yup_oauth2::{Authenticator, InstalledFlow};
use photosync::library::Library;
use photosync::web;
#[derive(Debug, StructOpt)]
enum Command {
/// List albums for the user of the given credentials. Optionally title filter.
ListAlbums {
#[structopt(flatten)]
auth: Auth,
title_filter: Option<Regex>,
},
SearchMediaItems {
#[structopt(flatten)]
auth: Auth,
album_id: String,
},
Sync {
#[structopt(flatten)]
auth: Auth,
/// Optional album title to filter. Default will mirror all albums.
#[structopt(short, long)]
title_filter: Option<Regex>,
/// Directory to store sync.
output: PathBuf,
},
Serve {
/// Directory of data fetched by `sync`.
root: PathBuf,
/// HTTP address to listen for web requests.
#[structopt(long = "addr", default_value = "0.0.0.0:0")]
addr: SocketAddr,
},
}
#[derive(Debug, StructOpt)]
struct Auth {
/// Path to json file containing Google client ID and secrets for out of band auth flow.
#[structopt(long)]
credentials: PathBuf,
/// Path to json file where photosync will store auth tokens refreshed from Google.
#[structopt(long)]
token_cache: PathBuf,
}
#[derive(Debug, StructOpt)]
@ -44,13 +67,6 @@ struct Opt {
#[structopt(short, parse(from_occurrences))]
verbose: usize,
/// Path to json file containing Google client ID and secrets for out of band auth flow.
#[structopt(long)]
credentials: PathBuf,
/// Path to json file where photosync will store auth tokens refreshed from Google.
#[structopt(long)]
token_cache: PathBuf,
#[structopt(subcommand)]
cmd: Command,
}
@ -179,67 +195,31 @@ fn sync_albums(
title_filter: Option<Regex>,
output_dir: PathBuf,
) -> Result<(), Box<dyn Error>> {
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();
let album_dir = output_dir.join(&album_id);
if !album_dir.exists() {
info!("making album directory {}", album_dir.to_string_lossy());
fs::create_dir_all(&album_dir)?;
}
let album = search_media_items(client, &album_id)?;
for (i, mi) in album.iter().enumerate() {
let media_items = search_media_items(client, &album_id)?;
lib.create_album(&album_id, &media_items)?;
for (i, mi) in media_items.iter().enumerate() {
let mi_id = mi.id.as_ref().expect("unset media item id").to_string();
let filename = mi
.filename
.as_ref()
.map_or("NO_FILENAME".to_string(), |s| s.to_string());
// Put images from all albums in common directory.
let image_path = output_dir.join("images").join(&mi_id);
if !image_path.exists() {
fs::create_dir_all(&image_path)?;
}
let image_path = image_path.join(&filename);
if image_path.exists() {
info!(
"Skipping already downloaded {} @ {}",
&filename,
image_path.to_string_lossy()
);
} else {
let download_path = image_path.with_extension("download");
info!(
"({}/{}) Downloading {} -> {}",
i + 1,
&album.len(),
&filename,
download_path.to_string_lossy()
);
let base_url = mi.base_url.as_ref().expect("missing base_url");
let url = format!("{}=d", base_url);
let mut r = reqwest::blocking::get(&url)?;
let mut w = File::create(&download_path)?;
let _n = io::copy(&mut r, &mut w)?;
info!(
"Rename {} -> {}",
download_path.to_string_lossy(),
image_path.to_string_lossy()
);
fs::rename(download_path, &image_path)?;
}
let base_url = mi.base_url.as_ref().expect("missing base_url");
let image_path = lib.download_image(&filename, &mi_id, &base_url)?;
info!(
"({}/{}) Checking {} -> {}",
i + 1,
&media_items.len(),
&filename,
image_path.to_string_lossy()
);
}
let j = serde_json::to_string(&album)?;
let path = album_dir.join("album.json");
info!("saving {}", path.to_string_lossy());
fs::write(path, j)?;
}
// 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(())
}
@ -278,6 +258,10 @@ fn list_albums(
.collect())
}
pub fn serve(addr: SocketAddr, root: PathBuf) -> Result<(), Box<dyn Error>> {
web::run(addr, root)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let opt = Opt::from_args();
stderrlog::new()
@ -286,19 +270,26 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.init()
.unwrap();
debug!("opt: {:?}", opt);
let client = new_client(&opt.credentials, &opt.token_cache)?;
match opt.cmd {
Command::ListAlbums { title_filter } => {
Command::ListAlbums { auth, title_filter } => {
let client = new_client(&auth.credentials, &auth.token_cache)?;
print_albums(list_albums(&client, title_filter)?);
Ok(())
}
Command::SearchMediaItems { album_id } => {
Command::SearchMediaItems { auth, album_id } => {
let client = new_client(&auth.credentials, &auth.token_cache)?;
print_media_items(search_media_items(&client, &album_id)?);
Ok(())
}
Command::Sync {
auth,
title_filter,
output,
} => sync_albums(&client, title_filter, output),
} => {
let client = new_client(&auth.credentials, &auth.token_cache)?;
sync_albums(&client, title_filter, output)?;
Ok(())
}
Command::Serve { addr, root } => serve(addr, root),
}
}

160
src/web.rs Normal file
View File

@ -0,0 +1,160 @@
use std::error::Error;
use std::net::SocketAddr;
use std::path::PathBuf;
use log::info;
use log::warn;
use prometheus::Encoder;
use rust_embed::RustEmbed;
use serde::Deserialize;
use warp;
use warp::http::header::{HeaderMap, HeaderValue};
use warp::reject::Rejection;
use warp::Filter;
use crate::library::Library;
fn metrics() -> impl Filter<Extract = (impl warp::reply::Reply,), Error = Rejection> + Clone {
let mut text_headers = HeaderMap::new();
text_headers.insert("content-type", HeaderValue::from_static("text/plain"));
warp::path("metrics")
.map(|| {
let mut buffer = Vec::new();
let encoder = prometheus::TextEncoder::new();
// Gather the metrics.
let metric_families = prometheus::gather();
// Encode them to send.
encoder.encode(&metric_families, &mut buffer).unwrap();
// TODO(wathiede): see if there's a wrapper like html()
buffer
})
.with(warp::reply::with::headers(text_headers))
}
fn index(path: warp::path::FullPath) -> Result<impl warp::Reply, warp::Rejection> {
let path = path.as_str();
let path = if path.ends_with("/") {
format!("{}index.html", path.to_string())
} else {
path.to_string()
};
let path = &path[1..];
match Asset::get(path) {
Some(bytes) => {
let mime = mime_guess::from_path(path).first_or_octet_stream();
Ok(warp::http::Response::builder()
.header("Content-Type", mime.essence_str())
.header("Content-Length", bytes.len())
.body(bytes.into_owned()))
}
None => Err(warp::reject::not_found()),
}
}
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> {
let album = lib.album(&id).map_err(|e| {
warn!("Couldn't find album {}: {}", id, e);
warp::reject::not_found()
})?;
Ok(warp::reply::json(&album))
}
#[derive(Debug, Deserialize)]
struct ImageParams {
w: Option<u32>,
h: Option<u32>,
c: Option<bool>,
}
fn image(
lib: Library,
media_items_id: String,
params: ImageParams,
) -> Result<impl warp::Reply, warp::Rejection> {
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)
}
}
}
#[derive(RustEmbed)]
#[folder = "react-debug/build/"]
struct Asset;
pub fn run(addr: SocketAddr, root: PathBuf) -> Result<(), Box<dyn Error>> {
let lib = Library::new(root)?;
let lib = warp::any().map(move || lib.clone());
let index = warp::get2().and(warp::path::full()).and_then(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::<ImageParams>())
.and_then(image);
let api = albums.or(album).or(image);
let api = warp::path("api").and(api);
// Fallback, always keep this last.
let api = api.or(index);
let api = api.with(warp::log("photosync"));
// We don't want metrics & heath checking filling up the logs, so we add this handler after
// wrapping with the log filter.
let routes = metrics().or(api);
warp::serve(routes).run(addr);
Ok(())
}