#!/usr/bin/env bash set -Eeuo pipefail # AmigaDB YouTube Shorts generator # Creates a 1080x1920, 50 FPS vertical MP4 from an existing Amiga video. # V3 expanded style pack for 4:3 Amiga footage, with demoscene-inspired FX and optional branded background image. SCRIPT_NAME="$(basename "$0")" INPUT="" OUTPUT="" START="00:00:00" DURATION="30" TITLE="" GROUP="" YEAR="" TYPE="" HOOK="THIS RAN ON AN AMIGA" WEBSITE="AMIGADB.NET" FONT_FILE="" STYLE="brand" BACKGROUND_IMAGE="amigadb_short.png" CRF="16" PRESET="slow" usage() { cat <&2 exit 1 } command_exists() { command -v "$1" >/dev/null 2>&1 } find_default_font() { local candidate="" if command_exists fc-match; then candidate="$(fc-match -f '%{file}\n' 'DejaVu Sans:style=Bold' 2>/dev/null | head -n 1 || true)" if [[ -n "$candidate" && -f "$candidate" ]]; then printf '%s\n' "$candidate" return 0 fi fi for candidate in \ /usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf \ /usr/share/fonts/truetype/liberation2/LiberationSans-Bold.ttf \ /usr/share/fonts/opentype/noto/NotoSans-Bold.ttf; do if [[ -f "$candidate" ]]; then printf '%s\n' "$candidate" return 0 fi done return 1 } while (($#)); do case "$1" in --input) (($# >= 2)) || fail "Missing value after --input" INPUT="$2" shift 2 ;; --output) (($# >= 2)) || fail "Missing value after --output" OUTPUT="$2" shift 2 ;; --start) (($# >= 2)) || fail "Missing value after --start" START="$2" shift 2 ;; --duration) (($# >= 2)) || fail "Missing value after --duration" DURATION="$2" shift 2 ;; --title) (($# >= 2)) || fail "Missing value after --title" TITLE="$2" shift 2 ;; --group) (($# >= 2)) || fail "Missing value after --group" GROUP="$2" shift 2 ;; --year) (($# >= 2)) || fail "Missing value after --year" YEAR="$2" shift 2 ;; --type) (($# >= 2)) || fail "Missing value after --type" TYPE="$2" shift 2 ;; --hook) (($# >= 2)) || fail "Missing value after --hook" HOOK="$2" shift 2 ;; --website) (($# >= 2)) || fail "Missing value after --website" WEBSITE="$2" shift 2 ;; --font) (($# >= 2)) || fail "Missing value after --font" FONT_FILE="$2" shift 2 ;; --background-image|--background) (($# >= 2)) || fail "Missing value after --background-image" BACKGROUND_IMAGE="$2" shift 2 ;; --style) (($# >= 2)) || fail "Missing value after --style" STYLE="${2,,}" shift 2 ;; --crf) (($# >= 2)) || fail "Missing value after --crf" CRF="$2" shift 2 ;; --preset) (($# >= 2)) || fail "Missing value after --preset" PRESET="$2" shift 2 ;; -h|--help) usage exit 0 ;; *) fail "Unknown argument: $1" ;; esac done command_exists ffmpeg || fail "ffmpeg is not installed or not in PATH" [[ -n "$INPUT" ]] || fail "--input is required" [[ -f "$INPUT" ]] || fail "Input file not found: $INPUT" [[ -n "$TITLE" ]] || fail "--title is required" [[ "$DURATION" =~ ^[0-9]+([.][0-9]+)?$ ]] || fail "--duration must be a positive number" awk -v d="$DURATION" 'BEGIN { exit !(d > 0) }' || fail "--duration must be greater than zero" [[ "$CRF" =~ ^[0-9]+$ ]] || fail "--crf must be an integer" case "$STYLE" in brand|pixel|mirror|crop|workbench|blur|crt|copper|stars|grid|tiles|scan|starfield|plasma|rasterbars|vhs|monitor|split|spectrum) ;; *) fail "Unknown style '$STYLE'. Use brand, pixel, mirror, crop, workbench, blur, crt, copper, stars, grid, tiles, scan, starfield, plasma, rasterbars, vhs, monitor, split, or spectrum" ;; esac if [[ -z "$FONT_FILE" ]]; then FONT_FILE="$(find_default_font || true)" fi [[ -n "$FONT_FILE" && -f "$FONT_FILE" ]] || fail "No usable font found. Install fonts-dejavu-core or use --font FILE" if [[ -z "$OUTPUT" ]]; then base="$(basename "$INPUT")" base="${base%.*}" OUTPUT="${base}-${STYLE}-short.mp4" fi mkdir -p "$(dirname "$OUTPUT")" TMP_DIR="$(mktemp -d -t amigadb-short.XXXXXX)" cleanup() { rm -rf "$TMP_DIR" } trap cleanup EXIT printf '%s\n' "$HOOK" > "$TMP_DIR/hook.txt" printf '%s\n' "$TITLE" > "$TMP_DIR/title.txt" META_PARTS=() [[ -n "$GROUP" ]] && META_PARTS+=("$GROUP") [[ -n "$YEAR" ]] && META_PARTS+=("$YEAR") [[ -n "$TYPE" ]] && META_PARTS+=("$TYPE") META="" if ((${#META_PARTS[@]})); then for part in "${META_PARTS[@]}"; do if [[ -n "$META" ]]; then META+=" ยท " fi META+="$part" done fi printf '%s\n' "$META" > "$TMP_DIR/meta.txt" printf '%s\n' "$WEBSITE" > "$TMP_DIR/website.txt" INFO_START="$(awk -v d="$DURATION" 'BEGIN { v=d-7; if (v<0) v=0; printf "%.3f", v }')" CTA_START="$(awk -v d="$DURATION" 'BEGIN { v=d-3.5; if (v<0) v=0; printf "%.3f", v }')" HOOK_END="$(awk -v d="$DURATION" 'BEGIN { v=2.7; if (d