diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-13 10:15:20 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-13 10:15:20 +0300 |
| commit | d4971998edae804c39797486be7a342d43e61e5c (patch) | |
| tree | 830c008847704b40b7f45d4c7c620beed03958d6 /src | |
| parent | e9437367b8fc7a4cb0ffed6c0a8ba89356a3b9ad (diff) | |
Refactor helper modules for im0
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/archive.source.sh | 52 | ||||
| -rw-r--r-- | src/lib/config.sync.source.sh | 23 | ||||
| -rw-r--r-- | src/lib/image.source.sh | 213 | ||||
| -rw-r--r-- | src/lib/imagemagick.source.sh | 71 | ||||
| -rw-r--r-- | src/lib/job-pool.source.sh | 137 | ||||
| -rw-r--r-- | src/lib/process.source.sh | 24 | ||||
| -rw-r--r-- | src/lib/random.source.sh | 74 | ||||
| -rw-r--r-- | src/lib/system.source.sh | 172 | ||||
| -rwxr-xr-x | src/shuriken.sh | 12 |
9 files changed, 391 insertions, 387 deletions
diff --git a/src/lib/archive.source.sh b/src/lib/archive.source.sh new file mode 100644 index 0000000..127a0c5 --- /dev/null +++ b/src/lib/archive.source.sh @@ -0,0 +1,52 @@ +tarball() { + local -r tarball_name="$1"; shift + local -r tarball_suffix="$TARBALL_SUFFIX" + local base + local old_tarball + local -a tar_opts=() + + # Cleanup tarballs from previous runs for the configured suffix. + while IFS= read -r -d '' old_tarball; do + if [[ "$old_tarball" == *"$tarball_suffix" ]]; then + rm -f "$old_tarball" + fi + done < <(find "$DIST_DIR" -maxdepth 1 -type f -print0) + + base=$(basename "$INCOMING_DIR") + resolve_tar_opts tar_opts + + log_info "Creating tarball $(_display_path "$DIST_DIR/$tarball_name")" \ + "from $INCOMING_DIR" + ( + cd "$(dirname "$INCOMING_DIR")" + run_with_timeout tar "$TAR_TIMEOUT" \ + tar "${tar_opts[@]}" -f "$DIST_DIR/$tarball_name" "$base" + ) +} + +resolve_tar_opts() { + local -n options_ref="$1"; shift + local tar_opts_decl + + options_ref=() + + if ! tar_opts_decl=$(declare -p TAR_OPTS 2>/dev/null); then + options_ref=(-c) + return + fi + + case "$tar_opts_decl" in + declare\ -a*\ TAR_OPTS=*) + options_ref=("${TAR_OPTS[@]}") + ;; + *) + if [ -n "${TAR_OPTS:-}" ]; then + read -r -a options_ref <<< "$TAR_OPTS" + fi + ;; + esac + + if (( ${#options_ref[@]} == 0 )); then + options_ref=(-c) + fi +} diff --git a/src/lib/config.sync.source.sh b/src/lib/config.sync.source.sh index b9362ac..68bb759 100644 --- a/src/lib/config.sync.source.sh +++ b/src/lib/config.sync.source.sh @@ -1,3 +1,26 @@ +resolve_sync_destinations() { + local -n destinations_ref="$1"; shift + local destinations_decl + + destinations_ref=() + + if ! destinations_decl=$(declare -p SYNC_DESTINATIONS 2>/dev/null); then + return + fi + + case "$destinations_decl" in + declare\ -a*\ SYNC_DESTINATIONS=*) + destinations_ref=("${SYNC_DESTINATIONS[@]}") + ;; + *) + if [ -n "${SYNC_DESTINATIONS:-}" ]; then + # shellcheck disable=SC2034 + read -r -a destinations_ref <<< "$SYNC_DESTINATIONS" + fi + ;; + esac +} + sync_dist() { local destination local -a rsync_args=(-av) diff --git a/src/lib/image.source.sh b/src/lib/image.source.sh index 568ab62..c190b87 100644 --- a/src/lib/image.source.sh +++ b/src/lib/image.source.sh @@ -80,144 +80,6 @@ scalephotos() { fi } -wait_for_parallel_job_pid() { - local -r pid="$1"; shift - local -i status=0 - - set +e - wait "$pid" - status=$? - set -e - - return "$status" -} - -parallel_job_pid_is_running() { - local -r pid="$1"; shift - local running_pid - - for running_pid in "$@"; do - if [ "$running_pid" = "$pid" ]; then - return 0 - fi - done - - return 1 -} - -reap_finished_parallel_jobs() { - local -r job_pids_name="$1"; shift - # shellcheck disable=SC2178 - local -n job_pids_ref="$job_pids_name" - local -n failed_ref="$1"; shift - local -i reaped_status="$1"; shift - local -i has_reaped_status=1 - local -i status=0 - local -a remaining_pids=() - local -a running_pids=() - local pid - - mapfile -t running_pids < <(jobs -rp) - - for pid in "${job_pids_ref[@]}"; do - if parallel_job_pid_is_running "$pid" "${running_pids[@]}"; then - remaining_pids+=("$pid") - continue - fi - - if wait_for_parallel_job_pid "$pid"; then - status=0 - else - status=$? - fi - if (( status == 127 && has_reaped_status != 0 )); then - status=$reaped_status - has_reaped_status=0 - fi - - if (( status != 0 )); then - failed_ref=1 - fi - done - - job_pids_ref=("${remaining_pids[@]}") -} - -wait_for_next_parallel_job() { - local -r job_pids_name="$1"; shift - local -r failed_name="$1"; shift - # shellcheck disable=SC2178 - local -n job_pids_ref="$job_pids_name" - local -i status=0 - - set +e - wait -n "${job_pids_ref[@]}" - status=$? - set -e - - reap_finished_parallel_jobs "$job_pids_name" "$failed_name" "$status" -} - -wait_for_parallel_job_slot() { - local -r job_pids_name="$1"; shift - local -r failed_name="$1"; shift - local -r max_jobs="$1"; shift - # shellcheck disable=SC2178 - local -n job_pids_ref="$job_pids_name" - - while (( ${#job_pids_ref[@]} >= max_jobs )); do - wait_for_next_parallel_job "$job_pids_name" "$failed_name" - done -} - -wait_for_parallel_jobs() { - # shellcheck disable=SC2178 - local -n job_pids_ref="$1"; shift - local -n failed_ref="$1"; shift - - : "$failed_ref" - while (( ${#job_pids_ref[@]} > 0 )); do - if ! wait_for_parallel_job_pid "${job_pids_ref[0]}"; then - failed_ref=1 - fi - job_pids_ref=("${job_pids_ref[@]:1}") - done -} - -wait_for_image_job_slot() { - local -r image_job_pids_name="$1"; shift - local -r failed_name="$1"; shift - - wait_for_parallel_job_slot \ - "$image_job_pids_name" \ - "$failed_name" \ - "$IMAGE_JOBS" -} - -wait_for_image_jobs() { - local -r image_job_pids_name="$1"; shift - local -r failed_name="$1"; shift - - wait_for_parallel_jobs "$image_job_pids_name" "$failed_name" -} - -wait_for_template_render_job_slot() { - local -r render_job_pids_name="$1"; shift - local -r failed_name="$1"; shift - - wait_for_parallel_job_slot \ - "$render_job_pids_name" \ - "$failed_name" \ - "$IMAGE_JOBS" -} - -wait_for_template_render_jobs() { - local -r render_job_pids_name="$1"; shift - local -r failed_name="$1"; shift - - wait_for_parallel_jobs "$render_job_pids_name" "$failed_name" -} - scale_photo() { local -r photo="$1"; shift local destphoto @@ -248,78 +110,3 @@ scale_photo() { "$destphoto" fi } - -random_seed_is_set() { - [ -n "$RANDOM_SEED" ] -} - -deterministic_index() { - local -r namespace="$1"; shift - local -r count="$1"; shift - local checksum - - checksum=$(printf '%s' "${RANDOM_SEED}:$namespace" | cksum) - checksum=${checksum%% *} - - printf '%s\n' $(( checksum % count )) -} - -random_index() { - local -r namespace="$1"; shift - local -r count="$1"; shift - - if random_seed_is_set; then - deterministic_index "$namespace" "$count" - else - printf '%s\n' $(( RANDOM % count )) - fi -} - -random_animation_css_class() { - local -r speed="$1"; shift - local -r context="${1:-$speed}" - local -i index - local -a classes=( - "animate-opacity-$speed" - "animate-top-$speed" - "animate-left-$speed" - "animate-right-$speed" - "animate-bottom-$speed" - "animate-zoom-$speed" - "animate-snap-rotate-$speed" - "animate-hard-zoom-$speed" - "animate-slam-left-$speed" - "animate-slam-right-$speed" - "animate-flash-in-$speed" - "animate-invert-pop-$speed" - "animate-posterize-pop-$speed" - "animate-skew-snap-$speed" - "animate-glitch-step-$speed" - ) - - index=$(random_index "animation:$speed:$context" "${#classes[@]}") - printf '%s\n' "${classes[index]}" -} - -deterministic_shuffle() { - local checksum - local line - - while IFS= read -r line; do - checksum=$(printf '%s' "${RANDOM_SEED}:shuffle:$line" | cksum) - checksum=${checksum%% *} - printf '%010u\t%s\n' "$checksum" "$line" - done | sort -n -k1,1 -k2,2 | cut -f2- -} - -maybe_shuffle() { - if [ "$SHUFFLE" = yes ]; then - if random_seed_is_set; then - deterministic_shuffle - else - sort -R - fi - else - sort - fi -} diff --git a/src/lib/imagemagick.source.sh b/src/lib/imagemagick.source.sh new file mode 100644 index 0000000..cdc909c --- /dev/null +++ b/src/lib/imagemagick.source.sh @@ -0,0 +1,71 @@ +# shellcheck disable=SC2034 +resolve_imagemagick_command() { + local -r operation="$1"; shift + local -n command_ref="$1"; shift + + command_ref=() + + case "$operation" in + convert) + if command -v magick >/dev/null 2>&1; then + command_ref=(magick) + return + fi + if command -v convert >/dev/null 2>&1; then + command_ref=(convert) + return + fi + ;; + identify) + if command -v magick >/dev/null 2>&1; then + command_ref=(magick identify) + return + fi + if command -v identify >/dev/null 2>&1; then + command_ref=(identify) + return + fi + if command -v convert >/dev/null 2>&1; then + command_ref=(convert) + return + fi + ;; + *) + printf 'ERROR: Unknown ImageMagick operation %s\n' \ + "$operation" >&2 + return 1 + ;; + esac + + printf 'ERROR: ImageMagick is required; install magick or convert\n' >&2 + return 127 +} + +imagemagick() { + local -a imagemagick_command=() + + resolve_imagemagick_command convert imagemagick_command || return + + run_with_timeout ImageMagick "$IMAGEMAGICK_TIMEOUT" \ + "${imagemagick_command[@]}" "$@" +} + +imagemagick_identify() { + local -a imagemagick_command=() + + resolve_imagemagick_command identify imagemagick_command || return + + if [ "${imagemagick_command[0]}" = convert ]; then + if [[ "${1:-}" = '-verbose' && $# -eq 2 ]]; then + run_with_timeout ImageMagick "$IMAGEMAGICK_TIMEOUT" \ + "${imagemagick_command[@]}" "$2" -verbose info: + else + run_with_timeout ImageMagick "$IMAGEMAGICK_TIMEOUT" \ + "${imagemagick_command[@]}" "$@" info: + fi + return + fi + + run_with_timeout ImageMagick "$IMAGEMAGICK_TIMEOUT" \ + "${imagemagick_command[@]}" "$@" +} diff --git a/src/lib/job-pool.source.sh b/src/lib/job-pool.source.sh new file mode 100644 index 0000000..5c629aa --- /dev/null +++ b/src/lib/job-pool.source.sh @@ -0,0 +1,137 @@ +wait_for_parallel_job_pid() { + local -r pid="$1"; shift + local -i status=0 + + set +e + wait "$pid" + status=$? + set -e + + return "$status" +} + +parallel_job_pid_is_running() { + local -r pid="$1"; shift + local running_pid + + for running_pid in "$@"; do + if [ "$running_pid" = "$pid" ]; then + return 0 + fi + done + + return 1 +} + +reap_finished_parallel_jobs() { + local -r job_pids_name="$1"; shift + # shellcheck disable=SC2178 + local -n job_pids_ref="$job_pids_name" + local -n failed_ref="$1"; shift + local -i reaped_status="$1"; shift + local -i has_reaped_status=1 + local -i status=0 + local -a remaining_pids=() + local -a running_pids=() + local pid + + mapfile -t running_pids < <(jobs -rp) + + for pid in "${job_pids_ref[@]}"; do + if parallel_job_pid_is_running "$pid" "${running_pids[@]}"; then + remaining_pids+=("$pid") + continue + fi + + if wait_for_parallel_job_pid "$pid"; then + status=0 + else + status=$? + fi + if (( status == 127 && has_reaped_status != 0 )); then + status=$reaped_status + has_reaped_status=0 + fi + + if (( status != 0 )); then + failed_ref=1 + fi + done + + job_pids_ref=("${remaining_pids[@]}") +} + +wait_for_next_parallel_job() { + local -r job_pids_name="$1"; shift + local -r failed_name="$1"; shift + # shellcheck disable=SC2178 + local -n job_pids_ref="$job_pids_name" + local -i status=0 + + set +e + wait -n "${job_pids_ref[@]}" + status=$? + set -e + + reap_finished_parallel_jobs "$job_pids_name" "$failed_name" "$status" +} + +wait_for_parallel_job_slot() { + local -r job_pids_name="$1"; shift + local -r failed_name="$1"; shift + local -r max_jobs="$1"; shift + # shellcheck disable=SC2178 + local -n job_pids_ref="$job_pids_name" + + while (( ${#job_pids_ref[@]} >= max_jobs )); do + wait_for_next_parallel_job "$job_pids_name" "$failed_name" + done +} + +wait_for_parallel_jobs() { + # shellcheck disable=SC2178 + local -n job_pids_ref="$1"; shift + local -n failed_ref="$1"; shift + + : "$failed_ref" + while (( ${#job_pids_ref[@]} > 0 )); do + if ! wait_for_parallel_job_pid "${job_pids_ref[0]}"; then + failed_ref=1 + fi + job_pids_ref=("${job_pids_ref[@]:1}") + done +} + +wait_for_image_job_slot() { + local -r image_job_pids_name="$1"; shift + local -r failed_name="$1"; shift + + wait_for_parallel_job_slot \ + "$image_job_pids_name" \ + "$failed_name" \ + "$IMAGE_JOBS" +} + +wait_for_image_jobs() { + local -r image_job_pids_name="$1"; shift + local -r failed_name="$1"; shift + + wait_for_parallel_jobs "$image_job_pids_name" "$failed_name" +} + +wait_for_template_render_job_slot() { + local -r render_job_pids_name="$1"; shift + local -r failed_name="$1"; shift + + wait_for_parallel_job_slot \ + "$render_job_pids_name" \ + "$failed_name" \ + "$IMAGE_JOBS" +} + +wait_for_template_render_jobs() { + local -r render_job_pids_name="$1"; shift + local -r failed_name="$1"; shift + + wait_for_parallel_jobs "$render_job_pids_name" "$failed_name" +} diff --git a/src/lib/process.source.sh b/src/lib/process.source.sh new file mode 100644 index 0000000..5596e03 --- /dev/null +++ b/src/lib/process.source.sh @@ -0,0 +1,24 @@ +run_with_timeout() { + local -r description="$1"; shift + local -r seconds="$1"; shift + local -i status=0 + + if ! command -v timeout >/dev/null 2>&1; then + printf 'ERROR: timeout command is required to run %s\n' \ + "$description" >&2 + return 127 + fi + + if timeout "$seconds" "$@"; then + return + else + status=$? + fi + + if (( status == 124 )); then + printf 'ERROR: %s timed out after %s seconds\n' \ + "$description" "$seconds" >&2 + fi + + return "$status" +} diff --git a/src/lib/random.source.sh b/src/lib/random.source.sh new file mode 100644 index 0000000..5a1ef59 --- /dev/null +++ b/src/lib/random.source.sh @@ -0,0 +1,74 @@ +random_seed_is_set() { + [ -n "$RANDOM_SEED" ] +} + +deterministic_index() { + local -r namespace="$1"; shift + local -r count="$1"; shift + local checksum + + checksum=$(printf '%s' "${RANDOM_SEED}:$namespace" | cksum) + checksum=${checksum%% *} + + printf '%s\n' $(( checksum % count )) +} + +random_index() { + local -r namespace="$1"; shift + local -r count="$1"; shift + + if random_seed_is_set; then + deterministic_index "$namespace" "$count" + else + printf '%s\n' $(( RANDOM % count )) + fi +} + +random_animation_css_class() { + local -r speed="$1"; shift + local -r context="${1:-$speed}" + local -i index + local -a classes=( + "animate-opacity-$speed" + "animate-top-$speed" + "animate-left-$speed" + "animate-right-$speed" + "animate-bottom-$speed" + "animate-zoom-$speed" + "animate-snap-rotate-$speed" + "animate-hard-zoom-$speed" + "animate-slam-left-$speed" + "animate-slam-right-$speed" + "animate-flash-in-$speed" + "animate-invert-pop-$speed" + "animate-posterize-pop-$speed" + "animate-skew-snap-$speed" + "animate-glitch-step-$speed" + ) + + index=$(random_index "animation:$speed:$context" "${#classes[@]}") + printf '%s\n' "${classes[index]}" +} + +deterministic_shuffle() { + local checksum + local line + + while IFS= read -r line; do + checksum=$(printf '%s' "${RANDOM_SEED}:shuffle:$line" | cksum) + checksum=${checksum%% *} + printf '%010u\t%s\n' "$checksum" "$line" + done | sort -n -k1,1 -k2,2 | cut -f2- +} + +maybe_shuffle() { + if [ "$SHUFFLE" = yes ]; then + if random_seed_is_set; then + deterministic_shuffle + else + sort -R + fi + else + sort + fi +} diff --git a/src/lib/system.source.sh b/src/lib/system.source.sh deleted file mode 100644 index 5d6f8e7..0000000 --- a/src/lib/system.source.sh +++ /dev/null @@ -1,172 +0,0 @@ -# shellcheck disable=SC2034 -resolve_imagemagick_command() { - local -r operation="$1"; shift - local -n command_ref="$1"; shift - - command_ref=() - - case "$operation" in - convert) - if command -v magick >/dev/null 2>&1; then - command_ref=(magick) - return - fi - if command -v convert >/dev/null 2>&1; then - command_ref=(convert) - return - fi - ;; - identify) - if command -v magick >/dev/null 2>&1; then - command_ref=(magick identify) - return - fi - if command -v identify >/dev/null 2>&1; then - command_ref=(identify) - return - fi - if command -v convert >/dev/null 2>&1; then - command_ref=(convert) - return - fi - ;; - *) - printf 'ERROR: Unknown ImageMagick operation %s\n' \ - "$operation" >&2 - return 1 - ;; - esac - - printf 'ERROR: ImageMagick is required; install magick or convert\n' >&2 - return 127 -} - -imagemagick() { - local -a imagemagick_command=() - - resolve_imagemagick_command convert imagemagick_command || return - - run_with_timeout ImageMagick "$IMAGEMAGICK_TIMEOUT" \ - "${imagemagick_command[@]}" "$@" -} - -imagemagick_identify() { - local -a imagemagick_command=() - - resolve_imagemagick_command identify imagemagick_command || return - - if [ "${imagemagick_command[0]}" = convert ]; then - if [[ "${1:-}" = '-verbose' && $# -eq 2 ]]; then - run_with_timeout ImageMagick "$IMAGEMAGICK_TIMEOUT" \ - "${imagemagick_command[@]}" "$2" -verbose info: - else - run_with_timeout ImageMagick "$IMAGEMAGICK_TIMEOUT" \ - "${imagemagick_command[@]}" "$@" info: - fi - return - fi - - run_with_timeout ImageMagick "$IMAGEMAGICK_TIMEOUT" \ - "${imagemagick_command[@]}" "$@" -} - -run_with_timeout() { - local -r description="$1"; shift - local -r seconds="$1"; shift - local -i status=0 - - if ! command -v timeout >/dev/null 2>&1; then - printf 'ERROR: timeout command is required to run %s\n' \ - "$description" >&2 - return 127 - fi - - if timeout "$seconds" "$@"; then - return - else - status=$? - fi - - if (( status == 124 )); then - printf 'ERROR: %s timed out after %s seconds\n' \ - "$description" "$seconds" >&2 - fi - - return "$status" -} - -tarball() { - local -r tarball_name="$1"; shift - local -r tarball_suffix="$TARBALL_SUFFIX" - local base - local old_tarball - local -a tar_opts=() - - # Cleanup tarballs from previous runs for the configured suffix. - while IFS= read -r -d '' old_tarball; do - if [[ "$old_tarball" == *"$tarball_suffix" ]]; then - rm -f "$old_tarball" - fi - done < <(find "$DIST_DIR" -maxdepth 1 -type f -print0) - - base=$(basename "$INCOMING_DIR") - resolve_tar_opts tar_opts - - log_info "Creating tarball $(_display_path "$DIST_DIR/$tarball_name")" \ - "from $INCOMING_DIR" - ( - cd "$(dirname "$INCOMING_DIR")" - run_with_timeout tar "$TAR_TIMEOUT" \ - tar "${tar_opts[@]}" -f "$DIST_DIR/$tarball_name" "$base" - ) -} - -resolve_tar_opts() { - local -n options_ref="$1"; shift - local tar_opts_decl - - options_ref=() - - if ! tar_opts_decl=$(declare -p TAR_OPTS 2>/dev/null); then - options_ref=(-c) - return - fi - - case "$tar_opts_decl" in - declare\ -a*\ TAR_OPTS=*) - options_ref=("${TAR_OPTS[@]}") - ;; - *) - if [ -n "${TAR_OPTS:-}" ]; then - read -r -a options_ref <<< "$TAR_OPTS" - fi - ;; - esac - - if (( ${#options_ref[@]} == 0 )); then - options_ref=(-c) - fi -} - -resolve_sync_destinations() { - local -n destinations_ref="$1"; shift - local destinations_decl - - destinations_ref=() - - if ! destinations_decl=$(declare -p SYNC_DESTINATIONS 2>/dev/null); then - return - fi - - case "$destinations_decl" in - declare\ -a*\ SYNC_DESTINATIONS=*) - destinations_ref=("${SYNC_DESTINATIONS[@]}") - ;; - *) - if [ -n "${SYNC_DESTINATIONS:-}" ]; then - # shellcheck disable=SC2034 - read -r -a destinations_ref <<< "$SYNC_DESTINATIONS" - fi - ;; - esac -} diff --git a/src/shuriken.sh b/src/shuriken.sh index d8a6923..4474600 100755 --- a/src/shuriken.sh +++ b/src/shuriken.sh @@ -76,12 +76,20 @@ declare -r SHURIKEN_SOURCE_DIR # SHURIKEN_LIB_SOURCES_BEGIN # shellcheck source=src/lib/bootstrap.source.sh source "$SHURIKEN_SOURCE_DIR/lib/bootstrap.source.sh" -# shellcheck source=src/lib/system.source.sh -source "$SHURIKEN_SOURCE_DIR/lib/system.source.sh" +# shellcheck source=src/lib/imagemagick.source.sh +source "$SHURIKEN_SOURCE_DIR/lib/imagemagick.source.sh" +# shellcheck source=src/lib/process.source.sh +source "$SHURIKEN_SOURCE_DIR/lib/process.source.sh" +# shellcheck source=src/lib/archive.source.sh +source "$SHURIKEN_SOURCE_DIR/lib/archive.source.sh" # shellcheck source=src/lib/template.source.sh source "$SHURIKEN_SOURCE_DIR/lib/template.source.sh" +# shellcheck source=src/lib/job-pool.source.sh +source "$SHURIKEN_SOURCE_DIR/lib/job-pool.source.sh" # shellcheck source=src/lib/image.source.sh source "$SHURIKEN_SOURCE_DIR/lib/image.source.sh" +# shellcheck source=src/lib/random.source.sh +source "$SHURIKEN_SOURCE_DIR/lib/random.source.sh" # shellcheck source=src/lib/album.source.sh source "$SHURIKEN_SOURCE_DIR/lib/album.source.sh" # shellcheck source=src/lib/config.source.sh |
