Files
marketing/text.sh
2025-10-25 18:17:56 +01:00

124 lines
3.7 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# title_one_line.sh — one centered line with fade in/out in a time window
# Requires: ffmpeg with libfreetype
show_help() {
cat <<'EOF'
Usage:
title_one_line.sh -i INPUT -o OUTPUT --text "YOUR TEXT" [options]
Required:
-i, --input Input video
-o, --output Output video
--text Text to display (one line)
Timing:
--start Start time seconds (default: 30)
--end End time seconds (default: 34)
--fade-in Fade-in seconds (default: 0.6)
--fade-out Fade-out seconds (default: 0.6)
Style:
--fs Font size (default: 78)
--dy Vertical nudge in px (default: 0) # negative = up
--font Font family (FontConfig), e.g. "DejaVu Sans"
--fontfile Path to TTF/OTF (overrides --font)
--shadow 1 to add shadow, 0 to disable (default: 1)
--color Text color (default: white)
Encoding:
--crf x264 CRF (default: 18)
--preset x264 preset (default: medium)
Examples:
title_one_line.sh -i in.mp4 -o out.mp4 --text "ROSE ASH VILLAGE HALL presents"
title_one_line.sh -i in.mp4 -o out.mp4 --text "Summer Fête" --start 12 --end 18 --fs 90 --dy -30
EOF
}
# --- defaults ---
INPUT=""
OUTPUT=""
TEXT=""
START="30"
END="34"
FADEIN="0.6"
FADEOUT="0.6"
FS="78"
DY="0"
FONT=""
FONTFILE=""
SHADOW="1"
COLOR="white"
CRF="18"
PRESET="medium"
# --- parse args ---
[[ $# -eq 0 ]] && { show_help; exit 1; }
while [[ $# -gt 0 ]]; do
case "$1" in
-i|--input) INPUT="${2:-}"; shift 2;;
-o|--output) OUTPUT="${2:-}"; shift 2;;
--text) TEXT="${2:-}"; shift 2;;
--start) START="${2:-}"; shift 2;;
--end) END="${2:-}"; shift 2;;
--fade-in) FADEIN="${2:-}"; shift 2;;
--fade-out) FADEOUT="${2:-}"; shift 2;;
--fs) FS="${2:-}"; shift 2;;
--dy) DY="${2:-}"; shift 2;;
--font) FONT="${2:-}"; shift 2;;
--fontfile) FONTFILE="${2:-}"; shift 2;;
--shadow) SHADOW="${2:-}"; shift 2;;
--color) COLOR="${2:-}"; shift 2;;
--crf) CRF="${2:-}"; shift 2;;
--preset) PRESET="${2:-}"; shift 2;;
-h|--help) show_help; exit 0;;
*) echo "Unknown arg: $1"; echo; show_help; exit 1;;
esac
done
[[ -z "$INPUT" || -z "$OUTPUT" || -z "$TEXT" ]] && { echo "Error: -i, -o and --text are required."; exit 1; }
command -v ffmpeg >/dev/null 2>&1 || { echo "Error: ffmpeg not found."; exit 1; }
# Escape text for drawtext (backslash, colon, percent, and single quote)
esc() {
local s="$1"
s="${s//\\/\\\\}" # backslashes
s="${s//:/\\:}" # colons
s="${s//%/\\%}" # percent
s="${s//\'/\\\'}" # single quotes
echo "$s"
}
ETEXT="$(esc "$TEXT")"
# Font options: prefer fontfile, else family, else default
FONT_OPTS=""
if [[ -n "$FONTFILE" ]]; then
FONT_OPTS="fontfile='${FONTFILE}'"
elif [[ -n "$FONT" ]]; then
FONT_OPTS="font='${FONT}'"
fi
# Optional shadow for contrast
SHADOW_OPTS=""
if [[ "$SHADOW" == "1" ]]; then
SHADOW_OPTS=":shadowcolor=black@0.6:shadowx=2:shadowy=2"
fi
# Alpha expression: fade in, hold, fade out (commas escaped)
ALPHA="if(lt(t\\,${START})\\,0\\,if(lt(t\\,${START}+${FADEIN})\\,(t-${START})/${FADEIN}\\,if(lt(t\\,${END}-${FADEOUT})\\,1\\,if(lt(t\\,${END})\\,(${END}-t)/${FADEOUT}\\,0))))"
DT="drawtext=${FONT_OPTS}:text='${ETEXT}':fontcolor=${COLOR}:fontsize=${FS}${SHADOW_OPTS}:x=(w-text_w)/2:y=(h-text_h)/2+${DY}:alpha='${ALPHA}'"
VF="${DT}"
echo "Running:"
echo " ffmpeg -hide_banner -y -i '$INPUT' -vf \"$VF\" -c:v libx264 -crf $CRF -preset $PRESET -c:a copy '$OUTPUT'"
ffmpeg -hide_banner -y -i "$INPUT" -vf "$VF" -c:v libx264 -crf "$CRF" -preset "$PRESET" -c:a copy "$OUTPUT"
echo "Done: $OUTPUT"