left4me/examples/script-overlays/cedapug_maps.sh
mwiegand 196d2db33e
feat(l4d2-web): seed example script overlays from examples/script-overlays/
Bundles four reference script overlays (cedapug_maps, l4d2center_maps,
competitive_rework, tickrate) and adds a `flask seed-script-overlays`
CLI that upserts each *.sh as a system-wide overlay. Test deploy
invokes it after the orphan-cleanup migration so fresh test servers
come up with the same overlays the user has been maintaining by hand.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:41:08 +02:00

109 lines
3.7 KiB
Bash

#!/bin/bash
# Cedapug custom-maps overlay.
# Scrapes the JS map list at https://cedapug.com/custom and keeps
# $OVERLAY/left4dead2/addons/ in sync. Re-downloads only when the zip's
# Last-Modified header changed.
set -euo pipefail
CEDAPUG_HOST="https://cedapug.com"
LIST_URL="$CEDAPUG_HOST/custom"
ADDONS_DIR="$OVERLAY/left4dead2/addons"
STATE_DIR="$OVERLAY/.cedapug"
WORK_DIR="$STATE_DIR/work"
MANIFEST="$STATE_DIR/manifest.tsv" # zip_basename \t last_modified \t vpk1,vpk2,...
mkdir -p "$ADDONS_DIR" "$STATE_DIR" "$WORK_DIR"
# --- 1. Discover the current zip list -----------------------------------
# The page embeds the map list inside renderCustomMapDownloads([...]) with
# JSON-escaped slashes (\/maps\/foo.zip). Unescape with sed before grepping.
echo ":: fetching $LIST_URL"
mapfile -t ZIP_PATHS < <(
curl -fsSL --retry 3 --retry-delay 2 "$LIST_URL" \
| sed 's@\\/@/@g' \
| grep -oE '"/maps/[A-Za-z0-9_.+-]+\.zip"' \
| tr -d '"' \
| sort -u
)
if [[ ${#ZIP_PATHS[@]} -eq 0 ]]; then
echo "!! no map zips found on $LIST_URL — page format may have changed" >&2
exit 1
fi
echo ":: ${#ZIP_PATHS[@]} custom map zip(s) listed"
# --- 2. Load previous manifest ------------------------------------------
declare -A PREV_LM PREV_VPKS
if [[ -f "$MANIFEST" ]]; then
while IFS=$'\t' read -r zname lm vpks; do
[[ -n "$zname" ]] || continue
PREV_LM[$zname]=$lm
PREV_VPKS[$zname]=$vpks
done < "$MANIFEST"
fi
# --- 3. Download / extract each desired zip -----------------------------
declare -A NEW_LM NEW_VPKS
for zpath in "${ZIP_PATHS[@]}"; do
zname="${zpath##*/}"
url="$CEDAPUG_HOST$zpath"
lm="$(curl -fsSI --retry 3 --retry-delay 2 "$url" \
| awk 'BEGIN{IGNORECASE=1} /^last-modified:/ {sub(/^[^:]*:[[:space:]]*/, ""); gsub(/\r/, ""); print; exit}')"
NEW_LM[$zname]=$lm
prev_lm="${PREV_LM[$zname]:-}"
prev_vpks="${PREV_VPKS[$zname]:-}"
# Skip if Last-Modified matches AND every recorded vpk is still on disk.
if [[ -n "$lm" && "$lm" == "$prev_lm" && -n "$prev_vpks" ]]; then
all_present=1
IFS=',' read -r -a v_arr <<<"$prev_vpks"
for v in "${v_arr[@]}"; do
[[ -f "$ADDONS_DIR/$v" ]] || { all_present=0; break; }
done
if (( all_present )); then
echo ":: $zname unchanged (Last-Modified: $lm) — skipped"
NEW_VPKS[$zname]=$prev_vpks
continue
fi
fi
echo ":: downloading $zname"
zfile="$WORK_DIR/$zname"
curl -fSL --retry 3 --retry-delay 2 -o "$zfile" "$url"
7z x -y -bd -o"$ADDONS_DIR" "$zfile" '*.vpk' >/dev/null
vpk_list="$(7z l -slt -ba "$zfile" \
| awk '/^Path = .*\.vpk$/ {sub(/^Path = /, ""); print}' \
| paste -sd, -)"
if [[ -z "$vpk_list" ]]; then
echo "!! warning: $zname contained no .vpk — leaving manifest entry empty" >&2
fi
NEW_VPKS[$zname]=$vpk_list
rm -f "$zfile"
done
# --- 4. Prune vpks from zips that disappeared from the listing ----------
for old_zname in "${!PREV_VPKS[@]}"; do
if [[ -z "${NEW_LM[$old_zname]:-}" ]]; then
IFS=',' read -r -a old_v_arr <<<"${PREV_VPKS[$old_zname]}"
for v in "${old_v_arr[@]}"; do
if [[ -n "$v" && -f "$ADDONS_DIR/$v" ]]; then
echo ":: pruning $v (zip $old_zname no longer listed)"
rm -f "$ADDONS_DIR/$v"
fi
done
fi
done
# --- 5. Rewrite manifest -------------------------------------------------
tmp_manifest="$(mktemp -p "$STATE_DIR")"
for zname in "${!NEW_LM[@]}"; do
printf '%s\t%s\t%s\n' "$zname" "${NEW_LM[$zname]}" "${NEW_VPKS[$zname]:-}" >>"$tmp_manifest"
done
mv -f "$tmp_manifest" "$MANIFEST"
rm -rf "$WORK_DIR"
echo ":: done — ${#NEW_LM[@]} zip(s) tracked, addons/ in sync"