#!/bin/sh # 20260228 ChatGPT # $Header$ # # This script creates links from the staging directory to # the glbs sibling directory. The staging directory is where the glbs are staged for rendering. # The glbs sibling directory is where the PNG & webP batch renderer expects to find the glbs. The files names # are sanitized to be filesystem safe and to avoid collisions by adding the path components # delimited by double underscores. # If a collision is detected, a numeric suffix is added to disambiguate. # Consequently, the name of the link is not the same as the original glb, but contains the original path # components in a sanitized form. This allows the batch renderer to process all the glbs without worrying # about name collisions, while still retaining some traceability to the original files. # # For example, if the staging directory contains: # /home/jlpoole/work/Voron/test1/Klicky-Probe/Printers/Voron/v1.8_v2.4_Legacy_Trident/v1.8_v2.4_Legacy_Trident_STL/Dock_mount_fixed_v2.glb # then this script will create the very long file name: # glbs/Klicky-Probe__Printers__Voron__...__Dock_mount_fixed_v2.stl.glb # # Note: the brackets in a file name, e.g. "[a]" are preserved. # Another example, if the staging directory contains: # /home/jlpoole/work/Voron/test1/Voron/Skirts/[a]_belt_guard_a_x2.stl.glb # then this script will create the file name: # 'Voron-2__STLs__Skirts__[a]_belt_guard_a_x2.stl.glb' # # Copy/paste: # cd ~/work/Voron/renderlab/web/batch_glb_png # chmod +x link_glbs.sh # ./link_glbs.sh /home/jlpoole/work/Voron/test1 # # This creates symlinks under ./glbs/ pointing to every *.glb under the staging directory. set -eu ROOT="${1:-}" [ -n "$ROOT" ] || { echo "Usage: $0 /path/to/root" >&2; exit 2; } [ -d "$ROOT" ] || { echo "ERROR: not a directory: $ROOT" >&2; exit 2; } OUTDIR="./glbs" mkdir -p "$OUTDIR" ts="$(date +%Y%m%d_%H%M%S)" log="link_glbs_${ts}.log" # Make ROOT absolute and strip any trailing slash for consistent relpath math. ROOT_ABS="$(cd "$ROOT" && pwd)" ROOT_ABS="${ROOT_ABS%/}" echo "ROOT: $ROOT_ABS" | tee "$log" echo "OUTDIR: $(cd "$OUTDIR" && pwd)" | tee -a "$log" echo "LOG: $log" | tee -a "$log" echo "" | tee -a "$log" # Sanitize: turn a path into a filesystem-safe basename. # Example: # Klicky-Probe/Printers/Voron/.../Dock_mount_fixed_v2.stl.glb # becomes: # Klicky-Probe__Printers__Voron__...__Dock_mount_fixed_v2.stl.glb sanitize() { # shellcheck disable=SC2001 echo "$1" \ | sed 's|^/||' \ | sed 's|/|__|g' #| sed 's|/|__|g' \ #| sed 's|[^A-Za-z0-9._-]|_|g' } count=0 skipped=0 collisions=0 # Use -print0 to handle spaces safely. find "$ROOT_ABS" -type f -name '*.glb' -print0 \ | while IFS= read -r -d '' f; do # Compute relative path under ROOT_ABS rel="${f#$ROOT_ABS/}" name="$(sanitize "$rel")" linkpath="$OUTDIR/$name" # If the link exists and points to the same target, skip quietly. if [ -L "$linkpath" ]; then target="$(readlink "$linkpath" || true)" if [ "$target" = "$f" ]; then echo "SKIP (already linked): $linkpath -> $f" >> "$log" skipped=$((skipped+1)) continue fi fi # If the name already exists but points elsewhere, disambiguate. if [ -e "$linkpath" ]; then i=1 base="$linkpath" while [ -e "$linkpath" ]; do linkpath="${base%.glb}_$i.glb" i=$((i+1)) done collisions=$((collisions+1)) fi ln -s "$f" "$linkpath" echo "LINK: $linkpath -> $f" >> "$log" count=$((count+1)) done # The while loop runs in a subshell in many /bin/sh implementations, # so counters above may not update. Summarize by counting log lines instead. linked_lines="$(grep -c '^LINK: ' "$log" 2>/dev/null || echo 0)" skipped_lines="$(grep -c '^SKIP ' "$log" 2>/dev/null || echo 0)" echo "" | tee -a "$log" echo "DONE. linked=$linked_lines skipped=$skipped_lines (details in $log)" | tee -a "$log"