#!/usr/bin/env bash # Report items whose poster is missing or does not resolve. Read-only. set -euo pipefail readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly COLLECTION_DIR="$(dirname "$SCRIPT_DIR")" # shellcheck source=../lib/plex-api.sh . "$COLLECTION_DIR/lib/plex-api.sh" # shellcheck source=../lib/posters.sh . "$COLLECTION_DIR/lib/posters.sh" SECTION="TV Shows" TYPE=2 FORMAT=text LIST_OK=0 # Global, not local to main(): an EXIT trap fires after main() returns, by which # point a local would be out of scope and `set -u` would abort the cleanup. WORKDIR="" cleanup() { [[ -n "$WORKDIR" ]] && rm -rf "$WORKDIR"; return 0; } trap cleanup EXIT usage() { cat <<'EOF' Usage: poster-audit [--section ] [--type ] [--json] [--list-ok] --section Library section, by id or exact title. Default: "TV Shows". --type Plex type code: 1=movie 2=show 3=season 4=episode. Default: 2. --json Emit JSON instead of a human report. --list-ok Also list items whose poster is fine. --jobs N Parallel HTTP checks (env POSTER_JOBS, default 8). --help Statuses: MISSING no thumb field on the item at all BROKEN thumb field present, but the URL returns non-200 Read-only: makes no changes. Use poster-repair to fix what this finds. EOF } main() { while (( $# )); do case "$1" in --section) SECTION="${2:?--section needs a value}"; shift ;; --type) TYPE="${2:?--type needs a value}"; shift ;; --json) FORMAT=json ;; --list-ok) LIST_OK=1 ;; --jobs) POSTER_JOBS="${2:?--jobs needs a value}"; export POSTER_JOBS; shift ;; --help|-h) usage; exit 0 ;; *) plex_die "unknown argument: $1 (try --help)" ;; esac shift done plex_require_deps plex_load_env "$COLLECTION_DIR/.env" plex_check_auth local section_id section_id="$(posters_resolve_section "$SECTION")" WORKDIR="$(mktemp -d)" posters_fetch_items "$section_id" "$TYPE" "$WORKDIR/items.json" posters_scan "$WORKDIR/items.json" | sort -t$'\t' -k3 > "$WORKDIR/scan.tsv" local total missing broken ok total="$(wc -l < "$WORKDIR/scan.tsv" | tr -d ' ')" missing="$(awk -F'\t' '$2=="MISSING"' "$WORKDIR/scan.tsv" | wc -l | tr -d ' ')" broken="$(awk -F'\t' '$2=="BROKEN"' "$WORKDIR/scan.tsv" | wc -l | tr -d ' ')" ok="$(awk -F'\t' '$2=="OK"' "$WORKDIR/scan.tsv" | wc -l | tr -d ' ')" if [[ "$FORMAT" == json ]]; then jq -Rn --arg section "$SECTION" --arg id "$section_id" --arg type "$TYPE" \ --argjson total "$total" --argjson missing "$missing" \ --argjson broken "$broken" --argjson ok "$ok" \ --rawfile scan "$WORKDIR/scan.tsv" \ '{section:$section, sectionId:$id, type:$type, totals:{total:$total, ok:$ok, missing:$missing, broken:$broken}, items:[$scan|split("\n")[]|select(length>0)|split("\t") |{ratingKey:.[0], status:.[1], title:.[2]}] | map(select(.status != "OK"))}' return 0 fi plex_log "Poster audit — section '${SECTION}' (id ${section_id}, type ${TYPE})" plex_log " total ${total} ok ${ok} broken ${broken} missing ${missing}" if (( broken + missing > 0 )); then plex_log "" plex_log "Needs repair:" awk -F'\t' '$2!="OK" {printf " %-8s %-8s %s\n", $1, $2, $3}' "$WORKDIR/scan.tsv" plex_log "" plex_log "Run: bin/poster-repair --section '${SECTION}' --dry-run" else plex_log "" plex_log "All posters resolve." fi if (( LIST_OK )); then plex_log "" plex_log "OK:" awk -F'\t' '$2=="OK" {printf " %-8s %s\n", $1, $3}' "$WORKDIR/scan.tsv" fi } main "$@"