first commit
This commit is contained in:
152
scan_and_prefix_pink.sh
Executable file
152
scan_and_prefix_pink.sh
Executable file
@ -0,0 +1,152 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user