summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-24 11:16:07 +0300
committerPaul Buetow <paul@buetow.org>2026-06-24 11:16:07 +0300
commit3b0ac59ded124ad8c8038ef6b1b6094a495a04c2 (patch)
tree0432c50200c2323a48b35645b8ae271109780697
parent928f28d0c51ca47f9958fda4c4bb7c8b0c571e34 (diff)
Split album-render.source.sh along its four seams (ar0)
album-render.source.sh bundled four concerns that change for unrelated reasons. Extract three focused siblings, leaving album-render as the page orchestrator: - album-tile-layout.source.sh tile_layout_for, build_tile_block, build_subdivided_tile - album-thumbnail-html.source.sh build_preview_thumbnail, append_preview_grid - album-photo-select.source.sh album_photo_files, album_page_records, splash_photo_files, random_splash_photo, randomphoto album-render.source.sh keeps page assembly, the per-photo view/details pages, navigation redirects, index/splash, and the job_pool_* plumbing. Every function moved whole with no body/signature change. The album_view_page_for_photo accessor and its private ALBUM_VIEW_PAGE_BY_PHOTO map stay in album-render so the stats mini-album boundary is unchanged. LIB_SOURCES (Justfile + src/shuriken.sh) sources the three new modules before album-render. Regenerated bin/shuriken. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-rw-r--r--Justfile2
-rwxr-xr-xbin/shuriken682
-rw-r--r--src/lib/album-photo-select.source.sh111
-rw-r--r--src/lib/album-render.source.sh333
-rw-r--r--src/lib/album-thumbnail-html.source.sh106
-rw-r--r--src/lib/album-tile-layout.source.sh140
-rwxr-xr-xsrc/shuriken.sh6
7 files changed, 742 insertions, 638 deletions
diff --git a/Justfile b/Justfile
index 59c7a1a..e802e74 100644
--- a/Justfile
+++ b/Justfile
@@ -7,7 +7,7 @@ PREFIX := env_var_or_default("PREFIX", "/usr")
BINDIR := env_var_or_default("BINDIR", PREFIX + "/bin")
DATADIR := env_var_or_default("DATADIR", PREFIX + "/share")
SYSCONFDIR := env_var_or_default("SYSCONFDIR", "/etc/default")
-LIB_SOURCES := "src/lib/logging.source.sh src/lib/bootstrap.source.sh src/lib/paths.source.sh src/lib/imagemagick.source.sh src/lib/process.source.sh src/lib/archive.source.sh src/lib/template.source.sh src/lib/job-pool.source.sh src/lib/image.source.sh src/lib/random.source.sh src/lib/metadata-label.source.sh src/lib/metadata-cache.source.sh src/lib/image-pipeline.source.sh src/lib/album-metadata.source.sh src/lib/generation-metadata.source.sh src/lib/dry-run.source.sh src/lib/album-render.source.sh src/lib/album.source.sh src/lib/stats-aggregate.source.sh src/lib/stats-render.source.sh src/lib/stats-filter-album.source.sh src/lib/config.source.sh src/lib/config.print.source.sh src/lib/config.sync.source.sh src/lib/config.staging.source.sh src/lib/config.validate.source.sh src/lib/config.cli.source.sh src/lib/action.source.sh"
+LIB_SOURCES := "src/lib/logging.source.sh src/lib/bootstrap.source.sh src/lib/paths.source.sh src/lib/imagemagick.source.sh src/lib/process.source.sh src/lib/archive.source.sh src/lib/template.source.sh src/lib/job-pool.source.sh src/lib/image.source.sh src/lib/random.source.sh src/lib/metadata-label.source.sh src/lib/metadata-cache.source.sh src/lib/image-pipeline.source.sh src/lib/album-metadata.source.sh src/lib/generation-metadata.source.sh src/lib/dry-run.source.sh src/lib/album-tile-layout.source.sh src/lib/album-thumbnail-html.source.sh src/lib/album-photo-select.source.sh src/lib/album-render.source.sh src/lib/album.source.sh src/lib/stats-aggregate.source.sh src/lib/stats-render.source.sh src/lib/stats-filter-album.source.sh src/lib/config.source.sh src/lib/config.print.source.sh src/lib/config.sync.source.sh src/lib/config.staging.source.sh src/lib/config.validate.source.sh src/lib/config.cli.source.sh src/lib/action.source.sh"
default: build
diff --git a/bin/shuriken b/bin/shuriken
index c668b2e..512806b 100755
--- a/bin/shuriken
+++ b/bin/shuriken
@@ -2491,228 +2491,20 @@ print_dry_run_plan() {
fi
}
-# Inlined from src/lib/album-render.source.sh
-# Maps each album photo filename to its view-page basename ("<page>-<preview>")
-# as assigned during render_album_pages. Declared globally so it always exists
-# for the accessor even when no album was rendered.
+# Inlined from src/lib/album-tile-layout.source.sh
+# Album thumbnail-grid tile layout / subdivision. Split out of
+# album-render.source.sh (task ar0) so the "how do consecutive thumbnails get
+# grouped into tiles" concern (feature 2x2 hero tiles, subdivided multi-thumb
+# tiles, plain squares) lives apart from the page orchestration and the raw
+# thumbnail HTML. These deciders change for visual/CSS reasons, independent of
+# the job plumbing or the photo-selection policy.
#
-# This is the album module's PRIVATE backing store (task pn0). Outside callers
-# must NOT index it directly: use the album_view_page_for_photo accessor below.
-# Keeping the map private behind a documented function decouples consumers (the
-# stats filter mini-albums) from how the album internally names or caches view
-# pages, so a change to the page-naming scheme stays contained in this module.
-declare -gA ALBUM_VIEW_PAGE_BY_PHOTO=()
-
-# Public album API (task pn0): return the view-page basename for a photo, or the
-# empty string when the photo was not rendered into the album. The stats filter
-# mini-albums call this to link each photo's "Details" to the album's own
-# details page, instead of reaching into ALBUM_VIEW_PAGE_BY_PHOTO directly.
-album_view_page_for_photo() {
- local -r photo="$1"; shift
-
- printf '%s' "${ALBUM_VIEW_PAGE_BY_PHOTO[$photo]:-}"
-}
-
-album_photo_files() {
- local -r photos_dir="$1"; shift
-
- find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
- | maybe_shuffle
-}
-
-start_preview_page() {
- local -r photos_dir="$1"; shift
- local -r html_dir="$1"; shift
- local -r blurs_dir="$1"; shift
- local -r backhref="$1"; shift
- local -r page_name="$1"; shift
- local -r header_bar="$1"; shift
- local background_image
-
- background_image=$(randomphoto "$photos_dir" "$page_name")
- template header "$page_name.html" \
- html_dir "$html_dir" \
- backhref "$backhref" \
- blurs_dir "$blurs_dir" \
- background_image "$background_image" \
- show_header_bar "$header_bar"
-}
-
-finish_preview_page() {
- local -r page_name="$1"; shift
- local -r html_dir="$1"; shift
- local -r backhref="$1"; shift
- local -r tarball_name="$1"; shift
-
- template footer "$page_name.html" \
- html_dir "$html_dir" \
- backhref "$backhref" \
- tarball_name "$tarball_name"
-}
-
-finish_preview_page_with_next() {
- local -r page_name="$1"; shift
- local -r html_dir="$1"; shift
- local -r backhref="$1"; shift
- local -r tarball_name="$1"; shift
- local -r next_page="$1"; shift
- local -r prev_page="$1"; shift
-
- template next "$page_name.html" \
- html_dir "$html_dir" \
- next "$next_page" \
- prev "$prev_page"
- finish_preview_page "$page_name" "$html_dir" "$backhref" "$tarball_name"
-}
-
-render_previous_page_link() {
- local -r page_name="$1"; shift
- local -r html_dir="$1"; shift
- local -r prev_page="$1"; shift
-
- template prev "$page_name.html" \
- html_dir "$html_dir" \
- prev "$prev_page"
-}
-
-# Assemble one complete preview page (page-N.html) in a single call: header,
-# optional previous-page link, every thumbnail in order, then the footer (with
-# a next-page link unless this is the last page). The appends to the one page
-# file happen here, in sequence, so the per-page ordering is preserved even when
-# this whole function runs as one backgrounded render job (parallelism is only
-# ACROSS pages, never within a page). The page's photos are the trailing
-# positional arguments; each photo's preview index is its 1-based position.
-render_full_preview_page() {
- local -r photos_dir="$1"; shift
- local -r html_dir="$1"; shift
- local -r thumbs_dir="$1"; shift
- local -r blurs_dir="$1"; shift
- local -r backhref="$1"; shift
- local -r tarball_name="$1"; shift
- local -r page_name="$1"; shift
- local -r page_num="$1"; shift
- local -r header_bar="$1"; shift
- local -r prev_page="$1"; shift
- local -r next_page="$1"; shift
-
- start_preview_page \
- "$photos_dir" "$html_dir" "$blurs_dir" "$backhref" "$page_name" \
- "$header_bar"
- if [ -n "$prev_page" ]; then
- render_previous_page_link "$page_name" "$html_dir" "$prev_page"
- fi
-
- # Batch all of this page's thumbnails into ONE template call. Building the
- # markup in bash and emitting it via the raw "preview_thumbs" field collapses
- # what used to be N "template preview" renders (one env -i bash per
- # thumbnail) into a single previewpage render per page. append_preview_grid
- # also groups the page's photos into tiles (some subdivided into smaller
- # thumbnails), so the per-page ordering and preview numbering stay here.
- local preview_thumbs=''
- # The main album's view pages are "<page_num>-<preview_num>.html", so the
- # shared grid builder gets "<page_num>-" as the href prefix.
- append_preview_grid preview_thumbs \
- "$thumbs_dir" "$backhref" "$page_num-" "$@"
- template previewpage "$page_name.html" \
- html_dir "$html_dir" \
- preview_thumbs "$preview_thumbs"
-
- if [ -n "$next_page" ]; then
- finish_preview_page_with_next \
- "$page_name" "$html_dir" "$backhref" "$tarball_name" \
- "$next_page" "$prev_page"
- else
- finish_preview_page "$page_name" "$html_dir" "$backhref" "$tarball_name"
- fi
-}
-
-# Enqueue one complete preview page as a background render job in the given job
-# pool (throttled to IMAGE_JOBS). Mirrors queue_album_view_render_job: job_pool_submit
-# waits for a free slot, backgrounds the whole-page assembly, and tracks its
-# pid/label so a failed job is reported by job_pool_wait and makes generation
-# fail loudly. The page's photos are passed as trailing positional args; the
-# background subshell forks a private copy of them, so the caller is free to
-# reuse its per-page accumulator for the next page.
-queue_preview_page_render_job() {
- local -r photos_dir="$1"; shift
- local -r html_dir="$1"; shift
- local -r thumbs_dir="$1"; shift
- local -r blurs_dir="$1"; shift
- local -r backhref="$1"; shift
- local -r tarball_name="$1"; shift
- local -r page_name="$1"; shift
- local -r page_num="$1"; shift
- local -r header_bar="$1"; shift
- local -r prev_page="$1"; shift
- local -r next_page="$1"; shift
- local -r pool="$1"; shift
- # Remaining positional args ("$@") are this page's photos in order.
-
- job_pool_submit "$pool" "template render job for preview $page_name" \
- render_full_preview_page \
- "$photos_dir" \
- "$html_dir" \
- "$thumbs_dir" \
- "$blurs_dir" \
- "$backhref" \
- "$tarball_name" \
- "$page_name" \
- "$page_num" \
- "$header_bar" \
- "$prev_page" \
- "$next_page" \
- "$@"
-}
-
-# Build a whole thumbnail-grid buffer by walking a list of photos and grouping
-# them into tiles. Most tiles are a single square thumbnail, but (controlled by
-# THUMB_FEATURE_PERCENT / THUMB_SUBDIVIDE_PERCENT) some become a 2x2 feature tile
-# or are subdivided into several smaller thumbnails. Each photo keeps its 1-based
-# position as its preview number (tiling only groups CONSECUTIVE photos visually,
-# never reorders them), so the view-page links stay correct. The view-page link
-# is "${href_prefix}${preview_num}.html": the main album passes "<page_num>-"; the
-# stats mini-albums pass "" (their view pages are bare "<index>.html"). This is
-# the single shared grid builder for both the main preview pages and the stats
-# mini-album galleries. Tile blocks are separated by a single newline; the
-# template adds the trailing newline.
-append_preview_grid() {
- local -n buffer_ref="$1"; shift
- local -r thumbs_dir="$1"; shift
- local -r backhref="$1"; shift
- local -r href_prefix="$1"; shift
- local -a photos=("$@")
- local -i i=0
- local -i count
- local layout
- local block
- # Cap the big 2x2 feature tiles at this many per page; once reached, later
- # tiles are no longer offered the "feature" layout (they fall back to
- # subdivided/single), so a page never gets crowded with hero tiles.
- local -ri max_features=2
- local -i features_used=0
-
- while (( i < ${#photos[@]} )); do
- # Decide this tile's layout from the photos still available; the first
- # photo's name is the seeded-random context so the choice is stable.
- # Features are only offered until the per-page cap is reached.
- read -r layout count < <(
- tile_layout_for "$(( ${#photos[@]} - i ))" "${photos[i]}" \
- "$(( features_used < max_features ? 1 : 0 ))"
- )
- if [ "$layout" = feature ]; then
- (( ++features_used ))
- fi
- block=$(build_tile_block \
- "$thumbs_dir" "$backhref" "$href_prefix" "$layout" "$(( i + 1 ))" \
- "${photos[@]:i:count}")
- if [ -z "$buffer_ref" ]; then
- buffer_ref="$block"
- else
- buffer_ref+=$'\n'"$block"
- fi
- (( i += count ))
- done
-}
+# tile_layout_for rolls the seeded random layout for the next tile;
+# build_tile_block / build_subdivided_tile emit the chosen tile's markup. They
+# call build_preview_thumbnail (album-thumbnail-html.source.sh) at runtime, and
+# are themselves driven by append_preview_grid there; all libs are sourced
+# before any code runs, so the cross-module calls resolve regardless of source
+# order.
# Decide the layout for the next tile, printing "<layout> <photo-count>". When
# feature_allowed is non-zero each tile rolls first for a "feature" (one photo
@@ -2841,6 +2633,70 @@ build_subdivided_tile() {
printf '</div>'
}
+# Inlined from src/lib/album-thumbnail-html.source.sh
+# Album thumbnail HTML. Split out of album-render.source.sh (task ar0) so the
+# "what markup does one thumbnail / a whole grid emit" concern lives apart from
+# the tile-layout deciders (album-tile-layout.source.sh), the page orchestration
+# and the photo-selection policy. This markup changes for HTML/CSS reasons
+# (escaping, classes, link shape), independent of how tiles are grouped or how
+# jobs are wired.
+#
+# append_preview_grid walks a photo list into tiles by calling tile_layout_for /
+# build_tile_block (album-tile-layout.source.sh); build_preview_thumbnail emits
+# one thumbnail's <a>/<img>. All libs are sourced before any code runs, so the
+# calls between this module and album-tile-layout resolve regardless of source
+# order.
+
+# Build a whole thumbnail-grid buffer by walking a list of photos and grouping
+# them into tiles. Most tiles are a single square thumbnail, but (controlled by
+# THUMB_FEATURE_PERCENT / THUMB_SUBDIVIDE_PERCENT) some become a 2x2 feature tile
+# or are subdivided into several smaller thumbnails. Each photo keeps its 1-based
+# position as its preview number (tiling only groups CONSECUTIVE photos visually,
+# never reorders them), so the view-page links stay correct. The view-page link
+# is "${href_prefix}${preview_num}.html": the main album passes "<page_num>-"; the
+# stats mini-albums pass "" (their view pages are bare "<index>.html"). This is
+# the single shared grid builder for both the main preview pages and the stats
+# mini-album galleries. Tile blocks are separated by a single newline; the
+# template adds the trailing newline.
+append_preview_grid() {
+ local -n buffer_ref="$1"; shift
+ local -r thumbs_dir="$1"; shift
+ local -r backhref="$1"; shift
+ local -r href_prefix="$1"; shift
+ local -a photos=("$@")
+ local -i i=0
+ local -i count
+ local layout
+ local block
+ # Cap the big 2x2 feature tiles at this many per page; once reached, later
+ # tiles are no longer offered the "feature" layout (they fall back to
+ # subdivided/single), so a page never gets crowded with hero tiles.
+ local -ri max_features=2
+ local -i features_used=0
+
+ while (( i < ${#photos[@]} )); do
+ # Decide this tile's layout from the photos still available; the first
+ # photo's name is the seeded-random context so the choice is stable.
+ # Features are only offered until the per-page cap is reached.
+ read -r layout count < <(
+ tile_layout_for "$(( ${#photos[@]} - i ))" "${photos[i]}" \
+ "$(( features_used < max_features ? 1 : 0 ))"
+ )
+ if [ "$layout" = feature ]; then
+ (( ++features_used ))
+ fi
+ block=$(build_tile_block \
+ "$thumbs_dir" "$backhref" "$href_prefix" "$layout" "$(( i + 1 ))" \
+ "${photos[@]:i:count}")
+ if [ -z "$buffer_ref" ]; then
+ buffer_ref="$block"
+ else
+ buffer_ref+=$'\n'"$block"
+ fi
+ (( i += count ))
+ done
+}
+
# Render the HTML for a single preview thumbnail (HTML-escaping every value the
# way preview.tmpl's context_html fields did). Returned without a trailing
# newline so callers control separators. The view-page link is
@@ -2885,6 +2741,296 @@ build_preview_thumbnail() {
"$thumbs_dir_html" "$photo_html"
}
+# Inlined from src/lib/album-photo-select.source.sh
+# Album photo listing and random selection. Split out of
+# album-render.source.sh (task ar0) so the "which photos are in this album, in
+# what order, and which one do we pick for a background/splash" concern lives
+# apart from the page orchestration, the tile-layout deciders and the thumbnail
+# HTML. This is selection POLICY (shuffle/sort, splash-requires-a-blur, seeded
+# random pick) and changes for different reasons than the rendering plumbing.
+#
+# These helpers are called by the orchestrator (album-render.source.sh) and by
+# the per-page render jobs at runtime; all libs are sourced before any code runs,
+# so availability does not depend on source order.
+
+album_photo_files() {
+ local -r photos_dir="$1"; shift
+
+ find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
+ | maybe_shuffle
+}
+
+# Group the album's photos into pages of at most MAXPREVIEWS, in their final
+# (shuffled/sorted) order. The result is emitted one line per page as a
+# tab-separated record "<page_num>\t<photo>\t<photo>..." so the caller can walk
+# pages without keeping every page in memory at once. Order is fully
+# deterministic (album_photo_files already applies the seeded shuffle), so the
+# downstream parallelism only changes timing, never which photo lands where.
+album_page_records() {
+ local -r photos_dir="$1"; shift
+ local photo
+ local -i num=1
+ local -i count=0
+ local line=''
+
+ while IFS= read -r photo; do
+ if (( count == MAXPREVIEWS )); then
+ printf '%d\t%s\n' "$num" "$line"
+ (( ++num ))
+ count=0
+ line=''
+ fi
+ if (( count == 0 )); then
+ line="$photo"
+ else
+ line="$line"$'\t'"$photo"
+ fi
+ (( ++count ))
+ done < <(album_photo_files "$photos_dir")
+
+ if (( count > 0 )); then
+ printf '%d\t%s\n' "$num" "$line"
+ fi
+}
+
+splash_photo_files() {
+ local -r photos_dir="$1"; shift
+ local -r blurs_dir="$1"; shift
+ local photo
+
+ while IFS= read -r photo; do
+ if [ -f "$DIST_DIR/$blurs_dir/$photo" ]; then
+ printf '%s\n' "$photo"
+ fi
+ done < <(
+ find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
+ | sort
+ )
+}
+
+random_splash_photo() {
+ local -r photos_dir="$1"; shift
+ local -r blurs_dir="$1"; shift
+ local -i index
+ local photo
+ local -a photos=()
+
+ while IFS= read -r photo; do
+ photos+=("$photo")
+ done < <(splash_photo_files "$photos_dir" "$blurs_dir")
+
+ if (( ${#photos[@]} == 0 )); then
+ printf 'ERROR: No splash photos found in %s with matching blurs in %s\n' \
+ "$(_display_path "$DIST_DIR/$photos_dir")" \
+ "$(_display_path "$DIST_DIR/$blurs_dir")" >&2
+ return 1
+ fi
+
+ index=$(random_index "photo:$photos_dir:splash" "${#photos[@]}")
+ printf '%s\n' "${photos[index]}"
+}
+
+randomphoto() {
+ local -r photos_dir="$1"; shift
+ local -r context="${1:-$photos_dir}"
+ local -i index
+ local photo
+ local -a photos=()
+
+ while IFS= read -r photo; do
+ photos+=("$photo")
+ done < <(
+ find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
+ | sort
+ )
+
+ if (( ${#photos[@]} == 0 )); then
+ printf 'ERROR: No photos found in %s\n' \
+ "$(_display_path "$DIST_DIR/$photos_dir")" >&2
+ return 1
+ fi
+
+ index=$(random_index "photo:$photos_dir:$context" "${#photos[@]}")
+ printf '%s\n' "${photos[index]}"
+}
+
+# Inlined from src/lib/album-render.source.sh
+# Album page orchestration. After the ar0 split this module owns only the page
+# assembly and the job plumbing: it builds the preview pages, the per-photo
+# view/details pages, the navigation redirects and the index/splash, driving the
+# job_pool_* pool so pages render in parallel. The three concerns it used to
+# bundle now live in siblings, all sourced before this file:
+# - album-tile-layout.source.sh (tile_layout_for/build_tile_block/...)
+# - album-thumbnail-html.source.sh (build_preview_thumbnail/append_preview_grid)
+# - album-photo-select.source.sh (album_photo_files/randomphoto/splash/...)
+# This module calls into all three at runtime; all libs are sourced before any
+# code runs, so the cross-module calls resolve regardless of source order.
+#
+# Maps each album photo filename to its view-page basename ("<page>-<preview>")
+# as assigned during render_album_pages. Declared globally so it always exists
+# for the accessor even when no album was rendered.
+#
+# This is the album module's PRIVATE backing store (task pn0). Outside callers
+# must NOT index it directly: use the album_view_page_for_photo accessor below.
+# Keeping the map private behind a documented function decouples consumers (the
+# stats filter mini-albums) from how the album internally names or caches view
+# pages, so a change to the page-naming scheme stays contained in this module.
+declare -gA ALBUM_VIEW_PAGE_BY_PHOTO=()
+
+# Public album API (task pn0): return the view-page basename for a photo, or the
+# empty string when the photo was not rendered into the album. The stats filter
+# mini-albums call this to link each photo's "Details" to the album's own
+# details page, instead of reaching into ALBUM_VIEW_PAGE_BY_PHOTO directly.
+album_view_page_for_photo() {
+ local -r photo="$1"; shift
+
+ printf '%s' "${ALBUM_VIEW_PAGE_BY_PHOTO[$photo]:-}"
+}
+
+start_preview_page() {
+ local -r photos_dir="$1"; shift
+ local -r html_dir="$1"; shift
+ local -r blurs_dir="$1"; shift
+ local -r backhref="$1"; shift
+ local -r page_name="$1"; shift
+ local -r header_bar="$1"; shift
+ local background_image
+
+ background_image=$(randomphoto "$photos_dir" "$page_name")
+ template header "$page_name.html" \
+ html_dir "$html_dir" \
+ backhref "$backhref" \
+ blurs_dir "$blurs_dir" \
+ background_image "$background_image" \
+ show_header_bar "$header_bar"
+}
+
+finish_preview_page() {
+ local -r page_name="$1"; shift
+ local -r html_dir="$1"; shift
+ local -r backhref="$1"; shift
+ local -r tarball_name="$1"; shift
+
+ template footer "$page_name.html" \
+ html_dir "$html_dir" \
+ backhref "$backhref" \
+ tarball_name "$tarball_name"
+}
+
+finish_preview_page_with_next() {
+ local -r page_name="$1"; shift
+ local -r html_dir="$1"; shift
+ local -r backhref="$1"; shift
+ local -r tarball_name="$1"; shift
+ local -r next_page="$1"; shift
+ local -r prev_page="$1"; shift
+
+ template next "$page_name.html" \
+ html_dir "$html_dir" \
+ next "$next_page" \
+ prev "$prev_page"
+ finish_preview_page "$page_name" "$html_dir" "$backhref" "$tarball_name"
+}
+
+render_previous_page_link() {
+ local -r page_name="$1"; shift
+ local -r html_dir="$1"; shift
+ local -r prev_page="$1"; shift
+
+ template prev "$page_name.html" \
+ html_dir "$html_dir" \
+ prev "$prev_page"
+}
+
+# Assemble one complete preview page (page-N.html) in a single call: header,
+# optional previous-page link, every thumbnail in order, then the footer (with
+# a next-page link unless this is the last page). The appends to the one page
+# file happen here, in sequence, so the per-page ordering is preserved even when
+# this whole function runs as one backgrounded render job (parallelism is only
+# ACROSS pages, never within a page). The page's photos are the trailing
+# positional arguments; each photo's preview index is its 1-based position.
+render_full_preview_page() {
+ local -r photos_dir="$1"; shift
+ local -r html_dir="$1"; shift
+ local -r thumbs_dir="$1"; shift
+ local -r blurs_dir="$1"; shift
+ local -r backhref="$1"; shift
+ local -r tarball_name="$1"; shift
+ local -r page_name="$1"; shift
+ local -r page_num="$1"; shift
+ local -r header_bar="$1"; shift
+ local -r prev_page="$1"; shift
+ local -r next_page="$1"; shift
+
+ start_preview_page \
+ "$photos_dir" "$html_dir" "$blurs_dir" "$backhref" "$page_name" \
+ "$header_bar"
+ if [ -n "$prev_page" ]; then
+ render_previous_page_link "$page_name" "$html_dir" "$prev_page"
+ fi
+
+ # Batch all of this page's thumbnails into ONE template call. Building the
+ # markup in bash and emitting it via the raw "preview_thumbs" field collapses
+ # what used to be N "template preview" renders (one env -i bash per
+ # thumbnail) into a single previewpage render per page. append_preview_grid
+ # also groups the page's photos into tiles (some subdivided into smaller
+ # thumbnails), so the per-page ordering and preview numbering stay here.
+ local preview_thumbs=''
+ # The main album's view pages are "<page_num>-<preview_num>.html", so the
+ # shared grid builder gets "<page_num>-" as the href prefix.
+ append_preview_grid preview_thumbs \
+ "$thumbs_dir" "$backhref" "$page_num-" "$@"
+ template previewpage "$page_name.html" \
+ html_dir "$html_dir" \
+ preview_thumbs "$preview_thumbs"
+
+ if [ -n "$next_page" ]; then
+ finish_preview_page_with_next \
+ "$page_name" "$html_dir" "$backhref" "$tarball_name" \
+ "$next_page" "$prev_page"
+ else
+ finish_preview_page "$page_name" "$html_dir" "$backhref" "$tarball_name"
+ fi
+}
+
+# Enqueue one complete preview page as a background render job in the given job
+# pool (throttled to IMAGE_JOBS). Mirrors queue_album_view_render_job: job_pool_submit
+# waits for a free slot, backgrounds the whole-page assembly, and tracks its
+# pid/label so a failed job is reported by job_pool_wait and makes generation
+# fail loudly. The page's photos are passed as trailing positional args; the
+# background subshell forks a private copy of them, so the caller is free to
+# reuse its per-page accumulator for the next page.
+queue_preview_page_render_job() {
+ local -r photos_dir="$1"; shift
+ local -r html_dir="$1"; shift
+ local -r thumbs_dir="$1"; shift
+ local -r blurs_dir="$1"; shift
+ local -r backhref="$1"; shift
+ local -r tarball_name="$1"; shift
+ local -r page_name="$1"; shift
+ local -r page_num="$1"; shift
+ local -r header_bar="$1"; shift
+ local -r prev_page="$1"; shift
+ local -r next_page="$1"; shift
+ local -r pool="$1"; shift
+ # Remaining positional args ("$@") are this page's photos in order.
+
+ job_pool_submit "$pool" "template render job for preview $page_name" \
+ render_full_preview_page \
+ "$photos_dir" \
+ "$html_dir" \
+ "$thumbs_dir" \
+ "$blurs_dir" \
+ "$backhref" \
+ "$tarball_name" \
+ "$page_name" \
+ "$page_num" \
+ "$header_bar" \
+ "$prev_page" \
+ "$next_page" \
+ "$@"
+}
+
render_view_page() {
local -r html_dir="$1"; shift
local -r photos_dir="$1"; shift
@@ -3168,39 +3314,6 @@ queue_album_view_render_job() {
"$photo"
}
-# Group the album's photos into pages of at most MAXPREVIEWS, in their final
-# (shuffled/sorted) order. The result is emitted one line per page as a
-# tab-separated record "<page_num>\t<photo>\t<photo>..." so the caller can walk
-# pages without keeping every page in memory at once. Order is fully
-# deterministic (album_photo_files already applies the seeded shuffle), so the
-# downstream parallelism only changes timing, never which photo lands where.
-album_page_records() {
- local -r photos_dir="$1"; shift
- local photo
- local -i num=1
- local -i count=0
- local line=''
-
- while IFS= read -r photo; do
- if (( count == MAXPREVIEWS )); then
- printf '%d\t%s\n' "$num" "$line"
- (( ++num ))
- count=0
- line=''
- fi
- if (( count == 0 )); then
- line="$photo"
- else
- line="$line"$'\t'"$photo"
- fi
- (( ++count ))
- done < <(album_photo_files "$photos_dir")
-
- if (( count > 0 )); then
- printf '%d\t%s\n' "$num" "$line"
- fi
-}
-
# Per-photo bookkeeping shared by the album loop: queue the view+details render
# job, record the page/preview as a rendered view page (for redirect generation)
# and remember the photo -> view-page mapping for the stats mini-albums. Kept
@@ -3315,67 +3428,6 @@ render_album_pages() {
render_album_index "$photos_dir" "$html_dir" "$blurs_dir" "$backhref"
}
-splash_photo_files() {
- local -r photos_dir="$1"; shift
- local -r blurs_dir="$1"; shift
- local photo
-
- while IFS= read -r photo; do
- if [ -f "$DIST_DIR/$blurs_dir/$photo" ]; then
- printf '%s\n' "$photo"
- fi
- done < <(
- find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
- | sort
- )
-}
-
-random_splash_photo() {
- local -r photos_dir="$1"; shift
- local -r blurs_dir="$1"; shift
- local -i index
- local photo
- local -a photos=()
-
- while IFS= read -r photo; do
- photos+=("$photo")
- done < <(splash_photo_files "$photos_dir" "$blurs_dir")
-
- if (( ${#photos[@]} == 0 )); then
- printf 'ERROR: No splash photos found in %s with matching blurs in %s\n' \
- "$(_display_path "$DIST_DIR/$photos_dir")" \
- "$(_display_path "$DIST_DIR/$blurs_dir")" >&2
- return 1
- fi
-
- index=$(random_index "photo:$photos_dir:splash" "${#photos[@]}")
- printf '%s\n' "${photos[index]}"
-}
-
-randomphoto() {
- local -r photos_dir="$1"; shift
- local -r context="${1:-$photos_dir}"
- local -i index
- local photo
- local -a photos=()
-
- while IFS= read -r photo; do
- photos+=("$photo")
- done < <(
- find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
- | sort
- )
-
- if (( ${#photos[@]} == 0 )); then
- printf 'ERROR: No photos found in %s\n' \
- "$(_display_path "$DIST_DIR/$photos_dir")" >&2
- return 1
- fi
-
- index=$(random_index "photo:$photos_dir:$context" "${#photos[@]}")
- printf '%s\n' "${photos[index]}"
-}
-
# Inlined from src/lib/album.source.sh
# Album coordinator: ties together the image pipeline (image-pipeline.source.sh),
# page rendering and parallel orchestration (album-render.source.sh) and the EXIF
diff --git a/src/lib/album-photo-select.source.sh b/src/lib/album-photo-select.source.sh
new file mode 100644
index 0000000..0a889bc
--- /dev/null
+++ b/src/lib/album-photo-select.source.sh
@@ -0,0 +1,111 @@
+# Album photo listing and random selection. Split out of
+# album-render.source.sh (task ar0) so the "which photos are in this album, in
+# what order, and which one do we pick for a background/splash" concern lives
+# apart from the page orchestration, the tile-layout deciders and the thumbnail
+# HTML. This is selection POLICY (shuffle/sort, splash-requires-a-blur, seeded
+# random pick) and changes for different reasons than the rendering plumbing.
+#
+# These helpers are called by the orchestrator (album-render.source.sh) and by
+# the per-page render jobs at runtime; all libs are sourced before any code runs,
+# so availability does not depend on source order.
+
+album_photo_files() {
+ local -r photos_dir="$1"; shift
+
+ find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
+ | maybe_shuffle
+}
+
+# Group the album's photos into pages of at most MAXPREVIEWS, in their final
+# (shuffled/sorted) order. The result is emitted one line per page as a
+# tab-separated record "<page_num>\t<photo>\t<photo>..." so the caller can walk
+# pages without keeping every page in memory at once. Order is fully
+# deterministic (album_photo_files already applies the seeded shuffle), so the
+# downstream parallelism only changes timing, never which photo lands where.
+album_page_records() {
+ local -r photos_dir="$1"; shift
+ local photo
+ local -i num=1
+ local -i count=0
+ local line=''
+
+ while IFS= read -r photo; do
+ if (( count == MAXPREVIEWS )); then
+ printf '%d\t%s\n' "$num" "$line"
+ (( ++num ))
+ count=0
+ line=''
+ fi
+ if (( count == 0 )); then
+ line="$photo"
+ else
+ line="$line"$'\t'"$photo"
+ fi
+ (( ++count ))
+ done < <(album_photo_files "$photos_dir")
+
+ if (( count > 0 )); then
+ printf '%d\t%s\n' "$num" "$line"
+ fi
+}
+
+splash_photo_files() {
+ local -r photos_dir="$1"; shift
+ local -r blurs_dir="$1"; shift
+ local photo
+
+ while IFS= read -r photo; do
+ if [ -f "$DIST_DIR/$blurs_dir/$photo" ]; then
+ printf '%s\n' "$photo"
+ fi
+ done < <(
+ find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
+ | sort
+ )
+}
+
+random_splash_photo() {
+ local -r photos_dir="$1"; shift
+ local -r blurs_dir="$1"; shift
+ local -i index
+ local photo
+ local -a photos=()
+
+ while IFS= read -r photo; do
+ photos+=("$photo")
+ done < <(splash_photo_files "$photos_dir" "$blurs_dir")
+
+ if (( ${#photos[@]} == 0 )); then
+ printf 'ERROR: No splash photos found in %s with matching blurs in %s\n' \
+ "$(_display_path "$DIST_DIR/$photos_dir")" \
+ "$(_display_path "$DIST_DIR/$blurs_dir")" >&2
+ return 1
+ fi
+
+ index=$(random_index "photo:$photos_dir:splash" "${#photos[@]}")
+ printf '%s\n' "${photos[index]}"
+}
+
+randomphoto() {
+ local -r photos_dir="$1"; shift
+ local -r context="${1:-$photos_dir}"
+ local -i index
+ local photo
+ local -a photos=()
+
+ while IFS= read -r photo; do
+ photos+=("$photo")
+ done < <(
+ find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
+ | sort
+ )
+
+ if (( ${#photos[@]} == 0 )); then
+ printf 'ERROR: No photos found in %s\n' \
+ "$(_display_path "$DIST_DIR/$photos_dir")" >&2
+ return 1
+ fi
+
+ index=$(random_index "photo:$photos_dir:$context" "${#photos[@]}")
+ printf '%s\n' "${photos[index]}"
+}
diff --git a/src/lib/album-render.source.sh b/src/lib/album-render.source.sh
index d438cef..e039b07 100644
--- a/src/lib/album-render.source.sh
+++ b/src/lib/album-render.source.sh
@@ -1,3 +1,14 @@
+# Album page orchestration. After the ar0 split this module owns only the page
+# assembly and the job plumbing: it builds the preview pages, the per-photo
+# view/details pages, the navigation redirects and the index/splash, driving the
+# job_pool_* pool so pages render in parallel. The three concerns it used to
+# bundle now live in siblings, all sourced before this file:
+# - album-tile-layout.source.sh (tile_layout_for/build_tile_block/...)
+# - album-thumbnail-html.source.sh (build_preview_thumbnail/append_preview_grid)
+# - album-photo-select.source.sh (album_photo_files/randomphoto/splash/...)
+# This module calls into all three at runtime; all libs are sourced before any
+# code runs, so the cross-module calls resolve regardless of source order.
+#
# Maps each album photo filename to its view-page basename ("<page>-<preview>")
# as assigned during render_album_pages. Declared globally so it always exists
# for the accessor even when no album was rendered.
@@ -19,13 +30,6 @@ album_view_page_for_photo() {
printf '%s' "${ALBUM_VIEW_PAGE_BY_PHOTO[$photo]:-}"
}
-album_photo_files() {
- local -r photos_dir="$1"; shift
-
- find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
- | maybe_shuffle
-}
-
start_preview_page() {
local -r photos_dir="$1"; shift
local -r html_dir="$1"; shift
@@ -170,227 +174,6 @@ queue_preview_page_render_job() {
"$@"
}
-# Build a whole thumbnail-grid buffer by walking a list of photos and grouping
-# t