#!/bin/bash set -eu set -o pipefail name="" src="/dev/sr0" #src="/dev/sr1" titles="" except_titles="" no_dvdnav="" file_extension="mkv" pre_dd=False pre_dded=False #audio_codec="copy:aac" audio_codec="" warn=True # Note that as of 2020-12-26, --to-dvd-res is untested to_dvd_res=False disc_is_dvd=True disc_is_bluray=False if tty -s then # "$(tput setaf 1)" "$(tput setaf 0)" red="$(tput setaf 1)$(tput smso)" no_color="$(tput setaf 0)$(tput rmso)" else red='' no_color='' fi function usage { retval="$1" echo "$0 --name name --source source --one-pass --as-mkv --pre-dd --no-dvdnav --skip-warning --to-dvd-res" echo " --bluray --except-titles title1 title2 ... titlen --titles title1 title2 ... titlen" 1>&2 echo 1>&2 echo "Note that --titles and --except-titles are mutually exclusive" 1>&2 exit "$retval" } while [ "$#" -ge 1 ] do case "$1" in --audio-codec) audio_codec="$2" shift ;; --to-dvd-res) to_dvd_res=True ;; --bluray) disc_is_dvd=False disc_is_bluray=True ;; --skip-warning) warn=False ;; --pre-dd) pre_dd=True ;; --as-avi) file_extension="avi" ;; --as-mp4) file_extension="mp4" ;; --as-mkv) file_extension="mkv" ;; --as-m4v) file_extension="m4v" ;; --no-dvdnav) no_dvdnav="--no-dvdnav" ;; --name) name="$2" shift ;; --source) src="$2" shift ;; --titles) shift titles="$*" break ;; --except-titles) shift except_titles="$*" break ;; --help|-h) usage 0 ;; *) echo "$0: Illegal option: $1" 1>&2 usage 1 ;; esac shift done case "$disc_is_dvd-$disc_is_bluray" in True-True|False-False) echo "$0: You must select exactly one of --dvd and --bluray" 1>&2 exit 1 ;; True-False|False-True) ;; *) echo "$0: \$disc_is_dvd-\$disc_is_bluray has a strange value: $disc_is_dvd-$disc_is_bluray" exit 1 esac case "$to_dvd_res" in True) to_dvd_res_opts="--loose-anamorphic --width 720" ;; False) to_dvd_res_opts="" ;; *) echo "$0: Internal error: \$to_dvd_res has a strange value: $to_dvd_res" 1>&2 exit 1 ;; esac # This only applies to handbrake, not makemkvcon audio_codec="aac" if [ "$titles" != "" ] && [ "$except_titles" != "" ] then # This should never actually happen; it's mostly here as a form of documentation echo "$0: --titles and --except-titles are mutually exclusive" 1>&2 usage 1 fi base_dir="" for candidate_base_dir in /mymount/movie /mymount/movie-ssh-read-write do if [ -d "$candidate_base_dir" ] then base_dir="$candidate_base_dir" break fi done if [ "$base_dir" = "" ] then echo "$0: Apologies: I cannot find where to write movies" 1>&2 exit 1 fi if ! [ -d "$base_dir"/Mission,\ The ] then echo "$0:$base_dir not mounted?" 1>&2 exit 1 fi if [ "$name" = "" ] then echo "$0: --name is a required option" 1>&2 exit 1 fi case "$disc_is_bluray" in True) dirname="makemkv" ;; False) dirname="handbrake" ;; *) echo "$0: disc_is_bluray has a strange value: $disc_is_bluray" 1>&2 exit 1 ;; esac bn=$(basename "$name") # Example output from lsdvd: # Disc Title: MAD_MEN_S1_DISC_4 # Title: 01, Length: 00:47:55.000 Chapters: 06, Cells: 06, Audio streams: 03, Subpictures: 02 # Title: 02, Length: 00:47:55.500 Chapters: 06, Cells: 06, Audio streams: 02, Subpictures: 02 # Title: 03, Length: 00:47:47.333 Chapters: 06, Cells: 06, Audio streams: 03, Subpictures: 02 # Example output from bluray_info: # Disc title: 'Under the Skin - Blu-ray™', Volume name: 'UNDER_THE_SKIN', Main title: 009, AACS: yes, BD-J: yes, BD+: no # Title: 001, Playlist: 0011, Length: 00:00:11.21, Chapters: 002, Video streams: 01, Audio streams: 00, Subtitles: 00, Angles: 01, Filesize: 00007 MBs # Title: 002, Playlist: 0018, Length: 00:00:05.04, Chapters: 002, Video streams: 01, Audio streams: 00, Subtitles: 00, Angles: 01, Filesize: 00001 MBs # Title: 003, Playlist: 0002, Length: 00:00:05.04, Chapters: 002, Video streams: 01, Audio streams: 00, Subtitles: 00, Angles: 01, Filesize: 00002 MBs function get_titles { case "$disc_is_dvd" in True) titles=$(set -eu; set -o pipefail; grep 'Length:' < "$disc_ls_filename" | \ sed 's/:/ /g' | \ awk ' { if ($4*60 + $5 >= 1) print $2 }' | \ sed 's/,//g' | \ sed 's/^0\([0-9]\)/\1/') ;; False) ;; *) echo "$0: \$disc_is_dvd has a strange value: $disc_is_dvd" 1>&2 exit 1 ;; esac case "$disc_is_bluray" in True) titles=$(set -eu; set -o pipefail; grep 'Length:' < "$disc_ls_filename" | \ sed 's/:/ /g' | \ awk ' { if ($6*60 + $7 >= 1) print $2 }' | \ sed 's/,//g' | \ sed -e 's/^00\([0-9]\)/\1/' -e 's/^0\([0-9]\)/\1/') ;; False) ;; *) echo "$0: \$disc_is_dvd has a strange value: $disc_is_dvd" 1>&2 exit 1 ;; esac echo "$titles" } function get_relevant { case "$disc_is_dvd" in True) grep 'Length:' < "$disc_ls_filename" | \ sed 's/:/ /g' | \ awk ' { if ($4*60 + $5 >= 1) print }' ;; False) ;; *) echo "$0: \$disc_is_dvd has a strange value: $disc_is_dvd" 1>&2 exit 1 ;; esac case "$disc_is_bluray" in True) grep 'Length:' < "$disc_ls_filename" | \ sed 's/:/ /g' | \ awk ' { if ($6*60 + $7 >= 1) print }' ;; False) ;; *) echo "$0: \$disc_is_dvd has a strange value: $disc_is_dvd" 1>&2 exit 1 ;; esac } function get_dvd_title { case "$disc_is_dvd" in True) grep '^Disc Title:' < "$disc_ls_filename" | sed 's/^Disc Title: //' ;; False) ;; *) echo "$0: \$disc_is_dvd has a strange value: $disc_is_dvd" 1>&2 exit 1 ;; esac case "$disc_is_bluray" in True) grep '^Disc title:' < "$disc_ls_filename" | sed "s/^Disc title: '\\([^']*\\)'.*$/\\1/" ;; False) ;; *) echo "$0: \$disc_is_bluray has a strange value: $disc_is_bluray" 1>&2 exit 1 ;; esac } function get_dvd_ls { case "$disc_is_dvd" in True) lsdvd "$src" ;; False) ;; *) echo "$0: \$disc_is_dvd has a strange value: $disc_is_dvd" 1>&2 exit 1 ;; esac case "$disc_is_bluray" in True) bluray_info "$src" ;; False) ;; *) echo "$0: \$disc_is_bluray has a strange value: $disc_is_bluray" 1>&2 exit 1 ;; esac } function get_dvd_ls_as { case "$disc_is_dvd" in True) lsdvd "$src" -a -s ;; False) ;; *) echo "$0: \$disc_is_dvd has a strange value: $disc_is_dvd" 1>&2 exit 1 ;; esac case "$disc_is_bluray" in True) bluray_info "$src" -a -s ;; False) ;; *) echo "$0: \$disc_is_bluray has a strange value: $disc_is_bluray" 1>&2 exit 1 ;; esac } directory="$base_dir/$name/$dirname" case "$warn" in True) if [ -d "$directory" ] then echo "$0: $directory already exists. Continue (y/n)?" 1>&2 while true do read -r yes_no case "$yes_no" in Y*|y*) break ;; N*|n*) echo "$0: Terminating without ripping by request of user" 1>&2 exit 0 ;; *) echo "$0: Please enter y or n" 1>&2 continue ;; esac done fi ;; False) # Nothing to do ;; *) echo "$0: internal error: \$warn has a strange value: $warn" 1>&2 exit 1 ;; esac mkdir -p "$directory" disc_ls_filename="$directory/Disc ls.txt" disc_ls_as_filename="$directory/Disc ls-as.txt" echo "$(set -eu; get_dvd_ls)" > "$disc_ls_filename" echo "$(set -eu; get_dvd_ls_as)" > "$disc_ls_as_filename" if [ "$titles" = "" ] then titles=$(set -eu; get_titles) if [ "$except_titles" != "" ] then # intentionally not quoting $titles and $except_titles # shellcheck disable=SC2086 echo $titles | tr ' ' '\012' | sort > /tmp/ripper.$$.titles # shellcheck disable=SC2086 echo $except_titles | tr ' ' '\012' | sort > /tmp/ripper.$$.except-titles # We only want titles in the first file; ignore files in the second file or in both files titles=$(set -eu; set -o pipefail; comm -23 /tmp/ripper.$$.titles /tmp/ripper.$$.except-titles | sort -n) rm -f /tmp/ripper.$$.titles rm -f /tmp/ripper.$$.except-titles fi if [ "$titles" = "" ] then case "$pre_dd" in True) #dd if="$src" of=/tmp/movie.iso bs=1024k ddrescue -b 2048 -n -v /dev/sr0 /tmp/movie.iso /tmp/movie.log pre_dded=True #/usr/local/bin/movie-mount src="/mnt/movie.iso" titles=$(set -eu; get_titles) ;; False) : ;; *) echo "$0: Unexpected value of \$pre_dd: $pre_dd" 1>&2 exit 1 ;; esac fi # shellcheck disable=SC2086 echo "titles are:" $titles sleep 4 fi disc_title_filename="$directory/Disc title.txt" disc_title="$(set -eu; get_dvd_title)" if [ -f "$disc_title_filename" ] then prior_title=$(set -eu; cat "$disc_title_filename") if [ "$disc_title" == "$prior_title" ] then echo "$0: old title and new title match: $disc_title" 1>&2 else echo "$0: $disc_title_filename already exists and has different content." 1>&2 echo "Disc in drive has title: $disc_title" 1>&2 echo "Prior rip has title: $prior_title" 1>&2 echo "Continue?" 1>&2 while true do read -r yes_no case "$yes_no" in Y*|y*) break ;; N*|n*) echo "$0: Terminating without ripping by request of user" 1>&2 exit 0 ;; *) echo "$0: Please enter y or n" 1>&2 continue ;; esac done fi fi echo "$disc_title" > "$disc_title_filename" echo echo "Title lengths:" awk ' { if ($4 != "") { print $4 } }' < "$disc_ls_filename" | histogram | sort -k 2 echo echo "Relevant titles:" get_relevant echo failed_titles="" function title_format { case "$disc_is_dvd" in True) format="%02d" ;; False) ;; *) echo "$0: \$disc_is_dvd has a strange value: $disc_is_dvd" 1>&2 exit 1 ;; esac case "$disc_is_bluray" in True) format="%03d" ;; False) ;; *) echo "$0: \$disc_is_dvd has a strange value: $disc_is_dvd" 1>&2 exit 1 ;; esac echo "$format" } # Intentionally not quoting $titles - it's whitespace separated for title in $titles do # shellcheck disable=SC2059 title_formatted=$(set -eu; printf "$(title_format)" "$title") echo "Ripping title $title of $name" # shellcheck disable=SC2086,SC2140,SC2059 filename="$directory/$bn $title_formatted"."$file_extension" # shellcheck disable=SC2154 if [ -f "$filename" ] then echo "$0: $filename already exists - skipping it" 1>&2 # failed_titles="$failed_titles $title" continue fi # --no-dvdnav case "$disc_is_bluray" in True) echo "rip with makemkvcon" if ! makemkvcon \ --robot \ mkv \ dev:"$src" \ "$title_formatted" \ "$directory" > "$directory/title-output-$title_formatted" 2>&1 then echo "$0: title $title failed" 1>&2 continue else echo "title $title succeeded" 1>&2 fi ;; False) echo "rip with HandBrakeCLI plus subtitles" # Intentionally not quoting $no_dvdnav - sometimes it needs to expand to nothing # shellcheck disable=SC2086 if ! HandBrakeCLI \ -C 1 \ --aencoder "$audio_codec" \ --title "$title" \ $no_dvdnav \ $to_dvd_res_opts \ --subtitle '1,2,3,4,5,6' \ --format av_mkv \ -i "$src" \ -o "$filename".failed > "$filename".rip-output 2>&1 then echo "$0: ripping title $title failed" 1>&2 failed_titles="$failed_titles $title" echo continue else rip_output_filename="$filename".rip-output if ! check-rip-result \ --rip-progress-file "$rip_output_filename" \ --rip-result-file "$rip_output_filename" then echo "$0: Rip failed" 1>&2 failed_titles="$failed_titles $title" echo continue fi xz -f "$rip_output_filename" echo "Ripping title $title succeeded" mv "$filename".failed "$filename" # Eliminate any lingering .failed file, if there is one. rm -f "$filename".failed fi ;; esac echo done case "$pre_dded" in True) #/usr/local/movie-umount rm -f /tmp/movie.iso /tmp/movie.log ;; False) : ;; *) echo "$0: Unexpected value in \$pre_dded: $pre_dded" 1>&2 exit 1 ;; esac if [ "$failed_titles" = "" ] then echo All titles ripped successfully exit 0 else ( # intentionally not quoting failed_titles # shellcheck disable=SC2086 printf "%s: %sOne or more titles failed to rip:%s\n" "$0" "$red" "$no_color" echo "$failed_titles" ) 1>&2 exit 1 fi