Create a gist now

Instantly share code, notes, and snippets.

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

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?

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.

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

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?

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

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