#!/usr/bin/env bash # scan_and_prefix_pink.sh # # Scan a directory for MP4 files, detect a pink/magenta intro, # and prefix matching files (unless already prefixed). # # Options: # --dry-run, --recursive/-r, --prefix PREFIX, etc. (same as before) set -euo pipefail # ---- Defaults ---- DRY_RUN=0 RECURSIVE=0 PREFIX="PINK_" DURATION="1.0" FPS="6" HUE_MIN="285" HUE_MAX="330" SAT_MIN="0.35" VAL_MIN="0.35" MIN_RATIO="0.6" # ---- Parse args ---- if [[ $# -lt 1 ]]; then echo "Usage: $0 [--dry-run] [--recursive|-r] [--prefix PREFIX] ..." >&2 exit 2 fi DIR="$1"; shift || true [[ -d "$DIR" ]] || { echo "Not a directory: $DIR" >&2; exit 2; } while [[ $# -gt 0 ]]; do case "$1" in --dry-run) DRY_RUN=1; shift ;; --recursive|-r) RECURSIVE=1; shift ;; --prefix) PREFIX="${2:-}"; shift 2 ;; --duration) DURATION="${2:-}"; shift 2 ;; --fps) FPS="${2:-}"; shift 2 ;; --hue-min) HUE_MIN="${2:-}"; shift 2 ;; --hue-max) HUE_MAX="${2:-}"; shift 2 ;; --sat-min) SAT_MIN="${2:-}"; shift 2 ;; --val-min) VAL_MIN="${2:-}"; shift 2 ;; --min-ratio) MIN_RATIO="${2:-}"; shift 2 ;; --help|-h) grep -E '^# ' "$0" | sed 's/^# //'; exit 0 ;; *) echo "Unknown option: $1" >&2; exit 2 ;; esac done # ---- Helpers ---- print_result() { # $1=status $2=file $3=detail printf "%-10s %s%s\n" "$1" "$2" "${3:+ ($3)}" } safe_rename() { # $1=path $2=prefix local path="$1" pre="$2" local dir base target dir=$(dirname -- "$path") base=$(basename -- "$path") target="${dir}/${pre}${base}" # Avoid collisions if [[ -e "$target" ]]; then local n=1 while [[ -e "${target}.${n}" ]]; do n=$((n+1)); done target="${target}.${n}" fi if [[ $DRY_RUN -eq 1 ]]; then print_result "RENAMED" "$path" "dry-run → $(basename -- "$target")" else if mv -- "$path" "$target"; then print_result "RENAMED" "$path" "→ $(basename -- "$target")" else print_result "ERROR" "$path" "rename failed" fi fi } detect_pink() { # $1=file -> echoes "total pink ratio" local file="$1" LC_ALL=C ffmpeg -hide_banner -v error \ -i "$file" -t "$DURATION" \ -vf "fps=${FPS},scale=1:1:flags=area,format=rgb24" \ -f rawvideo - 2>/dev/null \ | od -An -tu1 -w3 \ | awk -v HMIN="$HUE_MIN" -v HMAX="$HUE_MAX" -v SMIN="$SAT_MIN" -v VMIN="$VAL_MIN" ' function fmod(a,b){ return a - int(a/b)*b } function rgb2hsv(R,G,B, r,g,b,M,m,d,H,S,V) { r=R/255.0; g=G/255.0; b=B/255.0; M=r; if(g>M) M=g; if(b>M) M=b; m=r; if(g=HMIN && H<=HMAX && S>=SMIN && V>=VMIN) pink++; } END { if (total==0) { print "0 0 0"; exit; } printf "%d %d %.3f\n", total, pink, pink/total; }' } is_mp4() { case "$1" in *.mp4|*.MP4) return 0;; *) return 1;; esac; } # ---- File list ---- if [[ $RECURSIVE -eq 1 ]]; then mapfile -t FILES < <(find "$DIR" -type f \( -iname '*.mp4' \)) else mapfile -t FILES < <(ls "$DIR"/*.mp4 "$DIR"/*.MP4 2>/dev/null || true) fi # ---- Process ---- if [[ ${#FILES[@]} -eq 0 ]]; then echo "No MP4 files found in: $DIR" exit 0 fi for f in "${FILES[@]}"; do [[ -f "$f" ]] || { print_result "SKIPPED" "$f" "not a file"; continue; } is_mp4 "$f" || { print_result "SKIPPED" "$f" "not mp4"; continue; } base=$(basename -- "$f") if [[ "$base" == "$PREFIX"* ]]; then print_result "SKIPPED" "$f" "already prefixed" continue fi read -r total pink ratio < <(detect_pink "$f") if [[ "$total" -eq 0 ]]; then print_result "ERROR" "$f" "no frames parsed" continue fi awk -v r="$ratio" -v thr="$MIN_RATIO" 'BEGIN{exit !(r>=thr)}' && { safe_rename "$f" "$PREFIX" continue } print_result "NOT_MATCH" "$f" "ratio=${ratio} (<${MIN_RATIO})" done