Skip to content

Instantly share code, notes, and snippets.

@masterflitzer
Last active October 9, 2024 19:41
Show Gist options
  • Save masterflitzer/6bd4bd8c6650706ca5011f2d6a676e46 to your computer and use it in GitHub Desktop.
Save masterflitzer/6bd4bd8c6650706ca5011f2d6a676e46 to your computer and use it in GitHub Desktop.
Convert any movie to WebM (AV1/Opus/WebVTT)
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
shopt -s inherit_errexit
# e.g. "Movie name (Year)" or "Series name (Year) - S01E01 - Episode title"
TITLE="${TITLE:-}"
DESCRIPTION="${DESCRIPTION:-}"
INPUT="${INPUT:-}"
OUTPUT="${OUTPUT:-}"
OVERWRITE="${OVERWRITE:-0}"
EXTERNAL_SUB="${EXTERNAL_SUB:-1}"
PRESET="${PRESET:-6}"
CRF="${CRF:-30}"
PRESET_INTERVAL="${PRESET_INTERVAL:-"-1"}"
CRF_INTERVAL="${CRF_INTERVAL:-"-5"}"
PRESET_MAX="${PRESET_MAX:-${PRESET}}"
PRESET_MIN="${PRESET_MIN:-${PRESET}}"
CRF_MAX="${CRF_MAX:-${CRF}}"
CRF_MIN="${CRF_MIN:-${CRF}}"
PIX_FMT="${PIX_FMT:-yuv420p10le}"
KEYINT_SEC="${KEYINT_SEC:-5}"
SS="${SS:-}"
TO="${TO:-}"
MAX_RES="${MAX_RES:-}"
if test -z "${INPUT}" || test -z "${OUTPUT}"
then
echo "You need to set the INPUT & OUTPUT environment variable"
exit 1
fi
if test "${OUTPUT##*.}" == "webm"
then
EXTENSION="webm"
else
EXTENSION="mkv"
fi
if test "${OVERWRITE}" -eq 1
then
OVERWRITE="-y"
else
OVERWRITE="-n"
fi
PARAMS=()
FFPROBE="$(ffprobe -loglevel warning -print_format json -show_format -show_streams "${INPUT}")"
if test -z "${FFPROBE}"
then
echo "ffprobe..."
exit 1
fi
if test -n "${SS}"
then
PARAMS+=(-ss "${SS}")
fi
if test -n "${TO}"
then
PARAMS+=(-to "${TO}")
fi
if test -n "${MAX_RES}"
then
FFPROBE_VIDEO_WIDTH="$(echo "${FFPROBE}" | jq -r '[.streams[] | select(.codec_type == "video")] | .[0].width // ""')"
FFPROBE_VIDEO_HEIGHT="$(echo "${FFPROBE}" | jq -r '[.streams[] | select(.codec_type == "video")] | .[0].height // ""')"
VIDEO_DIM_DIFF="$(echo "${FFPROBE_VIDEO_WIDTH} ${FFPROBE_VIDEO_HEIGHT}" | awk '{ printf "%.3f\n", $1/$2 }')"
MAX_RES_WIDTH="${MAX_RES%x*}"
MAX_RES_HEIGHT="${MAX_RES#*x}"
SCALE_WIDTH="min(${MAX_RES_WIDTH},iw):-2"
SCALE_HEIGHT="-2:min(${MAX_RES_HEIGHT},ih)"
if test -z "${MAX_RES_WIDTH}" && test -z "${MAX_RES_HEIGHT}"
then
SCALE=""
elif test -n "${MAX_RES_WIDTH}" && test -z "${MAX_RES_HEIGHT}"
then
SCALE="${SCALE_WIDTH}"
elif test -z "${MAX_RES_WIDTH}" && test -n "${MAX_RES_HEIGHT}"
then
SCALE="${SCALE_HEIGHT}"
else
MAX_RES_DIFF="$(echo "${MAX_RES_WIDTH} ${MAX_RES_HEIGHT}" | awk '{ printf "%.3f\n", $1/$2 }')"
if $(echo "${VIDEO_DIM_DIFF} ${MAX_RES_DIFF}" | awk '{ exit !( $1 > $2 ) }')
then
SCALE="${SCALE_WIDTH}"
elif $(echo "${VIDEO_DIM_DIFF} ${MAX_RES_DIFF}" | awk '{ exit !( $1 < $2 ) }')
then
SCALE="${SCALE_HEIGHT}"
else
SCALE=""
fi
fi
if test -n "${SCALE}"
then
PARAMS+=(-filter:v:0 scale="'${SCALE}'")
fi
fi
FFPROBE_VIDEO_FRAME_RATE="$(echo "${FFPROBE}" | jq -r '[.streams[] | select(.codec_type == "video")] | .[0].r_frame_rate // ""')"
if test -n "${FFPROBE_VIDEO_FRAME_RATE}"
then
KEYINT="$(echo "${KEYINT_SEC}*${FFPROBE_VIDEO_FRAME_RATE}" | awk -F '[*/]' '{ printf "%.0f\n", $1*$2/$3 }')"
else
KEYINT="120"
fi
FFPROBE_VIDEO_COLOR_RANGE="$(echo "${FFPROBE}" | jq -r '[.streams[] | select(.codec_type == "video")] | .[0].color_range // ""')"
FFPROBE_VIDEO_COLOR_SPACE="$(echo "${FFPROBE}" | jq -r '[.streams[] | select(.codec_type == "video")] | .[0].color_space // ""')"
FFPROBE_VIDEO_COLOR_TRANSFER="$(echo "${FFPROBE}" | jq -r '[.streams[] | select(.codec_type == "video")] | .[0].color_transfer // ""')"
FFPROBE_VIDEO_COLOR_PRIMARIES="$(echo "${FFPROBE}" | jq -r '[.streams[] | select(.codec_type == "video")] | .[0].color_primaries // ""')"
COLOR_RANGE="${FFPROBE_VIDEO_COLOR_RANGE:-tv}"
COLOR_SPACE="${FFPROBE_VIDEO_COLOR_SPACE:-bt709}"
COLOR_TRANSFER="${FFPROBE_VIDEO_COLOR_TRANSFER:-bt709}"
COLOR_PRIMARIES="${FFPROBE_VIDEO_COLOR_PRIMARIES:-bt709}"
FFPROBE_AUDIO_CHANNELS="$(echo "${FFPROBE}" | jq -r '[.streams[] | select(.codec_type == "audio")] | .[0].channels // ""')"
if test -n "${FFPROBE_AUDIO_CHANNELS}"
then
BITRATE_AUDIO="$(echo "64*${FFPROBE_AUDIO_CHANNELS}" | awk -F '*' '{ printf "%.0f\n", $1*$2 }')K"
else
BITRATE_AUDIO="128K"
fi
FFPROBE_AUDIO_CHANNEL_LAYOUT="$(echo "${FFPROBE}" | jq -r '[.streams[] | select(.codec_type == "audio")] | .[0].channel_layout // ""')"
if test -n "${FFPROBE_AUDIO_CHANNEL_LAYOUT}" && test "${FFPROBE_AUDIO_CHANNEL_LAYOUT}" != "stereo" && test "${FFPROBE_AUDIO_CHANNEL_LAYOUT}" != "mono"
then
MAPPING_FAMILY="1"
CHANNEL_LAYOUTS="7.1|6.1|5.1|5.0|quad|3.0"
else
MAPPING_FAMILY="0"
CHANNEL_LAYOUTS="stereo|mono"
fi
for PRESET in $(seq ${PRESET_MAX} ${PRESET_INTERVAL} ${PRESET_MIN})
do
for CRF in $(seq ${CRF_MAX} ${CRF_INTERVAL} ${CRF_MIN})
do
INFO=""
if test "${PRESET_MAX}" -ne "${PRESET_MIN}"
then
INFO="${INFO}.pre${PRESET}"
fi
if test "${CRF_MAX}" -ne "${CRF_MIN}"
then
INFO="${INFO}.crf${CRF}"
fi
if test "${EXTERNAL_SUB}" -eq 1
then
ffmpeg \
-benchmark \
-probesize 9223372036854775807 \
-analyzeduration 9223372036854775807 \
-fflags +genpts+igndts \
-i "${INPUT}" \
-map "0:s:0?" -c:s:0 webvtt \
"${PARAMS[@]}" "${OVERWRITE}" \
"${OUTPUT%.*}${INFO}.en.vtt" || true
fi
set -x
ffmpeg \
-benchmark \
-probesize 9223372036854775807 \
-analyzeduration 9223372036854775807 \
-fflags +genpts+igndts \
-i "${INPUT}" \
-map "0:v:0?" \
-c:v:0 libsvtav1 \
-preset:v:0 "${PRESET}" \
-crf:v:0 "${CRF}" \
-pix_fmt:v:0 "${PIX_FMT}" \
-color_primaries:v:0 "${COLOR_PRIMARIES}" \
-color_trc:v:0 "${COLOR_TRANSFER}" \
-colorspace:v:0 "${COLOR_SPACE}" \
-color_range:v:0 "${COLOR_RANGE}" \
-svtav1-params:v:0 enable-overlays=1:enable-tf=0:fast-decode=0:film-grain=0:film-grain-denoise=0:keyint="${KEYINT}":scd=1:tune=0 \
-map "0:a:0?" \
-c:a:0 libopus \
-b:a:0 "${BITRATE_AUDIO}" \
-vbr:a:0 on \
-mapping_family:a:0 "${MAPPING_FAMILY}" \
-filter:a:0 aformat=channel_layouts="${CHANNEL_LAYOUTS}" \
-map "0:s:0?" \
-c:s:0 webvtt \
-map_chapters -1 \
-map_metadata -1 \
-metadata:g title="${TITLE}" \
-metadata:g description="${DESCRIPTION}" \
-metadata:s:v:0 language="" \
-metadata:s:v:0 title="" \
-metadata:s:a:0 language="eng" \
-metadata:s:a:0 title="English" \
-metadata:s:s:0 language="eng" \
-metadata:s:s:0 title="English" \
-disposition:v:0 default \
-disposition:a:0 default \
-disposition:s:0 default \
"${PARAMS[@]}" "${OVERWRITE}" \
"${OUTPUT%.*}${INFO}.${EXTENSION#.}"
set +x
done
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment