Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bashtheshell/2e3509e862e67112d7b0e123211caa3a to your computer and use it in GitHub Desktop.
Save bashtheshell/2e3509e862e67112d7b0e123211caa3a to your computer and use it in GitHub Desktop.
HandBrake Automated Conversion - Whole DVD to Individual M4V Chapter Files

HandBrake Automated Conversion - Whole DVD to Individual M4V Chapter Files

As the title suggested, HandBrake can be used to extract chapters from the DVD movie (assuming only one title exists on DVD) and write them to the data storage in M4V format using HandBrake's default preset, Fast 1080p30.

By default, HandBrake would copy the entire DVD and write them to a single M4V file. I've developed a wrapper script, leveraging the HandBrakeCLI command to perform the automation of reading a specific chapter from the DVD and writing it to an .m4v file.

The files would be written in a directory named after the filename of a required chapter markers file, which is an actual CSV file with .csv extension. Each chapter file with the .m4v extension would get its name from the string listed in the second column of the CSV file.

Creating CSV File

For the sake of demonstration, let's say we have a DVD movie called The Brief & Empty with only three chapters.

Because of the movie's name, we're going to create a chapter markers file with the name, The_Brief_and_Empty.csv.

You may noticed I didn't use the ampersand (&) here as it can do unexpected things on the command line if we are not careful. To remove the headaches of which special characters need to be escaped by backslash (\), it's far easier to have the filenames without those characters. Also, the underscore (_) is more convenient than spaces as spaces would have to be escaped as well.

Please keep in mind that you are not always able to extract chapter names directly from the DVD as not every DVD has them (at least not the 20 DVDs that I worked on). Assuming you only have one CD/DVD drive and it's set as your primary CD/DVD drive, run the command HandBrakeCLI --input /dev/sr0 --title 0 to see if there are chapter titles included in the output. You'd need handbrake-cli package installed to run the command.

In case you don't have the chapter titles included, you may would need to view the actual movie on the DVD and identify the chapter's name associated with the chapter's number. You may be fortunate to have a physical DVD case with the information available to you. Also, you can use the chapters option from the DVD's main menu. I was not as fortunate to have those options as the custom movies I worked on only had embedded title slides at the beginning of each chapter.

Here's the content of The_Brief_and_Empty.csv file.

1, The Great Beginning
2, Peaking Climax
3, Closure

As you can see, the file contains a blank new line at the end, which is very important. Otherwise, the last chapter (3) wouldn't be read.

Also, you would want to be mindful of the chapter names. It's perfectly okay to have spaces here. Maybe apostrophes (') and double quotes (") are fine, but absolutely no comma (,) should appear in the chapter names as it'd break the CSV formatting due to the comma being used as a delimiter between columns, which should only appear once on each line. Yes, you can technically use commas enclosed in quotes, but let's not introduce complications.

After running the wrapper script, we would have a directory named The_Brief_and_Empty/. In that directory, there would be three files:

  • The Great Beginning.m4v
  • Peaking Climax.m4v
  • Closure.m4v

Installing HandBrake and libdvdcss

Of course, you'd need to install HandBrake if it doesn't come pre-installed with your OS. I used Ubuntu MATE. Using Debian-based Linux distribution is highly recommended here as the specific instruction for Red Hat/Fedora isn't included in this document.

To install all required files:

sudo apt-get install handbrake handbrake-cli libdvd-pkg

The libdvd-pkg package is used to read encrypted DVDs. Please see this article (don't follow the instruction yet) for more information.

When done installing the above packages and answering all the prompts, you'd also need to install libdvdcss and have it auto-updated:

sudo dpkg-reconfigure libdvd-pkg

The article also said to install and run the regionset command, which I believe you can completely disregard to avoid permanently modify your CD/DVD drive firmware. The implication of the command is terrifying.

Running the Wrapper Script

To run the handbrake_automation.sh script:

./handbrake_automation.sh The_Brief_and_Empty.csv 2>./handbrake.log

You would not see any output beside the encoding due to 2> STDOUT redirection so that you can review the handbrake.log after it's done (or you can just tail -f it). Feel free to remove the 2>./handbrake.log from the command.

Please remember to review the handbrake.log before re-running the script or else it'd be overwritten. I've thought about including the current timestamp in the log file's name, but decided against it to avoid building up log files and filling up storage space.

Running into Errors?

You may see in the output or in the log file that there are lines starting with ERROR: dvdnav: Read Error, Expected NAV packet but none found.

No worries! It happens. The best part about the script is that you can add a second argument denoting only the chapter files that were not successfully written for various reasons (e.g. was truncated or malformed) so that you do not have to go through the entire process again. Please see below for instruction.

Converting Only Specific Chapters

The script accepts a second argument, which can be multiple numbers and can be separated by commas but are not required as spaces are acceptable. You would be able to write only the specific chapters from the DVD rather than writing all the chapters in the CSV file which would overwrite the existing .m4v files that were successfully written.

Let's suppose The Brief and Empty* has twelve consecutive chapters.

The following are acceptable:

# Only chapter 2, 9, and 12 will be read
./handbrake_automation.sh The_Brief_and_Empty.csv 2, 9, 12 2>./handbrake.log

# Only chapter 2, 9, and 12 will be read
./handbrake_automation.sh The_Brief_and_Empty.csv 12 9 2 2>./handbrake.log

# Only chapter 4 and 10 will be read anyway, but not 20. 
# 20 is skipped as it doesn't exist.
./handbrake_automation.sh The_Brief_and_Empty.csv 4, "10,20" 2>./handbrake.log

The following are not acceptable as they would not run:

# Chapter 2 through 10 must be explicitly listed instead.
# (e.g. 2,3,4,5,6,7,8,9,10)
# As a result no chapter was read.
./handbrake_automation.sh The_Brief_and_Empty.csv 2-10 2>./handbrake.log

# Chapter must be numbers only. Non-digit aren't accepted.
# As a result no chapter was read.
./handbrake_automation.sh The_Brief_and_Empty.csv 3 one 2>./handbrake.log
#!/bin/bash
# This Handbrake automation script is using chapter marker CSV files
# to generate video files that are named after each chapter in the CSV file.
# Each video files are chapters from the DVD and those files will be placed
# in the directory that is named after each CSV file without the extension.
# Of course, each CSV file is associated to a DVD.
# In an event that there's an error such as 'ERROR: dvdnav: Read Error, Expected NAV packet but none found.'
# while running the script, you would be able to rerun the script and only specify the chapters that need to
# go through the rip process again.
echo "<------------------------------ THIS IS THE BEGINNING OF AUTOMATION SCRIPT ------------------------------>" >&2
first_arg="${1}"
args_after_first=$(echo "${@:2}" | tr ',' ' ' | tr ' ' '\n' | sort -nu | tr '\n' ' ')
number_regex='^([0-9]|,|\ )+$'
if [[ $# -lt 1 ]]; then
echo 'ERROR: At least a valid CSV file is needed as the first argument (e.g. chapter marker file)' >&2
exit 1
elif [[ ! -f $first_arg ]]; then
echo "ERROR: This file [" $first_arg "] does not exist." >&2
exit 1
elif ! $(grep -q ".csv$" <<< "$first_arg"); then
echo 'ERROR: Only file with .csv extension can be accepted' >&2
exit 1
elif [[ $# -gt 1 && ! $args_after_first =~ $number_regex ]]; then
echo 'ERROR: Only numbers are accepted after first argument. They can be separated by comma in any order.'
exit 1
fi
chapter_marker_csv_file=$(basename "${first_arg}")
videos_output_dir_name=$(basename "${chapter_marker_csv_file}" .csv)
if [[ ! -d ~/Videos ]]; then
echo 'ERROR: The "~/Videos" directory does not exist.'
exit 1
elif [[ ! -d "$videos_output_dir_name" ]]; then
echo "++++++++++ CREATING VIDEO DIRECTORY: ${videos_output_dir_name} ++++++++++" >&2
mkdir "$videos_output_dir_name"
else
echo "++++++++++ DIRECTORY NAME ALREADY EXIST: ${videos_output_dir_name} ++++++++++" >&2
fi
cat $chapter_marker_csv_file | while read each_line
do
if [[ -z $each_line ]]; then
break
fi
chapter_number=$(echo "$each_line" | cut -d',' -f1)
chapter_title=$(echo "$each_line" | cut -d',' -f2)
run_handbrake () {
echo "++++++++++ RIPPING VIDEO FROM CHAPTER: ${chapter_number} ++++++++++" >&2
echo "++++++++++ VIDEO FILE NAME WILL BE: ${chapter_title}.m4v ++++++++++" >&2
HandBrakeCLI \
--preset "Fast 1080p30" \
--input /dev/sr0 \
--title 1 \
--chapters "${chapter_number}" \
--output ~/Videos/"${videos_output_dir_name}"/"${chapter_number}"_"${chapter_title}".m4v \
--no-markers \
--format "av_mp4" 0</dev/null
echo "++++++++++++++++++++++ EXIT STATUS OF LAST HANDBRAKE COMMAND: [ $? ] ++++++++++++++++++++++" >&2
echo "++++++++++++++++++++++ DEBUGGING - LINE IN THIS BLOCK WAS: ${each_line} ++++++++++++++++++++++" >&2
}
# Only run 'if' block with specific chapter number if directory exists and more than one arg was entered
# This implies the second and subsequent arguments are valid
if [[ -d "$videos_output_dir_name" && $# -gt 1 ]]; then
num_in_list_regex='(^|[[:space:]])'$chapter_number'($|[[:space:]])'
if [[ $args_after_first =~ $num_in_list_regex ]]; then
run_handbrake
else
continue
fi
else
run_handbrake
fi
done
echo "<------------------------------ THIS IS THE END OF THE AUTOMATION SCRIPT ------------------------------>" >&2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment