Instantly share code, notes, and snippets.

Embed
What would you like to do?
This is the shell script I use to drive HandBrakeCLI to re-encode video files in a format suitable for playback on Apple TV, Roku 3, iOS, OS X, etc.
#!/bin/bash
# encode.sh
#
# Copyright (c) 2013 Don Melton
#
# This version published on June 7, 2013.
#
# Re-encode video files in a format suitable for playback on Apple TV, Roku 3,
# iOS, OS X, etc.
#
# Input is assumed to be a single file readable by HandBrakeCLI and mediainfo,
# e.g. just about any .mkv, .avi, .mpg, etc. file.
#
# The script automatically calculates output video bitrate based on input. For
# Blu-ray Disc-quality input that's always 5000 Kbps. For DVD-quality input
# that's always 1800 Kbps. For other files that will vary.
#
# The script also automatically calculates video frame rates and audio channel
# configuration.
#
# If the input contains a VobSub (DVD-style) or PGS (Blu-ray Disc-style)
# subtitle, then it is burned into the video.
#
# Optional frame rate overrides and soft subtitles in .srt format are read
# from separate fixed locations in the `$frame_rates_location` and
# `$subtitles_location` variables defined below. Edit this script to redefine
# them.
#
# If your input file is named "foobar.mkv" then the optional frame rate file
# should be named "foobar.txt". And all it should contain is the frame rate
# number, e.g. "25" followed by a carriage return.
#
# If your input file is named "foobar.mkv" then the optional soft subtitle
# file should be named "foobar.srt".
#
# Output is an MP4 container with H.264 video, AAC audio and possibly AC-3
# audio if the input has more than two channels.
#
# No scaling or cropping is performed on the output. This is a good thing.
#
# The output .mp4 file and a companion .log file are written to the current
# directory.
#
# This script depends on two separate command line tools:
#
# HandBrakeCLI http://handbrake.fr/
# mediainfo http://mediainfo.sourceforge.net/
#
# Make sure both are in your `$PATH` or redefine the variables below.
#
# Usage:
#
# ./encode.sh [input file]
#
die() {
echo "$program: $1" >&2
exit ${2:-1}
}
escape_string() {
echo "$1" | sed "s/'/'\\\''/g;/ /s/^\(.*\)$/'\1'/"
}
readonly program="$(basename "$0")"
readonly input="$1"
if [ ! "$input" ]; then
die 'too few arguments'
fi
handbrake="HandBrakeCLI"
mediainfo="mediainfo"
frame_rates_location="/path/to/Frame Rates"
subtitles_location="/path/to/Subtitles"
# My advice is: do NOT change these HandBrake options. I've encoded over 300
# Blu-ray Discs, 30 DVDs and numerous other files with these settings and
# they've never let me down.
handbrake_options="--markers --large-file --encoder x264 --encopts vbv-maxrate=25000:vbv-bufsize=31250:ratetol=inf --crop 0:0:0:0 --strict-anamorphic"
width="$(mediainfo --Inform='Video;%Width%' "$input")"
height="$(mediainfo --Inform='Video;%Height%' "$input")"
if (($width > 1280)) || (($height > 720)); then
max_bitrate="5000"
elif (($width > 720)) || (($height > 576)); then
max_bitrate="4000"
else
max_bitrate="1800"
fi
min_bitrate="$((max_bitrate / 2))"
bitrate="$(mediainfo --Inform='Video;%BitRate%' "$input")"
if [ ! "$bitrate" ]; then
bitrate="$(mediainfo --Inform='General;%OverallBitRate%' "$input")"
bitrate="$(((bitrate / 10) * 9))"
fi
if [ "$bitrate" ]; then
bitrate="$(((bitrate / 5) * 4))"
bitrate="$((bitrate / 1000))"
bitrate="$(((bitrate / 100) * 100))"
if (($bitrate > $max_bitrate)); then
bitrate="$max_bitrate"
elif (($bitrate < $min_bitrate)); then
bitrate="$min_bitrate"
fi
else
bitrate="$min_bitrate"
fi
handbrake_options="$handbrake_options --vb $bitrate"
frame_rate="$(mediainfo --Inform='Video;%FrameRate_Original%' "$input")"
if [ ! "$frame_rate" ]; then
frame_rate="$(mediainfo --Inform='Video;%FrameRate%' "$input")"
fi
frame_rate_file="$(basename "$input")"
frame_rate_file="$frame_rates_location/${frame_rate_file%\.[^.]*}.txt"
if [ -f "$frame_rate_file" ]; then
handbrake_options="$handbrake_options --rate $(cat "$frame_rate_file")"
elif [ "$frame_rate" == '29.970' ]; then
handbrake_options="$handbrake_options --rate 23.976"
else
handbrake_options="$handbrake_options --rate 30 --pfr"
fi
channels="$(mediainfo --Inform='Audio;%Channels%' "$input" | sed 's/[^0-9].*$//')"
if (($channels > 2)); then
handbrake_options="$handbrake_options --aencoder ca_aac,copy:ac3"
elif [ "$(mediainfo --Inform='General;%Audio_Format_List%' "$input" | sed 's| /.*||')" == 'AAC' ]; then
handbrake_options="$handbrake_options --aencoder copy:aac"
fi
if [ "$frame_rate" == '29.970' ]; then
handbrake_options="$handbrake_options --detelecine"
fi
srt_file="$(basename "$input")"
srt_file="$subtitles_location/${srt_file%\.[^.]*}.srt"
if [ -f "$srt_file" ]; then
subtitle_format="$(mediainfo --Inform='Text;%Format%' "$input" | sed q)"
if [ "$subtitle_format" == 'VobSub' ] || [ "$subtitle_format" == 'PGS' ]; then
handbrake_options="$handbrake_options --subtitle 1 --subtitle-burned"
else
tmp=""
trap '[ "$tmp" ] && rm -rf "$tmp"' 0
trap '[ "$tmp" ] && rm -rf "$tmp"; exit 1' SIGHUP SIGINT SIGQUIT SIGTERM
tmp="/tmp/${program}.$$"
mkdir -m 700 "$tmp" || exit 1
temporary_srt_file="$tmp/subtitle.srt"
cp "$srt_file" "$temporary_srt_file" || exit 1
handbrake_options="$handbrake_options --srt-file $(escape_string "$temporary_srt_file") --srt-codeset UTF-8 --srt-lang eng --srt-default 1"
fi
fi
output="$(basename "$input")"
output="${output%\.[^.]*}.mp4"
echo "Encoding: $input" >&2
time "$handbrake" \
$handbrake_options \
--input "$input" \
--output "$output" \
2>&1 | tee -a "${output}.log"
@dennisoderwald

This comment has been minimized.

Show comment
Hide comment
@dennisoderwald

dennisoderwald Jun 15, 2014

Hi, i using Ubuntu 14.04 LTS - what dependencies need to be installed on my system?

I've always installed HandbrakeCLI & mediainfo (with ffmpeg, ..), when i try convert my mkv file:

[15:30:56] add_ffmpeg_subtitle: unknown subtitle stream type: 0x53526970
[15:30:56] scan: decoding previews for title 1

thanks,

Dennis

dennisoderwald commented Jun 15, 2014

Hi, i using Ubuntu 14.04 LTS - what dependencies need to be installed on my system?

I've always installed HandbrakeCLI & mediainfo (with ffmpeg, ..), when i try convert my mkv file:

[15:30:56] add_ffmpeg_subtitle: unknown subtitle stream type: 0x53526970
[15:30:56] scan: decoding previews for title 1

thanks,

Dennis

@notthenewsreader

This comment has been minimized.

Show comment
Hide comment
@notthenewsreader

notthenewsreader May 12, 2015

Hi, is it possible to get this to batch convert a load of .mpg files in a directory without having to run the script with each video filename on the end?

notthenewsreader commented May 12, 2015

Hi, is it possible to get this to batch convert a load of .mpg files in a directory without having to run the script with each video filename on the end?

@racinrandall

This comment has been minimized.

Show comment
Hide comment
@racinrandall

racinrandall Nov 23, 2015

I really like the script, however it looks like it uses the first audio track from what I can tell. Some of my videos have a different language as the first audio track than the one I want. I'm looking to modify it to pull the English audio track and copy DTS with a fall back of aac.

I found another script on the Internet that would encode everything in a given folder. I modified it to call this script rather than HandBrakeCLI directly. This allowed me to do all files using this script. Now to get the right language and foreign audio scan.

racinrandall commented Nov 23, 2015

I really like the script, however it looks like it uses the first audio track from what I can tell. Some of my videos have a different language as the first audio track than the one I want. I'm looking to modify it to pull the English audio track and copy DTS with a fall back of aac.

I found another script on the Internet that would encode everything in a given folder. I modified it to call this script rather than HandBrakeCLI directly. This allowed me to do all files using this script. Now to get the right language and foreign audio scan.

@xlr5

This comment has been minimized.

Show comment
Hide comment
@xlr5

xlr5 Jan 3, 2016

racinrandall, can you give us your script?

xlr5 commented Jan 3, 2016

racinrandall, can you give us your script?

@BubonicPestilence

This comment has been minimized.

Show comment
Hide comment
@BubonicPestilence

BubonicPestilence Aug 8, 2016

For people who need to batch run:

find . -maxdepth 1 -type f -iname "*.*" -exec encode.sh {} \;

You can replace *.* with file pattern

BubonicPestilence commented Aug 8, 2016

For people who need to batch run:

find . -maxdepth 1 -type f -iname "*.*" -exec encode.sh {} \;

You can replace *.* with file pattern

@brianroach

This comment has been minimized.

Show comment
Hide comment
@brianroach

brianroach Jan 26, 2017

racinrandall - care to share your script?

brianroach commented Jan 26, 2017

racinrandall - care to share your script?

@fahadshery

This comment has been minimized.

Show comment
Hide comment
@fahadshery

fahadshery Oct 11, 2017

great script.

However, I am getting the following error:

: not found
: not found
encode.sh: 58: Syntax error: Bad fd number

I saved the script and called as:

sh encode.sh /path/to/video_file.m2ts

I am using FreeNas which is a BSD based distro

fahadshery commented Oct 11, 2017

great script.

However, I am getting the following error:

: not found
: not found
encode.sh: 58: Syntax error: Bad fd number

I saved the script and called as:

sh encode.sh /path/to/video_file.m2ts

I am using FreeNas which is a BSD based distro

@auswalk

This comment has been minimized.

Show comment
Hide comment
@auswalk

auswalk Feb 14, 2018

To preserve copying files (in this case all m4v and mkv files) found recursively into the same directory:
$ find . -type f -path *.m4v -o -path *.mkv -exec encode.sh {} ;

update encode.sh lines 179-181

outputdir=$(dirname "${input}")
output="$(basename "$input")"
output="${outputdir}/${output%.[^.]*}.mp4"

auswalk commented Feb 14, 2018

To preserve copying files (in this case all m4v and mkv files) found recursively into the same directory:
$ find . -type f -path *.m4v -o -path *.mkv -exec encode.sh {} ;

update encode.sh lines 179-181

outputdir=$(dirname "${input}")
output="$(basename "$input")"
output="${outputdir}/${output%.[^.]*}.mp4"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment