#!/usr/bin/env bash
ME="[makedvd]:"
. tovid-init

# makedvd
# Part of the tovid suite
# =======================
# A bash script for creating a DVD VIDEO_TS/VOB structure and
# burning it to a recordable DVD.
#
# Project homepage: http://www.tovid.org
#
#
# Copyright (C) 2005 tovid.org <http://www.tovid.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Or see:
#
#           http://www.gnu.org/licenses/gpl.txt

SCRIPTNAME=`cat << EOF
--------------------------------
makedvd
A script to create a DVD-Video file structure and burn it to DVD
Part of the tovid suite, version $TOVID_VERSION
$TOVID_HOME_PAGE
--------------------------------
EOF`

USAGE=`cat << EOF
Usage: makedvd [OPTIONS] FILE.xml
   or: makedvd [OPTIONS] DVD_DIR

With FILE.xml, containing dvdauthor XML as generated by 'makexml',
or DVD_DIR, a directory containing a DVD filesystem (VIDEO_TS, namely).

OPTIONS may be any of the following:

  -author              Create DVD filesystem from FILE.xml
  -burn                Burn DVD filesystem from DVD_DIR
  -device DEVFS_NAME   DVD recorder device name (Default: /dev/dvdrw)
  -speed NUM           Burn speed (Default: automatic)
  -label DISC_LABEL    Disc label (Default: base name of FILE)

See the makedvd manual page ('man makedvd') for additional documentation.
EOF`

# Print script name, usage notes, and optional error message, then exit.
# Args: $1 == text string containing error message
usage_error ()
{
  echo "$USAGE"
  echo $SEPARATOR
  echo "$@"
  exit 1
}

# Print out a runtime error specified as an argument, and exit
runtime_error ()
{
    #killsubprocs
    echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
    echo "makedvd encountered an error during the DVD creation process:"
    echo "$@"
    echo "See if anything in the above output helps you diagnose the"
    echo "problem, and please file a bug report at tovid.org (_not_"
    echo "the dvdauthor list) containing the above output."
    echo "Sorry for the inconvenience!"
    echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
    exit 1
}

# Defaults
QUIET=false
NOASK=false
# Create the DVD filesystem hierarchy?
DO_AUTHOR=false
# Burn the image to disc?
DO_BURN=false
HAVE_DVD_MINUS_RW=false
HAVE_DVD_PLUS_RW=false

# Guess for the device
DVD_SIZE_MB=4300
DVDRW_DEVICE="/dev/dvdrw"
BURN_SPEED=""
OUT_DIR="makedvd_out"
DISC_LABEL=""

# Grab DVD media information
# Returns nothing, but sets the 'global' variables
#   MEDIA_INFO, DISC_STATUS, and DISC_TYPE
function probe_media ()
{
    # Grab entire media info
    MEDIA_INFO=$(dvd+rw-mediainfo $DVDRW_DEVICE 2>&1)

    # Extract the status (either "blank" or "complete")
    DISC_STATUS=$(awk '/Disc status/ {print $3}' <<< "$MEDIA_INFO")

    # Extract the disc type
    DISC_TYPE=$(awk '/Mounted Media/ {print $4}' <<< "$MEDIA_INFO")
    if test -z $DISC_TYPE; then
        if echo "$MEDIA_INFO" | grep -q "non-DVD"; then
            DISC_TYPE="non-DVD media"
        else
            DISC_TYPE="no disc"
        fi
    fi

    # Get disc capacity (in 2KB blocks)
    if [[ "$DISC_TYPE" = *RW ]]; then
        DISC_CAPACITY=$(awk -F '*|:' '/Track Size/ {print $2}' <<< "$MEDIA_INFO")
    else
        DISC_CAPACITY=$(awk -F '*|:' '/Free Blocks/ {print $2}' <<< "$MEDIA_INFO")
    fi
    if test -n $DISC_CAPACITY; then
        DISC_CAPACITY=$(expr $DISC_CAPACITY \* 2048)
    else
        DISC_CAPACITY=0
    fi
    DISC_CAPACITY=$(echo "$DISC_CAPACITY / 1024 / 1024" | bc)

    # Define usable DVDs
    HAVE_DVD_RW=$(verify "$DISC_TYPE" set "DVD-RW DVD+RW")
    if [[ "$DISC_TYPE" = *+RW ]]; then
        HAVE_DVD_PLUS_RW=:
    elif [[ "$DISC_TYPE" = *-RW ]]; then
        HAVE_DVD_MINUS_RW=:
    fi

    if $(verify "$DISC_TYPE" set "DVD-R DVD+R") && \
        test "$DISC_STATUS" = "blank";
    then
        HAVE_BLANK_DVD_R=:
    else
        HAVE_BLANK_DVD_R="false"
    fi
}


# ==========================================================
# EXECUTION BEGINS HERE
echo $"$SCRIPTNAME"

assert_dep "$dvd" "You are missing dependencies required for burning DVDs!"

# Make sure we can properly author DVDs
#   growisofs/mkisofs won't create an image if the DVD's filesystem names
#   aren't upper case. In vfat, the top-level dirs are called "video_ts" and
#   "audio_ts", which aren't compliant with the DVD specs. The filenames, too.
CURRENT_DIR="$(pwd)"
CURRENT_FS=$(get_filesystem "$CURRENT_DIR")
if test x"$CURRENT_FS" = "xvfat"; then
    runtime_error "Cannot make DVDs on $CURRENT_FS filsystems. Try ext3."
fi

if test $# -lt 1; then
    usage_error "Please provide a filename (.xml, or dvd filesystem folder)"
fi

while test $# -gt 1; do
    case "$1" in
        "-quiet" ) QUIET=: ;;
        "-noask" ) NOASK=: ;;
        "-author" ) DO_AUTHOR=: ;;
        "-burn" )   DO_BURN=: ;;
        "-device" )
            # Get device name
            shift
            DVDRW_DEVICE="$1"
            ;;
        "-speed" )
            shift
            BURN_SPEED="-speed=$1"
            ;;
        "-label" )
            shift
            DISC_LABEL="$1"
            ;;
        * )
            usage_error "Error: Unrecognized command-line option '$1'"
            ;;
    esac

    # Get next argument
    shift
done

if test -e "$1"; then
    DISC_NAME=$(readlink -f "$1")
else
    usage_error "File or DVD directory '$1' does not exist."
fi

# See what kind of file we're working with, and do the right thing
# Author an XML file
if echo "$DISC_NAME" | grep -q -i '\.xml$'; then
    DVDAUTHOR_XML="$DISC_NAME"
    DO_AUTHOR=:
    # Make sure the XML file exists
    if test ! -f "$DVDAUTHOR_XML"; then
        runtime_error "Could not open file: $DVDAUTHOR_XML"
    else
        echo "Authoring disc from file: $DVDAUTHOR_XML"
    fi
    # See if makexml generated the xml file
    if ! grep -q makexml "$DVDAUTHOR_XML"; then
        echo "Your xml file wasn't made with tovid's makexml!"
        echo "This may cause unexpected (and possibly disastrous) behavior."
        if $NOASK; then
            echo "Continuing anyway..."
        else
            echo -n "Would you like to continue? [y/N] "
            ANSWER="N"
            read ANSWER
            if ! ( test "$ANSWER" = "y" || test "$ANSWER" = "Y" ); then
                echo "Exiting..."
                exit 0
            else
                echo "Continuing..."
            fi
        fi
    fi
    # The dest= argument from the XML file will be the output directory
    OUT_DIR="$(grep 'dest=' "$DVDAUTHOR_XML" | awk -F '"' '{print $2}')"
    if test -z "$OUT_DIR"; then
        echo "Malformed xml! No output directory given."
        echo "Did you use makexml? It doesn't look like it."
        runtime_error "Malformed xml file: $DVDAUTHOR_XML"
    fi
    # Remove existing files in the output directory
    if test -d "./$OUT_DIR"; then
        for DVDFS_DIR in VIDEO_TS AUDIO_TS; do
            if test -d "./$OUT_DIR/$DVDFS_DIR"; then
                echo "Removing existing dvdfs files in ./$OUT_DIR/$DVDFS_DIR"
                rm -rvf "./$OUT_DIR/$DVDFS_DIR"
            fi
        done
    fi
    $QUIET || echo "Putting output in ./$OUT_DIR/"
# Burn a directory containing a VIDEO_TS subfolder
else
    if test -d "$DISC_NAME"; then
        # Ensure that it looks like a DVD folder
        if test -d "$DISC_NAME/VIDEO_TS"; then
            DO_BURN=:
            OUT_DIR="$DISC_NAME"
        else
            runtime_error "Authored structure directory is not of correct format."
            echo "Exiting..."
            exit 1
        fi
    else
        runtime_error "Authored structure directory does not exist."
        echo "Exiting..."
        exit 1

    fi
fi

# Set disc title and output directory based on given filename
# (without .xml, space to underscore)
test -z $DISC_LABEL && DISC_LABEL=$( basename "$DISC_NAME" ".xml" | tr ' ' '_')
# And, just in case that failed...
test -z $DISC_LABEL && DISC_LABEL="UNTITLED_DVD"

# Authoring
if $DO_AUTHOR; then
    # Determine how much space will be needed for authoring, by adding up
    # the sizes of all .vobs in the XML file.
    NUM_VOBS=$(grep vob "$DVDAUTHOR_XML" | wc -l)
    VOB_LIST=$(grep vob "$DVDAUTHOR_XML" | \
               awk -F '"' '{ print $2 }' | tr '\n' ':')
    i=1
    DISC_SUM=0
    while test $i -le $NUM_VOBS; do
      VOB=$(echo "$VOB_LIST" | awk -F ':' '{ print $'$i' }')
      VOB_SIZE=$(du -D -B M "$VOB" | awk -F ' ' '{print $1}' | tr -d M)
      DISC_SUM=$(expr $VOB_SIZE \+ $DISC_SUM)
      i=$(expr $i \+ 1)
    done
    OUTDIR=$(readlink -f "$OUT_DIR")
    AVAIL_SPACE=$(df -B M -P "${OUTDIR%/*}" | awk 'NR != 1 {print $4;}' | tr -d M)

    if test $DISC_SUM -gt $AVAIL_SPACE; then
       echo $SEPARATOR
       precho "Cannot continue! ${DISC_SUM}MB needed, but only ${AVAIL_SPACE}MB in $(pwd)"
       echo "Exiting..."
       exit 1
    fi
    if test $DISC_SUM -gt $DVD_SIZE_MB; then
       echo $SEPARATOR
       echo "Warning! Making a DVD image larger than a standard single-sided DVD."
       echo "Current DVD image is ${DISC_SUM}MB."
    fi

    # Create disc structure
    DVDAUTHOR_CMD="dvdauthor -x \"$DVDAUTHOR_XML\""
    echo $SEPARATOR
    $QUIET || echo "Authoring DVD-Video disc structure, estimated to require ${DISC_SUM}MB."
    echo "Creating disc structure with the following command:"
    echo $DVDAUTHOR_CMD
    echo $SEPARATOR

    if eval "$DVDAUTHOR_CMD" 2>&1; then
        echo $SEPARATOR
        $QUIET || echo "Disc structure successfully created in directory: $OUT_DIR."
    else
        runtime_error "Could not create the DVD-Video disc structure in $OUT_DIR. Leaving $OUT_DIR for inspection."
    fi

echo "Authoring completed."
fi

# If not burning, print a message and exit
if ! $DO_BURN; then
    if ! $QUIET; then
        echo "If you'd like to preview the disc before burning, try:"
        echo "    gxine \"dvd:/$(pwd)/$OUT_DIR\""
        echo "You can burn the disc with a command like this:"
        echo "    makedvd -burn \"$OUT_DIR\""
    fi

# Otherwise, get on with burning
else
    # Remind user to insert a DVD, check for DVD device
    if ! $QUIET; then
        echo $SEPARATOR
        echo "Please insert a blank DVD+/-R(W) disc into your DVD-recorder"
        echo "($DVDRW_DEVICE) if you have not already done so."
    fi

    # Sanity check: Make sure given device is valid (somehow)
    # before creating the image. Give option to proceed anyway?
    # (i.e., could there be any point to proceeding?)
    # Here's a simple way: just quit
    if test -b $DVDRW_DEVICE; then :
    else
      precho "Couldn't find $DVDRW_DEVICE! Are you sure your burner is $DVDRW_DEVICE? Specify your burner with '-device /path/to/burner'."
      echo "Stopping here."
      exit 1
    fi

    GROWISOFS_VER=$(growisofs -version | grep version | \
                    awk '{ print $6 }' | sed 's/,//g')

    # Make sure there is a blank disc to write to
    probe_media

    if ! $HAVE_DVD_RW && ! $HAVE_BLANK_DVD_R; then
      echo $SEPARATOR
      echo "Found $DISC_TYPE in $DVDRW_DEVICE. Cannot burn to this disc!"
      until $HAVE_DVD_RW || $HAVE_BLANK_DVD_R
      do
        TIME_SCALE=1
        # TODO: smarter loop. User feedback (ie "hit any key" [where's the "any"
        # key anyway?]) is bad for when makedvd is called from the gui. But, it
        # usually takes 2 cycles for the dvd player to recognize a new disc. Is
        # there a better interface to it? Waiting till the drive's LED goes off
        # is good, but how?
        echo "Found $DISC_STATUS $DISC_TYPE. Please insert a usable DVD into $DVDRW_DEVICE."
        for COUNTER in 5 4 3 2 1; do
          printf "Trying again in %2s seconds...\r" $(expr $COUNTER \* $TIME_SCALE)
          sleep ${TIME_SCALE}s
        done
        echo
        echo "Looking for usable media..."
        probe_media
      done
    fi

    echo $SEPARATOR
    echo "Found $DISC_STATUS $DISC_TYPE."

    # complete DVD+/-RW need explicit blanking
    # a bug report for tkDVD points out that -use-the-force-luke=tty is req'd to
    # burn to complete +/-RW discs:
    # http://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=10751
    # but this support is still spotty (works for grepper and his +RW, but not
    # friedrij and his -RW)
    if test "$DISC_STATUS" = "complete" && $HAVE_DVD_MINUS_RW; then
      echo $SEPARATOR
      echo "The disc in $DVDRW_DEVICE already has a DVD Video filesystem on it."
      echo "Blanking $DISC_TYPE with the following command:"
      echo "dvd+rw-format -blank $DVDRW_DEVICE"
      dvd+rw-format -blank $DVDRW_DEVICE
      # Re-probe media for new free space
      probe_media
    fi

    DISC_SUM=$(du -s -B M "$OUT_DIR" | awk '{print $1}' | tr -d M)
    if test $DISC_SUM -gt $DISC_CAPACITY; then
       echo $SEPARATOR
       echo "Cannot continue! DVD image (${DISC_SUM}MB) exceeds the DVD's capacity (${DISC_CAPACITY}MB)."
       echo "Exiting..."
       exit 1
    fi

    # Extract a valid volume ID
    VOLID=$(echo "$DISC_LABEL" | tr a-z A-Z)
    # Make sure we have a valid VOLID at this point...can't be too long
    VALID_VOLID=$(echo $VOLID | awk '{ print substr($0, 0, 32) }')
    if test $VOLID != $VALID_VOLID; then
        echo "Disk label is too long. Truncating to $VALID_VOLID"
        VOLID=$VALID_VOLID
    else
        $QUIET || echo "Using disk label \"$VOLID\""
    fi

    # Burn it already!
    BURN_CMD="growisofs -use-the-force-luke=dao -dvd-compat $BURN_SPEED -Z $DVDRW_DEVICE -dvd-video -V \"$VOLID\" \"$OUT_DIR\""
    echo $SEPARATOR
    echo "Burning with growisofs $GROWISOFS_VER using the following command:"
    echo "$BURN_CMD"
    echo $SEPARATOR
    if eval "$BURN_CMD" 2>&1; then
        echo $SEPARATOR
        if ! $QUIET; then
            echo "Done. You should now have a working DVD. Please visit"
            echo "the tovid homepage: $TOVID_HOME_PAGE"
        else
            echo "Done."
        fi
    else
        runtime_error "Could not burn the disc to $DVDRW_DEVICE"
    fi
fi

$QUIET || echo $SEPARATOR
$QUIET || echo "Thanks for using makedvd!"

exit 0
