#!/usr/bin/env bash
# Caption style measurements for the Koldflux guide "readable-caption-styles".
#
# What it measures
#   1. Cap height of a burned-in caption at 7 font sizes on a 1080x1920 frame, and
#      the same height after the frame is scaled to a 390 CSS px wide phone view.
#   2. Contrast between the caption fill colour and the pixels the glyphs actually
#      touch, for 5 caption styles, on 2 real CC0 clips (one dark, one bright).
#   3. How often the caption band of a real clip contains a pixel as bright as
#      white text (ffmpeg signalstats on the cropped band).
#   4. Characters per second for sample captions, against Netflix's published limit.
#
# Inputs (both CC0, Wikimedia Commons; full licence rows in PROVENANCE.tsv)
#   Marmalade Hoverfly garden 31 7 2020 1b 4K UHD.webm   (dark garden frame)
#   Snowfall in Hockenheim.ogv                            (bright street frame)
#
# Requires: ffmpeg built with libfreetype (the drawtext filter) and python3.
#           No third-party python packages are needed.
# Usage: bash test-run.sh [output-directory]
set -euo pipefail

OUT="${1:-out}"
HERE="$(cd "$(dirname "$0")" && pwd)"
mkdir -p "$OUT"
cd "$HERE"

# ---------------------------------------------------------------- inputs
DARK_URL="https://upload.wikimedia.org/wikipedia/commons/9/94/Marmalade_Hoverfly_garden_31_7_2020_1b_4K_UHD.webm"
BRIGHT_URL="https://upload.wikimedia.org/wikipedia/commons/5/57/Snowfall_in_Hockenheim.ogv"
DARK="src-dark.webm"
BRIGHT="src-bright.ogv"

# A bold sans. DejaVu Sans Bold on Debian/Ubuntu, Liberation Sans Bold elsewhere.
FONT=""
for f in "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" \
         "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf" \
         "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" \
         "/System/Library/Fonts/Supplemental/Arial Bold.ttf"; do
  if [ -f "$f" ]; then FONT="$f"; break; fi
done
[ -n "$FONT" ] || { echo "No bold sans font found"; exit 1; }

TEXT="TAP TO SEE THE FULL ROUTINE"
SIZES="32 40 48 56 64 72 96"
CAPTION_SIZE=48
PHONE_W=390
FRAME_W=1080
FRAME_H=1920
BAND_H=260
BAND_Y=1180

say() { printf '\n=== %s\n' "$1"; }
run() { echo "\$ $*"; "$@"; }

say "date and environment"
date -u "+%Y-%m-%dT%H:%M:%SZ"
uname -srm
ffmpeg -version | head -1
echo "font used: $FONT"
ffmpeg -hide_banner -filters 2>/dev/null | grep -w drawtext || { echo "drawtext filter missing"; exit 1; }

say "download inputs"
[ -f "$DARK" ]   || curl -sL -o "$DARK" "$DARK_URL"
[ -f "$BRIGHT" ] || curl -sL -o "$BRIGHT" "$BRIGHT_URL"

say "ffprobe inputs"
run ffprobe -v error -show_entries format=duration,size,format_name \
  -show_entries stream=codec_name,width,height,avg_frame_rate -of default=nw=1 "$DARK"
run ffprobe -v error -show_entries format=duration,size,format_name \
  -show_entries stream=codec_name,width,height,avg_frame_rate -of default=nw=1 "$BRIGHT"

say "centre-crop both clips to 1080x1920 (9:16)"
# 1920x1080 source: crop 608x1080 from the centre, then scale to 1080x1920.
run ffmpeg -v error -y -i "$DARK" -t 4 -vf "crop=608:1080:656:0,scale=1080:1920:flags=lanczos,fps=30" \
  -c:v libx264 -preset veryfast -crf 18 -pix_fmt yuv420p -an "$OUT/dark-9x16.mp4"
run ffmpeg -v error -y -i "$BRIGHT" -t 4 -vf "crop=608:1080:656:0,scale=1080:1920:flags=lanczos,fps=30" \
  -c:v libx264 -preset veryfast -crf 18 -pix_fmt yuv420p -an "$OUT/bright-9x16.mp4"
run ffprobe -v error -show_entries stream=width,height,nb_frames -of default=nw=1 "$OUT/dark-9x16.mp4"
run ffprobe -v error -show_entries stream=width,height,nb_frames -of default=nw=1 "$OUT/bright-9x16.mp4"

# A caption that is centred horizontally and sits in the lower third of the frame.
CAP_X="(w-text_w)/2"
CAP_Y="1180"

say "measurement 1: cap height at 7 font sizes"
for S in $SIZES; do
  run ffmpeg -v error -y -f lavfi -i "color=c=black:s=${FRAME_W}x${FRAME_H}:d=1" \
    -vf "drawtext=fontfile='${FONT}':text='${TEXT}':fontcolor=white:fontsize=${S}:x=${CAP_X}:y=${CAP_Y}" \
    -frames:v 1 -f rawvideo -pix_fmt rgb24 "$OUT/size-${S}.raw"
  run ffmpeg -v error -y -f lavfi -i "color=c=black:s=${FRAME_W}x${FRAME_H}:d=1" \
    -vf "drawtext=fontfile='${FONT}':text='${TEXT}':fontcolor=white:fontsize=${S}:x=${CAP_X}:y=${CAP_Y}" \
    -frames:v 1 "$OUT/size-${S}.png"
done

say "measurement 2: pick the frame where plain white text has the least room"
# For each candidate second we measure the brightest pixel inside the caption band
# and keep the second with the highest value: that is the frame where a plain
# white caption has the least contrast against the footage.
BEST_T=0
BEST_LUM=0
for T in 0 1 2 3; do
  ffmpeg -v error -y -ss "$T" -i "$OUT/bright-9x16.mp4" -t 1 \
    -vf "crop=${FRAME_W}:${BAND_H}:0:${BAND_Y},signalstats,metadata=print:file=$OUT/pick-$T.txt" -f null -
  L=$(grep -m1 -o "YMAX=[0-9.]*" "$OUT/pick-$T.txt" | cut -d= -f2)
  echo "second $T: brightest pixel in the caption band YMAX=$L"
  if [ "$(printf '%s\n' "$L" > "$L")" = "$(printf '%s\n' "$BEST_LUM" > "$BEST_LUM")" ] && [ "$L" -gt "$BEST_LUM" ]; then
    BEST_LUM=$L; BEST_T=$T
  fi
done
echo "chosen second: $BEST_T (brightest pixel YMAX=$BEST_LUM)"

say "measurement 2: caption styles on 2 real frames"
# style name | drawtext options
STYLES=(
  "white-plain|fontcolor=white:fontsize=${CAPTION_SIZE}"
  "white-outline|fontcolor=white:fontsize=${CAPTION_SIZE}:borderw=6:bordercolor=black"
  "white-box60|fontcolor=white:fontsize=${CAPTION_SIZE}:box=1:boxcolor=black@0.6:boxborderw=24"
  "white-box80|fontcolor=white:fontsize=${CAPTION_SIZE}:box=1:boxcolor=black@0.8:boxborderw=24"
  "white-box100|fontcolor=white:fontsize=${CAPTION_SIZE}:box=1:boxcolor=black@1.0:boxborderw=24"
  "yellow-box60|fontcolor=0xFFE24A:fontsize=${CAPTION_SIZE}:box=1:boxcolor=black@0.6:boxborderw=24"
)
for CLIP in dark bright; do
  for STYLE in "${STYLES[@]}"; do
    NAME="${STYLE%%|*}"; OPT="${STYLE#*|}"
    TS=1; [ "$CLIP" = "bright" ] && TS=$BEST_T
    run ffmpeg -v error -y -ss $TS -i "$OUT/${CLIP}-9x16.mp4" -frames:v 1 \
      -vf "drawtext=fontfile='${FONT}':text='${TEXT}':${OPT}:x=${CAP_X}:y=${CAP_Y}" \
      -f rawvideo -pix_fmt rgb24 "$OUT/${CLIP}-${NAME}.raw"
    run ffmpeg -v error -y -ss $TS -i "$OUT/${CLIP}-9x16.mp4" -frames:v 1 \
      -vf "drawtext=fontfile='${FONT}':text='${TEXT}':${OPT}:x=${CAP_X}:y=${CAP_Y}" \
      "$OUT/${CLIP}-${NAME}.png"
  done
  # A drop shadow: one dark copy offset by 4 px, then the white fill on top.
  TS=1; [ "$CLIP" = "bright" ] && TS=$BEST_T
  run ffmpeg -v error -y -ss $TS -i "$OUT/${CLIP}-9x16.mp4" -frames:v 1 \
    -vf "drawtext=fontfile='${FONT}':text='${TEXT}':fontcolor=black@0.75:fontsize=${CAPTION_SIZE}:x=${CAP_X}+4:y=${CAP_Y}+4,drawtext=fontfile='${FONT}':text='${TEXT}':fontcolor=white:fontsize=${CAPTION_SIZE}:x=${CAP_X}:y=${CAP_Y}" \
    -f rawvideo -pix_fmt rgb24 "$OUT/${CLIP}-white-shadow.raw"
  run ffmpeg -v error -y -ss $TS -i "$OUT/${CLIP}-9x16.mp4" -frames:v 1 \
    -vf "drawtext=fontfile='${FONT}':text='${TEXT}':fontcolor=black@0.75:fontsize=${CAPTION_SIZE}:x=${CAP_X}+4:y=${CAP_Y}+4,drawtext=fontfile='${FONT}':text='${TEXT}':fontcolor=white:fontsize=${CAPTION_SIZE}:x=${CAP_X}:y=${CAP_Y}" \
    "$OUT/${CLIP}-white-shadow.png"
  # The same frame with no caption at all.
  run ffmpeg -v error -y -ss $TS -i "$OUT/${CLIP}-9x16.mp4" -frames:v 1 \
    -f rawvideo -pix_fmt rgb24 "$OUT/${CLIP}-plain.raw"
  run ffmpeg -v error -y -ss $TS -i "$OUT/${CLIP}-9x16.mp4" -frames:v 1 "$OUT/${CLIP}-plain.png"
done

say "figure frames: the same caption at three sizes on the real dark clip"
for S in 40 48 56; do
  run ffmpeg -v error -y -ss 1 -i "$OUT/dark-9x16.mp4" -frames:v 1 \
    -vf "drawtext=fontfile='${FONT}':text='${TEXT}':fontcolor=white:fontsize=${S}:x=${CAP_X}:y=${CAP_Y}" \
    "$OUT/figure-size-${S}.png"
done

say "measurement 3: brightness of the caption band over time (ffmpeg signalstats)"
for CLIP in dark bright; do
  run ffmpeg -v error -y -i "$OUT/${CLIP}-9x16.mp4" \
    -vf "crop=${FRAME_W}:${BAND_H}:0:${BAND_Y},fps=10,signalstats,metadata=print:file=${OUT}/stats-${CLIP}.txt" \
    -f null -
  echo "--- ${CLIP}: per-frame luma of the ${FRAME_W}x${BAND_H} band at y=${BAND_Y} ---"
  grep -E "YMIN|YMAX|YAVG" "$OUT/stats-${CLIP}.txt" | head -6
  echo "..."
  grep -c "YMAX" "$OUT/stats-${CLIP}.txt"
done

say "measurement 4: characters per second of sample captions"
cat > "$OUT/cps.tsv" <<'EOF'
caption	characters	duration_seconds	chars_per_second
TAP TO SEE THE FULL ROUTINE	27	1.2	22.5
TAP TO SEE THE FULL ROUTINE	27	1.6	16.9
TAP TO SEE THE FULL ROUTINE	27	2.0	13.5
THIS TEXT IS FAR TOO SMALL TO READ	35	1.5	23.3
THIS TEXT IS FAR TOO SMALL TO READ	35	2.0	17.5
EOF
cat "$OUT/cps.tsv"

say "measure the rendered pixels"
run python3 "$HERE/measure.py" "$OUT" "$FONT" "$PHONE_W" "$FRAME_W" "$FRAME_H" "$TEXT"

say "files produced"
ls -la "$OUT" | head -60
