153 lines
4.1 KiB
Bash
Executable File
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
|