Files
Vimix-Hyprcursor/svg/svg-recolor.sh
2026-01-20 22:00:13 -05:00

157 lines
4.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Recolour all SVGs under the current directory in place using /tmp (tmpfs),
# skipping symlinked directories and files; parallel by physical cores by default.
set -euo pipefail
# Defaults
PALETTE="${PALETTE:-$HOME/dotfiles/theme/color-theme.json}"
RECOLOR="${RECOLOR:-$HOME/dotfiles/scripts/svg-recolor.py}"
SIGMA="${SIGMA:-20}"
ALPHA="${ALPHA:-0.6}"
THREADS="${THREADS:-auto}" # auto => number of physical cores
QUIET=0
DRY_RUN=0
usage() {
cat <<EOF
Usage: $(basename "$0") [--sigma S] [--alpha A] [--palette PATH] [--recolor PATH] [--threads N|auto] [--dry-run] [--quiet]
--sigma S Std-dev in Lab units (default: ${SIGMA})
--alpha A Pull strength 0..1 (default: ${ALPHA})
--palette P Palette JSON path (default: ${PALETTE})
--recolor R Recolor script path (default: ${RECOLOR})
--threads N Parallel workers (default: auto = physical cores)
--dry-run Only print files that would be recoloured
--quiet Suppress per-file messages
Environment overrides: SIGMA, ALPHA, PALETTE, RECOLOR, THREADS
EOF
}
# Parse options
while [[ $# -gt 0 ]]; do
case "$1" in
--sigma) SIGMA="$2"; shift 2 ;;
--alpha) ALPHA="$2"; shift 2 ;;
--palette) PALETTE="$2"; shift 2 ;;
--recolor) RECOLOR="$2"; shift 2 ;;
--threads) THREADS="$2"; shift 2 ;;
--dry-run) DRY_RUN=1; shift ;;
--quiet) QUIET=1; shift ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown option: $1" >&2; usage; exit 2 ;;
esac
done
# Preflight
[[ -f "$PALETTE" ]] || { echo "Palette not found: $PALETTE" >&2; exit 1; }
[[ -f "$RECOLOR" ]] || { echo "Recolor script not found: $RECOLOR" >&2; exit 1; }
# Get physical core count (prefer lscpu), fall back to nproc
get_physical_cores() {
if command -v lscpu >/dev/null 2>&1; then
# Unique Core,Socket pairs => physical cores
local n
n=$(lscpu -p=Core,Socket 2>/dev/null | grep -v '^#' | sort -u | wc -l)
if [[ "$n" -gt 0 ]]; then
echo "$n"; return
fi
# Fallback via totals / threads-per-core
local cpu tpc
cpu=$(lscpu | awk -F: '/^CPU\(s\)/{gsub(/ /,"",$2);print $2;exit}')
tpc=$(lscpu | awk -F: '/^Thread\(s\) per core/{gsub(/ /,"",$2);print $2;exit}')
if [[ -n "${cpu:-}" && -n "${tpc:-}" && "$tpc" -gt 0 ]]; then
echo $(( (cpu + tpc - 1) / tpc )); return
fi
fi
# Last resort
if command -v nproc >/dev/null 2>&1; then
echo "$(nproc)"
else
echo 1
fi
}
calc_jobs() {
local files="$1"
local j
if [[ "$THREADS" == "auto" || -z "$THREADS" ]]; then
j=$(get_physical_cores)
else
j="$THREADS"
fi
# Bound by number of files (avoid spawning more workers than tasks)
if [[ "$files" -lt "$j" ]]; then
j="$files"
fi
# Ensure >=1
[[ "$j" -ge 1 ]] || j=1
echo "$j"
}
TMP_LIST=()
cleanup() {
for t in "${TMP_LIST[@]:-}"; do [[ -e "$t" ]] && rm -f -- "$t"; done
}
trap cleanup EXIT
process_one() {
local f="$1"
# Double guard: skip if symlink file (in addition to find -type f)
[[ -L "$f" ]] && { (( QUIET == 0 )) && echo "Skip symlink file: $f"; return 0; }
local tmp
tmp="$(mktemp /tmp/svg-recolor.XXXXXX.svg)"
TMP_LIST+=("$tmp")
if (( DRY_RUN )); then
echo "Would recolor: $f"
rm -f -- "$tmp"; TMP_LIST=("${TMP_LIST[@]:0:${#TMP_LIST[@]}-1}")
return 0
fi
if ! python3 "$RECOLOR" "$f" "$PALETTE" "$tmp" --sigma "$SIGMA" --alpha "$ALPHA"; then
echo "ERROR: recolor failed on: $f" >&2
rm -f -- "$tmp"; TMP_LIST=("${TMP_LIST[@]:0:${#TMP_LIST[@]}-1}")
return 1
fi
if cmp -s -- "$f" "$tmp"; then
(( QUIET == 0 )) && echo "Unchanged: $f"
rm -f -- "$tmp"; TMP_LIST=("${TMP_LIST[@]:0:${#TMP_LIST[@]}-1}")
return 0
fi
chmod --reference="$f" "$tmp" 2>/dev/null || true
touch --reference="$f" "$tmp" 2>/dev/null || true
mv -f -- "$tmp" "$f"
TMP_LIST=("${TMP_LIST[@]:0:${#TMP_LIST[@]}-1}")
(( QUIET == 0 )) && echo "Recoloured: $f"
}
export -f process_one
export RECOLOR PALETTE SIGMA ALPHA QUIET DRY_RUN TMP_LIST
# Collect file list first to decide threads; -P => never follow symlinks
mapfile -d '' FILES < <(find -P . -type f -iname '*.svg' -print0)
TOTAL="${#FILES[@]}"
if (( TOTAL == 0 )); then
(( QUIET == 0 )) && echo "No SVG files found."
exit 0
fi
JOBS="$(calc_jobs "$TOTAL")"
(( QUIET == 0 )) && echo "Processing $TOTAL SVG(s) with $JOBS worker(s)..."
# Parallel execution (preserving NUL safety)
if (( JOBS > 1 )); then
printf '%s\0' "${FILES[@]}" | \
xargs -0 -n1 -P"$JOBS" bash -c 'process_one "$@"' _
else
# serial fallback
for path in "${FILES[@]}"; do
process_one "$path"
done
fi