125 lines
3.8 KiB
Bash
Executable file
125 lines
3.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Pre-fetch every alternative declared in audio-options.json so the
|
|
# design app at /audio can preview them via its dropdown selector.
|
|
#
|
|
# Files land at .local/audio-alternatives/<safe_path>/<idx>.ogg —
|
|
# outside the shipped assets tree, so audio-validate.py's orphan scan
|
|
# is not affected. The design app exposes them via Vite's `@audio-alts`
|
|
# alias.
|
|
#
|
|
# Idempotent: skips alternatives whose .ogg already exists.
|
|
|
|
set -uo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
THEME="${AUDIO_THEME:-age-of-dwarves}"
|
|
OPTIONS_JSON="$REPO_ROOT/public/games/$THEME/data/audio-options.json"
|
|
ALT_ROOT="$REPO_ROOT/.local/audio-alternatives"
|
|
STAGING="$REPO_ROOT/.local/audio-staging"
|
|
|
|
if [ ! -f "$OPTIONS_JSON" ]; then
|
|
echo "audio-options.json not found at $OPTIONS_JSON" >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$ALT_ROOT" "$STAGING"
|
|
|
|
ok=0
|
|
skip=0
|
|
fail=0
|
|
|
|
# Walk every (output_path, idx, source_url) triple via python (jq isn't
|
|
# guaranteed everywhere; python3 is). One line per triple, tab-separated.
|
|
mapfile -t ROWS < <(python3 -c "
|
|
import json
|
|
data = json.load(open('$OPTIONS_JSON'))
|
|
for output_path, opts in data.get('options', {}).items():
|
|
for i, opt in enumerate(opts):
|
|
url = opt.get('source_url', '')
|
|
if not url:
|
|
continue
|
|
print(f'{output_path}\t{i}\t{url}')
|
|
")
|
|
|
|
for row in "${ROWS[@]}"; do
|
|
IFS=$'\t' read -r output_path idx source_url <<< "$row"
|
|
[ -z "$output_path" ] && continue
|
|
|
|
safe_key="$(printf '%s' "$output_path" | tr '/' '_' | sed 's/\.ogg$//')"
|
|
out_dir="$ALT_ROOT/$safe_key"
|
|
out_file="$out_dir/$idx.ogg"
|
|
|
|
if [ -f "$out_file" ]; then
|
|
skip=$((skip + 1))
|
|
continue
|
|
fi
|
|
|
|
echo "→ $output_path[$idx]"
|
|
mkdir -p "$out_dir"
|
|
|
|
fetch_url="$source_url"
|
|
inner_path=""
|
|
if [[ "$source_url" == *"#"* ]]; then
|
|
fetch_url="${source_url%%#*}"
|
|
inner_path="${source_url#*#}"
|
|
fi
|
|
case "$fetch_url" in
|
|
https://github.com/*/blob/*)
|
|
fetch_url="$(echo "$fetch_url" | sed -e 's|github.com|raw.githubusercontent.com|' -e 's|/blob/|/|')"
|
|
;;
|
|
esac
|
|
|
|
if [ -n "$inner_path" ]; then
|
|
zip_hash="$(printf '%s' "$fetch_url" | shasum | cut -c1-8)"
|
|
zip_cache="$STAGING/_zip_${zip_hash}.zip"
|
|
zip_extract_dir="$STAGING/_zip_${zip_hash}"
|
|
if [ ! -f "$zip_cache" ]; then
|
|
if ! curl -sfL -o "$zip_cache" "$fetch_url"; then
|
|
echo " ✗ ZIP download failed: $fetch_url" >&2
|
|
fail=$((fail + 1)); continue
|
|
fi
|
|
fi
|
|
if [ ! -d "$zip_extract_dir" ]; then
|
|
mkdir -p "$zip_extract_dir"
|
|
if ! unzip -q -o "$zip_cache" -d "$zip_extract_dir"; then
|
|
echo " ✗ ZIP extract failed" >&2
|
|
fail=$((fail + 1)); continue
|
|
fi
|
|
fi
|
|
staged="$zip_extract_dir/$inner_path"
|
|
if [ ! -f "$staged" ]; then
|
|
echo " ✗ ZIP missing inner file: $inner_path" >&2
|
|
fail=$((fail + 1)); continue
|
|
fi
|
|
else
|
|
src_ext="${fetch_url##*.}"
|
|
case "$src_ext" in
|
|
wav|ogg|mp3|flac) ;;
|
|
*) src_ext="bin" ;;
|
|
esac
|
|
staged="$STAGING/_alt_${safe_key}_${idx}.${src_ext}"
|
|
if ! curl -sfL -o "$staged" "$fetch_url"; then
|
|
echo " ✗ download failed: $fetch_url" >&2
|
|
fail=$((fail + 1)); continue
|
|
fi
|
|
fi
|
|
|
|
if ! ffmpeg -y -hide_banner -loglevel error \
|
|
-i "$staged" \
|
|
-af "loudnorm=I=-16:TP=-3:LRA=11,aresample=44100" \
|
|
-c:a libvorbis -b:a 128k \
|
|
"$out_file"; then
|
|
echo " ✗ encode failed" >&2
|
|
fail=$((fail + 1)); continue
|
|
fi
|
|
ok=$((ok + 1))
|
|
done
|
|
|
|
echo ""
|
|
echo "── alternatives summary ────"
|
|
echo " ok: $ok"
|
|
echo " skip: $skip"
|
|
echo " fail: $fail"
|
|
[ $fail -gt 0 ] && exit 1
|
|
exit 0
|