#!/bin/zsh # vg-transcode — batch-normalize old footage to ProRes LT for editing. # Usage: vg-transcode SRC_DIR [DEST_DIR] [--h264] # Recurses SRC_DIR, converts every video to ProRes LT .mov (or H.264 with --h264), # mirrors the directory structure, skips files already done, deinterlaces # interlaced sources (bwdif to progressive at double rate). # Heartbeat: ~/.jobs/vidgod-transcode.status export PATH=/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin set -u SRC="${1:?usage: vg-transcode SRC_DIR [DEST_DIR] [--h264]}" DEST="${2:-}" CODEC=prores [[ "${2:-}" == "--h264" ]] && { DEST=""; CODEC=h264; } [[ "${3:-}" == "--h264" ]] && CODEC=h264 SRC="${SRC:A}" [[ -d "$SRC" ]] || { echo "not a dir: $SRC" >&2; exit 1; } [[ -z "$DEST" ]] && DEST="${SRC}-prores" mkdir -p "$DEST" hb(){ mkdir -p ~/.jobs; echo "$(date '+%F %T') | $1 | $2" > ~/.jobs/vidgod-transcode.status; } typeset -a files files=() while IFS= read -r -d '' f; do files+=("$f"); done < <( find "$SRC" \( -iname '*.avi' -o -iname '*.wmv' -o -iname '*.mpg' -o -iname '*.mpeg' \ -o -iname '*.asf' -o -iname '*.flv' -o -iname '*.rm' -o -iname '*.rmvb' -o -iname '*.vob' \ -o -iname '*.m2v' -o -iname '*.divx' -o -iname '*.mov' -o -iname '*.mp4' -o -iname '*.mkv' \ -o -iname '*.webm' -o -iname '*.m4v' -o -iname '*.3gp' -o -iname '*.mts' -o -iname '*.m2ts' \) \ -not -path "$DEST/*" -print0 | sort -z) total=${#files[@]} [[ $total -eq 0 ]] && { echo "no video files under $SRC"; exit 0; } echo "transcoding $total files: $SRC -> $DEST ($CODEC)" n=0; ok=0; skip=0; fail=0 for f in "${files[@]}"; do n=$((n+1)) rel="${f#$SRC/}" out="$DEST/${rel%.*}.mov" [[ "$CODEC" == "h264" ]] && out="$DEST/${rel%.*}.mp4" mkdir -p "${out:h}" hb "$n/$total" "${rel:t}" if [[ -s "$out" && "$out" -nt "$f" ]]; then skip=$((skip+1)); continue; fi # detect interlacing fo=$(ffprobe -v error -select_streams v:0 -show_entries stream=field_order -of csv=p=0 "$f" 2>/dev/null) vf="format=yuv422p10le" case "$fo" in tt|bb|tb|bt) vf="bwdif=mode=1:parity=auto,format=yuv422p10le" ;; esac if [[ "$CODEC" == "prores" ]]; then ffmpeg -hide_banner -loglevel error -y -i "$f" -vf "$vf" \ -c:v prores_ks -profile:v 1 -vendor apl0 \ -c:a pcm_s16le -map 0:v:0 -map '0:a:0?' "$out" &2; fi done hb "DONE $n/$total" "ok=$ok skip=$skip fail=$fail" echo "done: $ok converted, $skip skipped (already done), $fail failed" [[ $fail -eq 0 ]]