#!/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
frame_rate=""

if tty -s
then
    # "$(tput setaf 1)" "$(tput setaf 0)"
    red="$(tput setaf 1)$(tput smso)"
    no_color="$(tput sgr0)"
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
    echo "If you fail to get a good rip, try:"
    echo "    --no-dvdnav"
    echo "    --source /dev/sr1"
    echo "    --frame-rate n"
    echo "        You can get n from the 'fps' in ffmpeg -i /dev/sr0"
    echo "    Cleaning the disc with rubbing alcohol and a soft, damp cloth. Wipe radially, not concentrically"
	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
			;;
		--frame-rate)
			frame_rate="$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 frame_rate_option
{
    case "$frame_rate" in
        "")
            ;;
        *)
            echo "-r $frame_rate"
            ;;
    esac
}

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
        echo 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,SC2046
            if !  HandBrakeCLI \
                $(frame_rate_option) \
                -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