Here's a script I use quite a bit........I have a friend who films and edits videos (singers and bands), which get put on a local Comcast channel, periodically. Then he has a master disc made and I rip-n-burn a bunch for him.....
So this script actually started out as two separate scripts, about one page of code for each........Then I started adding a few features, here and there. But, after a few mishaps, I started adding sanity checks until I felt comfortable and could rest easy that I did a good job.......If there was a mistake to be made, I probably made it........
This is not for editing, only ripping and burning.....But rest assured, it will get done right, or the script will scream and holler if something is wrong......
A few of the features include:
* Burn one disc, or a bunch at a time, and let you know which disc you're on
* Tray will pop out for loading discs, and when the job(s) get done
* Will automatically find the correct device for burning or ripping, or let you know if nothing appropriate is found
* Will accept "file:///" URLS in path name
The script is well commented, so the script is also the documentation. Save it as
burnvideo, and create a symlink called
ripvideo in the same directory. A good place is to put it is in
/usr/local/bin.
Also, this script is not only bash-specific but also made to run on Linux, due to probing the /proc directory for getting the drive info.......I suppose it could be ported to other OSes, but I'll leave that to you.........
burnvideo/ripvideo - NOTE: This script will always have the latest changes/updates included, while any changes made will be announced in one of the posts below.
Code:
#!/bin/sh
################################################################################
# burnvideo/ripvideo - burn and rip DVD videos
#
# This is actually two programs combined into one. The script is saved as
# '/usr/local/bin/burnvideo', and then a 'ripvideo' symlink is made in the same
# directory. Example:
# ln -s burnvideo /usr/local/bin/ripvideo
#
# Requirements:
# Linux OS
# dvdbackup growisofs mkisofs <-- might have to install these
# cat dd isosize fold grep mkdir mount rm rmdir sed tac tr umount
#
# NOTE: This is a Linux only program, due to probing the /proc directory for
# device info. For the external programs, the first three programs may have
# to be installed, while the rest are standard programs and shipped with the
# majority of Linux distros.
#
#
# Written by CTWaley (aka, thegeekster), (c) February 2006
#
# You may do anything you want with this script, as long as the line directly
# above remains with it. Which means, give credit where credit is due!
#
################################################################################
## This is the directory to place the temporary video image when burning discs.
## It should be located in a partition with plenty of room to hold the image.
VIDTMP="/home/fun/video/tmp"
###### DO NOT EDIT BELOW THIS POINT ############################################
## Since we'll be creating files, and possibly directories, make sure we have
## sane permissions
umask 0022
readonly PROG=${0##*/}
## Assigning aliases for easier typing and not forgetting some needed options:
alias echo="echo -e"
alias fold="fold -s"
alias growisofs="growisofs -dvd-compat \
-use-the-force-luke=wrvfy,noload,dao \
-speed=1"
alias sed="sed -r"
alias toUpper="tr [[:lower:]] [[:upper:]]"
## The hex value '\x1b' is the same as the more familiar octal value '\033',
## which represents the <Esc> key on the keyboard
B="\x1b[1m" # Bold
b="\x1b[0m" # Unbold (actually 'undo' whatever was 'done' in the first place)
## These two help identify what the hex values we're using mean"
TAB="\x09"
LF="\x0a"
## CD/DVD drive info using the /proc filesystem instead of 'cdrecord -scanbus'
DEVINFO="$(sed -n '/drive name:/,/DVD-RAM/p' </proc/sys/dev/cdrom/info | sed "s/$TAB+/$TAB/g")"
## Arrays to hold the results of the DEVINFO query. Note - We must reverse the
## results in case there's more than one valid device. The dev/cdrom/info lists
## the devices in reverse order, such as:
## drive name: sr1 sr0
##
## Useful lines to check for in '/proc/sys/dev/cdrom/info' are:
## drive name:
## Can play audio:
## Can write CD-R:
## Can write CD-RW:
## Can read DVD:
## Can write DVD-R:
## Can write DVD-RAM:
##
DRIVE=($(grep 'drive name:' <<<"$DEVINFO" | sed "s/.+:$TAB(.+)/\1/; s/$TAB/$LF/" | tac))
NUMDRIVE=${#DRIVE[@]} ## Number of drives found
# Can read DVDs (0 means 'no', 1 means 'yes')
READDVD=($(grep 'read DVD:' <<<"$DEVINFO" | sed "s/.+:$TAB(.+)/\1/; s/$TAB/$LF/" | tac))
# Can write DVDs (0 means 'no', 1 means 'yes')
WRITEDVDR=($(grep 'write DVD-R:' <<<"$DEVINFO" | sed "s/.+:$TAB(.+)/\1/; s/$TAB/$LF/" | tac))
unset DEVINFO ## We're done with this variable, so let's free up a little memory
##
## Program functions
##
error_msg(){
echo "\n===> $PROG: ERROR: $*" | fold >&2
}
warn_msg(){
echo "\n===> WARNING: $*" | fold >&2
}
## Find the proper type of device asked for and assign to DEV, if it exists.
## Usage: find_dev ${READDVD[*]} -or- find_dev ${WRITEDVD[*]}
find_dev() {
local -i SUB
local -a TEMP=($1)
## Looping in case more than one drive found
for ((SUB=0 ; SUB<NUMDRIVE ; SUB++))
do ## Find first value of 1, assign corresponding drive to DEV and return successful
test ${TEMP[$SUB]} -eq 1 && export DEV=/dev/${DRIVE[$SUB]} && return 0
done
## Else found only 0(s) (not 1), so return unsuccessful
return 1
}
## Find iso size. Usage: find_size image|device
find_size(){
# du -sb <"$1" | cut -f1
isosize "$1"
}
## Random generator function, called from the rip_video() function. (Some of you
## might recognize this as a variation of "gen_pass()" from my version of a
## random password generator <http://bashscripts.org/viewtopic.php?p=808#808> ;-) )
mk_temp(){ ## Usage: VAR=$(mk_temp)
local RNDDEV
test -c /dev/urandom && RNDDEV=/dev/urandom || RNDDEV=/dev/random
echo /tmp/ripvid.$(LC_ALL=C tr -dc "[:alnum:]" <$RNDDEV | head -c 6)
}
## Called from the burn_video() function, if needed. Usage: tmp_iso $SOURCE
tmp_iso(){
local ISOINFO="$(isoinfo -d -i $SOURCE 2>/dev/null)"
export IMAGE="$(sed -n 's/Volume id: (.+)/\1/p' <<<"$ISOINFO").tmp"
## Function that reads the raw data from a device, using 'dd'.
## NOTE; This function is based on a script called 'rawread' by Steve Litt,
## which can be found at "Coasterless CD Burning"
## <http://www.troubleshooters.com/linux/coasterless.htm#rawread>:
raw_read(){
local -i BS COUNT
BS=$(sed -n 's/Logical block size is: (.+)/\1/p' <<<"$ISOINFO")
test -z "$BS" && error_msg "No block size present\n" && exit 1
COUNT=$(sed -n 's/Volume size is: (.+)/\1/p' <<<"$ISOINFO")
test -z "$COUNT" && error_msg "No block count present\n" && exit 1
dd if=$1 bs=$BS count=$COUNT conv=notrunc,noerror > "$VIDTMP/$IMAGE"
IMGSIZE="$(find_size $VIDTMP/$IMAGE)" ## Finding image size for comparison
}
test ! -d "$VIDTMP" && ## Making sure the temp directory exists
{ warn_msg "The directory, ${B}${VIDTMP}${b}, does not exist."
return 1;}
SRCSIZE="$(find_size $SOURCE)" ## Finding the source size on the disc
## If an image already exists, is it the same size as what's on the disc?
if [ -f "$VIDTMP/$IMAGE" ];then
IMGSIZE="$(find_size $VIDTMP/$IMAGE)"
test $IMGSIZE -ne $SRCSIZE && ## If size mismatch, do it over
{ echo "Recreating temp image, ${B}${IMAGE}${b}..."
raw_read "$SOURCE";}
else ## No image found
echo "===> Creating temp image of ${B}${SOURCE}${b}..."
raw_read "$SOURCE"
fi
test $IMGSIZE -ne $SRCSIZE && ## Double-checking for any size mismatches
{ warn_msg "There is a size mismatch between ${B}${SOURCE}${b} and ${B}$VIDTMP/${IMAGE}${b}. This may mean there isn't enough room to create the image, or there may be hardware/software issues to be taken care of."
return 1;}
## Reassigning SOURCE to the temp image
SOURCE="$VIDTMP/$IMAGE"
return 0
}
print_usage(){ ## Usage: print_usage
if [ "$PROG" == "ripvideo" ];then
echo "\n\tUsage: $PROG /path/to/video/directory\n"
elif [ "$PROG" == "burnvideo" ];then
cat<<_USAGE_
Usage: $PROG [-c NUM] /my/video/directory
$PROG [-c NUM] /my/video/filename.iso
The name given to the video will be the name of the directory/ISO given. NUM is
the number of copies to be made (optional).
_USAGE_
fi
}
## This is an important function to determine what we need to do, based on the
## return code. Usage: test_iso image|block device
test_iso(){
isoinfo -i "$1" >/dev/null 2>&1
return $?
## Some results of the return code.
:<<_RETURNS_
0: target is ISO 9660 formatted
1: target is not an ISO image, it is a file or corrupt image
2: target does not exist
5: target is audio formatted
6: target is not an ISO, it is an empty device (no disc in tray)
13: permission denied to open target (fix perms or run as root)
19: target is not an ISO, it is an unused character device
21: target is not an ISO, it is a directory
22: target is not an ISO, it is a blank/unformatted disc
29: target is not an ISO, it is a character device
30: target is unreadable (missing or bad)
255: target is too small to be a valid image
_RETURNS_
}
## 'burnvideo' program converted to a function. Usage: burn_video "$@"
burn_video(){
## Check for the existence of a DVD writer:
find_dev "${WRITEDVDR[*]}" ||
{ error_msg "No DVD writer found\n" && exit 1;}
## See if we have 'growisofs'. If not, say so and bail
which growisofs >/dev/null 2>&1 ||
{ error_msg "Cannot find 'growisofs', which is needed for burning DVDs.\n"
exit 1;}
## Does more than one disc need to be burned? If so, chop off the
## option switch in $1 to see if NUM is appended to the switch
case $1 in
--copy*) ## We'll only shift once here since NUM _must_ be appended
test -z "${1/--copy=/}" && ## If empty, say so and bail
{ error_msg "No number of copies specified in ${B}${1}${b}.\n"
print_usage <&2 && exit 1;}
NUM=${1/--copy=/} && shift 1
;;
-c*)
if [ -z ${1/-c/} ]; then
## If empty, assign $2 to NUM and shift the command args twice
NUM="$2" && shift 2
## If still empty, say so and bail
test -z "$NUM" &&
{ error_msg "Not enough arguments on the command line."
print_usage >&2 && exit 1;}
else ## It must not be empty, so we'll only need to shift once
NUM="${1/-c/}" && shift 1
fi
;;
*) ## We don't have any request for more than one disc to burn
NUM=1
;;
esac
## Sanity checks
# Okay let's see if we have a valid number to work with:
[[ $NUM =~ [^[0-9]]* ]] && ## If not all numbers, say so and bail
{ error_msg "${B}${NUM}${b} is not a valid, whole decimal number.\n"
exit 1;}
# Now, let's see if we have any args left after the shifting:
test -z "$1" &&
{ error_msg "Not enough arguments on the command line."
print_usage >&2 && exit 1;}
# Assign SOURCE before test if $1 exists (will also accept 'file:///' URLs):
test "$1" == "." && SOURCE="$(pwd)" || SOURCE="$(sed 's|file:/{1,3}|/|' <<<"$1")"
# Finally, if the last arg does not exist, say so and bail:
test ! -e "$SOURCE" &&
{ error_msg "${B}${SOURCE}${b} does not exist. Might be a typo.\n" && exit 1;}
## This test is to see what action is needed, based on the return code:
test_iso "$SOURCE"
case $? in
0) #<--- We're dealing with a valid iso (either an image or block device)
## Sanity check
test $(egrep '(AUDIO|VIDEO)_TS' <<<"$(isoinfo -p -i $SOURCE)" | wc -l) -ne 2 &&
{ error_msg "The ISO, ${B}${SOURCE##*/}${b}, does not contain a valid video directory structure. You will need to make another one. Make sure there is both a ${B}VIDEO_TS${b} and an ${B}AUDIO_TS${b} subdirectory present.\n" && exit 1;}
## If the source is a block device (not an iso image) and more
## than one burn is requested, then create a temorary image.
## This will reduce the overall time it takes to produce the
## finished videos:
if [ -b $SOURCE -a $NUM -gt 1 ];then
tmp_iso $SOURCE && eject $SOURCE 2>/dev/null ||
{ echo -n "If you wish to continue, the copying will be done directly from drive to drive (on-the-fly). However, the time to create the new discs will be much longer for each disc. Do you wish to continue? [Y|n] " | fold
## A 20-second timeout should be plenty of time to answer
read -t20
## If reply equal "n[o]", abort:
[[ "$REPLY" =~ "^[nN][oO]?$" ]] && echo "===> Aborting\n" && exit 0;}
fi; unset REPLY
SRCSIZE="$(find_size $SOURCE)"
## See if we have enough room to burn a 4.7G DVD:
if [ $SRCSIZE -ge 4700000000 ]; then
warn_msg "The source is too big to fit on a 4.7G DVD. You will need a 'double-layer' disc, and a DVD writer that supports burning double-layered discs. Do you wish to continue? [N|y] " | fold && read -t20
## Reply must equal "y[es]", or abort:
[[ "$REPLY" =~ "^[yY]([eE][sS])?$" ]] || echo "===> Aborting\n" && exit 0
fi
## Looping for the number of discs to burn
for ((INT=1 ; INT<=NUM ; INT++))
do
echo "${B}---> Starting disc $INT of ${NUM}:${b}"
## Feature - Insert blank disc before burning
test_iso $DEV
test $? -ne 22 &&
{ eject $DEV 2>/dev/null
read -p "Place blank DVD in tray and press <Enter> to continue...";}
## Burn, baby, burn.....Or holler a warning if something goes wrong:
growisofs -Z $DEV="$SOURCE" ||
{ warn_msg "Problem burning ISO image to disk. :-(\n" && continue;}
## Sanity checks
test_iso $DEV
if [ $? -ne 0 ];then
warn_msg "This newly created disc is not a valid ISO 9660 formatted disc. You will need to burn another one. :-("
else
test $(find_size $DEV) -ne $SRCSIZE &&
{ warn_msg "This newly created disc is not the same size as the source. You will need to burn another one. :-(";}
fi
done
;;
1) #<--- Probably an ordinary file, or corrupt image file
error_msg "${B}${SOURCE}${b} is not an ISO 9660 formatted file.\n" && exit 1
;;
2) #<--- File or device doesn't exist
error_msg "No such file or device\n --> ${B}${SOURCE}${b} <--\n" && exit 2
;;
13) #<--- Not enough permissions
error_msg "Not enough permissions to read ${B}${SOURCE}${b}. Either fix the permissions problem, or run this script as root.\n" && exit 13
;;
21) #<--- We're dealing with a directory
## Sanity check
test ! -d "${SOURCE}/VIDEO_TS" -o ! -d "${SOURCE}/AUDIO_TS" &&
{ error_msg "The directory structure is incorrect for a video DVD. There must be a ${B}VIDEO_TS${b} /AND/ ${B}AUDIO_TS${b} subdirectory present. Also, make sure you supplied the correct path on the command line.\n" && exit 1;}
## Extra growisofs options for burning from a directory tree
## and not an image:
OPTS="-Z $DEV \
-dvd-video \
-volid '${SOURCE##*/}' \
-sysid '' \
-appid '' \
-preparer '' \
'$SOURCE'"
## Looping for the number of discs to burn
for ((INT=1 ; INT<=NUM ; INT++))
do
echo "${B}---> Starting disc $INT of ${NUM}:${b}"
## Feature - Insert blank disc before burning
test_iso $DEV
test $? -ne 22 &&
{ eject $DEV 2>/dev/null
read -p "Place blank DVD in tray and press <Enter> to continue...";}
## Burn, baby, burn.....Or holler a warning if something goes wrong:
growisofs $OPTS ||
{ warn_msg "Problem burning ISO image to disk. :-(\n" && continue;}
## Sanity check
test_iso $DEV
test $? -ne 0 &&
{ warn_msg "This newly created disc is not a valid ISO 9660 formatted disc. You will need to burn another one. :-(";}
done
;;
30) #<--- Either no DVD in tray or a corrupt DVD
error_msg "Cannot read ${B}${SOURCE}${b}. Make sure there's a DVD in the tray.\n"
exit 30
;;
esac
## Cleanup
test "$IMAGE" && rm -f "$VIDTMP/$IMAGE"
echo "...Done"
}
## 'ripvideo' program converted to a function. Usage: rip_video "$@"
rip_video(){
## Check for the existence of a DVD drive:
find_dev "${READDVD[*]}" ||
{ error_msg "No DVD drive found.\n" && exit 1;}
## See if we have 'dvdbackup'. If not, say so and bail.
## NOTE: Dvdbackup requires 'libdvdread'. If you want to rip encrypted
## DVDs, such as retail ones, you will need to have 'libdvdcss'
## installed as well.
which dvdbackup >/dev/null 2>&1 ||
{ error_msg "Cannot find 'dvdbackup', which is needed for ripping DVDs.\n"
exit 1;}
whereis libdvdcss | grep -q 'libdvdcss:$' &&
{ warn_msg "Cannot find 'libdvdcss'. This is required only for backing up encrypted DVDs, such as retail movie discs."
read -t20 -p "Do you wish to continue? [Y|n]"
[[ "$REPLY" =~ "^[nN][oO]?$" ]] && echo "===> Aborting\n" && exit 0;}
unset REPLY
## Sanity check
test -d "$1" && DEST=${1%/} || ## If destination isn't a directory, bail
{ error_msg "${B}${1}${b} is not a valid directory. Aborting operation.\n" >&2
exit 1;}
test "$DEST" == "." && DEST=$(pwd)
## This test is to see what action is needed, based on the return code:
test_iso "$DEV"
case $? in
0) #<--- We're dealing with a valid iso (an image or block device)
## Instead of making multiple calls to 'isoinfo', make one call
## and save results.
ISOINFO="$(isoinfo -d -i $DEV 2>/dev/null)"
## Now we'll start weeding out the desired info we saved:
# For LABEL, we'll ask if the current one found is okay:
LABEL="$(sed -n 's/Volume id: (.+)/\1/p' <<<"$ISOINFO" | toUpper)"
LABEL="${LABEL//[^[:alnum:]]/_}"
read -t20 -p "
Enter a name of your choosing for the video, or simply hit <Enter> to use the default [$LABEL]: "
test "$REPLY" && LABEL="$(toUpper <<<"${REPLY//[^[:alnum:]]/_}")"
unset REPLY
# The rest of the info we'll be needing for later:
SYSID="$(sed -n 's/System id: (.+)/\1/p' <<<"$ISOINFO")"
VOLSETID="$(sed -n 's/Volume set id: (.+)/\1/p' <<<"$ISOINFO")"
PUBLISHER="$(sed -n 's/Publisher id: (.+)/\1/p' <<<"$ISOINFO")"
COPYRIGHT="$(sed -n 's/Copyright File id: (.+)/\1/p' <<<"$ISOINFO")"
BIBLIO="$(sed -n 's/Bibliographic File id: (.+)/\1/p' <<<"$ISOINFO")"
VOLSETSIZE="$(sed -n 's/Volume set size is: (.+)/\1/p' <<<"$ISOINFO")"
VOLSETSEQ="$(sed -n 's/Volume set sequence number is: (.+)/\1/p' <<<"$ISOINFO")"
if [ "$LABEL" ];then ## If we have a name, then it's a go
## Sanity check to make sure we don't overwrite an existing backup tree:
if [ ! -d "$DEST/$LABEL/VIDEO_TS" ];then
echo "\n===> Backing up directory structure..."
## Let 'er rip. If somethng bad happens in the process, bail
dvdbackup -M -i $DEV -o "$DEST" -n $LABEL ||
{ RETVAL= $? && echo "Aborting operation." && exit $RETVAL;}
else ## Found an existing 'VIDEO_TS' directory
error_msg "${B}$DEST/$LABEL/VIDEO_TS${b} already exists and will not be overwritten. Choose another destination, or remove ${B}$DEST/$LABEL/VIDEO_TS${b} to continue.\n" && exit 1
fi
## Make an 'AUDIO_TS' directory for backward compatibility
## on old DVD players. Even if there was one on the
## original disc, dvdbackup doesn't bother with it, probably
## because it's always empty, nowadays.
mkdir -p "$DEST/$LABEL/AUDIO_TS" || exit $?
else ## If no name, bail
error_msg "No name for ISO. Aborting operation.\n" && exit 1
fi
## For some retail DVDs, there might be extra stuff in it, so we
## might as well copy them, too:
MOUNT=$(mk_temp) && mkdir $MOUNT
mount $DEV $MOUNT -t udf -o ro
( cd $MOUNT
ls -A | egrep -iv 'video_ts|audio_ts' | while read
do
test ! -f $DEST/$LABEL/$REPLY && cp -R $REPLY $DEST/$LABEL
done )
sync && umount $MOUNT && rmdir $MOUNT
# Make sure filenames are uppercase:
find $DEST/$LABEL | sort -r | while read
do
NEW="$(toUpper <<<${REPLY##*/})"
test "${REPLY%/*}/$NEW" != "$REPLY" && mv $REPLY ${REPLY%/*}/$NEW
done
## Now we'll create an iso image of the backup for storage,
## using the info from the original disc:
echo "\n===> Creating DVD image..."
mkisofs -dvd-video \
-volid "$LABEL" \
-sysid "$SYSID" \
-appid "" \
-publisher "$PUBLISHER" \
-copyright "$COPYRIGHT" \
-biblio "$BIBLIO" \
-volset "$VOLSETID" \
-volset-size "$VOLSETSIZE" \
-volset-seqno "$VOLSETSEQ"\
-preparer "" \
-o "$DEST/$LABEL.dvd" \
"$DEST/$LABEL" ||
{ RETVAL=$? && echo "Aborting operation." && exit $RETVAL;}
## Let's make sure we have a good image (more sanity checks)
test_iso $DEST/$LABEL.dvd
if [ $? -ne 0 ];then ## If not good, bail
{ error_msg "The image, ${B}$LABEL.dvd${b}, is not a valid ISO 9660 image. You will need to start over." && exit 1;}
else ## If good, recheck for proper video structure
## This test may seem redundant and unnecessary, but I've
## acually burned a batch of DVDs with an incorect video
## tree in the image :-\
test $(egrep '(AUDIO|VIDEO)_TS' <<<"$(isoinfo -p -i $DEST/$LABEL.dvd)" |
wc -l) -ne 2 &&
{ error_msg "The ISO, ${B}$LABEL.dvd${b}, does not contain a valid video directory structure. You will need to make another one. Make sure there is both a ${B}VIDEO_TS${b} and an ${B}AUDIO_TS${b} subdirectory present.\n" && exit 1;}
fi
## Another redundant sanity check before cleaning up :-\
test "$DEST/$LABEL" != "$DEST/" && rm -rf "$DEST/$LABEL"
## Whew! Made it all the way....... :-D
echo "\n===> Done. Saved DVD iso as: ${B}$DEST/$LABEL.dvd${b}\n"
;;
1) #<--- Probably an ordinary file, or corrupt image file
error_msg "The disc in ${B}${DEV}${b} is not an ISO 9660 formatted disc.\n"
exit 1
;;
13) #<--- Not enough permissions
error_msg "Not enough permissions to read ${B}${DEV}${b}. Either fix the permissions problem, or run this script as root.\n" && exit 13
;;
22) #<--- An unformatted (blank) disc
error_msg "The disc in ${B}${DEV}${b} is a blank disc.\n"
exit 22
;;
30) #<--- Either no DVD in tray or a corrupt DVD
error_msg "Cannot read ${B}${DEV}${b}. Make sure there's a DVD in the tray.\n"
exit 30
;;
esac
}
##################
##### MAIN #####
##################
test -z "$1" &&
{ error_msg "No arguments given on command line."
print_usage >&2 && exit 1;}
## checking for required 'mkisofs' program
which mkisofs >/dev/null 2>&1 ||
{ error_msg "Cannot find 'mkisofs'.\n" && exit 1;}
case $PROG in # How were we called?
burnvideo) burn_video "$@"
;;
ripvideo) rip_video "$@"
;;
*) ## Making sure the script hasn't been renamed accidently... ^_^
echo "\n===> ERROR: This program has been renamed. The proper name for this program is 'burnvideo', along with a symlink (in the same directory) pointing to it called 'ripvideo'.\n" | fold >&2
exit 1
;;
esac
eject $DEV 2>/dev/null
exit 0
Enjoy
---thegeekster