#!/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"