Subversion Repositories pub

Compare Revisions

No changes between revisions

Ignore whitespace Rev 293 → Rev 739

/ATTIC/video-contact-sheet/tags/1.0.2b/vcs
0,0 → 1,779
#!/bin/bash
#
# $Rev$ $Date$
#
# vcs
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos
#
# Copyright (C) 2007 Toni Corvera
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# Author: Toni Corvera <outlyer@outlyer.net>
#
 
declare -r VERSION="1.0.2b"
#
# History:
#
# 1.0.2b: (2007-04-14)
# * Licensed under LGPL (was unlicensed before)
# * Renamed variables and constants to me more congruent
# * Added DEFAULT_COLS
# * BUGFIX: Fixed program signature
# * Streamlined error codes
# * Added cleanup on failure and on delayed cleanup on success
# * Changed default signature background to SlateGray (blue-ish gray)
#
# 1.0.1a: (2007-04-13)
# * Print output filename
# * Added manual mode (all timestamps provided by user)
# * More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs)
# * BUGFIX: Discard repeated timestamps
# * Added "set -e". TODO: Add more verbose error messages when called
# programs fail.
# * Added basic support for a user configuration file.
#
# 1.0a: (2007-04-10)
# * First release keeping track of history
# * Put vcs' url in the signature
# * Use system username in signature
# * Added --shoehorn (you get the idea, right?) to feed extra commands to
# the cappers. Lowelevel and not intended to be used anyway :P
# * When just a vidcap is requested, take it from the middle of the video
# * Added -H|--height
#
# 0.99.1a: Interim version, renamed to 1.0a
#
# 0.99a:
# * Added shadows
# * More colourful headers
# * Easier change of colours/fonts
#
# 0.5a: * First usable version
# 0.1: * First proof of concept
 
set -e
 
# Configuration file, please, use this file to modify the behaviour of the
# script. Using this allows overriding some variables (see below)
# to your liking. Only lines with a variable assignment are evaluated,
# it should follow bash syntax, note though that ';' can't be used
# currently in the variable values; e.g.:
#
# # Sample configuration for vcs
# user=myname # Sign all compositions as myname
# bg_heading=gray # Make the heading gray
#
# The variables that can be overriden are below the block of constants ahead.
declare -r CFGFILE=~/.vcs.conf
 
# Constants {{{
# see $decoder
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3
# See $timecode_from
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8
# These can't be overriden, modify this line if you feel the need
declare -r PROGRAM_SIGNATURE="with Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>"
 
# }}} # End of constants
 
# Override-able variables {{{
 
declare -i DEFAULT_INTERVAL=300
declare -i DEFAULT_NUMCAPS=16
declare -i DEFAULT_COLS=2
# Text before the user name in the signature
declare user_signature="Preview created by"
# By default sign as the system's username
declare user=$(id -un)
# Which of the two methods should be used to guess the number of thumbnails
declare -i timecode_from=$TC_INTERVAL
# Which of the two vidcappers should be used (see -F, -M)
# mplayer seems to fail for mpeg or WMV9 files, at least on my system
declare -i decoder=$DEC_FFMPEG
# Options used in imagemagick, these options set the final aspect
# of the contact sheet
declare output_format=png # ImageMagick decides the type from the extension
declare -i output_quality=92 # Output image quality (only affects the final
# image and obviously only in lossy formats)
# Colours, see convert -list color to get the list
declare bg_heading=YellowGreen # Background for meta info (size, codec...)
declare bg_sign=SlateGray # Background for signature
declare fg_heading=black # Font colour for meta info box
declare fg_sign=black # Font colour for signature
declare fg_tstamps=white # Font colour for timestamps
# Fonts, see convert -list type to get the list
declare font_tstamps=courier # Used for timestamps behind the thumbnails
declare font_heading=helvetica # Used for meta info box
declare font_sign=$font_heading # Used for the signature box
# Font sizes, in points
declare pts_tstamps=18 # Used for the timestamps
declare pts_meta=16 # Used for the meta info box
declare pts_sign=11 # Used for the signature
# See --shoehorn
declare shoehorned=
 
# }}} # End of override-able variables
 
# Options and other internal usage variables, no need to mess with this!
declare -i interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps)
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval)
declare title=""
declare -i fromtime=0 # Starting second (see -f)
declare -i totime=-1 # Ending second (see -t)
declare -a initial_stamps=( ) # Manually added stamps (see -S)
declare -i th_height= # Height of the thumbnails, by default use same as input
declare -i cols=$DEFAULT_COLS # Number of output columns
declare -i manual_mode=0 # if 1, only command line timestamps will be used
 
declare -a TEMPSTUFF=( ) # Temporal files
 
# Exit codes, same codes as /usr/include/sysexits.h
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \
EX_INTERRUPTED=79 # This one is not on sysexits.h
 
 
load_config() {
# These are the variables allowed to be overriden in the config file,
# please.
# They're REGEXes, they'll be concatenated to form a regex like
# (override1|override2|...).
# Don't mess with this unless you're pretty sure of what you're doing.
# All this extra complexity is done to avoid including the config
# file directly for security reasons.
declare -ra ALLOWED_OVERRIDES=(
'user'
'user_signature'
'bg_.*'
'font_.*'
'pts_.*'
'fg_.*'
'output_quality'
'DEFAULT_INTERVAL'
'DEFAULT_NUMCAPS'
'DEFAULT_COLS'
'decoder'
'output_format'
'shoehorned'
'timecode_from'
)
 
if [ ! -f "$CFGFILE" ]; then return 0 ; fi
 
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} )
 
while read line ; do # auto variable $line
# Don't allow ';', FIXME: dunno how secure that really is
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$line" ; then
continue
fi
if ! egrep -q "^($compregex)=" <<<"$line" ; then
continue
fi
# FIXME: Only print in verbose mose
# FIXME: Only for really overridden ones
echo "Overridden variable" $(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<$line)
eval $line
done <$CFGFILE
}
 
# {{{ # Convenience functions
 
is_number() {
egrep -q '^[0-9]+$' <<<"$1"
return $?
}
 
# The current code is a tad permissive, it allows e.g. things like
# 10m1h (equivalent to 1h10m)
# 1m1m (equivalent to 2m)
get_interval() {
if is_number "$1" ; then echo $1 ; return 0 ; fi
 
local s=$(tr '[A-Z]' '[a-z]' <<<"$1")
 
# Only allowed characters
if ! grep -q '[0-9smh]' <<<"$s"; then
return $EX_USAGE;
fi
 
# FIXME: Find some cleaner way
local i= c= num= sum=0
for i in $(seq 0 $(( ${#s} - 1)) ); do
c=${s:$i:1}
if is_number $c ; then
num+=$c
else
case $c in
h) num=$(($num * 3600)) ;;
m) num=$(($num * 60)) ;;
s) ;;
*)
return $EX_SOFTWARE
;;
esac
sum=$(($sum + $num))
num=
fi
done
 
# If last element was a number, it's seconds and they weren't added
if is_number $c ; then
sum=$(( $sum + $num ))
fi
 
echo $sum
 
return 0
}
 
pad() {
local len=$1
local str=$2
 
while [ ${#str} -lt $len ]; do
str=0$str
done
 
echo $str
}
 
pretty_stamp() {
if ! is_number "$1" ; then return $EX_USAGE ; fi
 
local t=$1
local h=$(( $t / 3600 ))
t=$(( $t % 3600 ))
local m=$(( $t / 60 ))
t=$(( $t % 60 ))
local s=$t
 
local R=""
 
if [ $h -gt 0 ]; then
R+="$h:"
fi
R+=$(pad 2 "$m"):$(pad 2 $s)
 
echo $R
}
 
get_pretty_size() {
local f="$1"
 
local bytes=$(du -DL --bytes "$f" | cut -f1)
local size=""
 
if [ "$bytes" -gt $(( 1024**3 )) ]; then
local gibs=$(( $bytes / 1024**3 ))
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 ))
size="${gibs}.${mibs:0:2} GiB"
elif [ "$bytes" -gt $(( 1024**2)) ]; then
local mibs=$(( $bytes / 1024**2 ))
local kibs=$(( ( $bytes % 1024**2 ) / 1024 ))
size="${mibs}.${kibs:0:2} MiB"
elif [ "$bytes" -gt 1024 ]; then
local kibs=$(( $bytes / 1024 ))
bytes=$(( $bytes % 1024 ))
size="${kibs}.${bytes:0:2} KiB"
else
size="${bytes} B"
fi
 
echo $size
}
 
# Rename a file, if the target exists, try with -1, -2, ...
safe_rename() {
local from="$1"
local to="$2"
 
local ext=$(sed -r 's/.*\.(.*)/\1/g' <<<"$to")
 
local n=1
while [ -f "$to" ]; do
to="$(basename "$2" .$ext)-$n.$ext"
n=$(( $n + 1))
done
 
mv "$from" "$to"
echo "$to" >&2
}
 
test_programs() {
for prog in mplayer convert montage ffmpeg ; do
type -pf "$prog" >/dev/null
local retval=$?
if [ $retval -ne 0 ] ; then
error "Required program $prog not found!"
return $retval
fi
done
}
 
cleanup() {
if [ -z $TEMPSTUFF ]; then return 0 ; fi
echo "Cleaning up..." >&2 # TODO: Only in verbose mode
rm -rf ${TEMPSTUFF[*]}
TEMPSTUFF=( )
}
 
exithdlr() {
cleanup
}
 
# Print some text to stderr
error() {
echo "$1" >&2
}
 
# }}} # Convenience functions
 
# {{{ # Core functionality
 
process() {
local f=$1
 
local numcols=$cols
 
if [ ! -f "$f" ]; then
error "File \"$f\" doesn't exist"
return $EX_NOINPUT
fi
echo "Processing $f..." >&2
 
# Meta data extraction
# Don't change the -vc as it would affect $vdec
local mplayer_cache=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID)
local vcodec=$(grep ID_VIDEO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) # FourCC
local acodec=$(grep ID_AUDIO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2)
local vdec=$(grep ID_VIDEO_CODEC <<<"$mplayer_cache" | cut -d'=' -f2) # Decoder
local width=$(grep ID_VIDEO_WIDTH <<<"$mplayer_cache" | cut -d'=' -f2)
local height=$(grep ID_VIDEO_HEIGHT <<<"$mplayer_cache" | cut -d'=' -f2)
local fps=$(grep ID_VIDEO_FPS <<<"$mplayer_cache" | cut -d'=' -f2)
# Vidcap/Thumbnail height
local vidcap_height=$th_height
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then
vidcap_height=$height
fi
 
local numsecs=$(grep ID_LENGTH <<<"$mplayer_cache"| cut -d'=' -f2 | cut -d. -f1)
if ! is_number $numsecs ; then
error "Internal error!"
return $EX_SOFTWARE
fi
 
local nc=$numcaps
local in=$interval
 
# Start bound:
local startsec=0
if [ $startsec -lt $fromtime ]; then
startsec=$fromtime
fi
 
# End bound:
local endsec=$numsecs
if [ $totime -ne -1 ] && [ $endsec -gt $totime ]; then
endsec=$totime
fi
if [ $startsec -ne 0 ] || [ $endsec -ne $numsecs ]; then
echo "Restricting to range [$startsec..$endsec]" >&2
fi
 
local delta=$(( $endsec - $startsec ))
# FIXME: the total # of caps is currently broken when using -f and -t
 
# Note that when numcaps mandates the interval is obtained from
# the actually allowed secods (hence it is not movie_length / numcaps )
# Adjust interval/numcaps:
if [ "$timecode_from" -eq "$TC_INTERVAL" ]; then
# Interval rules => it doesn't change
nc=$(( $delta / $interval ))
# If a multiple, an extra vidcap is generated (at the last second)
if [ $(( $delta % $interval )) -eq 0 ]; then
nc=$(( $nc + 1 ))
fi
elif [ "$timecode_from" -eq "$TC_NUMCAPS" ]; then
# Numcaps rules => it doesn't change
if [ $numcaps -eq 1 ]; then # If just one cap, center it
in=$(( $numsecs / 2 ))
else
in=$(( $numsecs / $numcaps ))
fi
else
error "Internal error!"
return $EX_SOFTWARE
fi
 
# Let's try to make some sense...
# Minimum interval allowance:
if [ $in -gt $numsecs ]; then
error "The interval is longer than the video length."
error "Use a lower interval or numcaps instead."
error "Skipping \"$f\"."
return $EX_USAGE
fi
# Contact sheet minimum cols:
if [ $nc -lt $numcols ]; then
numcols=$nc
fi
 
# Tempdir
 
local dir=$(mktemp -d -p . vcs.XXXXXX)
if [ "$?" -ne 0 ]; then
error "Error creating temporary directory"
return $EX_CANTCREAT
fi
TEMPSTUFF+=( "$dir" )
 
local n=
 
# Get the stamps (if in auto mode)...
 
local stamps=( )
if [ $manual_mode -ne 1 ]; then
 
n=$(( $startsec + $in ))
stamps=( ${initial_stamps[*]} $n )
 
while [ $n -le $endsec ]; do
n=$(( $n + $in ))
if [ $n -gt $endsec ]; then break; fi
 
stamps=( ${stamps[*]} $n )
done
else
stamps=( ${initial_stamps[*]} )
fi
 
n=1
local p=""
local cap=""
local montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \
-gravity SouthEast -fill white "
local output=$(tempfile --prefix "vcs-" --suffix '-preview.png' -d .)
TEMPSTUFF+=( "$output" )
 
# Let's reorder the stamps, this away user-added stamps get their correct
# position also remove duplicates. Note AFAIK sort only sorts lines, that's
# why y replace spaces by newlines.
#
# Note that stamps keeps being an array and stamps[1..N] still hold their
# old values, although it's treated as a string after this
stamps=$( sed 's/ /\n/g' <<<"${stamps[*]}" | sort -n | uniq )
 
local VIDCAPFILE=00000001.png
 
# If the temporal vidcap already exists, abort
if [ -f $VIDCAPFILE ]; then
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!."
return $EX_CANTCREAT
fi
 
local NUMSTAMPS=$(wc -w <<<"$stamps")
TEMPSTUFF+=( $VIDCAPFILE )
# TODO: Aspect ratio
for stamp in $stamps; do
# Note that it must be checked against numsecs and not endsec, to allow
# the user manually setting stamps beyond the boundaries
if [ $stamp -gt $numsecs ]; then continue; fi
 
echo "Generating capture #${n}/${NUMSTAMPS}..." >&2
 
if [ $decoder -eq $DEC_MPLAYER ]; then
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \
-frames 1 -ss $stamp $shoehorned "$f" >/dev/null 2>&1
elif [ $decoder -eq $DEC_FFMPEG ]; then
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \
-f rawvideo $shoehorned $VIDCAPFILE >/dev/null 2>&1
else
error "Internal error!"
return $EX_SOFTWARE
fi || {
local retval=$?
error "The capturing program failed!"
return $retval
}
 
p=$(pad 6 $stamp).png
# mv 00000001.png $dir/$p
n=$(( $n + 1 ))
 
cap=$dir/$p
# Add the timestamp to each vidcap, doing it hear is much powerful/simple
# than with the next montage command
convert -box '#000000aa' \
-fill $fg_tstamps -pointsize $pts_tstamps -gravity SouthEast \
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \
$VIDCAPFILE "$cap"
 
montage_command+=" $cap"
done
 
# geometry affects the source images, not the target one!
# Note the file name could also be added by using the "-title" option, but I reserved
# it for used set titles
montage_command+=" -geometry x${vidcap_height}+10+5 -tile ${numcols}x -shadow"
if [ "$title" ]; then
montage_command+=" -font $font_heading -fill $fg_heading -title '$title'"
fi
montage_command+=" $output"
 
echo "Composing contact sheet..." >&2
eval $montage_command # eval is required to evaluate correctly the text in quotes!
mv "$output" . 2>/dev/null || true
 
# Codec "prettyfication"
case $( tr '[A-Z]' '[a-z]' <<<$vcodec) in
xvid) vcodec=Xvid ;;
dx50) vcodec="DivX 5" ;;
0x10000001) vcodec="MPEG-1" ;;
0x10000002) vcodec="MPEG-2" ;;
avc1) vcodec="MPEG-4 AVC" ;;
wmv2) vcodec="WMV 8" ;; # WMV2 is v8
esac
if [ "$vdec" == "ffodivx" ]; then
vcodec+=" (MPEG-4)"
elif [ "$vdec" == "ffh264" ]; then
vcodec+=" (h.264)"
fi
 
case $( tr '[A-Z]' '[a-z]' <<<$acodec ) in
85) acodec='MPEG-1 Layer III (MP3)' ;;
80) acodec='MPEG-1 Layer II (MP2)' ;;
mp4a) acodec='MPEG-4 AAC' ;;
353) acodec='WMA 2' ;;
"") acodec="no audio" ;;
esac
 
 
local meta="Filename: $f
File size: $(get_pretty_size "$f")
Length: $(pretty_stamp "$numsecs")"
local meta2="Dimensions: ${width}x${height}
Format: $vcodec / $acodec
FPS: $fps"
local signature="$user_signature $user
$PROGRAM_SIGNATURE"
 
# Now let's add meta info
# This one enlarges the image to add the text, and puts
# meta info in two columns
convert -font $font_heading -pointsize $pts_meta \
-background $bg_heading -fill $fg_heading -splice 0x$(( $pts_meta * 4 )) \
-gravity NorthWest -draw "text 10,10 '$meta'" \
-gravity NorthEast -draw "text 10,10 '$meta2'" \
"$output" "$output"
# Finishing touch, signature
 
convert -gravity South -font $font_sign -pointsize $pts_sign \
-background $bg_sign -splice 0x34+0-0 \
-fill $fg_sign -draw "text 10,3 '$signature'" "$output" "$output"
 
if [ $output_format != "png" ]; then
local newout="$(basename "$output" .png).$output_format"
convert -quality $output_quality "$output" "$newout"
output="$newout"
fi
echo -n "Output wrote to " >&2
safe_rename "$output" "$(basename "$f").$output_format"
 
cleanup
}
 
show_help() {
local P=$(basename $0)
cat <<EOF
Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera
 
Usage: $P [options] <file>
 
Options:
-i|--interval <arg> Set the interval to arg. An optional unit can be
used (case-insensitive) , e.g.:
Seconds: 90
Minutes: 3m
Hours: 1h
Use either -i or -n.
-n|--numcaps <arg> Set the number of captured images to arg. Use either
-i or -n.
-f|--from <arg> Set starting time. No caps before this. Same format
as -i.
-t|--to <arg> Set ending time. No caps beyond this. Same format
as -i.
-T|--title <arg> Add a title above the vidcaps.
-u|--user <arg> Set the username found in the signature to this.
-S|--stamp <arg> Add the image found at the timestamp "arg", same format
as -i.
-m|--manual Manual mode: Only timestamps indicated by the user are
used (use in conjunction with -S), when using this
-i and -n are ignored.
-H|--height <arg> Set the output (individual thumbnail) height. Width is
derived accordingly. Note width cannot be manually set.
-j|--jpeg Output in jpeg (by default output is in png).
-h|--help Show this text.
 
Options used for debugging purposes or to tweak the internal workings:
-M|--mplayer Force the usage of mplayer.
-F|--ffmpeg Force the usage of ffmpeg.
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You probably don't need it.
 
Examples:
Create a contact sheet with default values (vidcaps at intervals of
$DEFAULT_INTERVAL seconds), the resulting file will be called
input.avi.png:
\$ $P input.avi
 
Create a sheet with vidcaps at intervals of 3 and a half minutes:
\$ $P -i 3m30 input.avi
 
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins,
add an extra vidcap at 2m and another one at 19m:
\$ $P -f 3m -t 18m -S2m -S 19m input.avi
 
EOF
}
 
# }}} # Core functionality
 
#### Execution starts here ####
 
# Execute exithdlr on exit
trap exithdlr EXIT
 
# Test requirements
test_programs || exit $EX_UNAVAILABLE
 
load_config
 
# {{{ # Command line parsing
 
# Based on getopt-parse.bash example.
# On debian systems see </usr/share/doc/util-linux/examples/getopt-parse.bash.gz>
 
# FIXME: use username / no name at all with -u noarg, and not -u
TEMP=$(getopt -o i:n:u:T:f:t:S:jhFMH:c:m \
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,shoehorn:,mplayer,ffmpeg,height:,columns:,manual" \
-n $0 -- "$@")
 
eval set -- "$TEMP"
 
while true ; do
case "$1" in
-i|--interval)
if ! interval=$(get_interval "$2") ; then
error "Interval must be a number (given $2)"
exit $EX_USAGE
fi
if [ "$interval" -le 0 ]; then
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL"
interval=$DEFAULT_INTERVAL
fi
timecode_from=$TC_INTERVAL
shift # Option arg
;;
-n|--numcaps)
if ! is_number "$2" ; then
error "Number of caps must be a number! (given $2)"
exit $EX_USAGE
fi
numcaps="$2"
timecode_from=$TC_NUMCAPS
shift # Option arg
;;
-u|--username) user="$2" ; shift ;;
-T|--title) title="$2" ; shift ;;
-f|--from)
if ! fromtime=$(get_interval "$2") ; then
error "Starting timestamp must be a valid interval"
exit $EX_USAGE
fi
shift
;;
-t|--to)
if ! totime=$(get_interval "$2") ; then
error "Ending timestamp must be a valid interval"
exit $EX_USAGE
fi
if [ "$totime" -eq 0 ]; then
error "Ending timestamp was set to 0, set to movie length"
totime=-1
fi
shift
;;
-S|--stamp)
if ! temp=$(get_interval "$2") ; then
error "Timestamps must be a valid interval"
exit $EX_USAGE
fi
initial_stamps=( ${initial_stamps[*]} $temp )
shift
;;
-j|--jpeg) output_format=jpg ;;
-h|--help) show_help ; exit $EX_OK ;;
--shoehorn)
shoehorned="$2"
shift
;;
-F) decoder=$DEC_FFMPEG ;;
-M) decoder=$DEC_MPLAYER ;;
-H|--height)
if ! is_number "$2" ; then
error "Height must be a number (given $2)"
exit $EX_USAGE
fi
th_height="$2"
shift
;;
-c|--columns)
if ! is_number "$2" ; then
error "Columns must be a number (given $2)"
exit $EX_USAGE
fi
cols="$2"
shift
;;
-m|--manual) manual_mode=1 ;;
--) shift ; break ;;
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ;
esac
shift
done
 
# Remaining arguments
if [ ! "$1" ]; then
show_help
exit $EX_USAGE
fi
# If -m is used then -S must be used
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then
error "You must provide timestamps (-S) when using manual mode (-m)"
exit $EX_USAGE
fi
 
set +e # Don't fail automatically
for arg do process "$arg" ; done
 
# }}} # Command line parsing
 
 
# vim:set ts=4 ai: #
Property changes:
Added: svn:executable
Added: svn:keywords
+Rev Id Date
\ No newline at end of property
/ATTIC/video-contact-sheet/tags/1.0.2b/.
Property changes:
Added: svn:mergeinfo
Merged /video-contact-sheet/branches/1.0a:r262-263
Merged /video-contact-sheet/branches/1.0.1a:r266-267
Merged /video-contact-sheet/tags/0.99a:r261
Merged /video-contact-sheet/branches/1.0.2b:r270-271