Files
wcx_script/scan_and_prefix_pink.sh
2025-10-09 10:55:52 +02:00

153 lines
4.1 KiB
Bash
Executable File

#!/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 <directory> [--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<m) m=g; if(b<m) m=b;
d=M-m; V=M;
if (M==0) S=0; else S=(d==0?0:d/M);
if (d==0) H=0;
else if (M==r) H = 60.0 * fmod(((g-b)/d), 6);
else if (M==g) H = 60.0 * (((b-r)/d) + 2);
else H = 60.0 * (((r-g)/d) + 4);
if (H < 0) H += 360.0;
hsv[1]=H; hsv[2]=S; hsv[3]=V;
}
BEGIN{ total=0; pink=0; }
NF==3 {
R=$1; G=$2; B=$3; total++;
rgb2hsv(R,G,B); H=hsv[1]; S=hsv[2]; V=hsv[3];
if (H>=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