album page rendering
This commit is contained in:
parent
f1c007845a
commit
5e54b84f04
14 changed files with 554 additions and 327 deletions
279
Cargo.lock
generated
279
Cargo.lock
generated
|
@ -155,6 +155,25 @@ version = "2.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.7.7"
|
||||
|
@ -258,6 +277,15 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
|
@ -298,6 +326,26 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
|
@ -367,6 +415,16 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
|
@ -400,6 +458,30 @@ dependencies = [
|
|||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
"log",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globwalk"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"ignore",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.6.0"
|
||||
|
@ -422,6 +504,22 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"globset",
|
||||
"log",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"same-file",
|
||||
"walkdir",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.6"
|
||||
|
@ -543,6 +641,12 @@ version = "0.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
|
@ -715,6 +819,51 @@ version = "1.0.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.12",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "photojawn"
|
||||
version = "0.2.0"
|
||||
|
@ -727,6 +876,7 @@ dependencies = [
|
|||
"mktemp",
|
||||
"serde",
|
||||
"serde_yml",
|
||||
"tera",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
|
@ -978,6 +1128,15 @@ version = "1.0.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[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 = "serde"
|
||||
version = "1.0.219"
|
||||
|
@ -998,6 +1157,18 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
|
@ -1022,6 +1193,17 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
@ -1085,6 +1267,22 @@ version = "0.12.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tera"
|
||||
version = "1.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee"
|
||||
dependencies = [
|
||||
"globwalk",
|
||||
"lazy_static",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"unic-segment",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
|
@ -1170,6 +1368,68 @@ dependencies = [
|
|||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||
|
||||
[[package]]
|
||||
name = "unic-char-property"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
|
||||
dependencies = [
|
||||
"unic-char-range",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-char-range"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
|
||||
|
||||
[[package]]
|
||||
name = "unic-common"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
|
||||
|
||||
[[package]]
|
||||
name = "unic-segment"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"
|
||||
dependencies = [
|
||||
"unic-ucd-segment",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-ucd-segment"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"
|
||||
dependencies = [
|
||||
"unic-char-property",
|
||||
"unic-char-range",
|
||||
"unic-ucd-version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-ucd-version"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
|
||||
dependencies = [
|
||||
"unic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
|
@ -1214,6 +1474,16 @@ version = "0.9.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
@ -1293,6 +1563,15 @@ version = "0.1.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
|
|
@ -15,6 +15,7 @@ image = "^0.25.6"
|
|||
log = "^0.4.27"
|
||||
serde = { version = "^1.0", features = ["derive"] }
|
||||
serde_yml = "^0.0.12"
|
||||
tera = { version = "^1.20", default-features = false }
|
||||
thiserror = "^2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
2
Makefile
2
Makefile
|
@ -24,7 +24,7 @@ lint:
|
|||
cargo clippy
|
||||
|
||||
test:
|
||||
RUST_BACKTRACE=1 cargo test
|
||||
RUST_BACKTRACE=1 cargo test --release
|
||||
|
||||
|
||||
test-watch:
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% if not album_dir.is_root %}
|
||||
{% if root_path %}
|
||||
<h1>
|
||||
<a href="{{root_path}}">Home</a>
|
||||
{% for href, name in breadcrumbs %}
|
||||
/ <a href="{{href}}">{{name}}</a>
|
||||
{% for crumb in breadcrumbs %}
|
||||
/ <a href="{{crumb.path}}">{{crumb.name}}</a>
|
||||
{% endfor %}
|
||||
/ {{album_dir.path.name}}
|
||||
/ {{name}}
|
||||
</h1>
|
||||
<hr>
|
||||
{% endif %}
|
||||
|
@ -18,20 +18,18 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if album_dir.children %}
|
||||
{% if children %}
|
||||
<h2>Albums</h2>
|
||||
<div id="album-children">
|
||||
{% for child in album_dir.children %}
|
||||
{% for child in children %}
|
||||
<div class="album">
|
||||
<a href="{{child.path.name}}/">
|
||||
<a href="{{child.name}}/">
|
||||
<div>
|
||||
{% if child.cover_path %}
|
||||
<img
|
||||
src="{{child.cover_path.path.parent.relative_to(album_dir.path)}}/slides/{{child.cover_path.thumbnail_filename()}}" />
|
||||
{% endif %}
|
||||
src="{{child.cover_thumbnail_path}}" />
|
||||
</div>
|
||||
<div>
|
||||
{{child.path.name}}
|
||||
{{child.name}}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -39,14 +37,14 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
{% if album_dir.images %}
|
||||
{% if album_dir.children %}
|
||||
{% if children %}
|
||||
<h2>Photos</h2>
|
||||
{% endif %}
|
||||
<div id="album-photos">
|
||||
{% for image in album_dir.images %}
|
||||
<div class="thumbnail">
|
||||
<a href="slides/{{image.html_filename()}}">
|
||||
<img src="slides/{{image.thumbnail_filename()}}" />
|
||||
<a href="slides/{{image.html_filename}}">
|
||||
<img src="slides/{{image.thumb_filename}}" />
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
document.onkeydown = function(event) {
|
||||
if (event.key == "ArrowLeft") {
|
||||
{% if prev_image %}
|
||||
location.href = "{{prev_image.html_filename()}}";
|
||||
location.href = "{{prev_image.html_path}}";
|
||||
{% endif %}
|
||||
} else if (event.key == "ArrowRight") {
|
||||
{% if next_image %}
|
||||
location.href = "{{next_image.html_filename()}}";
|
||||
location.href = "{{next_image.html_path}}";
|
||||
{% endif %}
|
||||
}
|
||||
}
|
||||
|
@ -17,13 +17,13 @@
|
|||
|
||||
{% block content %}
|
||||
<div id="photo">
|
||||
<img src="{{image_path.display_filename()}}" />
|
||||
<img src="{{image_path.screen_path}}" />
|
||||
</div>
|
||||
|
||||
<div id="nav">
|
||||
<div>
|
||||
{% if prev_image %}
|
||||
<a href="{{prev_image.html_filename()}}">
|
||||
<a href="{{prev_image.html_path}}">
|
||||
<i class="arrow arrow-left"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
@ -35,7 +35,7 @@
|
|||
</div>
|
||||
<div>
|
||||
{% if next_image %}
|
||||
<a href="{{next_image.html_filename()}}">
|
||||
<a href="{{next_image.html_path}}">
|
||||
<i class="arrow arrow-right"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #ba1200;
|
||||
}
|
||||
|
||||
#header {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#header * {
|
||||
margin-top: 0;
|
||||
text-decoration: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
#content {
|
||||
text-align: center;
|
||||
max-width: 1200px;
|
||||
margin: 0.5em;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#content > * {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
#album-children {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#album-children > * {
|
||||
margin: 1em;
|
||||
padding: 0.75em;
|
||||
background-color: lightgrey;
|
||||
height: min-content;
|
||||
border: thin solid black;
|
||||
box-shadow: 0.25em 0.25em #ccc;
|
||||
}
|
||||
|
||||
#album-photos {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
margin: 1em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.thumbnail * {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#nav {
|
||||
width: 150px;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
#nav div {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#photo img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.caption {
|
||||
max-width: 700px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
border: solid black;
|
||||
border-width: 0 3px 3px 0;
|
||||
display: inline-block;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.arrow-right {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.arrow-left {
|
||||
transform: rotate(135deg);
|
||||
-webkit-transform: rotate(135deg);
|
||||
}
|
||||
|
||||
.arrow-up {
|
||||
transform: rotate(-135deg);
|
||||
-webkit-transform: rotate(-135deg);
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% if not album_dir.is_root %}
|
||||
<h1>
|
||||
<a href="{{root_path}}">Home</a>
|
||||
{% for href, name in breadcrumbs %}
|
||||
/ <a href="{{href}}">{{name}}</a>
|
||||
{% endfor %}
|
||||
/ {{album_dir.path.name}}
|
||||
</h1>
|
||||
<hr>
|
||||
{% endif %}
|
||||
|
||||
{% if album_dir.description %}
|
||||
<div class="caption">
|
||||
{{ album_dir.description | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if album_dir.children %}
|
||||
<h2>Albums</h2>
|
||||
<div id="album-children">
|
||||
{% for child in album_dir.children %}
|
||||
<div class="album">
|
||||
<a href="{{child.path.name}}/">
|
||||
<div>
|
||||
{% if child.cover_path %}
|
||||
<img
|
||||
src="{{child.cover_path.path.parent.relative_to(album_dir.path)}}/slides/{{child.cover_path.thumbnail_filename()}}" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{{child.path.name}}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if album_dir.images %}
|
||||
{% if album_dir.children %}
|
||||
<h2>Photos</h2>
|
||||
{% endif %}
|
||||
<div id="album-photos">
|
||||
{% for image in album_dir.images %}
|
||||
<div class="thumbnail">
|
||||
<a href="slides/{{image.html_filename()}}">
|
||||
<img src="slides/{{image.thumbnail_filename()}}" />
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -1,24 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>My Photos</title>
|
||||
<link rel="stylesheet" href="{{ root_path }}/static/index.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<a href="{{ root_path }}">
|
||||
<h1>My Photos</h1>
|
||||
</a>
|
||||
</div>
|
||||
<div id="content">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
{% block js %}
|
||||
{% endblock %}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,54 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block js %}
|
||||
// Do left/right navigation on keypresses
|
||||
document.onkeydown = function(event) {
|
||||
if (event.key == "ArrowLeft") {
|
||||
{% if prev_image %}
|
||||
location.href = "{{prev_image.html_filename()}}";
|
||||
{% endif %}
|
||||
} else if (event.key == "ArrowRight") {
|
||||
{% if next_image %}
|
||||
location.href = "{{next_image.html_filename()}}";
|
||||
{% endif %}
|
||||
}
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="photo">
|
||||
<img src="{{image_path.display_filename()}}" />
|
||||
</div>
|
||||
|
||||
<div id="nav">
|
||||
<div>
|
||||
{% if prev_image %}
|
||||
<a href="{{prev_image.html_filename()}}">
|
||||
<i class="arrow arrow-left"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<a href="..">
|
||||
<i class="arrow arrow-up"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
{% if next_image %}
|
||||
<a href="{{next_image.html_filename()}}">
|
||||
<i class="arrow arrow-right"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="photo-description" class="caption">
|
||||
{% if image_path.description %}
|
||||
{{ image_path.description | safe }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="download">
|
||||
<a href="../{{image_path.path.name}}">view full size</a>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,5 +0,0 @@
|
|||
# Max size of thumbnails when viewing an album
|
||||
thumbnail_size: [64, 64]
|
||||
|
||||
# Max size of images when viewing a single one on screen
|
||||
view_size: [640, 380]
|
|
@ -1,9 +1,9 @@
|
|||
use anyhow::Context;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct Config {
|
||||
/// Tuple of how big thumbnails should be - (width, height)
|
||||
|
|
163
src/generate.rs
163
src/generate.rs
|
@ -1,14 +1,116 @@
|
|||
mod album_dir;
|
||||
pub mod album_dir;
|
||||
|
||||
use crate::config::Config;
|
||||
pub use album_dir::AlbumDir;
|
||||
use album_dir::{AlbumDir, Image};
|
||||
use anyhow::anyhow;
|
||||
use image::imageops::FilterType;
|
||||
use serde::Serialize;
|
||||
use std::collections::VecDeque;
|
||||
use std::env;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tera::Tera;
|
||||
|
||||
const IMG_RESIZE_FILTER: FilterType = FilterType::Lanczos3;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
struct Breadcrumb {
|
||||
path: PathBuf,
|
||||
name: OsString,
|
||||
}
|
||||
|
||||
/// A Tera context for album pages
|
||||
#[derive(Serialize, Debug)]
|
||||
struct AlbumContext {
|
||||
// Path required to get back to the root album
|
||||
root_path: PathBuf,
|
||||
|
||||
// TODO: Do we actualy need the whole albumDir? Probably better off pulling data we need out of
|
||||
// it.
|
||||
// album_dir: AlbumDir,
|
||||
name: OsString,
|
||||
|
||||
// TODO: images
|
||||
|
||||
// Path to the cover image thumbnail within /slides/. Used when linking to an album from a
|
||||
// parent album
|
||||
cover_thumbnail_path: PathBuf,
|
||||
|
||||
// list of:
|
||||
// - relative dir to walk up to root, e.g. "../../.."
|
||||
// - dir name
|
||||
breadcrumbs: Vec<Breadcrumb>,
|
||||
|
||||
// Immediate children of this album
|
||||
children: Vec<AlbumContext>,
|
||||
}
|
||||
|
||||
impl TryFrom<&AlbumDir> for AlbumContext {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(album: &AlbumDir) -> anyhow::Result<Self> {
|
||||
let name: OsString = match album.path.file_name() {
|
||||
Some(n) => n.to_os_string(),
|
||||
None => OsString::new(),
|
||||
};
|
||||
|
||||
// Build breadcrumbs
|
||||
let mut breadcrumbs: Vec<Breadcrumb> = Vec::new();
|
||||
{
|
||||
let mut album_path = album.path.clone();
|
||||
let mut relpath: PathBuf = PathBuf::new();
|
||||
while album_path.pop() {
|
||||
let filename: &OsStr = album_path.file_name().unwrap_or(OsStr::new("Home"));
|
||||
relpath.push("..");
|
||||
breadcrumbs.push(Breadcrumb {
|
||||
path: relpath.clone(),
|
||||
name: filename.into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
breadcrumbs.reverse();
|
||||
log::debug!("Crumbs for {}: {breadcrumbs:?}", album.path.display());
|
||||
|
||||
// The first breadcrumb path is the relative path to the root album
|
||||
let root_path = if breadcrumbs.len() > 0 {
|
||||
breadcrumbs[0].path.clone()
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
// Pick a cover image thumbnail and build a path to it
|
||||
let cover_thumbnail_path = match &album.cover {
|
||||
Some(i) => i.path.clone(),
|
||||
None => PathBuf::from(""),
|
||||
};
|
||||
|
||||
let children: Vec<AlbumContext> = album
|
||||
.children
|
||||
.iter()
|
||||
.map(|a| AlbumContext::try_from(a))
|
||||
.collect::<anyhow::Result<Vec<AlbumContext>>>()?;
|
||||
|
||||
Ok(AlbumContext {
|
||||
name,
|
||||
breadcrumbs,
|
||||
root_path,
|
||||
children,
|
||||
cover_thumbnail_path,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A Tera context for slide (individual image) pages
|
||||
#[derive(Serialize)]
|
||||
struct SlideContext {
|
||||
// TODO: Path or String?
|
||||
root_path: PathBuf,
|
||||
image: Image,
|
||||
prev_image: Image,
|
||||
next_image: Image,
|
||||
}
|
||||
|
||||
pub fn generate(root_path: &PathBuf) -> anyhow::Result<PathBuf> {
|
||||
log::debug!("Generating album in {}", root_path.display());
|
||||
let config = Config::from_album(root_path.to_path_buf())?;
|
||||
|
@ -18,6 +120,7 @@ pub fn generate(root_path: &PathBuf) -> anyhow::Result<PathBuf> {
|
|||
env::set_current_dir(root_path)?;
|
||||
let album = AlbumDir::try_from(root_path)?;
|
||||
|
||||
copy_static(&config)?;
|
||||
generate_images(&config, &album)?;
|
||||
generate_html(&config, &album)?;
|
||||
|
||||
|
@ -25,6 +128,10 @@ pub fn generate(root_path: &PathBuf) -> anyhow::Result<PathBuf> {
|
|||
Ok(root_path.join(config.output_dir))
|
||||
}
|
||||
|
||||
fn copy_static(config: &Config) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_images(config: &Config, album: &AlbumDir) -> anyhow::Result<()> {
|
||||
let output_path = album.path.join(&config.output_dir);
|
||||
// TODO: use par_iter() ?
|
||||
|
@ -32,7 +139,12 @@ fn generate_images(config: &Config, album: &AlbumDir) -> anyhow::Result<()> {
|
|||
for img in album.iter() {
|
||||
let orig_image = image::open(&img.path)?;
|
||||
|
||||
let thumb_path = output_path.join(img.thumb_path()?);
|
||||
let thumb_path = output_path.join(&img.thumb_path);
|
||||
log::info!(
|
||||
"Resizing {} -> {}",
|
||||
img.path.display(),
|
||||
thumb_path.display()
|
||||
);
|
||||
fs::create_dir_all(thumb_path.parent().unwrap_or(Path::new("")))?;
|
||||
orig_image
|
||||
.resize(
|
||||
|
@ -41,23 +153,54 @@ fn generate_images(config: &Config, album: &AlbumDir) -> anyhow::Result<()> {
|
|||
IMG_RESIZE_FILTER,
|
||||
)
|
||||
.save(&thumb_path)?;
|
||||
log::info!("Resized {} -> {}", img.path.display(), thumb_path.display());
|
||||
|
||||
// TODO: resize to screen size
|
||||
let screen_path = output_path.join(img.screen_path()?);
|
||||
let screen_path = output_path.join(&img.screen_path);
|
||||
log::info!(
|
||||
"Resizing {} -> {}",
|
||||
img.path.display(),
|
||||
screen_path.display()
|
||||
);
|
||||
fs::create_dir_all(thumb_path.parent().unwrap_or(Path::new("")))?;
|
||||
orig_image
|
||||
.resize(config.view_size.0, config.view_size.1, IMG_RESIZE_FILTER)
|
||||
.save(&screen_path)?;
|
||||
log::info!(
|
||||
"Resized {} -> {}",
|
||||
img.path.display(),
|
||||
screen_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_html(config: &Config, album: &AlbumDir) -> anyhow::Result<()> {
|
||||
let output_path = album.path.join(&config.output_dir);
|
||||
let tera = Tera::new(
|
||||
album
|
||||
.path
|
||||
.join("_templates/*.html")
|
||||
.to_str()
|
||||
.ok_or(anyhow!("Missing _templates dir in album dir"))?,
|
||||
)?;
|
||||
|
||||
// Queue of album dir and depth (distance from root AlbumDir)
|
||||
let mut dir_queue: VecDeque<&AlbumDir> = VecDeque::from([album]);
|
||||
while let Some(album) = dir_queue.pop_front() {
|
||||
// TODO: create AlbumContext from AlbumDir - TryFrom<&AlbumDir> ?
|
||||
// TODO: make a function to figure out the cover image and cover_thumbnail_path for each
|
||||
// albumdir
|
||||
// TODO: Move breadcrumb generation into From thing?
|
||||
|
||||
let html_path = output_path.join(&album.path).join("index.html");
|
||||
log::info!("Rendering album {}", html_path.display());
|
||||
let ctx = AlbumContext::try_from(album)?;
|
||||
log::debug!("Album context: {ctx:?}");
|
||||
fs::write(
|
||||
output_path.join(&album.path).join("index.html"),
|
||||
tera.render("album.html", &tera::Context::from_serialize(&ctx)?)?,
|
||||
)?;
|
||||
|
||||
for child in album.children.iter() {
|
||||
dir_queue.push_back(&child);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::anyhow;
|
||||
use image::ImageReader;
|
||||
use serde::Serialize;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::slice::Iter;
|
||||
|
@ -10,6 +11,7 @@ use std::slice::Iter;
|
|||
pub struct AlbumDir {
|
||||
pub path: PathBuf,
|
||||
pub images: Vec<Image>,
|
||||
pub cover: Option<Image>,
|
||||
// TOOD: Remove the parent reference? Causes a lot of issues
|
||||
// parent: Option<Box<&'a AlbumDir>>,
|
||||
pub children: Vec<AlbumDir>,
|
||||
|
@ -27,6 +29,7 @@ impl AlbumDir {
|
|||
/// relative to the root.
|
||||
fn from_path(p: &Path, root: &Path) -> anyhow::Result<Self> {
|
||||
let mut images = vec![];
|
||||
let mut cover: Option<Image> = None;
|
||||
let mut children = vec![];
|
||||
let mut description = String::new();
|
||||
|
||||
|
@ -41,6 +44,11 @@ impl AlbumDir {
|
|||
let _conents = fs::read_to_string(entry_path)?;
|
||||
// TODO: render markdown
|
||||
todo!();
|
||||
} else if filename.to_string_lossy().starts_with("cover") {
|
||||
cover = Some(Image::new(
|
||||
entry_path.strip_prefix(&root)?.to_path_buf(),
|
||||
String::new(),
|
||||
)?);
|
||||
} else {
|
||||
let reader = ImageReader::open(&entry_path)?.with_guessed_format()?;
|
||||
if reader.format().is_some() {
|
||||
|
@ -56,10 +64,10 @@ impl AlbumDir {
|
|||
todo!();
|
||||
}
|
||||
|
||||
images.push(Image {
|
||||
path: entry_path.strip_prefix(&root)?.to_path_buf(),
|
||||
images.push(Image::new(
|
||||
entry_path.strip_prefix(&root)?.to_path_buf(),
|
||||
description,
|
||||
});
|
||||
)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,11 +86,14 @@ impl AlbumDir {
|
|||
}
|
||||
}
|
||||
|
||||
// Find all directories in directory and make AlbumDirs out of them,
|
||||
// but skip dirs known to have interesting stuff
|
||||
if cover.is_none() && images.len() > 0 {
|
||||
cover = Some(images[0].clone());
|
||||
}
|
||||
|
||||
Ok(AlbumDir {
|
||||
path: p.strip_prefix(root)?.to_path_buf(),
|
||||
images,
|
||||
cover,
|
||||
children,
|
||||
description,
|
||||
})
|
||||
|
@ -141,40 +152,85 @@ pub struct Image {
|
|||
|
||||
/// Text description of the image which is displayed below it on the HTML page
|
||||
pub description: String,
|
||||
|
||||
pub thumb_filename: OsString,
|
||||
pub thumb_path: PathBuf,
|
||||
pub screen_filename: OsString,
|
||||
pub screen_path: PathBuf,
|
||||
pub html_filename: OsString,
|
||||
pub html_path: PathBuf,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
pub fn thumb_path(&self) -> anyhow::Result<PathBuf> {
|
||||
self.slide_path("thumb")
|
||||
pub fn new(path: PathBuf, description: String) -> anyhow::Result<Self> {
|
||||
let thumb_filename = Self::slide_filename(&path, "thumb", true)?;
|
||||
let thumb_path = Self::slide_path(&path, "thumb")?;
|
||||
let screen_filename = Self::slide_filename(&path, "screen", true)?;
|
||||
let screen_path = Self::slide_path(&path, "screen")?;
|
||||
// TODO: add "slides" in html path?
|
||||
let html_filename = Self::slide_filename(&path, "html", false)?;
|
||||
let html_path = path.with_extension("html");
|
||||
|
||||
Ok(Image {
|
||||
path,
|
||||
description,
|
||||
thumb_filename,
|
||||
thumb_path,
|
||||
screen_filename,
|
||||
screen_path,
|
||||
html_filename,
|
||||
html_path,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn screen_path(&self) -> anyhow::Result<PathBuf> {
|
||||
self.slide_path("screen")
|
||||
/// Returns the filename for a given slide type. For example if ext = "thumb" and the current
|
||||
/// filename is "blah.jpg" this will return "blah.thumb.jpg". If keep_ext if false, it would
|
||||
/// return "blah.thumb"
|
||||
fn slide_filename(path: &PathBuf, ext: &str, keep_ext: bool) -> anyhow::Result<OsString> {
|
||||
let mut new_ext: OsString = ext.into();
|
||||
if keep_ext {
|
||||
if let Some(e) = path.extension() {
|
||||
new_ext = OsString::from(
|
||||
ext.to_string()
|
||||
+ "."
|
||||
+ e.to_str().ok_or(anyhow!(
|
||||
"Image {} extension is not valid UTF-8",
|
||||
path.display()
|
||||
))?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let new_path = path.with_extension(new_ext);
|
||||
let new_name = new_path
|
||||
.file_name()
|
||||
.ok_or(anyhow!("Image {} missing a file name", path.display()))?;
|
||||
|
||||
Ok(new_name.into())
|
||||
}
|
||||
|
||||
/// Returns the path to the file in the slides dir with the given extention insert, e.g.
|
||||
/// "thumb" or "display"
|
||||
fn slide_path(&self, ext: &str) -> anyhow::Result<PathBuf> {
|
||||
let new_ext = match self.path.extension() {
|
||||
fn slide_path(path: &PathBuf, ext: &str) -> anyhow::Result<PathBuf> {
|
||||
let new_ext = match path.extension() {
|
||||
Some(e) => {
|
||||
ext.to_string()
|
||||
+ "."
|
||||
+ e.to_str().ok_or(anyhow!(
|
||||
"Image {} extension is not valid UTF-8",
|
||||
self.path.display()
|
||||
path.display()
|
||||
))?
|
||||
}
|
||||
None => ext.to_string(),
|
||||
};
|
||||
|
||||
let new_path = self.path.with_extension(new_ext);
|
||||
let new_path = path.with_extension(new_ext);
|
||||
let new_name = new_path
|
||||
.file_name()
|
||||
.ok_or(anyhow!("Image {} missing a file name", self.path.display()))?;
|
||||
let parent = self
|
||||
.path
|
||||
.ok_or(anyhow!("Image {} missing a file name", path.display()))?;
|
||||
let parent = path
|
||||
.parent()
|
||||
.ok_or(anyhow!("Image {} has no parent dir", self.path.display()))?;
|
||||
.ok_or(anyhow!("Image {} has no parent dir", path.display()))?;
|
||||
|
||||
Ok(parent.join("slides").join(new_name))
|
||||
}
|
||||
|
@ -190,15 +246,10 @@ mod tests {
|
|||
let mut ad = AlbumDir {
|
||||
path: "".into(),
|
||||
description: "".to_string(),
|
||||
cover: Some(Image::new("foo".into(), "".to_string()).unwrap()),
|
||||
images: vec![
|
||||
Image {
|
||||
path: "foo".into(),
|
||||
description: "".to_string(),
|
||||
},
|
||||
Image {
|
||||
path: "bar".into(),
|
||||
description: "".to_string(),
|
||||
},
|
||||
Image::new("foo".into(), "".to_string()).unwrap(),
|
||||
Image::new("bar".into(), "".to_string()).unwrap(),
|
||||
],
|
||||
children: vec![],
|
||||
};
|
||||
|
@ -206,29 +257,27 @@ mod tests {
|
|||
ad.children.push(AlbumDir {
|
||||
path: "subdir".into(),
|
||||
description: "".to_string(),
|
||||
cover: Some(Image::new("subdir/foo".into(), "".to_string()).unwrap()),
|
||||
images: vec![
|
||||
Image {
|
||||
path: "subdir/foo".into(),
|
||||
description: "".to_string(),
|
||||
},
|
||||
Image {
|
||||
path: "subdir/bar".into(),
|
||||
description: "".to_string(),
|
||||
},
|
||||
Image::new("subdir/foo".into(), "".to_string()).unwrap(),
|
||||
Image::new("subdir/bar".into(), "".to_string()).unwrap(),
|
||||
],
|
||||
children: vec![AlbumDir {
|
||||
path: "subdir/deeper_subdir".into(),
|
||||
description: "".to_string(),
|
||||
images: vec![Image {
|
||||
path: "subdir/deeper_subdir/image.jpg".into(),
|
||||
description: "".to_string(),
|
||||
}],
|
||||
cover: Some(
|
||||
Image::new("subdir/deeper_subdir/image.jpg".into(), "".to_string()).unwrap(),
|
||||
),
|
||||
images: vec![
|
||||
Image::new("subdir/deeper_subdir/image.jpg".into(), String::new()).unwrap(),
|
||||
],
|
||||
children: vec![],
|
||||
}],
|
||||
});
|
||||
// A child album with no images
|
||||
ad.children.push(AlbumDir {
|
||||
description: "".to_string(),
|
||||
cover: None,
|
||||
path: "another_subdir".into(),
|
||||
images: vec![],
|
||||
children: vec![],
|
||||
|
@ -247,16 +296,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn image_paths() {
|
||||
let img = Image {
|
||||
path: PathBuf::from("foo/bar/image.jpg"),
|
||||
description: String::new(),
|
||||
};
|
||||
let img = Image::new(PathBuf::from("foo/bar/image.jpg"), String::new()).unwrap();
|
||||
assert_eq!(
|
||||
img.thumb_path().unwrap(),
|
||||
img.thumb_path,
|
||||
PathBuf::from("foo/bar/slides/image.thumb.jpg")
|
||||
);
|
||||
assert_eq!(
|
||||
img.screen_path().unwrap(),
|
||||
img.screen_path,
|
||||
PathBuf::from("foo/bar/slides/image.screen.jpg")
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
// orrrr make a function in our CLI which does everything and test _that_
|
||||
use mktemp::Temp;
|
||||
use photojawn::generate::generate;
|
||||
use std::collections::VecDeque;
|
||||
use photojawn::skel::make_skeleton;
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
@ -25,6 +26,8 @@ fn make_test_album() -> Temp {
|
|||
let tmpdir = Temp::new_dir().unwrap();
|
||||
let source_path = Path::new("resources/test_album");
|
||||
|
||||
make_skeleton(&tmpdir.to_path_buf()).unwrap();
|
||||
|
||||
let mut dirs: VecDeque<PathBuf> = VecDeque::from([source_path.to_path_buf()]);
|
||||
while let Some(dir) = dirs.pop_front() {
|
||||
for entry in dir.read_dir().unwrap() {
|
||||
|
@ -46,7 +49,10 @@ fn make_test_album() -> Temp {
|
|||
|
||||
/// Does basic sanity checks on an output album
|
||||
fn check_album(album_dir: PathBuf) -> anyhow::Result<()> {
|
||||
log::debug!("Checking dir {}", album_dir.display());
|
||||
log::debug!("Checking album dir {}", album_dir.display());
|
||||
|
||||
// The _static dir should have gotten copied into <output>/static
|
||||
assert!(album_dir.join("static/index.css").exists());
|
||||
|
||||
let mut dirs: VecDeque<PathBuf> = VecDeque::from([album_dir]);
|
||||
while let Some(dir) = dirs.pop_front() {
|
||||
|
@ -92,6 +98,10 @@ fn check_album(album_dir: PathBuf) -> anyhow::Result<()> {
|
|||
let slides_path = path.join("slides");
|
||||
assert!(slides_path.is_dir());
|
||||
|
||||
// No two images should have the same path
|
||||
let image_set: HashSet<&PathBuf> = files.iter().collect();
|
||||
assert_eq!(image_set.len(), files.len());
|
||||
|
||||
// For each image in the album (including the cover), in slides there should be a:
|
||||
// - <image>.html
|
||||
// - <image>.screen.<ext>
|
||||
|
|
Loading…
Add table
Reference in a new issue