Last active
October 9, 2024 19:41
-
-
Save masterflitzer/6bd4bd8c6650706ca5011f2d6a676e46 to your computer and use it in GitHub Desktop.
Convert any movie to WebM (AV1/Opus/WebVTT)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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