Skip to content

Instantly share code, notes, and snippets.

@rdump
Created October 29, 2023 17:57
Show Gist options
  • Save rdump/9f3c14544b8479c9fc7810e10a965927 to your computer and use it in GitHub Desktop.
Save rdump/9f3c14544b8479c9fc7810e10a965927 to your computer and use it in GitHub Desktop.
Script for Handbrake CLI encoding standards including languages and subtitles
#!/bin/sh
# Handbrake ISO tracks transcode
# Mount an ISO, and give this script
# -i the path to the ISO's mount directory
# -d the path to the destination file directory (will be created)
# TODO: Figure out how to do AC3 passthrough for 2nd track of audios
# Handbrake Presets for reference, a subset from 'HandbrakeCLI -z'
# & https://handbrake.fr/docs/en/latest/technical/official-presets.html
# ...
# HQ 1080p30 Surround
# High quality H.264 video (up to 1080p30), AAC stereo audio,
# and Dolby Digital (AC-3) surround audio, in an MP4
# container.
# HQ 720p30 Surround
# High quality H.264 video (up to 720p30), AAC stereo audio,
# and Dolby Digital (AC-3) surround audio, in an MP4
# container.
# ...
# Super HQ 1080p30 Surround
# Super high quality H.264 video (up to 1080p30), AAC stereo
# audio, and Dolby Digital (AC-3) surround audio, in an MP4
# container.
# Super HQ 720p30 Surround
# Super high quality H.264 video (up to 720p30), AAC stereo
# audio, and Dolby Digital (AC-3) surround audio, in an MP4
# container.
# ...
# Vimeo YouTube HQ 2160p60 4K
# High quality H.264 video (up to 2160p60) and high bit rate
# AAC stereo audio in an MP4 container. Compatible with video
# hosting services supporting 4K video, such as Vimeo and
# YouTube.
# Vimeo YouTube HQ 1440p60 2.5K
# High quality H.264 video (up to 1440p60) and high bit rate
# AAC stereo audio in an MP4 container. Compatible with video
# hosting services supporting 2.5K video, such as Vimeo and
# YouTube.
# Vimeo YouTube HQ 1080p60
# High quality H.264 video (up to 1080p60) and high bit rate
# AAC stereo audio in an MP4 container. Compatible with video
# hosting services supporting 1080p60, such as Vimeo and
# YouTube.
# Vimeo YouTube HQ 720p60
# High quality H.264 video (up to 720p60) and high bit rate
# AAC stereo audio in an MP4 container. Compatible with video
# hosting services supporting 720p60, such as Vimeo and
# YouTube.
# ...
# Android 1080p30
# H.264 video (up to 1080p30) and AAC stereo audio, in an MP4
# container. Compatible with Android devices.
# Android 720p30
# H.264 video (up to 720p30) and AAC stereo audio, in an MP4
# container. Compatible with Android devices.
# ...
# Apple 1080p30 Surround
# H.264 video (up to 1080p30), AAC stereo audio, and Dolby
# Digital (AC-3) surround audio, in an MP4 container.
# Compatible with Apple iPhone 5, 5s, SE, 6, 6 Plus, 6s, 6s
# Plus and later; iPod touch 6th Generation and later; iPad
# Pro; iPad Air; iPad 3rd, 4th Generation and later; iPad
# mini; Apple TV 3rd Generation and later.
# Apple 720p30 Surround
# H.264 video (up to 720p30), AAC stereo audio, and Dolby
# Digital (AC-3) surround audio, in an MP4 container.
# Compatible with Apple iPhone 4, 4S and later; iPod touch
# 4th, 5th Generation and later; Apple TV 2nd Generation and
# later.
# Apple 540p30 Surround
# H.264 video (up to 540p30), AAC stereo audio, and Dolby
# Digital (AC-3) surround audio, in an MP4 container.
# Compatible with Apple iPhone 1st Generation, 3G, 3GS, and
# later; iPod touch 1st, 2nd, 3rd Generation and later; iPod
# Classic; Apple TV 1st Generation and later.
export PATH="~/bin:/opt/local/bin:/opt/local/sbin:/sw/bin:/sw/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"
PROG=`basename $0`
HB="HandbrakeCLI"
MAXAUDIOTRACKS=8 # DVDs support at most 8 audio tracks on each video title
esc_str() {
echo "$1" | sed "s/'/'\\\''/g;/ /s/^\(.*\)$/'\1'/"
}
getopt -T > /dev/null
if [ $? -eq 4 ]; then
# GNU enhanced getopt is available"
ARGS=$(getopt -o i:d: -l "iso:,destination:" -n "${PROG}" -- "$@");
else
# Original getopt is available"
ARGS=$(getopt i:d: "$@");
fi
if [ $? -ne 0 ]; then
echo "${PROG}: Usage: read the source, Luke" >&2
exit 2
fi
eval set -- "$ARGS";
while [ $# -gt 0 ]; do
case "$1" in
-i | --iso )
if [ -n "$2" ]; then
SOURCE="$2"
echo "${PROG}: ISO: ${SOURCE}"
fi
shift
;;
-d | --destination )
if [ -n "$2" ]; then
DEST="$2"
echo "${PROG}: Destination: ${DEST}"
fi
shift
;;
-- )
break
;;
esac
shift
done
if [ "X${SOURCE}" == "X" ] || [ "X${DEST}" == "X" ]; then
echo "${PROG}: Usage: read the source, Luke" >&2
exit 2
else
BASESOURCE=`basename "$SOURCE"`;
mkdir -p "$DEST"
for TYPE in shq1080p a540p; do
case $TYPE in
a540p)
PRESET='Apple 540p30 Surround'
SUBOPT="-s 1,2,3,4,5,6,7,8"
HBAUDIOSTYLE='slim'
EXT="m4v"
MINDUR=20 # minimum duration to bother with, in seconds
;;
shq1080p)
PRESET='Super HQ 1080p30 Surround'
SUBOPT="-s 1,2,3,4,5,6,7,8"
HBAUDIOSTYLE='fat'
EXT="m4v"
MINDUR=11 # minimum duration to bother with, in seconds
;;
webhq1440p)
PRESET='Vimeo YouTube HQ 1440p60 2.5K'
SUBOPT="-s 1,2,3,4,5,6,7,8"
HBAUDIOSTYLE='fat'
EXT="m4v"
MINDUR=11 # minimum duration to bother with, in seconds
;;
webhq2160p)
PRESET='Vimeo YouTube HQ 2160p60 4K'
SUBOPT="-s 1,2,3,4,5,6,7,8"
HBAUDIOSTYLE='fat'
EXT="m4v"
MINDUR=11 # minimum duration to bother with, in seconds
;;
esac
# Enumerate titles with a scan
echo "$PROG: Enumerating $BASESOURCE titles for $TYPE with minimum duration $MINDUR seconds"
SCANFILE=`mktemp -t ${PROG}-Scan`
echo "$PROG: Scan File: $SCANFILE"
TITLESFILE=`mktemp -t ${PROG}-Titles`
echo "$PROG: Titles File: $TITLESFILE"
$HB --verbose 0 --title 0 --min-duration $MINDUR -i "$SOURCE" 2> "$SCANFILE"
grep "^+ title " $SCANFILE | awk -F" |:" '{print $3}' > "$TITLESFILE"
NUMTITLESFOUND=`wc -l "$TITLESFILE" | awk '{print $1}'`
echo "$PROG: Found $NUMTITLESFOUND total titles of sufficient duration to transcode for $TYPE"
# Transcode the enumerated titles
TITLELIST=""
while read TITLENUM junk; do
TITLELIST="$TITLENUM $TITLELIST"
done < "$TITLESFILE"
rm -f "$TITLESFILE"
echo "$PROG: Complete title list for ${TYPE}: ${TITLELIST}."
echo ================
for TN in $TITLELIST; do
# Process audio info for the current title of interest alone
echo "$PROG: Scanning audio languages for title $TN"
LANGSFILE=`mktemp -t ${PROG}-Lang`
echo "$PROG: Langs File: $LANGSFILE"
$HB --verbose 0 --title $TN --scan -i "$SOURCE" 2> "$SCANFILE"
# We always take audio track 1 to hopefully harmlessly work
# around track labeling bug in some DVD reads (e.g., track 4
# is 3cc=eng, but is blank, and track 1 has the audio despite
# no 3cc= or other label
echo "1" >> "$LANGSFILE"
i=2;
while [ $i -le "$MAXAUDIOTRACKS" ]; do
echo "$PROG: Looking for audio track $i of $MAXAUDIOTRACKS"
grep -A1 "scan: checking audio $i" "$SCANFILE" | egrep "(3cc=|lang=)"
if [ $? -eq 0 ]; then
echo "$PROG: Found language info for audio track $i, looks real"
case "$HBAUDIOSTYLE" in
# For fat, we want all audio tracks in addition to
# track 1 from above
fat)
echo $i >> "$LANGSFILE"
;;
# For slim, we want 'eng' or 'und' in addition to
# track 1 from above
slim)
grep " + $i, .*iso639-2.*Hz," "$SCANFILE" | \
egrep "iso639-2: (eng|und)"
if [ $? == 0 ]; then
echo $i >> "$LANGSFILE"
fi
;;
esac
fi
i=`expr $i + 1`
done
echo ================
# Generate -a -R -D-E for the proper number of tracks based on languages, & slim or fat
echo "$PROG: Audio tracks of interest for style $HBAUDIOSTYLE in title $TN:"
cat "$LANGSFILE"
a_OPT=""
R_OPT=""
D_OPT=""
E_OPT=""
case "$HBAUDIOSTYLE" in
# For slim, we just transcode aac
# For fat, we transcode aac, and copy ac3
fat)
while read TRACKNUM junk; do
echo "$PROG: Adding style $HBAUDIOSTYLE options for audio track $TRACKNUM in title $TN"
if [ ! -z $a_OPT ]; then
a_OPT="${a_OPT},$TRACKNUM,$TRACKNUM"
R_OPT="${R_OPT},Auto,Auto"
D_OPT="${D_OPT},0.0,0.0"
E_OPT="${E_OPT},ca_aac,copy:ac3"
else
a_OPT="$TRACKNUM,$TRACKNUM"
R_OPT="Auto,Auto"
D_OPT="0.0,0.0"
E_OPT="ca_aac,copy:ac3"
fi
done < "$LANGSFILE"
;;
slim)
while read TRACKNUM junk; do
echo "$PROG: Adding style $HBAUDIOSTYLE options for audio track $TRACKNUM in title $TN"
if [ ! -z $a_OPT ]; then
a_OPT="${a_OPT},$TRACKNUM"
R_OPT="${R_OPT},Auto"
D_OPT="${D_OPT},0.0"
E_OPT="${E_OPT},ca_aac"
else
a_OPT="$TRACKNUM"
R_OPT="Auto"
D_OPT="0.0"
E_OPT="ca_aac"
fi
done < "$LANGSFILE"
;;
esac
# Build final option string for non-empty audio tracks
if [ ! -z $a_OPT ]; then
a_OPT="-a ${a_OPT}"
R_OPT="-R ${R_OPT}"
D_OPT="-D ${D_OPT}"
E_OPT="-E ${E_OPT}"
fi
rm -f "$LANGSFILE"
echo ================
echo "$PROG: Transcoding $TYPE title number $TN."
echo "$PROG: HandbrakeCLI options: -Z $PRESET $a_OPT $R_OPT $D_OPT $E_OPT $SUBOPT -t $TN -i $SOURCE -o ${DEST}/${BASESOURCE}-t${TN}-${TYPE}.${EXT}"
$HB -Z "$PRESET" -t $TN "$a_OPT" "$R_OPT" "$D_OPT" "$E_OPT" "$SUBOPT" -i "$SOURCE" -o "${DEST}/${BASESOURCE}-t${TN}-${TYPE}.${EXT}" 1> /dev/null
echo "$PROG: Finished transcoding $TYPE title number $TN."
echo ================
done
echo
rm -f "$SCANFILE"
done
fi
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment