Subversion Repositories pub

Compare Revisions

No changes between revisions

Ignore whitespace Rev 454 → Rev 455

/video-contact-sheet/trunk/pkg/rpm/vcs.spec.in
48,7 → 48,7
BuildArch: noarch
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg
Requires: mplayer / ffmpeg
Requires: bash >= 2.05b
Requires: bash >= 3.1
Requires: ImageMagick >= 6.3.5-7
Requires: coreutils
URL: http://p.outlyer.net/vcs/
/video-contact-sheet/trunk/pkg/debian/control
8,7 → 8,7
 
Package: vcs
Architecture: all
Depends: bash (>= 2.05b), imagemagick (>= 6.3.5-7), mplayer | ffmpeg
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg
Recommends: lsdvd, ttf-dejavu-core
Description: tool to create contact sheets (previews) from videos
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact
/video-contact-sheet/trunk/pkg/debian/changelog
1,3 → 1,10
vcs (1.12.3-upstream.1) experimental; urgency=low
 
* New version.
* debian/control: Bump minimum bash version
 
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200
 
vcs (1.12.2-upstream.1) experimental; urgency=medium
 
* New version. Medium priority due to temporary files cleanup bug.
/video-contact-sheet/trunk/pkg/CHANGELOG
1,3 → 1,17
1.12.3 (2011-07-17):
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169]
* BUGFIX: Correct parsing of -U [#187]
* OTHER:
- Fix printing of remaining options on command-line error
- Switch to a minimum of bash 3.1 [#173]
- Avoid re-capturing the same frame twice [#122]
- Use getent instead of /etc/passwd when available
* INTERNAL:
- Use of Bash's 'caller' in 'assert' and 'trace'
- 'assert' prints a call trace on error
- 'assert_if'
- Don't use mplayer's length as a ceil for timecode removal [#174]
 
1.12.2 (2010-08-24):
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167]
Submitted by Jason Tackaberry.
/video-contact-sheet/trunk/pkg/vcs
31,7 → 31,7
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>
 
 
declare -r VERSION="1.12.2"
declare -r VERSION="1.12.3"
declare -r RELEASE=1
 
set -e
49,20 → 49,15
# syntax
# See the "Bash syntax notes" section for details
[ "$BASH_VERSINFO" ] && {
# Absolute minimum right now is 2.05b
if [ "${BASH_VERSINFO[0]}" -le 2 ]; then # Only bash <=2 needs extra testing
# I guess we can't expect any new bash2 release
if [ "${BASH_VERSINFO[0]}" -lt 2 ] ||
( [ "${BASH_VERSINFO[0]}" -eq 2 ] && [ "${BASH_VERSINFO[1]}" != '05b' ] ); then
echo "Bash 2.05b or higher is required" >&2
# Absolute minimum right now is 3.1
if [ "${BASH_VERSINFO[0]}" -lt 3 ] ||
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then
echo "Bash 3.1 or higher is required" >&2
exit 1
fi
fi
}
 
# {{{ # TO-DO
# * (1.12 or 1.13) Start replacing 'grep' with bash's '[[ =~ ]]'. Will break bash 2 compatibility
# for good
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files.
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. =>
#+ SUS v2: egrep is deprecated, grep -E replaces it
69,15 → 64,15
# * Change default DVD_TITLE to 0
# * Deprecations:
# OPTION/VAR -> ALTERNATIVE DEPRECATED FROM VERSION REMOVAL ETA
# --undocumented shoehorn -> NONE 1.12 1.13 or 1.14
# --undocumented shoehorn -> NONE 1.12 1.13
# --funky -> --profile ? ?+1
# --end_offset -> --end-offset 1.12 (silent), 1.13 (warn) 1.14
# (new var names introduced in 1.12) 1.12 1.14
# > Usage of ./vcs.conf is also deprecated since it doesn't mesh well with profiles
# (and it was a bad placeholder for them). Will remain to be loadable with
# -C:pwd.
# Loaded by default in 1.12 and maybe 1.13.
# Not loaded from 1.14 onwards (maybe 1.13), a warning may be shown if the file
# exists
# Loaded by default up to 1.13 (with warning in 1.13).
# Not loaded from 1.14 onwards
# * Variables cleanup:
# Variables will use a more uniform scheme, with prefixes where appropriate:
# - INTERNAL_*: Used internally to adapt messages and the like to the input
238,8 → 233,8
# Override-able since 1.11:
# Height of the thumbnails, by default use same as input
declare th_height='100%'
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps)
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval)
declare interval=$DEFAULT_INTERVAL # Interval of captures (~length/numcaps)
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (~length/interval)
# This is the horizontal padding added to each capture.
# Beware when changing this since extended set's alignment might break.
# When shadows are enabled this is ignored since they already add padding.
314,6 → 309,10
# It is executed by create_contact_sheet()
declare CSHEET_DELEGATE=csheet_montage
 
# Holds a list of captured frames (to avoid recapturing)
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>...
declare CAPTURES=
 
# Gravity of the timestamp (will be override-able in the future)
declare grav_timestamp=SouthEast
 
338,8 → 337,8
# $ env PATH=/whatever:$PATH vcs ...
# or use the undocumented (and unchecked!) appropriate option:
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg
declare MPLAYER=
declare FFMPEG=
declare MPLAYER_BIN=
declare FFMPEG_BIN=
 
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted
# and will trigger some custom tests.
512,7 → 511,6
"DEFAULT_END_OFFSET:end_offset:deprecated=end_offset:I"
 
# TODO TBA:
#"use_nonlatinfont::"
#"noboldfeedback::" # Colour but not bold
 
"shoehorned::striked"
532,20 → 530,22
#+(making it equivalent to '#'), there's no way to include a literal ';'
# load_config_file($1 = file, [$2 = type (description) = 'Settings'])
load_config_file() {
trace $@
local cfgfile=$1
local desc=$2
[ "$desc" ] || desc='Settings'
[[ $desc ]] || desc='Settings'
 
local por= # Parsed override
local varname= tmp= flag= bashcode= feedback= ov=
while read line ; do # auto variable $line
parse_override "$line" # Feeding it comments should be harmless
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments
parse_override "$line"
por=$RESULT
if [ "$por" ]; then
if [[ $por ]]; then
varname=${por/% *} # Everything up to the first space...
tmp=${por#* } # Rest of string
flag=${tmp/% *}
if [ "$flag" == '=' ]; then
if [[ $flag == '=' ]]; then
# No need to override...
feedback="$varname(=)"
else
554,9 → 554,9
ov="$ov, $feedback"
fi
done <$cfgfile
[ -z "$ov" ] || inf "$desc from $cfgfile:$NL ${ov:2}"
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}"
# No loaded overrides but errors/warnings to print, do print the file name
if [ -z "$ov" -a "$BUFFER" ]; then
if [[ ( -z $ov ) && $BUFFER ]]; then
inf "In $cfgfile:"
fi
flush_buffered ' '
568,7 → 568,7
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf )
 
for cfgfile in "${CONFIGS[@]}" ;do
[ -f "$cfgfile" ] || continue
[[ -f "$cfgfile" ]] || continue
load_config_file "$cfgfile"
done
}
584,10 → 584,11
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr
# load_profile($1 = profile name)
load_profile() {
trace $@
local p=$1 prof=
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ )
 
if [ ${p:0:1} == ':' ]; then
if [[ ${p:0:1} == ':' ]]; then
case $p in
:list)
# No need to be efficient here...
606,7 → 607,7
| while read profname ; do
for path in "${PATHS[@]}" ; do
path=$path$profname.conf
[ -f "$path" ] || continue
[[ -f $path ]] || continue
echo -n "$profname"
# [ ] here contains <space><tab>. Mawk doesn't understand
# [[:space:]]
628,7 → 629,7
 
for prof in "${PATHS[@]}" ; do
prof="$prof$p.conf"
[ -f "$prof" ] || continue
[[ -f $prof ]] || continue
load_config_file "$prof" 'Profile'
return 0
done
644,9 → 645,9
local n=$1 v=$2 p=$3
# Get constraint
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$n:")
[ "$map" ] || return 0
[[ $map ]] || return 0
local ct=$(cut -d':' -f4 <<<"$map")
[ "$ct" ] || return 0
[[ $ct ]] || return 0
local checkfn= domain=
case $ct in
n) checkfn=is_number ; domain=numbers ;;
660,8 → 661,8
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;;
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;;
esac
if [ "$checkfn" ] && ! $checkfn "$v" ; then
[ "$p" ] || p=$n
if [[ $checkfn ]] && ! $checkfn "$v" ; then
[[ $p ]] || p=$n
ERROR_MSG="Illegal value for '$p', only $domain are accepted"
ERROR_CODE=$EX_USAGE
return $ERROR_CODE
680,10 → 681,14
# This function always returns true
# parse_override($1 = override assignment)
parse_override() {
trace $@
local o="$1"
RESULT=''
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*=.*' <<<"$o" ; then
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*"
 
if [[ ! $o =~ $ERE ]] ; then
return
fi
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name
690,7 → 695,7
local lcvarname=$(echo "$varname" | tr '[A-Z]' '[a-z]')
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:")
 
[ "$mapping" ] || return 0
[[ $mapping ]] || return 0
 
local varval=${o#*=} # No trimming here (yet)
# 1) Trim from ; (if present) to finish
700,13 → 705,13
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval")
# Is varval empty?
[ "$varval" ] || return 0
[[ $varval ]] || return 0
 
local mvar=$(cut -d':' -f1 <<<"$mapping")
local ivar=$(cut -d':' -f2 <<<"$mapping")
local flags=$(cut -d':' -f3 <<<"$mapping")
local constraints=$(cut -d':' -f4 <<<"$mapping")
{ [ "$ivar" ] && [ "$ivar" != "=" ] ; } || ivar="$mvar"
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar"
 
# Note using "\$(echo $varval)" would allow a more flexible syntax but
#+enforce special handling of escaping, which with the currently available
717,11 → 722,12
return 0
}
 
[ "$varval" ] || return 0 # If empty value, ignore it
[[ $varval ]] || return 0 # If empty value, ignore it
 
local evcode=''
if [ "$flags" ] && [ $flags != "=" ] && [ $flags != 'alias' ]; then
if echo "$flags" | grep -q '^deprecated=' ; then
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then
local ERE='^deprecated='
if [[ $flags =~ $ERE ]]; then
local new=$(echo "$flags" | sed 's/^deprecated=//')
buffered warn "Variable '$varname' will be removed in the future,$NL please use '$new' instead."
else
744,16 → 750,16
fi
fi
 
[ -z "$constraints" ] || check_constraint $ivar "$varval" $varname || {
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || {
buffered error "$ERROR_MSG"
return 0
}
 
eval local curvarval='$'"$ivar" retflag='+'
if [ "$curvarval" == "$varval" ]; then
if [[ $curvarval == "$varval" ]]; then
retflag='='
else
if [ "$constraints" == "t" ]; then
if [[ $constraints == 't' ]]; then
varval=$(get_interval "$varval")
fi
# Escape single quotes, since it will be single-quoted:
800,15 → 806,15
# cmdline_override($1 = override assignment)
#+e.g. cmdline_override 'decoder=$DEC_FFMPEG'
cmdline_override() {
trace $FUNCNAME $@
trace $@
parse_override "$1"
local r=$RESULT
[ "$r" ] || return 0
[[ $r ]] || return 0
local varname=${r/% *} # See load_config()
local tmp=${r#* }
local flag=${tmp/% *}
 
if [ "$flag" == '=' ]; then
if [[ $flag == '=' ]]; then
varname="$varname(=)"
fi
 
824,7 → 830,7
for cback in $funcs ; do
local fn=${cback/:*}
local arg=${cback/*:}
[ "$arg" != "$cback" ] || arg=''
[[ $arg != $cback ]] || arg=''
$fn $arg
done
}
831,12 → 837,12
 
# Print the list of command-line overrides
cmdline_overrides_flush() {
trace $FUNCNAME $@
if [ "$CMDLINE_OVERRIDES" ]; then
trace $@
if [[ $CMDLINE_OVERRIDES ]]; then
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}"
fi
if [ "$BUFFER" ]; then
[ "$CMDLINE_OVERRIDES" ] || warn "In command-line overrides:"
if [[ $BUFFER ]]; then
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:"
flush_buffered ' '
fi
}
850,25 → 856,25
 
## Natural number
is_number() {
#egrep -q '^[0-9]+$' <<<"$1" ;
# From [[abs]], test if '[ ]' can parse input as numbers
# Returns 2 for failed test, expected to return 1
[ "$1" -ne 0 -o "$1" -eq 0 ] 2>/dev/null || return 1
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0
#+or otherwise a valid number. Must return 1 on error.
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1
}
## Number > 0
is_positive() { is_number "$1" && [ $1 -gt 0 ]; }
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; }
## Bool (0 or 1)
is_bool() { [ "$1" == "0" -o "$1" == "1" ] 2>/dev/null ; }
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; }
## Float (XX.YY; XX.; ;.YY) (.24=0.24)
is_float() { egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" ; }
is_float() { local P='^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' ; [[ $1 =~ $P ]] ; }
## Percentage (xx% or xx.yy%)
is_percentage() {
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$'<<<"$1"
local P='^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$'
[[ $1 =~ $P ]]
}
## Interval
is_interval() {
local i=$(get_interval "$1" || true)
[ "$i" ] && fptest $i -gt 0
[[ $i ]] && fptest $i -gt 0
}
## Interval or percentage
is_interv_or_percent() {
876,7 → 882,7
}
## Positive or percentage
is_pos_or_percent() {
is_number "$1" && [ "$1" -gt 0 ] || is_percentage "$1"
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1"
}
## Float (>=0) or fraction
is_float_or_frac() {
884,20 → 890,21
}
## Fraction, strictly (X/Y, but no X; Y!=0)
is_fraction() {
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" && {
local d=$(echo "$1" | cut -d'/' -f2)
[ "$d" -ne 0 ]
local P='^[0-9]+/[0-9]+$'
[[ $1 =~ $P ]] && {
local d=${1#*/} # .../X
[[ $d -ne 0 ]]
}
}
## Decoder ($DEC_* constants)
is_decoder() { [ "$1" == $DEC_FFMPEG -o "$1" == $DEC_MPLAYER ]; }
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; }
## Time calculation source ($TC_* constants)
is_tcfrom() { [ "$1" == $TC_INTERVAL -o "$1" == $TC_NUMCAPS ]; }
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; }
### Verbosity level ($V_* constants)
is_vlevel() {
is_number "$1" && \
[ $1 -eq $V_ALL -o $1 -eq $V_NONE -o $1 -eq $V_ERROR -o \
$1 -eq $V_WARN -o $1 -eq $V_INFO ]
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]]
}
 
#### }}}} # End of type checkers
934,8 → 941,8
rtomult() {
local n=$1 d=$2
local r=$(( $n % $d ))
if [ $r -ne 0 ]; then
let 'n += ( d - r )'
if [[ $r -ne 0 ]]; then
(( n += ( d - r ) , 1 ))
fi
echo $n
}
951,11 → 958,11
-le) op='<=' ;;
-eq) op='==' ;;
-ne) op='!=' ;;
*) assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" && return $EX_SOFTWARE
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE
esac
# Empty operands
if [ -z "$1" ] || [ -z "$3" ]; then
assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false"
if [[ ( -z $1 ) || ( -z $3 ) ]]; then
assert "[[ \"'$1'\" && \"'$3'\" ]] && false"
else
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }"
fi
969,7 → 976,8
 
# Keep a number of decimals, last decimal rounded to lower
keepdecimals_lower() {
grep -q '\.' <<<"$1" || { echo "$1" ; return ; }
local ERE='\.'
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; }
local D=${1/#*.} # Decimals only
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals
}
996,7 → 1004,7
# converts spaces to newlines in a x-platform way [[FNL]]
# stonl([$1 = string])
stonl() {
if [ "$1" ]; then
if [[ $1 ]]; then
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$'
else
awk '{gsub(" ", "\n");print}' | egrep -v '^$'
1006,7 → 1014,7
# Converts newlines to spaces portably
# nltos([$1 = string])
nltos() {
if [ "$1" ]; then
if [[ $1 ]]; then
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//'
else
awk '{printf "%s ",$0}' | sed 's/ *//'
1021,7 → 1029,8
 
# Get file extension
filext() {
grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2
#grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2
expr match "$1" '.*\.\(.*\)'
}
 
# Wrapper around $RANDOM, not called directly, wrapped again in rand().
1069,7 → 1078,7
local v="$1"
local -i hv=15031
local c=
if [ "$v" ]; then
if [[ $v ]]; then
for i in $(seqr 0 ${#v} ); do
c=$( ord ${v:$i:1} )
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT ))
1114,7 → 1123,7
# I don't see reason to make it more anal, though.
# get_interval($1 = interval)
get_interval() {
trace $FUNCNAME $@
trace $@
# eval it even if it's numeric to strip leading zeroes. Note the quoting
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi
 
1121,14 → 1130,12
local s=$(tolower "$1") t r n
 
# Only allowed characters
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then
return $EX_USAGE;
fi
local ERE='^[0-9smhSMH.]+$'
[[ $s =~ $ERE ]] || return $EX_USAGE
 
# Two consecutive dots are no longer accepted
if egrep -q '\.\.' <<<"$s" ; then
return $EX_USAGE
fi
# ([.] required for bash 3.1 + bash 3.2 compat)
[[ ! $s =~ [.][.] ]] || return $EX_USAGE
 
# Newer parsing code: replaces units by a product
# and feeds the resulting string to awk for evaluation
1150,9 → 1157,10
# Seconds without unit. They must be preceded by h, m or s at this point
local secs=$(echo $s | egrep -o '.?[0-9]*$')
# When preceded by '.', they're ms
[ "$secs" ] && grep -q '\.'<<<"$secs" && secs=
local ERE='\.'
[[ $secs && ( $secs =~ $ERE ) ]] && secs=
# Quote and addition. Note BSD grep/egrep wants the anchor ($) or won't match
[ "$secs" ] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + "
[[ $secs ]] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + "
t=${t//h/ * 3600 + }
t=${t//m/ * 60 + }
t=${t//s/ + }
1161,8 → 1169,8
r=$(awkexf "$t" 2>/dev/null)
 
# Negative and empty intervals
assert $LINENO "[ '$r' ] && [ '$t' ]"
assert $LINENO "fptest $r -gt 0"
assert "[[ '$r' && '$t' ]]"
assert "fptest $r -gt 0"
 
echo $r
}
1171,12 → 1179,10
# the indicated length
# pad($1 = minimum length, $2 = string)
pad() {
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers
local str=$2
while [ "${#str}" -lt $1 ]; do
str="0$str"
done
echo $str
# Must allow non-numbers
local l; (( l = $1 - ${#2} , 1 ))
[[ $l -le 0 ]] || printf "%0${l}d" '0'
echo $2
}
 
# Get Image Width
1204,7 → 1210,7
# e.g.: 3600 becomes 1:00:00
# pretty_stamp($1 = seconds)
pretty_stamp() {
assert $LINENO "is_float '$1'"
assert "is_float '$1'"
# Fully implemented in AWK to discard bc.
# As a bonus now it's much faster and compact
awk "BEGIN {
1228,15 → 1234,15
local bytes=$1
local size=
 
if [ "$bytes" -gt $(( 1024**3 )) ]; then
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
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
elif [[ $bytes -gt 1024 ]]; then
local kibs=$(( $bytes / 1024 ))
bytes=$(( $bytes % 1024 ))
size="${kibs}.${bytes:0:2} KiB"
1262,13 → 1268,13
# Since safe_rename() is called from $() it won't be able to affect global variables directly
# Hopefully soon this won't be needed
sanitise_rename_pattern() {
if ! grep -q '%e' <<<"$safe_rename_pattern" ||
! grep -q '%N' <<<"$safe_rename_pattern" ||
! grep -q '%b' <<<"$safe_rename_pattern" ; then
# No quoting! => Regex
if [[ ! $safe_rename_pattern =~ %e || ! $safe_rename_pattern =~ %N || \
! $safe_rename_pattern =~ %b ]]; then
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT
fi
# Hashmarks will break the regex used in safe_rename()
if grep -q '#' <<<"$safe_rename_pattern" ; then
if [[ $safe_rename_pattern =~ \# ]]; then
warn "Illegal character \"#\" found in safe renaming pattern, resetting it"
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT
fi
1287,7 → 1293,7
# safe_rename($1 = original file, $2 = target file)
# XXX: Note it fails if target has no extension
safe_rename() {
trace $FUNCNAME $@
trace $@
local from="$1"
local to="$2"
 
1297,14 → 1303,12
local b=${to%.$ext}
 
local n=1
while [ -f "$to" ]; do # Only executes if $2 exists
while [[ -f $to ]]; do # Only executes if $2 exists
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%'
# Sed is a safer bet
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern")
to=$(sed "s#%N#$n#g" <<<"$to")
to=$(sed "s#%e#$ext#g" <<<"$to")
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$safe_rename_pattern")
 
let 'n++';
(( n++ ));
done
 
mvq "$from" "$to"
1338,7 → 1342,7
# Gets the size of the dvd device, in DVD mode
get_dvd_size() {
# FIXME: Case sensivity might break with iso9660
if [ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]; then
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then
# Some VOBs available
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB"
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end.
1370,40 → 1374,40
local retval=0 last=0
local nopng=0
 
MPLAYER=$(type -pf mplayer) || true
FFMPEG=$(type -pf ffmpeg) || true
MPLAYER_BIN=$(type -pf mplayer) || true
FFMPEG_BIN=$(type -pf ffmpeg) || true
 
# Test we can actually use FFmpeg
[ "$FFMPEG" ] && {
[[ $FFMPEG_BIN ]] && {
# Newer FF has -codecs, -formats, -protocols, older has only -formats
#+png is a codec so it's on different lists on newer and older
if ! "$FFMPEG" -formats 2>/dev/null | grep -q 'EV.* png' && \
! "$FFMPEG" -codecs 2>/dev/null | grep -q 'EV.* png' ; then
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then
warn "FFmpeg can't output to png, won't be able to use it."
FFMPEG=''
FFMPEG_BIN=''
nopng=1
fi
}
# Same for Mplayer
[ "$MPLAYER" ] && {
if ! "$MPLAYER" -vo help 2>&1 | grep -q 'png' ; then
[[ $MPLAYER_BIN ]] && {
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then
warn "MPlayer can't output to png, won't be able to use it."
MPLAYER=''
MPLAYER_BIN=''
nopng=1
fi
}
 
[ "$MPLAYER" ] || [ "$FFMPEG" ] || {
[[ ( -n $MPLAYER_BIN ) || ( -n $FFMPEG_BIN ) ]] || {
local pngwarn=
[ $nopng -eq 1 ] && pngwarn=', with PNG output support,'
[[ $nopng -eq 1 ]] && pngwarn=', with PNG output support,'
error "mplayer and/or ffmpeg$pngwarn are required!"
let 'retval++,1'
(( retval++ ,1 ))
}
 
 
if [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then
if [[ ( $decoder -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then
decoder=$DEC_MPLAYER
elif [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then
elif [[ ( $decoder -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then
decoder=$DEC_FFMPEG
fi
 
1411,13 → 1415,13
for prog in convert montage identify mktemp grep egrep cut sed awk ; do
if ! type -pf "$prog" ; then
error "Required program $prog not found!"
let 'retval++,1'
(( retval++ ,1 ))
fi >/dev/null
done
# TODO: [[x2]]
 
# Early exit
[ $retval -eq 0 ] || return $EX_UNAVAILABLE
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE
 
# ImageMagick version. 6 is a must, I'm probably using some
# features that require a higher minor version
1426,7 → 1430,7
local ver
ver=$(identify -version | head -n1 | grep -o 'ImageMagick[[:space:]]*[^ ]*' |\
cut -f 2 -d' ')
if [ "$ver" ]; then
if [[ $ver ]]; then
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist
local major=$(cut -d'.' -f1 <<<"$verx")
local minor=$(cut -d'.' -f2 <<<"$verx")
1433,22 → 1437,22
local micro=$(cut -d'.' -f3 <<<"$verx")
local rev=$(cut -d'.' -f4 <<<"$verx")
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev))
if [ "$serial" -lt 630507 ]; then
if [[ $serial -lt 630507 ]]; then
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ;
let 'retval++,1'
(( retval++ ,1 ))
fi
else
error "Failed to check ImageMagick version."
let 'retval++,1'
(( retval++ ,1 ))
fi
 
[ $retval -eq 0 ] || return $EX_UNAVAILABLE
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE
}
 
# Test wether $GETOP is a compatible version; try to choose an alternate if
# possible
choose_getopt() {
if ! type -pf $GETOPT ; then
if ! type -pf "$GETOPT" ; then
# getopt not in path
error "Required program getopt not found!"
return $EX_UNAVAILABLE
1457,13 → 1461,13
# Try getopt. If there's more than one in the path, try all of them
for goe in $(type -paf $GETOPT) ; do
"$goe" -T || gor=$?
if [ $gor -eq 4 ]; then
if [[ $gor -eq 4 ]]; then
# Correct getopt found
GETOPT="$goe"
break;
fi
done >/dev/null
if [ $gor -ne 4 ]; then
if [[ $gor -ne 4 ]]; then
error "No compatible version of getopt in path, can't continue."
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>"
return $EX_UNAVAILABLE
1475,7 → 1479,7
# Does nothing if none has been created so far
# cleanup()
cleanup() {
if [ -z $TEMPSTUFF ]; then return 0 ; fi
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi
inf "Cleaning up..."
rm -rf "${TEMPSTUFF[@]}"
unset VCSTEMPDIR
1487,7 → 1491,7
# exithdlr()
exithdlr() {
# I don't think that's really required anyway
if [ "$RANDFUNCTION" == "filerand" ]; then
if [[ $RANDFUNCTION == 'filerand' ]]; then
7<&- # Close FD 7
fi
cleanup
1500,8 → 1504,8
#
# error($1 = text)
error() {
if [ $verbosity -ge $V_ERROR ]; then
[ $plain_messages -eq 0 ] && echo -n "$prefix_err"
if [[ $verbosity -ge $V_ERROR ]]; then
[[ $plain_messages -eq 0 ]] && echo -n "$prefix_err"
# sgr0 is always used, this way if
# a) something prints inbetween messages it isn't affected
# b) if plain_messages is overridden colour stops after the override
1514,8 → 1518,8
# Print a non-fatal error or warning
# warning($1 = text)
warn() {
if [ $verbosity -ge $V_WARN ]; then
[ $plain_messages -eq 0 ] && echo -n "$prefix_warn"
if [[ $verbosity -ge $V_WARN ]]; then
[[ $plain_messages -eq 0 ]] && echo -n "$prefix_warn"
echo "$1$suffix_fback"
fi >&2
}
1523,8 → 1527,8
# Print an informational message
# inf($1 = text)
inf() {
if [ $verbosity -ge $V_INFO ]; then
[ $plain_messages -eq 0 ] && echo -n "$prefix_inf"
if [[ $verbosity -ge $V_INFO ]]; then
[[ $plain_messages -eq 0 ]] && echo -n "$prefix_inf"
echo "$1$suffix_fback"
fi >&2
}
1533,7 → 1537,7
# Same as inf but with no colour ever.
# infplain($1 = text)
infplain() {
if [ $verbosity -ge $V_INFO ]; then
if [[ $verbosity -ge $V_INFO ]]; then
echo "$1" >&2
fi
}
1546,7 → 1550,6
# buffered($1 = feedback function, $2 = arguments)
buffered() {
local grab=$( $1 "$2" 2>&1 )
# BUFFER=( "${BUFFER[@]}" -- "$grab" )
BUFFER=$BUFFER$grab$NL
}
 
1554,17 → 1557,16
# Print buffered feedback to stderr
# flush_buffered([$1 = indentation])
flush_buffered() {
[ "${BUFFER[*]}" ] || return 0
[[ ${BUFFER[*]} ]] || return 0
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1
BUFFER=''
}
 
 
#
# trace($1 = function name = $FUNCNAME, function arguments...)
# trace(... = function arguments)
trace() {
if [ "$DEBUG" -ne "1" ]; then return; fi
echo "[TRACE]: $@" >&2
[[ $DEBUG -eq 1 ]] || return 0
echo "[TRACE]: $(caller 0 | cut -d' ' -f2) $*" >&2
}
 
# Print an error message and exit
1573,9 → 1575,9
# If no exit_code is provided, use $ERROR_CODE
die() {
local m=$1 ec=$2
[ "$ec" ] || ec=$ERROR_CODE
[ "$ec" ] || ec=1
[ "$m" ] || m=$ERROR_MSG
[[ $ec ]] || ec=$ERROR_CODE
[[ $ec ]] || ec=1
[[ $m ]] || m=$ERROR_MSG
error "$m"
exit $ec
}
1586,7 → 1588,7
has_filter() {
local filter= ref=$1
for filter in ${FILTERS_IND[@]} ; do
[ "$filter" == "$ref" ] || continue
[[ $filter == $ref ]] || continue
return 0
done
return 1
1621,7 → 1623,7
fi >/dev/null
fi
 
if [ -z "$HAS_COLORS" ]; then
if [[ -z $HAS_COLORS ]]; then
# tput was not an option, let's try ANSI escape codes instead [[AEC]]
# TODO: Detect support
# Alternatively: $ perl -e 'print "\e[31m\e[1m"'
1635,7 → 1637,7
fi
 
# Finally, if there's no colour support, use prefixes instead
if [ -z "$HAS_COLORS" ]; then
if [[ -z $HAS_COLORS ]]; then
set_feedback_prefixes
fi
}
1648,24 → 1650,44
# seqr($1 = from, $2 = to, $3 = increment)
seqr() {
local from=$1 to=$2 inc=$3
[ "$inc" ] || inc=1
[[ $inc ]] || inc=1
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }"
}
 
#
# assertion operator
# assert($1 = line, $* = code)
# TODO: Limit usage to values that will expand correctly always (i.e. not with quotes)
# Note: Use single quotes for globals, no need to expand in release
# assert(... = code)
assert() {
[ $RELEASE -eq 1 ] && return
LINE=$1
shift
[[ $RELEASE -eq 0 ]] || {
function assert { :; } # Redefine to avoid check
}
local c=$(caller 0) # <num> <func> <file>
c=${c% *} # <num> <func>
local LIN=${c% *} FN=${c#* }
eval "$@" || {
error "Internal error at line $LINE: $@"
error "Internal error at $FN:$LIN: $@"
local cal=$(caller 1)
[[ $level ]] && error " Stack trace:"
local level=2
while [[ $cal ]]; do
cal=${cal% *}
error " ${cal#* }:${cal% *}"
cal=$(caller $level)
(( level++ ))
done
exit $EX_SOFTWARE
}
}
 
# Conditional assertion
# assert_if($1 = condition, $2 = assert if $1 true)
assert_if() {
[[ $RELEASE -eq 1 ]] && return
if eval "$1" ; then
assert "$2"
fi
}
 
# }}} # Convenience functions
 
# {{{ # Core functionality
1673,21 → 1695,21
# Creates a new temporary directory
# create_temp_dir()
create_temp_dir() {
trace $FUNCNAME $@
trace $@
 
[ "$VCSTEMPDIR" ] && return 0
[[ -z $VCSTEMPDIR ]] || return 0
 
# Try to use /dev/shm if available, this provided a very small
# benefit on my system but me of help for huge files. Or maybe won't.
# Passing a full path template is more x-platform than using
# -t / -p
if [ -d /dev/shm ] && [ -w /dev/shm ]; then
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX)
else
[ "$TMPDIR" ] || TMPDIR="/tmp"
[[ $TMPDIR ]] || TMPDIR="/tmp"
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX")
fi
if [ ! -d "$VCSTEMPDIR" ]; then
if [[ ! -d $VCSTEMPDIR ]]; then
error "Error creating temporary directory"
return $EX_CANTCREAT
fi
1703,9 → 1725,9
# Create a new temporal file and print its filename
# new_temp_file($1 = suffix)
new_temp_file() {
trace $FUNCNAME $@
trace $@
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX")
if [ ! -f "$r" ]; then
if [[ ! -f $r ]]; then
error "Failed to create temporary file"
return $EX_CANTCREAT
fi
1722,10 → 1744,10
# or colour combination you like.
# randomize_look()
randomize_look() {
trace $FUNCNAME $@
trace $@
local mode=f lineno
 
if [ "f" == $mode ]; then # Random mode
if [[ $mode == 'f' ]]; then # Random mode
# There're 5 rows of extra info printed
local ncolours=$(( $(convert -list color | wc -l) - 5 ))
randcolour() {
1788,7 → 1810,7
# from the current video
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps)
compute_timecodes() {
trace $FUNCNAME $@
trace $@
 
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0
local eff_eo= # Effective end_offset (for percentages)
1811,7 → 1833,7
local runlen=$(awkexf "$end - $st")
 
if fptest "($end-$eo-$st)" -le 0 ; then
if fptest "$eo" -gt 0 && [ -z "$USR_end_offset" ] ; then
if fptest "$eo" -gt 0 && [[ -z $USR_end_offset ]] ; then
warn "Default end offset was too high for the video, ignoring it."
eo=0
else
1822,12 → 1844,12
fi
 
local inc=
if [ "$tcfrom" -eq $TC_INTERVAL ]; then
if [[ $tcfrom -eq $TC_INTERVAL ]]; then
inc=$tcint
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then
# Numcaps mandates: timecodes are obtained dividing the length
# by the number of captures
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it
inc=$(awkexf "(($end-$st)/2 + 1)")
else
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)")
1849,7 → 1871,7
local bound=$(awkexf "$end - $eo")
local last=
while fptest $stamp -le "$bound"; do
assert $LINENO fptest $stamp -ge 0
assert fptest $stamp -ge 0
LTC=( "${LTC[@]}" "$stamp" )
last=$stamp
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3)
1856,7 → 1878,7
done
inf "Capturing in range [$(pretty_stamp $inc)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})"
unset LTC[0] # Discard initial cap (=$st)
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) # Don't quote or extra empty stamp!
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" )
}
 
# Tries to guess an aspect ratio comparing width and height to some
1863,16 → 1885,16
# known values (e.g. VCD resolution turns into 4/3)
# guess_aspect($1 = width, $2 = height)
guess_aspect() {
trace $FUNCNAME $@
trace $@
local w=$1 h=$2 ar
 
case "$w" in
352)
if [ $h -eq 288 ] || [ $h -eq 240 ]; then
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then
# Ambiguous, could perfectly be 16/9
# VCD / DVD @ VCD Res. / Half-D1 / CVD
ar=4/3
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then
# Ambiguous, could perfectly be 16/9
# Half-D1 / CVD
ar=4/3
1879,25 → 1901,25
fi
;;
704|720)
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB
# Ambiguous, could perfectly be 16/9
ar=4/3
fi
;;
480)
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD
ar=4/3
fi
;;
esac
 
if [ -z "$ar" ]; then
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD
if [[ -z $ar ]]; then
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD
ar=16/9
fi
fi
 
if [ -z "$ar" ]; then
if [[ -z $ar ]]; then
warn "Couldn't guess aspect ratio."
ar="$w/$h" # Don't calculate it yet
fi
1905,81 → 1927,48
echo $ar
}
 
# Capture a frame with ffmpeg
# capture_ffmpeg($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts])
capture_ffmpeg() {
trace $FUNCNAME $@
local f=$1
local o=$2
local ts=$3
# XXX: It would be nice to show a message if it takes too long
# See wa_ss_* declarations at the start of the file for details
"$FFMPEG" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \
-dframes 1 -vframes 1 -vcodec png \
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr"
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ]
}
# Capture a frame
# capture($1 = filename, $2 = second, $3 = output file)
capture() {
trace $@
local f=$1 stamp=$2 ofile=$3
# globals: $shoehorned $decoder
 
# Capture a frame with mplayer
# capture_mplayer($1 = inputfile, $2 = UNUSED, $3 = timestamp[, $4 = extra opts])
capture_mplayer() {
trace $FUNCNAME $@
# Note mplayer CAN'T set the output filename, newer mplayer can set output
#+dir though.
local f="$1"
local o=00000005.png
local ts=$3
 
# No point in passing ms to mplayer
ts=$(cut -d'.' -f1 <<<"$ts")
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1])
 
{
if [ $DVD_MODE -eq 1 ]; then
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \
-frames 5 -ss "$ts" $shoehorned -dvd-device "$f" \
$4 "dvd://$DVD_TITLE"
# Avoid recapturing if timestamp is already captured.
# The extended set includes the standard set so when using the extended mode
#+this will avoid some captures, specially with mplayer, since it doesn't
#+have ms precission
local key=
# Normalise key values' decimals
if [[ $decoder -eq $DEC_MPLAYER ]]; then
key=$(awkex "int($stamp)")
else
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \
-frames 5 -ss "$ts" $4 $shoehorned "$f"
key=$(awkex $stamp)
fi
local cached=$(grep "^$key:" <<<"$CAPTURES")
if [[ $cached ]]; then
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe?
return $?
fi
 
} >"$stdout" 2>"$stderr"
rm -f 0000000{1,2,3,4}.png # Remove the first four
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ]
}
 
# Capture a frame
# capture($1 = filename, $2 = second)
capture() {
trace $FUNCNAME $@
local f=$1 stamp=$2
local VIDCAPFILE=00000005.png
# globals: $shoehorned $decoder
 
if [ $decoder -eq $DEC_MPLAYER ]; then
capture_mplayer "$f" 'IGNOREME' "$stamp"
elif [ $decoder -eq $DEC_FFMPEG ]; then
assert '[[ $decoder -eq $DEC_MPLAYER || $decoder -eq $DEC_FFMPEG ]]'
if [[ $decoder -eq $DEC_MPLAYER ]]; then
mplayer_capture "$f" "$ofile" "$stamp"
elif [[ $decoder -eq $DEC_FFMPEG ]]; then
# FIXME: ffmpeg can put the temporary file anywhere
capture_ffmpeg "$f" "$VIDCAPFILE" "$stamp"
else
error "Internal error!"
return $EX_SOFTWARE
fi || true
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then
[ $decoder -eq $DEC_MPLAYER ] && stamp=${stamp/%.*}
ffmpeg_capture "$f" "$ofile" "$stamp"
fi || {
[[ $decoder -ne $DEC_MPLAYER ]] || stamp=${stamp/%.*}
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)"
return $EX_SOFTWARE
fi
 
return 0
}
CAPTURES="$CAPTURES$key:$ofile$NL"
}
 
# Applies all individual vidcap filters
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..])
filter_vidcap() {
trace $FUNCNAME $@
trace $@
# For performance purposes each filter simply prints a set of options
# to 'convert'. That's less flexible but enough right now for the current
# filters.
1990,7 → 1979,7
local t=$(new_temp_file .png)
eval "convert '$1' $cmdopts '$t'"
# If $t doesn't exist returns non-zero
[ -f "$t" ] && mvq "$t" "$1"
[[ -f $t ]] && mvq "$t" "$1"
}
 
# Applies all global vidcap filters
2000,7 → 1989,7
#}
 
filt_resize() {
trace $FUNCNAME $@
trace $@
local f="$1" t=$2 w=$3 h=$4
 
# Note the '!', required to change the aspect ratio
2010,20 → 1999,20
# Draw a timestamp in the file
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index)
filt_apply_stamp() {
trace $FUNCNAME $@
trace $@
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6
 
local pts=$pts_tstamps
if [ $height -lt 200 ]; then
if [[ $height -lt 200 ]]; then
pts=$(( $pts_tstamps / 3 ))
elif [ $height -lt 400 ]; then
elif [[ $height -lt 400 ]]; then
pts=$(( $pts_tstamps * 2 / 3 ))
fi
# If the size is too small they won't be readable at all
# With the original font 8 was the minimum, with DejaVu 7 is readable
if [ $pts -le 7 ]; then
if [[ $pts -le 7 ]]; then
pts=7
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then
warn "Very small timestamps in use. Disabling them with -dt might be preferable"
fi
fi
2038,7 → 2027,7
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid>
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height)
filt_photoframe() {
trace $FUNCNAME $@
trace $@
# local file="$1" ts=$2 w=$3 h=$4
# Tweaking the size gives a nice effect too
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) ))
2046,7 → 2035,7
# Should probably be bigger for really big frames
# Note that only images below 21600px (e.g. 160x120) go below a 6px border
local border=$(( ($3*$4) / 3600 ))
[ $border -lt 7 ] || border=6
[[ $border -lt 7 ]] || border=6
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 "
}
 
2061,10 → 2050,10
# Based on filt_photoframe(), with a bigger lower border
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height)
filt_polaroid() {
trace $FUNCNAME $@
trace $@
# local file="$1" ts=$2 w=$3 h=$4
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details
[ $border -lt 7 ] || border=6
[[ $border -lt 7 ]] || border=6
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} "
# FIXME: This is rather ugly (double-flipping) there's sure a better way
echo -n "\( -flip -splice 0x$(( $border*5 )) \) "
2075,7 → 2064,7
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid>
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height)
filt_randrot() {
trace $FUNCNAME $@
trace $@
# Rotation angle [-18..18]
local angle=$(( ($(rand) % 37) - 18 ))
echo "-background none -rotate $angle "
2084,7 → 2073,7
# This one requires much more work, the results are pretty rough, but ok as
# a starting point / proof of concept
filt_film() {
trace $FUNCNAME $@
trace $@
local file="$1" ts=$2 w=$3 h=$4
# Base reel dimensions
local rw=$(rmultiply $w,0.08) # 8% width
2108,9 → 2097,9
# Repeat it until the height is reached and crop to the exact height
local sh=$(imh "$base_reel") in=
local repeat=$( ceilmultiply $h/$sh)
while [ $repeat -gt 1 ]; do
while [[ $repeat -gt 1 ]]; do
in="$in '$base_reel' "
let 'repeat--'
(( repeat-- ));
done
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \
"$reel_strip"
2126,7 → 2115,7
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height,
# $5...$# = vidcaps) : output
create_contact_sheet() {
trace $FUNCNAME $@
trace $@
$CSHEET_DELEGATE "$@"
}
 
2134,7 → 2123,7
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height,
# $5... = vidcaps) : output
csheet_montage() {
trace $FUNCNAME $@
trace $@
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png)
shift 4
# Padding is no longer dependant upong context since alignment of the
2163,7 → 2152,7
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height,
# $5... = $vidcaps) : output
csheet_overlap() {
trace $FUNCNAME $@
trace $@
local cols=$1 ctx=$2 width=$3 height=$4
# globals: $VID
shift 4
2222,7 → 2211,7
# Step through vidcaps (col=[0..cols-1])
for col in $(seqr 0 $(( $cols - 1 ))); do
# More cols than files in the last iteration (e.g. -n10 -c4)
if [ -z "$1" ]; then break; fi
if [[ -z $1 ]]; then break; fi
w=$(imw "$1")
 
# Stick the vicap in the canvas
2267,7 → 2256,7
# Sorts timestamps and removes duplicates
# clean_timestamps($1 = space separated timestamps)
clean_timestamps() {
trace $FUNCNAME $@
trace $@
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines
local s=$1
stonl "$s" | sort -n | uniq
2284,19 → 2273,19
 
# This time a resize filter is applied to the player to produce smaller
# output
if [ $decoder -eq $DEC_MPLAYER ]; then
if [[ $decoder -eq $DEC_MPLAYER ]]; then
tempfile=00000005.png
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$tempfile" )
if ! capture_mplayer "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then
if ! mplayer_capture "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then
ret=1
fi
elif [ $decoder -eq $DEC_FFMPEG ]; then
elif [[ $decoder -eq $DEC_FFMPEG ]]; then
tempfile=$(new_temp_file '-safelen.png')
if ! capture_ffmpeg "$f" "$tempfile" "$ts" "-s 96x96"; then
if ! ffmpeg_capture "$f" "$tempfile" "$ts" "-s 96x96"; then
ret=1
fi
else
assert $LINENO false
assert false
ret=1
fi
rm -f "$tempfile"
2307,7 → 2296,7
# starting point
# safe_length_measure($1 = filename)
safe_length_measure() {
trace $FUNCNAME $@
trace $@
local f="$1"
local len=${VID[$LEN]}
local tempfile=
2458,7 → 2447,8
local acid="$1"
local acodec=
 
if grep -q '[ -]' <<<"$acid" ; then
local ERE='[ -]'
if [[ $acid =~ $ERE ]]; then
# Won't be recognised anyway
echo "$acid"
return
2530,21 → 2520,23
 
##### }}}} # Codec names
 
# {{{{ # Mplayer support
 
# Try to identify video properties using mplayer
# Fills $MPLAYER_CACHE with the relevant output and $VID_MPLAYER with
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with
# the actual values. See identify_video()
# mplayer_identify($1 = file)
mplayer_identify() {
trace $FUNCNAME $@
[ "$MPLAYER" ] || return
trace $@
[[ $MPLAYER_BIN ]] || return
local f="$1"
local mi=( )
# Note to self: Don't change the -vc as it would affect $vdec
if [ $DVD_MODE -eq 0 ]; then
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \
if [[ $DVD_MODE -eq 0 ]]; then
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \
-quiet "$f" 2>"$stderr" | grep ^ID)
else
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \
-quiet -dvd-device "$f" dvd://$DVD_TITLE \
2>"$stderr" | grep ^ID)
fi
2558,62 → 2550,97
# For some reason my (one track) samples have two ..._NCH, first one 0
#+Also multichannel is detected as 2 ch
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1)
if [ $DVD_MODE -eq 0 ]; then
if [[ $DVD_MODE -eq 0 ]]; then
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH.
#+Both appear valid.
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2)
[ "${mi[$LEN]}" ] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2)
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2)
else
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2)
fi
# Voodoo :P Remove (one) trailing zero
if [ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == "0" ]; then
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}"
fi
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1)
# If none set, delete it
[ "${mi[$ASPECT]}" ] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]=''
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]=''
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}")
if [ "${mi[$VDEC]}" == "ffodivx" ] && [ "${mi[$VCNAME]}" != "MPEG-4" ]; then
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)"
elif [ "${mi[$VDEC]}" == "ffh264" ]; then # At least two different fourccs use h264, maybe more
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more
mi[$VCNAME]="${mi[$VCNAME]} (h.264)"
fi
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}")
if [ "${mi[$ACODEC]}" == "samr" ] ; then
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2)
if [ "$adec" == "ffamrnb" ]; then
if [[ $adec == 'ffamrnb' ]]; then
mi[$ACNAME]="AMR-NB";
fi
fi
 
# Array assignment
VID_MPLAYER=("${mi[@]}")
MPLAYER_ID=("${mi[@]}")
}
 
# Capture a frame with mplayer
# mplayer_capture($1 = inputfile, $2 = output file, $3 = timestamp[, $4 = extra opts])
mplayer_capture() {
trace $@
# Note mplayer CAN'T set the output filename, newer mplayer can set output
#+dir though.
local f="$1"
local cap=00000005.png o=$2
local ts=$3
 
# No point in passing ms to mplayer
ts=$(cut -d'.' -f1 <<<"$ts")
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1])
 
{
if [[ $DVD_MODE -eq 1 ]]; then
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \
-frames 5 -ss "$ts" $shoehorned -dvd-device "$f" \
$4 "dvd://$DVD_TITLE"
else
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \
-frames 5 -ss "$ts" $4 $shoehorned "$f"
fi
 
} >"$stdout" 2>"$stderr"
rm -f 0000000{1,2,3,4}.png # Remove the first four
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && {
mvq "$cap" "$o"
}
}
 
# }}}} # Mplayer support
 
# {{{{ # FFmpeg support
 
# Try to identify video properties using ffmpeg
# Fills $FFMPEG_CACHE with the relevant output and $VID_FFMPEG with
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with
# the actual values. See identify_video()
# mplayer_identify($1 = file)
ffmpeg_identify() {
trace $FUNCNAME $@
[ "$FFMPEG" ] || return
trace $@
[[ $FFMPEG_BIN ]] || return
local f="$1"
# DVD Devices *MUST* be mounted for the identifying process to even start
assert $LINENO "[ $DVD_MODE -eq 0 ] || [ '$DVD_MOUNTP' ]"
[ $DVD_MODE -eq 1 ] && {
assert '[[ ( $DVD_MODE -eq 0 ) || ( $DVD_MOUNTP ) ]]'
if [[ $DVD_MODE -eq 1 ]]; then
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB"
if [ ! -r "$vfile" ]; then
if [[ ! -r $vfile ]]; then
error "Failed to locate mounted DVD. Detection will be less accurate."
return 0 # We can continue anyway
fi
f="$vfile"
}
fi
# XXX: FFmpeg detects mpeg1video in DVDs??
 
local fi=( ) vs= as= obs= vsid=
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing
FFMPEG_CACHE=$("$FFMPEG" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)')
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)')
# Only the first streams of each type are honored. FIXME: Add multi-audio support.
vs=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Video:' | head -1)
as=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Audio:' | head -1)
2653,7 → 2680,7
# Newer CHANS and some older...
fi[$CHANS]=$(egrep -o '[0-9]* channels' <<<"$as" | cut -d' ' -f1)
# ...fallback for older
if [ -z "${fi[$CHANS]}" ]; then
if [[ -z ${fi[$CHANS]} ]]; then
local chans=$(egrep -o 'Hz, [^,]*' <<<"$as" | cut -d' ' -f2)
case $chans in
mono) fi[$CHANS]=1 ;;
2672,11 → 2699,11
# can be re-calculated.
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1)
# Let's convert e.g. 23.98 into 23.976...:
if [ "${fi[$FPS]}" ] && grep -q '\.' <<<"${fi[$FPS]}" ; then
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then
# Decimals, see if we got better values available
local vsobs=$(grep "stream $vsid" <<<"$obs")
# Observations regarding video stream found
if [ "$vsobs" ] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then
# FPS candidate
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2)
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3)
2684,17 → 2711,17
fi
# ...fallback for older. The older version I tried seems to round further, i.e.
# 23.976 became 24 so no fix for this one
if [ -z "${fi[$FPS]}" ]; then
if [[ -z ${fi[$FPS]} ]]; then
# No k suffix here, 1000 is 1000
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]* fps' <<<"$vs" | cut -d' ' -f1)
fi
# Be consistent with mplayer's output: at least two decimals
[ "${fi[$FPS]}" ] && {
[[ ${fi[$FPS]} ]] && {
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3)
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$
}
fi[$LEN]=$(egrep -o 'Duration: [^,]*' <<<"$FFMPEG_CACHE" | cut -d' ' -f2)
if [ "${fi[$LEN]}" == "N/A" ]; then # It might be unable to detect
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect
fi[$LEN]=""
fi
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') )
2703,12 → 2730,30
# TODO: Replace tail -1 with some better option (see the double DAR example above)
fi[$ASPECT]=$(egrep -o 'DAR [0-9]*:[0-9]*'<<<"$FFMPEG_CACHE" | tail -1 | cut -d' ' -f2 | sed 's#:#/#')
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial
[ $DVD_MODE -eq 0 ] || fi[$LEN]=''
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]=''
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}"))
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}"))
VID_FFMPEG=("${fi[@]}")
FFMPEG_ID=("${fi[@]}")
}
 
# Capture a frame with ffmpeg
# ffmpeg_capture($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts])
ffmpeg_capture() {
trace $@
local f=$1
local o=$2
local ts=$3
# XXX: It would be nice to show a message if it takes too long
# See wa_ss_* declarations at the start of the file for details
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \
-dframes 1 -vframes 1 -vcodec png \
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr"
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]]
}
 
# }}}} # FFmpeg support
 
# Use the available tools to identify video meta-data
# fills $VID with the values
# Return codes:
2716,21 → 2761,22
# 4: Failed to detect width or height
# identify_video($1 = file)
identify_video() {
trace $FUNCNAME $@
trace $@
local RET_NOLEN=3 RET_NODIM=4
 
[ "$MPLAYER" ] && mplayer_identify "$1"
[[ $MPLAYER_BIN ]] && mplayer_identify "$1"
# ffmpeg_identify in DVD mode only works when the DVD is mounted:
[ $DVD_MODE -eq 0 ] && [ "$FFMPEG" ] && ffmpeg_identify "$1"
[ $DVD_MODE -eq 1 ] && [ "$FFMPEG" ] && [ "$DVD_MOUNTP" ] && ffmpeg_identify "$1"
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1"
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1"
 
# Fail early if none detected length
[ -z "${VID_MPLAYER[$LEN]}" ] && [ -z "${VID_FFMPEG[$LEN]}" ] && return $RET_NOLEN
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN
 
assert '[[ $MPLAYER_BIN || $FFMPEG_BIN ]]'
# Classic mode, use both mplayer and ffmpeg when available
if [ "$MPLAYER" ] && [ "$FFMPEG" ]; then
if [[ $MPLAYER_BIN && $FFMPEG_BIN ]]; then
# By default take mplayer's values
VID=("${VID_MPLAYER[@]}")
VID=("${MPLAYER_ID[@]}")
# FFmpeg seems better at getting the correct number of FPS, specially with
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the
# inconsistency in container vs codec and guesses better, *but* it only
2737,24 → 2783,27
# uses two decimals so 23.976 becomes 23.98. So it is only used when
# the number of decimals seems right.
# When a "Seems..." line is printed the correct FPS can be obtained though.
[ -z "${VID_MPLAYER[$FPS]}" ] && VID[$FPS]=${VID_FFMPEG[$FPS]}
[ "${VID_MPLAYER[$FPS]}" ] && [ "${VID_FFMPEG[$FPS]}" ] && {
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${FFMPEG_ID[$FPS]}
[[ ( -n ${MPLAYER_ID[$FPS]} ) && ( -n ${FFMPEG_ID[$FPS]} ) ]] && {
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong
local ffps=${VID_FFMPEG[$FPS]}
echo $ffps | grep -q '\.[0-9][0-9][0-9]' && VID[$FPS]=$ffps || {
fptest "${VID_MPLAYER[$FPS]}" -gt 500 && VID[$FPS]=$ffps
}
local ffps=${FFMPEG_ID[$FPS]}
local ERE='\.[0-9][0-9][0-9]'
if [[ $ffps =~ $ERE ]]; then
VID[$FPS]=$ffps
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then
VID[$FPS]=$ffps
fi
}
# It doesn't appear to need any workarounds for num. channels either
[ "${VID_FFMPEG[$CHANS]}" ] && VID[$CHANS]=${VID_FFMPEG[$CHANS]}
[ "${VID_FFMPEG[$ASPECT]}" ] && VID[$ASPECT]=${VID_FFMPEG[$ASPECT]}
[[ ${FFMPEG_ID[$CHANS]} ]] && VID[$CHANS]=${FFMPEG_ID[$CHANS]}
[[ ${FFMPEG_ID[$ASPECT]} ]] && VID[$ASPECT]=${FFMPEG_ID[$ASPECT]}
# There's a huge inconsistency with some files, both mplayer vs ffmpeg
# same application on different OSes
local fflen=${VID_FFMPEG[$LEN]} mplen=${VID_MPLAYER[$LEN]} # Shorthands
[ -z "$fflen" ] && fflen=0
local fflen=${FFMPEG_ID[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands
[[ -z $fflen ]] && fflen=0
# If both report 0, there's no good value...
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN
if [ $DVD_MODE -eq 0 ] && [ $QUIRKS -eq 0 ]; then # In DVD mode ffmpeg has no length
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length
# Quirks disabled, should be enabled?
local delta=$(abs $(awkexf "($fflen - $mplen)"))
# If they don't agree, take the shorter as a starting point,
2772,24 → 2821,22
QUIRKS=1
fi
fi
elif [ "$MPLAYER" ]; then
elif [[ $MPLAYER_BIN ]]; then
# Must do with mplayer only...
VID=("${VID_MPLAYER[@]}")
VID=("${MPLAYER_ID[@]}")
# Warn if a known pitfall is found
# See above for 1000 fps
[ "${VID[$FPS]}" == "1000.00" ] && \
[[ ${VID[$FPS]} == '1000.00' ]] && \
warn "Possible inaccuracy in FPS detection." && \
warn " Install both mplayer and ffmpeg for better detection."
# Number of channels 0 happened for WMA in non-x86
[ "${VID[$CHANS]}" == "0" ] && \
[[ ${VID[$CHANS]} == '0' ]] && \
warn "Failed to detect number of audio channels." && \
warn " Install both mplayer and ffmpeg for better detection."
elif [ "$FFMPEG" ]; then
elif [[ $FFMPEG_BIN ]]; then
# Must do with mplayer only...
VID=("${VID_FFMPEG[@]}")
VID=("${FFMPEG_ID[@]}")
# So far I know of no weird results. Yet.
else
assert $LINENO 'false'
fi
 
# Ensure sanity of the most important values
2796,15 → 2843,15
is_float "${VID[$LEN]}" || return $RET_NOLEN
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM
 
if [ "$FFMPEG" ]; then
if [[ $FFMPEG_BIN ]]; then
# FPS at least with two decimals
if [ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]; then
if [[ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]]; then
VID[$FPS]="${VID[$FPS]}.00"
fi
fi
 
local mfps="${VID_MPLAYER[$FPS]}"
if [ $QUIRKS -eq 0 ] && [ "$MPLAYER" ] && fptest "$mfps" -eq 1000 ; then
local mfps="${MPLAYER_ID[$FPS]}"
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then
warn "Suspect file. Safe measuring enabled."
QUIRKS=1
fi
2811,7 → 2858,7
 
# Last safeguard: Try to reach the detected length, if it fails, trigger
# quirks mode
if [ $QUIRKS -eq 0 ]; then
if [[ $QUIRKS -eq 0 ]]; then
if ! probe_video "$1" "${VID[$LEN]}" ; then
warn "Detected video length can't be reached. Safe measuring enabled."
QUIRKS=1
2818,29 → 2865,29
fi
fi
 
if [ $QUIRKS -eq 1 ]; then
if [[ $QUIRKS -eq 1 ]]; then
VID[$LEN]=$(safe_length_measure "$1")
if [ -z "${VID[$LEN]}" ]; then
if [[ -z ${VID[$LEN]} ]]; then
error "Couldn't measure length in a reasonable amount of tries."
if [ $INTERNAL_MAXREWIND_REACHED -eq 1 ]; then
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then
error " Will not be able to capture this file with the current settings."
else
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp=''
[ $reqs -eq 1 ] && reqp=" -WP" || reqp=" -WP$reqs"
[ $reqs -ge 3 ] && reqs=" -WS" || { # Third try => Recommend -WS
[ $reqs -eq 1 ] && reqs=" -Ws" || reqs=" -Ws$reqs"
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs"
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs"
}
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0'
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND)
warn " Capturing won't work, video is at least $offby shorter than reported."
local dname='ffmpeg'
[ $decoder -eq $DEC_MPLAYER ] && dname='mplayer'
[[ $decoder -eq $DEC_MPLAYER ]] && dname='mplayer'
warn " Does $dname support ${VID[$VCODEC]}?."
warn " Try re-running with$reqs$reqp."
fi
return 1
fi
elif [ $QUIRKS -eq -2 ]; then
elif [[ $QUIRKS -eq -2 ]]; then
warn "Safe mode disabled."
fi
 
2850,46 → 2897,46
}
 
dump_idinfo() {
trace $FUNCNAME $@
[ "$MPLAYER" ] && echo "Mplayer: $MPLAYER"
[ "$FFMPEG" ] && echo "FFmpeg: $FFMPEG"
[ "$MPLAYER" ] && cat <<-EODUMP
trace $@
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN"
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN"
[[ $MPLAYER_BIN ]] && cat <<-EODUMP
=========== Mplayer Identification ===========
Length: $(pretty_stamp ${VID_MPLAYER[$LEN]})
Length: $(pretty_stamp ${MPLAYER_ID[$LEN]})
Video
Codec: ${VID_MPLAYER[$VCODEC]} (${VID_MPLAYER[$VCNAME]})
Dimensions: ${VID_MPLAYER[$W]}x${VID_MPLAYER[$H]}
FPS: ${VID_MPLAYER[$FPS]}
Aspect: ${VID_MPLAYER[$ASPECT]}
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]})
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]}
FPS: ${MPLAYER_ID[$FPS]}
Aspect: ${MPLAYER_ID[$ASPECT]}
Audio
Codec: ${VID_MPLAYER[$ACODEC]} (${VID_MPLAYER[$ACNAME]})
Channels: ${VID_MPLAYER[$CHANS]}
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]})
Channels: ${MPLAYER_ID[$CHANS]}
==============================================
 
EODUMP
local ffl="${VID_FFMPEG[$LEN]}"
[ "$ffl" ] && ffl=$(pretty_stamp "$ffl")
if [ -z "$ffl" -a $DVD_MODE -eq 1 ]; then
local ffl="${FFMPEG_ID[$LEN]}"
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl")
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then
ffl="(unavailable in DVD mode)"
fi
[ "$FFMPEG" ] && cat <<-EODUMP
[[ $FFMPEG_BIN ]] && cat <<-EODUMP
=========== FFmpeg Identification ===========
Length: $ffl
Video
Codec: ${VID_FFMPEG[$VCODEC]} (${VID_FFMPEG[$VCNAME]})
Dimensions: ${VID_FFMPEG[$W]}x${VID_FFMPEG[$H]}
FPS: ${VID_FFMPEG[$FPS]}
Aspect: ${VID_FFMPEG[$ASPECT]}
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]})
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]}
FPS: ${FFMPEG_ID[$FPS]}
Aspect: ${FFMPEG_ID[$ASPECT]}
Audio
Codec: ${VID_FFMPEG[$ACODEC]} (${VID_FFMPEG[$ACNAME]})
Channels: ${VID_FFMPEG[$CHANS]}
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]})
Channels: ${FFMPEG_ID[$CHANS]}
=============================================
 
EODUMP
local xar=
if [ "${VID[$ASPECT]}" ]; then
if [[ ${VID[$ASPECT]} ]]; then
xar=$(keepdecimals "${VID[$ASPECT]}" 4)
[ "$xar" ] && xar=" ($xar)"
[[ $xar ]] && xar=" ($xar)"
fi
cat <<-EODUMP
=========== Combined Identification ===========
2905,20 → 2952,19
=============================================
EODUMP
 
 
}
 
# Try to pick some font capable of handling non-latin text
set_extended_font() {
trace $FUNCNAME $@
trace $@
# This selection includes japanese fonts
local candidates=$(identify -list font | grep 'Font: ' | \
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*')
if [ -z "$candidates" ]; then
if [[ -z $candidates ]]; then
error "Unable to auto-select filename font, please provide one (see -fullhelp)"
return 1
else
if [ "$DEBUG" -eq 1 ]; then
if [[ $DEBUG -eq 1 ]]; then
local list=$(echo "$candidates" | sed 's/^/ >/g')
inf "Available non-latin fonts detected:$NL$list"
fi
2925,11 → 2971,14
fi
 
# Bias towards the Sazanami family
if grep -qi 'sazanami' <<<"$candidates" ; then
shopt -s nocasematch
local ERE='sazanami'
if [[ $candidates =~ $ERE ]]; then
FONT_MINCHO=$(grep -i 'sazanami' <<<"$candidates" | head -1)
else
FONT_MINCHO=$(head -1 <<<"$candidates")
fi
shopt -u nocasematch
}
 
# Checks if the provided arguments make sense and are allowed to be used
2936,9 → 2985,9
#+together. When an incoherence is found, sets some sane values if reasonable
#+or fails otherwise.
coherence_check() {
trace $FUNCNAME $@
trace $@
# If -m is used then -S must be used
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then
if [[ ( $manual_mode -eq 1 ) && ( -z $initial_stamps ) ]]; then
error "You must provide timestamps (-S) when using manual mode (-m)"
return $EX_USAGE
fi
2948,22 → 2997,22
extended_factor=0
fi
 
if [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then
if [[ ( $decoder -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then
inf "No mplayer available. Using ffmpeg only."
decoder=$DEC_FFMPEG
elif [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then
elif [[ ( $decoder -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then
inf "No ffmpeg available. Using mplayer only."
decoder=$DEC_MPLAYER
fi
 
if [ $DVD_MODE -eq 1 ]; then
if [[ $DVD_MODE -eq 1 ]]; then
# Since 1.12 DVD mode can work with multiple inputs too
 
# DVD Mode only works with mplayer, the decoder is changed when
# the DVD mode option is found, so if it's ffmpeg at this point,
# it's by user request (i.e. -F after -V)
if [ $decoder -ne $DEC_MPLAYER ]; then
if [ "$MPLAYER" ]; then
if [[ $decoder -ne $DEC_MPLAYER ]]; then
if [[ $MPLAYER_BIN ]]; then
warn "DVD mode requires the use of mplayer, falling back to it"
decoder=$DEC_MPLAYER
else
2974,14 → 3023,14
fi
 
local filter=
if [ $DISABLE_TIMESTAMPS -eq 0 ] &&
local -a filts=( )
local -a filts=( )
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] &&
has_filter filt_polaroid && has_filter filt_apply_stamp ; then
 
for filter in ${FILTERS_IND[@]} ; do
if [ "$filter" == "filt_polaroid" ]; then
if [[ $filter == 'filt_polaroid' ]]; then
filts=( "${filts[@]}" "$filter" filt_apply_stamp )
elif [ "$filter" == "filt_apply_stamp" ]; then
elif [[ $filter == 'filt_apply_stamp' ]]; then
continue;
else
filts=( "${filts[@]}" $filter )
3002,12 → 3051,12
# differently. On previous versions disabling shadows only affected
# the montage shadow (but e.g. the polaroid mode preserved them),
# this is no longer true
if [ $DISABLE_SHADOWS -ne 1 ]; then
if [[ $DISABLE_SHADOWS -ne 1 ]]; then
end_filts[100]="filt_softshadow"
fi
;;
filt_apply_stamp)
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then
filts=( "${filts[@]}" "$filter" )
fi
;;
3022,7 → 3071,7
 
# If in non-latin mode and no nonlatin font has been picked try to pick one.
# Should it fail, fallback to latin font.
if [ $NONLATIN_FILENAMES -eq 1 ] && [ -z "$FONT_MINCHO" ]; then
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $FONT_MINCHO ) ]]; then
set_extended_font || {
# set_extended_font already warns about lack of fonts
warn " Falling back to latin font"
3038,34 → 3087,35
# If the OS hasn't registered TTF fonts with IM, try to use a saner value
#+*only* for fonts not overridden
sanitise_fonts() {
trace $FUNCNAME $@
trace $@
 
# Any default font in use? If all of them are overridden, return
if [ "$USR_font_heading" -a "$USR_font_title" -a "$USR_font_tstamps" -a "$USR_font_sign" ]; then
if [[ $USR_font_heading && $USR_font_title && \
$USR_font_tstamps && $USR_font_sign ]]; then
return
fi
# If the user edits any font in the script, stop messing with this
[ -z "$USR_font_heading" ] && [ "$font_heading" != 'DejaVu-Sans-Book' ] && return
[ -z "$USR_font_title" ] && [ "$font_title" != 'DejaVu-Sans-Book' ] && return
[ -z "$USR_font_tstamps" ] && [ "$font_tstamps" != 'DejaVu-Sans-Book' ] && return
[ -z "$USR_font_sign" ] && [ "$font_sign" != 'DejaVu-Sans-Book' ] && return
[[ ( -z $USR_font_heading ) && ( $font_heading != 'DejaVu-Sans-Book' ) ]] && return
[[ ( -z $USR_font_title ) && ( $font_title != 'DejaVu-Sans-Book' ) ]] && return
[[ ( -z $USR_font_tstamps ) && ( $font_tstamps != 'DejaVu-Sans-Book' ) ]] && return
[[ ( -z $USR_font_sign ) && ( $font_sign != 'DejaVu-Sans-Book' ) ]] && return
# Try to locate DejaVu Sans
local dvs=''
if [ -d /usr/local/share/fonts ]; then
if [[ -d /usr/local/share/fonts ]]; then
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf')
fi
if [ -z "$dvs" -a -d /usr/share/fonts ]; then
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf')
fi
if [ -z "$dvs" ]; then
if [[ -z $dvs ]]; then
warn "Unable to locate DejaVu Sans font. Falling back to helvetica."
dvs=helvetica
fi
[ -z "$USR_font_heading" ] && font_heading="$dvs"
[ -z "$USR_font_title" ] && font_title="$dvs"
[ -z "$USR_font_tstamps" ] && font_tstamps="$dvs"
[ -z "$USR_font_sign" ] && font_sign="$dvs"
[ $DEBUG -eq 1 ] || { return 0; }
[[ -z $USR_font_heading ]] && font_heading="$dvs"
[[ -z $USR_font_title ]] && font_title="$dvs"
[[ -z $USR_font_tstamps ]] && font_tstamps="$dvs"
[[ -z $USR_font_sign ]] && font_sign="$dvs"
[[ $DEBUG -eq 1 ]] || { return 0; }
cat >&2 <<-EOFF
Font Sanitation:
font_heading: $font_heading
3079,7 → 3129,7
# Creates the contact sheet.
# process($1 = file)
process() {
trace $FUNCNAME $@
trace $@
local f=$1
 
local numcols=
3089,14 → 3139,15
local pre_aspect_ratio=$aspect_ratio
local pre_output_format="$output_format"
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file
CAPTURES=''
 
DVD_MOUNTP= DVD_TITLE= # Re-set for each file
if [ $DVD_MODE -eq 1 ]; then
if [[ $DVD_MODE -eq 1 ]]; then
local dvdn=$(realpathr "$f")
# Is it an ISO?
if [ -f "$dvdn" ]; then
if [[ -f $dvdn ]]; then
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn")
if [ -z "$DVD_MOUNTP" ]; then
if [[ -z $DVD_MOUNTP ]]; then
# Only in Linux does this matter
if ! is_linux ; then
warn "Video properties detection for ISO files is not accurate"
3115,7 → 3166,7
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3)
dvdn="DVD $f"
fi
if [ ! -r "$f" ]; then
if [[ ! -r $f ]]; then
error "Can't access DVD ($f)"
return $EX_NOINPUT
fi
3124,7 → 3175,7
unset dvdn
DVD_TITLE=${DVD_TITLES[0]}
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array
if [ -z "$DVD_TITLE" -o "$DVD_TITLE" == "0" ]; then
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \
cut -d' ' -f3- | sed 's/^0*//')"
if ! is_number "$dt" ; then
3137,7 → 3188,7
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2)
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'"
else # Not DVD Mode:
if [ ! -f "$f" ]; then
if [[ ! -f $f ]]; then
error "File \"$f\" doesn't exist"
return $EX_NOINPUT
fi
3149,7 → 3200,7
# {{SET_E}} Beware, set -e will break this
identify_video "$f"
local ecode=$?
[ $ecode -eq 0 ] || {
[[ $ecode -eq 0 ]] || {
case $ecode in
3) error "Unable to find length of file \"$f\". Can't continue." ;;
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;;
3159,47 → 3210,45
}
 
# Identification-only mode?
[ "$UNDFLAG_IDONLY" ] && dump_idinfo && return 0
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0
 
# Vidcap/Thumbnail height
local vidcap_height=$th_height
if is_percentage "$th_height" && [ "$th_height" != '100%' ]; then
if is_percentage "$th_height" && [[ $th_height != '100%' ]]; then
vidcap_height=$(rpercent ${VID[$H]} ${th_height})
inf "Height: $th_height of ${VID[$H]} = $vidcap_height"
fi
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then
vidcap_height=${VID[$H]}
fi
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise
#+ honor detected value
[ "-2" == "$aspect_ratio" ] && [ -z "${VID[$ASPECT]}" ] && aspect_ratio=-1
[ "-2" == "$aspect_ratio" ] && [ "${VID[$ASPECT]}" ] && aspect_ratio=0
if [ "0" == "$aspect_ratio" ]; then
if [ "${VID[$ASPECT]}" ]; then
if [[ $aspect_ratio -eq -2 ]]; then
[[ ${VID[$ASPECT]} ]] && aspect_ratio=0 || aspect_ratio=-1
elif [[ $aspect_ratio -eq 0 ]]; then
if [[ ${VID[$ASPECT]} ]]; then
# Aspect ratio in file headers, honor it
aspect_ratio=$(awkexf "${VID[$ASPECT]}")
else
aspect_ratio=$(awkexf "${VID[$W]} / ${VID[$H]}")
fi
elif [ "-1" == "$aspect_ratio" ]; then
elif [[ $aspect_ratio -eq -1 ]]; then
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]})
inf "Aspect ratio set to $aspect_ratio."
fi
local vidcap_width=$(compute_width $vidcap_height)
 
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1)
local nc=$numcaps
 
unset TIMECODES
# Compute the stamps (if in auto mode)...
if [ $manual_mode -eq 1 ]; then
if [[ $manual_mode -eq 1 ]]; then
# Note TIMECODES must be set as an array to get the correct count in
# manual mode; in automatic mode it will be set correctly inside
# compute_timecodes()
TIMECODES=( ${initial_stamps[@]} )
TIMECODES=( "${initial_stamps[@]}" )
else
TIMECODES=${initial_stamps[@]}
TIMECODES=( "${initial_stamps[@]}" )
compute_timecodes $timecode_from $interval $numcaps || {
return $?
}
3206,17 → 3255,11
fi
 
local output=$(new_temp_file '-preview.png')
local VIDCAPFILE=00000005.png
 
# If the temporal vidcap already exists, abort
if [ -f $VIDCAPFILE ]; then
error "File $VIDCAPFILE exists and would be overwritten, move it out before running."
return $EX_CANTCREAT
fi
# mplayer will re-write also 00000001.png-00000004.png
if [ $decoder -eq $DEC_MPLAYER ]; then
for f_ in 1 2 3 4; do
if [ -f "0000000${f_}.png" ]; then
# If the temporal vidcaps for mplayer already exist, abort
if [[ $decoder -eq $DEC_MPLAYER ]]; then
for f_ in 1 2 3 4 5; do
if [[ -f "0000000${f_}.png" ]]; then
error "File 0000000${f_}.png exists and would be overwritten, move it out before running."
return $EX_CANTCREAT
fi
3223,33 → 3266,33
done
fi
 
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VIDCAPFILE" )
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' )
 
# Highlights
local hlfile n=1 # hlfile Must be outside the if!
if [ "$HLTIMECODES" ]; then
if [[ $HLTIMECODES ]]; then
local hlcapfile= pretty=
local -a capfiles
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi
pretty=$(pretty_stamp $stamp)
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..."
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png")
 
capture "$f" $stamp || return $?
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || {
capture "$f" $stamp "$hlcapfile" || return $?
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || {
local r=$?
error "Failed to apply transformations to the capture."
return $r
}
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png")
mvq "$VIDCAPFILE" "$hlcapfile"
capfiles=( "${capfiles[@]}" "$hlcapfile" )
let 'n++'
(( ++n ))
done
 
let 'n--' # There's an extra inc
if [ "$n" -lt "$cols" ]; then
assert "[[ '"$n"' -gt 1 ]]"
(( n-- )) # There's an extra inc
if [[ $n -lt $cols ]]; then
numcols=$n
else
numcols=$cols
3262,26 → 3305,25
unset n
 
# Normal captures
# TODO: Don't reference $VIDCAPFILE
local capfile pretty n=1
unset capfiles ; local -a capfiles
unset capfiles ; local -a capfiles ; local tfile=
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do
pretty=$(pretty_stamp $stamp)
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..."
# identified by capture number, padded to 6 characters
tfile=$(new_temp_file "-cap-$(pad 6 $n).png")
 
capture "$f" $stamp || return $?
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $?
capture "$f" $stamp "$tfile" || return $?
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $?
 
# identified by capture number, padded to 6 characters
capfile=$(new_temp_file "-cap-$(pad 6 $n).png")
mvq "$VIDCAPFILE" "$capfile"
capfiles=( "${capfiles[@]}" "$capfile" )
let 'n++' # $n++
capfiles=( "${capfiles[@]}" "$tfile" )
(( n++ ))
done
#filter_all_vidcaps "${capfiles[@]}"
 
let 'n--' # there's an extra inc
if [ "$n" -lt "$cols" ]; then
assert "[[ '"$n"' -gt 1 ]]"
(( n-- )) # there's an extra inc
if [[ $n -lt $cols ]]; then
numcols=$n
else
numcols=$cols
3293,11 → 3335,11
 
# Extended mode
local extoutput=
if [ "$extended_factor" != 0 ]; then
if [[ $extended_factor != 0 ]]; then
# Number of captures. Always rounded to a multiplier of *double* the
# number of columns (the extended caps are half width, this way they
# match approx with the standard caps width)
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $extended_factor)") $((2*$numcols)))
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $extended_factor)") $((2*numcols)))
 
unset TIMECODES # required step to get the right count
declare -a TIMECODES # Note the manual stamps are not included anymore
3307,21 → 3349,21
local n=1 w= h= capfile= pretty=
unset capfiles ; local -a capfiles
# The image size of the extra captures is 1/4, adjusted to compensante the padding
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width'
(( w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width ,1 ))
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]"
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do
pretty=$(pretty_stamp $stamp)
capfile=$(new_temp_file "-excap-$(pad 6 $n).png")
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..."
capture "$f" $stamp || return $?
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $?
capture "$f" $stamp "$capfile" || return $?
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $?
 
capfile=$(new_temp_file "-excap-$(pad 6 $n).png")
mvq "$VIDCAPFILE" "$capfile"
capfiles=( "${capfiles[@]}" "$capfile" )
let 'n++'
(( n++ ))
done
 
let 'n--' # There's an extra inc
if [ $n -lt $(( $cols * 2 )) ]; then
(( n-- )) # There's an extra inc
if [[ $n -lt 'cols*2' ]]; then
numcols=$n
else
numcols=$(( $cols * 2 ))
3336,8 → 3378,8
local vcodec=${VID[$VCNAME]}
local acodec=${VID[$ACNAME]}
 
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then
if [ ${VID[$CHANS]} -eq 1 ]; then
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then
if [[ ${VID[$CHANS]} -eq 1 ]]; then
acodec="$acodec (mono)"
else
acodec="$acodec (${VID[$CHANS]}ch)"
3346,29 → 3388,30
 
local csw=$(imw "$output") exw= hlw=
local width=$csw
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then
if [[ -n $HLTIMECODES || ( $extended_factor != '0' ) ]]; then
inf "Merging contact sheets..."
if [ "$HLTIMECODES" ]; then
if [[ -n $HLTIMECODES ]]; then
local hlw=$(imw "$hlfile")
if [ $hlw -gt $width ]; then width=$hlw ; fi
if [[ $hlw -gt $width ]]; then width=$hlw ; fi
fi
if [ "$extended_factor" != "0" ]; then
if [[ $extended_factor != '0' ]]; then
local exw=$(imw $extoutput)
if [ $exw -gt $width ]; then width=$exw ; fi
if [[ $exw -gt $width ]]; then width=$exw ; fi
fi
fi
if [ $csw -lt $width ]; then
if [[ $csw -lt $width ]]; then
local csh=$(imh "$output")
# Expand the standard set to the maximum width of the sets by padding both sides
# For some reason the more obvious (to me) convert command-lines lose
# the transparency
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output"
unset csh
local csw2= ; (( csw2 = (width-csw) / 2 ))
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \
\( -size ${csw2}x$csh xc:transparent \) +append "$output"
unset csh csw2
fi
 
# If there were highlights then mix them in
if [ "$HLTIMECODES" ]; then
if [[ $HLTIMECODES ]]; then
# For some reason adding the background also adds padding with:
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \
# \( "$output" \) -append "$output"
3375,9 → 3418,11
# replacing it with a "-composite" operation apparently works
# Expand the highlights to the correct size by padding
local hlh=$(imh "$hlfile")
if [ $hlw -lt $width ]; then
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile"
if [[ $hlw -lt $width ]]; then
local hlw2= ; (( hlw2=(width - hlw) / 2 ))
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile"
unset hlw2
fi
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \
\( -size ${width}x1 xc:black \) \
3385,13 → 3430,14
unset hlh
fi
# Extended captures
if [ "$extended_factor" != 0 ]; then
if [[ $extended_factor != 0 ]]; then
# Already set local exw=$(imw "$extoutput")
local exh=$(imh "$extoutput")
if [ $exw -lt $width ]; then
if [[ $exw -lt $width ]]; then
# Expand the extended set to be the correct size
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput"
local exw2 = ; (( exw2=(width - exv) / 2 ))
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput"
fi
convert "$output" -background Transparent "$extoutput" -append "$output"
fi
3398,19 → 3444,16
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank
#+space is added at the top
local dotrim=
[ $DISABLE_SHADOWS -eq 1 ] && [ -z "$HLTIMECODES" ] && dotrim=-trim
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim
convert -background "$bg_contact" "$output" -flatten $dotrim "$output"
 
 
# Let's add meta inf and signature
inf "Adding header and footer..."
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}
Format: $vcodec / $acodec
FPS: ${VID[$FPS]}"
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}"
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}"
local signature
if [ $anonymous_mode -eq 0 ]; then
signature="$user_signature $user
with $PROGRAM_SIGNATURE"
if [[ $anonymous_mode -eq 0 ]]; then
signature="$user_signature $user${NL}with $PROGRAM_SIGNATURE"
else
signature="Created with $PROGRAM_SIGNATURE"
fi
3417,7 → 3460,7
local headwidth=$(imw "$output") headheight=
local heading=$(new_temp_file .png)
# Add the title if any
if [ "$title" ]; then
if [[ $title ]]; then
local tlheight=$(line_height "$font_title" "$pts_title")
convert \
\( \
3431,7 → 3474,7
unset tlheight
fi
local fn_font= # see $NONLATIN_FILENAMES
if [ $NONLATIN_FILENAMES -ne 1 ]; then
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then
fn_font="$font_heading"
else
fn_font="$FONT_MINCHO"
3440,13 → 3483,13
#+which character is used it's always the same height.
local lineheight=$(line_height "$font_heading" "$pts_meta")
# Since filename can be set in a different font check it too
if [ "$fn_font" != "$font_heading" ]; then
if [[ $fn_font != $font_heading ]]; then
local fnlineheight=$lineheight
fnlineheight=$(line_height "$fn_font" "$pts_meta")
[ $fnlineheight -le $lineheight ] || lineheight=$fnlineheight
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight
unset fnlineheight
fi
headheight=$(( $lineheight * 3 ))
headheight=$(( lineheight * 3 ))
# Talk about voodoo... feel the power of IM... let's try to explain what's this:
# It might technically be wrong but it seems to work as I think it should
# (hence the voodoo I was talking)
3474,12 → 3517,12
local filesize_label="File size"
local filename_value=
local filesize_value=
if [ $DVD_MODE -eq 1 ]; then
if [[ $DVD_MODE -eq 1 ]]; then
# lsdvd is guaranteed to be installed if DVD mode is enabled
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-)
# There's no guarantee that titles are on separate VTS, I have no idea
# how to compute the actual title size
if [ "$DVD_MOUNTP" ]; then
if [[ $DVD_MOUNTP ]]; then
filename_label="Disc label"
filename_value="$dvd_label"
filesize_label="Titleset size"
3499,10 → 3542,10
fi
 
local signlh=$(line_height "$font_sign" "$pts_sign")
local signheight=$(( 4 + ( $signlh * 2 ) ))
local signheight=$(( 4 + ( signlh * 2 ) ))
convert \
\( \
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \
-size $(( headwidth - 18 ))x1 "xc:$bg_heading" +size \
-font "$font_heading" -pointsize "$pts_meta" \
-background "$bg_heading" -fill "$fg_heading" \
\( \
3533,16 → 3576,18
unset signature meta2 headwidth headheight heading fn_font signheight signlh
 
local wanted_name=${OUTPUT_FILES[$FILEIDX]}
[ "$wanted_name" ] && \
if egrep -q '\.[^\.]+$' <<<"$wanted_name" ; then
if [[ -n $wanted_name ]]; then
local ERE='\.[^.]+$'
if [[ $wanted_name =~ $ERE ]]; then
output_format=$(filext "$wanted_name")
inf "Output format set from output filename"
else # No file extension in wanted_name
wanted_name="$wanted_name.$output_format"
fi
[ "$wanted_name" ] || wanted_name="$(basename "$f").$output_format"
fi
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$output_format"
 
if [ $output_format != "png" ]; then
if [[ $output_format != 'png' ]]; then
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format"
convert -quality $output_quality "$output" "$newout"
output="$newout"
3554,8 → 3599,8
}
inf "Done. Output wrote to $output_name"
 
let 'FILEIDX++,1' #,1 so that it's always ok
[ "$UNDFLAG_HANG" ] && read -p 'Main loop paused, hit Enter key to continue... '
(( FILEIDX++ ,1 )) #,1 so that it's always ok
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... '
cleanup
 
# Re-set variables (for multi-file input)
3579,18 → 3624,18
local SEQ=$(type -pf seq)
local JOT=$(type -pf jot)
local ex rex
if [ "$SEQ" ]; then
if [[ $SEQ ]]; then
ex=$($SEQ 1 10)
elif [ "$JOT" ]; then
elif [[ $JOT ]]; then
ex=$($JOT 10 1)
else
warn "Can't check seqr() correctness, neither seq nor jot found"
fi
if [ "$ex" ]; then
if [[ $ex ]]; then
exr=$(seqr 1 10)
if [ "$exr" != "$ex" ]; then
if [[ $exr != "$ex" ]]; then
error "Failed test: seqr() not consistent with external result"
let 'retval++,1'
(( retval++ ,1 ))
else
inf "Passed test (seq replacement): consistent result"
fi
3647,14 → 3692,12
# Expected value
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1)
op=$(sed "s! $val #$comm\$!!g" <<<$t)
if [ -z "$comm" ]; then
comm=unnamed
fi
[[ -n $comm ]] || comm=unnamed
ret=$($op) || true
 
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then
error "Failed test ($comm): '$op $val'. Got result '$ret'."
let 'retval++,1' # The ,1 ensures let doesn't fail
(( retval++ ,1 )) # The ,1 ensures '((...))' doesn't fail
else
inf "Passed test ($comm): '$op $val'."
fi
3680,8 → 3723,13
"is_float 1/3 1 #Non-float recognition"
 
"is_fraction 1/1 0 #Fraction recognition"
"is_fraction 1 1 #non-fraction recognition"
"is_fraction 1 1 #Non-fraction recognition"
"is_fraction 1.1 1 #Non-fraction recognition"
 
"is_pos_or_percent 33 0 #Positive recognition"
"is_pos_or_percent 33% 0 #Percent recognition"
"is_pos_or_percent 4/4% 1 #Percent recognition"
"is_pos_or_percent % 1 #Percent recognition"
)
for t in "${TESTS[@]}"; do
comm=$(sed 's!.* #!!g' <<<$t)
3688,19 → 3736,17
# Expected value
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1)
op=$(sed "s! $val #$comm\$!!g" <<<$t)
if [ -z "$comm" ]; then
comm=unnamed
fi
[[ -n $comm ]] || comm=unnamed
ret=0
$op || {
ret=$?
}
 
if [ $val -eq $ret ]; then
if [[ $val -eq $ret ]]; then
inf "Passed test ($comm): '$op; returns $val'."
else
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'"
let 'retval++,1'
(( retval++ ,1 ))
fi
done
 
3715,8 → 3761,8
# Prints the program identification to stderr
show_vcs_info() { # Won't be printed in quiet modes
local inff=inf
[ "$HAS_COLORS" ] && inff=infplain
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2010 Toni Corvera" "sgr0"
[[ $HAS_COLORS ]] || inff=infplain
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2010 Toni Corvera"
}
 
# Prints the list of options to stdout
3725,12 → 3771,12
local P=$(basename $0)
local showlong=$1
local mpchosen= ffchosen= longhelp= funkyex=
[ -z "$MPLAYER" ] && mpchosen=' [Not available]'
[ "$MPLAYER" ] && [ $decoder == $DEC_MPLAYER ] && mpchosen=' [Selected]'
[ -z "$FFMPEG" ] && ffchosen=', Not available'
[ "$FFMPEG" ] && [ $decoder == $DEC_FFMPEG ] && ffchosen=', Selected'
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]'
[[ $MPLAYER_BIN && ( $decoder == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]'
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available'
[[ $FFMPEG_BIN && ( $decoder == $DEC_FFMPEG ) ]] && ffchosen=', Selected'
# This portion of help is only shown when in full help mode (--fullhelp)
[ "$showlong" ] && longhelp=\
[[ $showlong ]] && longhelp=\
" --anonymous Disable the 'Preview created by' line in the footer.
-Ij|-Ik|-Ij=fontname|-Ik=fontname
--nonlatin Use an alternate font in the heading for the video file
3772,7 → 3818,7
"
# The --funky help is really long, so make it shorter by default,
# only show the complete help when --fullhelp is used
[ "$showlong" ] && funkyex="
[[ $showlong ]] && funkyex="
These are toy output modes in which the contact sheet
gets a more informal look.
Order *IS IMPORTANT*. A bad order gets a bad result :P
3798,7 → 3844,7
Imitates filmstrip look.
\"random\": Use '-kx' or '--funky random'
Randomizes colours and fonts."
[ -z "$showlong" ] && funkyex="
[[ -z $showlong ]] && funkyex="
Available: overlap, rotate, photoframe, polaroidframe,
photos, polaroid, film, random
Use --fullhelp for more details."
3919,19 → 3965,19
while read ovname ; do
f=${ovname/:*}
t=${ovname#*:}
if [ -z "$t" ] || [ "$t" == "=" ]; then t=$f ; fi
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi
eval v=\$USR_$t
[ -z "$v" ] || {
[[ -z $v ]] || {
# Symbolic values:
case "$t" in
timecode_from)
x='$TC_NUMCAPS'
[ $v -eq $TC_NUMCAPS ] || x='$TC_INTERVAL'
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL'
v=$x
;;
decoder)
x='$DEC_FFMPEG'
[ $v -eq $DEC_FFMPEG ] || x='$DEC_MPLAYER'
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER'
v=$x
;;
verbosity)
4020,18 → 4066,25
-U|--fullname)
# -U accepts an optional argument, 0, to make an anonymous signature
# --fullname accepts no argument
if [ "$2" ]; then # With argument, special handling
if [ "$2" != "0" ]; then
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'"
error " to sign as My Name. Got -U$2"
exit $EX_USAGE
if [[ $1 = '-U' ]]; then # -U always provides an argument
if [[ -n $2 ]]; then # With argument, special handling
if [[ $2 != '0' ]]; then
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'"
error " to sign as My Name. Got -U$2"
exit $EX_USAGE
fi
anonymous_mode=1
fi
anonymous_mode=1
shift
else # No argument, default handling (try to guess real name)
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g')
if [ -z "$user" ]; then
user=$(id -un)
username=$(id -un)
if type -p getent >/dev/null ; then
user=$(getent passwd "$username" | cut -d':' -f5 |sed 's/,.*//g')
else
user=$(grep "^$username:" /etc/passwd | cut -d':' -f5 |sed 's/,.*//g')
fi
if [[ -z $user ]]; then
user=$username
error "No fullname found, falling back to default ($user)"
fi
fi
4050,7 → 4103,7
check_constraint 'end_offset' "$2" "$1" || die
is_p='y'
is_percentage "$2" || is_p=''
if [ "$is_p" ]; then
if [[ $is_p ]]; then
end_offset="$2"
else
end_offset=$(get_interval "$2")
4092,9 → 4145,9
USR_output_format=jp2
;;
-j|--jpeg)
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000
# 2000 is also accepted
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then
if [[ $2 != '2' && $2 != '2000' ]]; then
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'."
exit $EX_USAGE
fi
4107,8 → 4160,8
;;
-h|--help) show_help ; exit $EX_OK ;;
--fullhelp) show_help 'full' ; exit $EX_OK ;;
-F) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;;
-M) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;;
-F|--ffmpeg) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;;
-M|--mplayer) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;;
-H|--height)
check_constraint 'height' "$2" "$1" || die
th_height="$2"
4137,7 → 4190,7
# Optional argument quirks: $2 is always present, set to '' if unused
# from the commandline it MUST be directly after the -e (-e2 not -e 2)
# the long format is --extended=VAL
if [ "$2" ]; then
if [[ $2 ]]; then
check_constraint 'extended_factor' "$2" "$1" || die
extended_factor="$2"
else
4148,7 → 4201,7
;;
# Unlike -I, --nonlatin does not accept a font name
--nonlatin)
if [ -z "$USR_FONT_MINCHO" ]; then
if [[ -z $USR_FONT_MINCHO ]]; then
NONLATIN_FILENAMES=1
USR_NONLATIN_FILENAMES=1
set_extended_font
4161,7 → 4214,7
# -Ij: Try to pick automatically a CJK font. Might fail and abort
# -Ij='Font name or file': Set font manually
 
if [ "$2" ] ; then
if [[ $2 ]] ; then
# If an argument is passed, test it is one of the known ones
case "$2" in
k|j|k=*|j=*) ;;
4172,7 → 4225,7
# which convert would understand without giving the full path
NONLATIN_FILENAMES=1
USR_NONLATIN_FILENAMES=1
if [ ${#2} -gt 1 ]; then
if [[ ${#2} -gt 1 ]]; then
# j=, k= syntax
FONT_MINCHO="${2:2}"
USR_FONT_MINCHO="$FONT_MINCHO"
4179,7 → 4232,7
inf "Filename font set to '$FONT_MINCHO'"
fi
# If the user didn't pick one, try to select automatically
if [ -z "$USR_FONT_MINCHO" ]; then
if [[ -z $USR_FONT_MINCHO ]]; then
set_extended_font
inf "Filename font set to '$FONT_MINCHO'"
fi
4187,11 → 4240,12
;;
-O|--override)
# Rough test
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then
RE='[a-zA-Z_]+=[^;]*'
if [[ ! $2 =~ $RE ]]; then
error "Wrong override format, it should be variable=value. Got '$2'."
exit $EX_USAGE
fi
if grep -q 'GETOPT=' <<<"$2" ; then
if [[ $2 =~ 'GETOPT=' ]] ; then
# If we're here, getopt has already been found and works, so it makes no
# sense to override it; on the other hand, if it hasn't been correctly
# set/detected we won't reach here
4211,7 → 4265,7
# Twice: Disable prefixes too
c)
set_feedback_prefixes
[ "$UNDFLAG_NOPREFIX" ] && plain_messages=1
[[ -n $UNDFLAG_NOPREFIX ]] && plain_messages=1
UNDFLAG_NOPREFIX=1
;;
# Double length of video probed in safe measuring
4219,9 → 4273,9
# - Can be repeated, will double for each instance
# - -Ws -Ws -Ws = -Ws3
s|s[0-9]|s[0-9][0-9])
[ ${#2} -gt 1 ] && n=${2:1} || n=1
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)")
let 'INTERNAL_WS_C+=n,1'
(( INTERNAL_WS_C+=n ,1 ))
;;
# Brute force -Ws: Test all the length of the file if required
S) QUIRKS_MAX_REWIND=-1 ;;
4228,9 → 4282,9
# Increase precission of safe length measuring (halve the stepping)
# Like -Ws can be repeated
p|p[0-9]|p[0-9][0-9])
[ ${#2} -gt 1 ] && n=${2:1} || n=1
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)")
let 'INTERNAL_WP_C+=n,1'
(( INTERNAL_WP_C+=n ,1 ))
;;
# Inverse of -Wp: Decrease precission of safe length measuring
# i.e.: will try less times <-> will be quicker but less accurate
4237,9 → 4291,9
# desirable when -Ws or -WS are used.
# Can also be repeated
P|P[0-9]|P[0-9][0-9])
[ ${#2} -gt 1 ] && n=${2:1} || n=1
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)")
let 'INTERNAL_WP_C-=n,1'
(( INTERNAL_WP_C-=n ,1 ))
;;
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting
#+broken/partial files. Only makes sense when testing or in combination
4321,7 → 4375,7
;;
-C|--config)
if echo "$2" | grep -q '^:' ; then
if [ $2 = ':pwd' ]; then
if [[ $2 = ':pwd' ]]; then
cfg=./vcs.conf
else
error "Configuration names starting with ':' are reserved."
4330,12 → 4384,12
else
cfg=$2
fi
[ -f "$cfg" ] || {
[[ -f $cfg ]] || {
error "Configuration file '$cfg' not found"
exit $EX_USAGE
}
# ./vcs.conf doesn't need the vcs:conf: mark
if [ $2 != ':pwd' ]; then
if [[ $2 != ':pwd' ]]; then
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || {
error "No vcs:conf: mark found in '$cfg'"
4346,7 → 4400,7
shift
;;
-R|--randomsource)
if [ ! -r "$2" ]; then
if [[ ! -r $2 ]]; then
error "Random source file '$2' can't be read"
exit $EX_USAGE
fi
4359,7 → 4413,7
case $(tolower "$2") in
# timestamp (with no final s) is undocumented but will stay
t|timestamps|timestamp)
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then
inf "Timestamps disabled."
# They'll be removed from the filter chain in coherence_check
DISABLE_TIMESTAMPS=1
4366,7 → 4420,7
fi
;;
s|shadows|shadow)
if [ $DISABLE_SHADOWS -eq 0 ]; then
if [[ $DISABLE_SHADOWS -eq 0 ]]; then
inf "Shadows disabled."
# They will be removed from the filter chain in coherence_check
DISABLE_SHADOWS=1
4373,7 → 4427,7
fi
;;
p|padding)
if [ $HPAD -ne 0 ] ; then
if [[ $HPAD -ne 0 ]] ; then
inf "Padding disabled." # Kinda...
HPAD=0
fi
4408,7 → 4462,7
-q|--quiet)
# -q to only show errors
# -qq to be completely quiet
if [ $verbosity -gt $V_ERROR ]; then
if [[ $verbosity -gt $V_ERROR ]]; then
verbosity=$V_ERROR
else
verbosity=$V_NONE
4429,27 → 4483,27
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;;
# ffmpeg path
set_ffmpeg=*)
FFMPEG=$(realpathr "$(cut -d'=' -f2<<<"$2")")
assert $LINENO 'test -x "$FFMPEG"'
warn "[U] FFMPEG=$FFMPEG"
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")")
assert '[[ -x $FFMPEG_BIN ]]'
warn "[U] FFMPEG_BIN=$FFMPEG_BIN"
;;
# mplayer path
set_mplayer=*)
MPLAYER=$(realpathr "$(cut -d'=' -f2<<<"$2")")
assert $LINENO 'test -x "$MPLAYER"'
warn "[U] MPLAYER=$MPLAYER"
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")")
assert '[[ -x $MPLAYER_BIN ]]'
warn "[U] MPLAYER_BIN=$MPLAYER_BIN"
;;
# Ignore one of the players
disable_ffmpeg)
FFMPEG=''
FFMPEG_BIN=''
warn "FFmpeg disabled"
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]"
assert '[[ $MPLAYER_BIN ]]'
decoder=$DEC_MPLAYER
;;
disable_mplayer)
MPLAYER=''
MPLAYER_BIN=''
warn "Mplayer disabled"
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]"
assert '[[ $FFMPEG_BIN ]]'
decoder=$DEC_FFMPEG
;;
# This is an old option from the first versions when the script
4471,9 → 4525,9
while read ovname ; do
f=${ovname/:*}
t=${ovname#*:}
if [ "$t" -a "$t" != "=" ]; then f="$t" ; fi
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi
eval v=\$USR_$f
[ -z "$v" ] || echo "$(tolower $f)=$v"
[[ -z $v ]] || echo "$(tolower $f)=$v"
done
exit 0
;;
4495,15 → 4549,15
shift
;;
-D) # Repeat to just test consistency
if [ $DEBUGGED -gt 0 ]; then
[ $decoder -eq $DEC_MPLAYER ] && d='mplayer'
[ $decoder -eq $DEC_FFMPEG ] && d='ffmpeg'
if [[ $DEBUGGED -gt 0 ]]; then
[[ $decoder -eq $DEC_MPLAYER ]] && d='mplayer'
[[ $decoder -eq $DEC_FFMPEG ]] && d='ffmpeg'
infplain '[ svn $Rev$ ]'
cat >&2 <<-EOD
=== Setup ===
GETOPT: $GETOPT
MPLAYER: $MPLAYER
FFMPEG: $FFMPEG
MPLAYER: $MPLAYER_BIN
FFMPEG: $FFMPEG_BIN
AWK: $(realpathr $(type -pf awk))
Filterchain: [ ${FILTERS_IND[*]} ]
Decoder: $d
4523,7 → 4577,7
title="$(basename "$0") $ARGS"
;;
--) shift ; break ;;
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ;
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ;
esac
shift
done
4530,13 → 4584,13
 
# Avoid coherence_check if there's no arguments and no cmdline post
# processing
[ "$1" -o "$POST_GETOPT_HOOKS" ] || {
[ $verbosity -eq $V_NONE ] || show_help
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || {
[[ $verbosity -eq $V_NONE ]] || show_help
exit $EX_USAGE
}
 
# More than one argument...
if [ "$2" ]; then
if [[ -n $2 ]]; then
multiple_input_files=1
fi
# }}} # Command line parsing
4551,8 → 4605,8
post_getopt_hooks
 
# Remaining arguments
if [ ! "$1" ]; then
[ $verbosity -eq $V_NONE ] || show_help
if [[ -z $1 ]]; then
[[ $verbosity -eq $V_NONE ]] || show_help
exit $EX_USAGE
fi
 
4581,7 → 4635,9
# inside let
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces
# in elements though
# * bash3: |& (inherited from csh?) pipes both stdout and stderr
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking)
# bash 3.2 changed semantics vs bash 3.1
# * bash4: |& (inherited from csh?) pipes both stdout and stderr
# * performance: bash loops are often slower than awk or perl
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement
# }}} # Bash syntax notes
/video-contact-sheet/trunk/pkg/arch/PKGBUILD.in
13,7 → 13,7
arch=('any')
url="http://p.outlyer.net/vcs/"
license=('LGPL')
depends=('bash>=2.05b' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg')
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg')
makedepends=('bzip2')
optdepends=('lsdvd: for DVD support'
'perl: for DVD support')
/video-contact-sheet/trunk/.
Property changes:
Modified: svn:mergeinfo
Merged /video-contact-sheet/branches/1.12.3:r435-454