move eisting functions to separate files

This commit is contained in:
Vincent Riquer 2013-04-07 01:27:07 +02:00
parent 48e9314c23
commit 66e4b0c1a8
48 changed files with 1568 additions and 1596 deletions

1604
atom

File diff suppressed because it is too large Load Diff

27
lib/config/getConfig Normal file
View File

@ -0,0 +1,27 @@
getConfig() {
while read key value
do
case $key in
'#'*)
#comment
;;
'')
#empty line
;;
'[general]')
context=General
;;
'[source]')
context=Source
;;
\[*\])
context=Destination
destination="${key#[}"
destination="${destination%]}"
;;
*)
getConfig$context
;;
esac
done < ~/.atom/atom.cfg
}

View File

@ -0,0 +1,170 @@
getConfigDestination() {
case "$key" in
'path')
destinationpath["$destination"]="$value"
;;
'format')
case "$value" in
'mp3')
destinationformat["$destination"]=mp3
;;
'opus')
destinationformat["$destination"]=opus
;;
'vorbis')
destinationformat["$destination"]=vorbis
;;
*)
echo "Unsupported destination format: $value" >2&
exit $EFORMAT
;;
esac
;;
'quality')
expr='^[0-9]*$'
if ! [[ $value =~ $expr ]]
then
echo "Invalid quality value: $value" >&2
exit $EQUALITY
fi
unset expr
case "${destinationformat["$destination"]}" in
'vorbis')
destinationquality["$destination"]="$value"
;;
*)
echo "Invalid parameter \"$key\" for format \"${destinationformat["$destination"]}\"" >&2
exit $EFMTINVPARM
;;
esac
;;
'normalize')
case $value in
'true'|'on'|'yes')
destinationnormalize["$destination"]=1
;;
'false'|'off'|'no')
destinationnormalize["$destination"]=0
;;
*)
echo "normalize takes values:" \
"'yes' ,'true' ,'on', 'no', 'false',"\
"'off'"
;;
esac
;;
'bitrate')
expr='^[0-9]*$'
if ! [[ $value =~ $expr ]]
then
echo "Invalid bitrate value: $value" >&2
exit $EQUALITY
fi
unset expr
case "${destinationformat["$destination"]}" in
'opus')
destinationquality["$destination"]="$value"
;;
'mp3')
destinationquality["$destination"]="$value"
;;
*)
echo "$Invalid parameter \"$key\" for format \"${destinationformat["$destination"]}\"" >&2
exit $EFMTINVPARM
;;
esac
;;
'loss')
expr='^[0-9]*$'
if ! [[ $value =~ $expr ]]
then
echo "Invalid loss value: $value" >&2
exit $EQUALITY
fi
unset expr
case "${destinationformat["$destination"]}" in
'opus')
destinationloss["$destination"]="$value"
;;
*)
echo "$Invalid parameter \"$key\" for format \"${destinationformat["$destination"]}\"" >&2
exit $EFMTINVPARM
;;
esac
;;
'channels')
expr='^[0-9]*$'
if ! [[ $value =~ $expr ]]
then
echo "Invalid channel count: $value" >&2
exit $ECHANNEL
fi
unset expr
destinationchannels["$destination"]=$value
;;
'frequency')
expr='^[0-9]*$'
if ! [[ $value =~ $expr ]]
then
echo "Invalid frequency value: $value" >&2
exit $ECHANNEL
fi
unset expr
destinationfrequency["$destination"]=$value
;;
'noresample')
case $value in
'true'|'on'|'yes')
destinationnoresample["$destination"]=1
;;
'false'|'off'|'no')
destinationnoresample["$destination"]=0
;;
*)
echo "noresample takes values:" \
"'yes' ,'true' ,'on', 'no', 'false',"\
"'off'"
;;
esac
;;
'rename')
case "$value" in
*/*)
destinationrenamepath["$destination"]="${value%/*}"
;;
esac
destinationrename["$destination"]="${value##*/}"
;;
'fat32compat')
case $value in
'true'|'on'|'yes')
destinationfat32compat["$destination"]=1
;;
'false'|'off'|'no')
destinationfat32compat["$destination"]=0
;;
*)
echo "fat32compat takes values:" \
"'yes' ,'true' ,'on', 'no', 'false',"\
"'off'"
;;
esac
;;
'skip_mime-type')
destinationskipmime[$destination]="${destinationskipmime[$destination]:+${destinationskipmime[$destination]}|}$value"
;;
'copy_mime-type')
destinationcopymime[$destination]="${destinationcopymime[$destination]:+${destinationcopymime[$destination]}|}$value"
;;
'higher-than')
expr='^[0-9]*$'
if ! [[ $value =~ $expr ]]
then
echo "Invalid higher-than bitrate value: $value" >&2
exit $EMAXBPS
fi
unset expr
destinationmaxbps[$destination]="$value"
;;
esac
}

View File

@ -0,0 +1,35 @@
getConfigGeneral() {
case $key in
'max-load')
expr='^[0-9]*$'
if [[ $value =~ $expr ]]
then
maxload="$value"
else
echo "Invalid max-load value: $value" >&2
exit $ELOAD
fi
unset expr
;;
'load-interval')
expr='^[0-9]*$'
if [[ $value =~ $expr ]]
then
loadinterval="$value"
else
echo "Invalid load-interval value: $value" >&2
exit $EINTERVAL
fi
unset expr
;;
'temporary-directory')
tempdir="$value"
;;
'database')
database="$value"
;;
debug)
(( value > debug )) && debug=$value
;;
esac
}

View File

@ -0,0 +1,10 @@
getConfigSource() {
case "$key" in
'path')
sourcepath="$value"
;;
'skip')
skippeddirectories+=( "$value" )
;;
esac
}

18
lib/copy/checkCopy Normal file
View File

@ -0,0 +1,18 @@
checkCopy() {
(
[ -z "${destinationfrequency[$destination]}" ] \
|| (( ${rate:-0} == ${destinationfrequency[$destination]} ))
) && (
[ -z "${destinationchannels[$destination]}" ] \
|| (( ${channels:-0} == ${destinationchannels[$destination]} ))
) && (
(( ${bitrate:-1000} == ${destinationquality[$destination]} )) \
|| (
[ -n "${destinationmaxbps[$destination]}" ] \
|| ((
${bitrate:-1000}
<= ${destinationmaxbps[$destination]:-0}
))
)
)
}

28
lib/copy/copyFile Normal file
View File

@ -0,0 +1,28 @@
copyFile() {
extension="${filename##*.}"
cp -al \
"$sourcepath/$filename" \
"$destdir/$destfile.$extension" \
2>/dev/null \
|| cp -a \
"$sourcepath/$filename" \
"$destdir/$destfile.$extension"
echo \
"UPDATE destination_files" \
"SET filename=\"${filename//\"/\"\"}\"," \
" last_change=(" \
" SELECT last_change" \
" FROM source_files" \
" WHERE id=$fileid" \
" )," \
" old_filename=(" \
" SELECT filename" \
" FROM destination_files" \
" WHERE id=$destfileid" \
" )," \
" rename_pattern=" \
"\"${destinationrenamepath[$destination]}/${destinationrename[$destination]}:${destinationfat32compat["$destination"]}\""\
"WHERE id=$destfileid;" \
>&3
(( ++copies ))
}

23
lib/database/Delete Normal file
View File

@ -0,0 +1,23 @@
Delete() {
#Delete table < where_key where_operator where_value
# [where_key where_operator where_value
# […]]
local \
table="$1" \
key \
operator \
value \
where_statement \
results
while read key operator value
do
(( ${#where_statement} )) && where_statement+=( "AND" )
if [[ $value == NULL ]]
then
where_statement+=( "$key is NULL" )
else
where_statement+=( "$key $operator "'"'"${value//\"/\"\"}"'"' )
fi
done
echo "DELETE from $table WHERE ${where_statement[@]};" >&3
}

37
lib/database/Insert Normal file
View File

@ -0,0 +1,37 @@
Insert() {
#Insert table [no_id] < key value
# [key value
# […]]
local \
table="$1" \
no_id="${2:-0}" \
insert_keys \
insert_values \
results
while read key value
do
(( ${#insert_keys} )) && insert_keys+=","
insert_keys+='`'"$key"'`'
(( ${#insert_values} )) && insert_values+=","
case $value in
'NULL')
insert_values+="NULL"
;;
+([0-9])?(.+([0-9])))
insert_values+=$value
;;
*)
insert_values+='"'"${value//\"/\"\"}"'"'
;;
esac
done
echo "INSERT INTO $table" \
"( $insert_keys )" \
"VALUES" \
"( $insert_values );" >&3
(( no_id )) || {
echo 'SELECT LAST_INSERT_ROWID();' >&3
read -u 4 results
echo "$results"
}
}

View File

@ -0,0 +1,42 @@
InsertIfUnset() {
#InsertIfUnset table [no_id] < key value \n key value
local \
table="$1" \
no_id="${2:-0}" \
column \
key \
keys \
results \
value \
values
while read key value
do
keys+=( "$key" )
values+=( "$value" )
done
if (( no_id ))
then
column="${keys[0]}"
else
column='id'
fi
if ! results=$(
Select "$table" "$column" < <(
for key in ${!keys[@]}
do
echo "${keys[$key]}" = "${values[$key]}"
done
)
)
then
results=$(
Insert "$table" < <(
for key in ${!keys[@]}
do
echo "${keys[$key]}" "${values[$key]}"
done
)
)
fi
echo "$results"
}

View File

@ -0,0 +1,66 @@
InsertOrUpdate() {
#InsertOrUpdate table set_key set_value [set_key set_value […]] < where_key where_value
# [where_key where_value
# […]]
local \
table="$1" \
argument \
key \
keys \
set_keys \
set_values \
value \
values \
what \
results
shift
what=key
for argument
do
case $what in
key)
set_keys+=( "$argument" )
what=value
;;
value)
set_values+=( "$argument" )
what=key
;;
esac
done
while read key value
do
keys+=( "$key" )
values+=( "$value" )
done
if results=$(
Select "$table" ${keys[0]} < <(
for key in ${!keys[@]}
do
echo "${keys[$key]}" = "${values[$key]}"
done
)
)
then
Update "$table" "$@" < <(
for key in ${!keys[@]}
do
echo "${keys[$key]}" = "${values[$key]}"
done
)
else
results=$(
Insert "$table" < <(
for key in ${!set_keys[@]}
do
echo "${set_keys[$key]}" "${set_values[$key]}"
done
for key in ${!keys[@]}
do
echo "${keys[$key]}" "${values[$key]}"
done
)
)
fi
echo "$results"
}

35
lib/database/Select Normal file
View File

@ -0,0 +1,35 @@
Select() {
#Select table [col1 [col2 [..]]] < WHERE_key WHERE_operator WHERE_value
# [WHERE_key WHERE_operator WHERE_value
# […]]
local \
table="$1" \
col \
columns \
operator \
results \
where_statement
shift
for col
do
(( ${#columns} )) && columns+=','
columns+="$col"
done
while read key operator value
do
(( ${#where_statement} )) && where_statement+=( "AND" )
where_statement+=( "$key $operator "'"'"${value//\"/\"\"}"'"' )
done
echo "SELECT IFNULL(" \
"(SELECT $columns FROM $table" \
"WHERE ${where_statement[@]})" \
",'SQL::Select:not found'" \
");" >&3
read -u 4 results
if ! [[ $results == "SQL::Select:not found" ]]
then
echo "$results"
else
return 1
fi
}

62
lib/database/Update Normal file
View File

@ -0,0 +1,62 @@
Update() {
#Update table set_key set_value [set_key set_value […]] < where_key where_operator where_value
# [where_key where_operator where_value
# […]]
local \
table="$1" \
key \
argument \
operator \
value \
set_statement \
where_keys \
where_values \
what \
where_statement \
results
shift
what=key
for argument
do
case $what in
key)
set_statement="${set_statement:+${set_statement},}\`$argument\`"
what=value
;;
value)
case $argument in
'NULL')
set_statement+=" = NULL"
;;
+([0-9])?(.+([0-9])))
set_statement+=" = $argument"
;;
*)
set_statement+=" = "'"'"${argument//\"/\"\"}"'"'
;;
esac
what=key
;;
esac
done
while read key operator value
do
(( ${#where_statement} )) && where_statement+=( "AND" )
case $value in
'NULL')
where_statement+=( "$key is NULL" )
;;
+([0-9.]))
where_statement+=( "$key $operator $value" )
;;
*)
where_statement+=( "$key $operator "'"'"${value//\"/\"\"}"'"' )
;;
esac
done
echo "UPDATE '$table' SET" \
"$set_statement" \
"WHERE" \
"${where_statement[@]}" \
";" >&3
}

View File

@ -0,0 +1,9 @@
closeDatabase() {
echo .quit >&3
(( debug )) && echo -n "Waiting for SQLite to terminate... "
wait
(( debug )) && echo OK
exec 3>&-
exec 4<&-
rm "$tempdir"/sqlite.{in,out}
}

27
lib/database/openDatabase Normal file
View File

@ -0,0 +1,27 @@
openDatabase() {
if [ ! -d "$tempdir" ]
then
mkdir -p "$tempdir"
fi
rm -f "$tempdir"/sqlite.{in,out}
mkfifo "$tempdir"/sqlite.{in,out}
if [ ! -f "$database" ]
then
if [ ! -d "${database%/*}" ]
then
mkdir -p "${database%/*}"
fi
sqlite3 "$database" < $schema
fi
sqlite3 -bail "$database" \
< "$tempdir/sqlite.in" \
> "$tempdir/sqlite.out" &
exec 3> "$tempdir"/sqlite.in
exec 4< "$tempdir"/sqlite.out
if (( debug > 2 ))
then
exec 5>&3
exec 3> >(tee -a $tempdir/debug.log >&5)
fi
echo 'PRAGMA foreign_keys = ON;' >&3
}

48
lib/decode/decodeFile Normal file
View File

@ -0,0 +1,48 @@
decodeFile() {
if ! decodetaskid=$(
Select tasks id <<<"key = $tmpfile"
)
then
decodetaskid=$(
Insert tasks <<-EOInsert
key $tmpfile
source_file $fileid
$(
for key in ${!commandline[@]}
do
echo "cmd_arg$key ${commandline[key]}"
done
)
status 0
EOInsert
)
progressSpin
fi
if (( sox_needed ))
then
cleanup="$tempdir/$tmpfile"
decodeSox "$tempdir/$tmpfile.wav"
if ! soxtaskid=$(
Select tasks id <<<"key = $tmpfile"
)
then
soxtaskid=$(
Insert tasks <<-EOInsert
key $tmpfile
source_file $fileid
$(
for key in ${!commandline[@]}
do
echo "cmd_arg$key ${commandline[key]}"
done
)
requires $decodetaskid
required $decodetaskid
status 0
cleanup $cleanup
EOInsert
)
progressSpin
fi
fi
}

4
lib/decode/decodeMpcdec Normal file
View File

@ -0,0 +1,4 @@
decodeMpcdec() {
tmpfile="${fileid}mpcdec"
commandline=(mpcdec "$sourcepath/$filename" "$tempdir/$tmpfile.wav")
}

4
lib/decode/decodeOpusdec Normal file
View File

@ -0,0 +1,4 @@
decodeOpusdec() {
tmpfile="${fileid}opusdec"
commandline=(opusdec "$sourcepath/$filename" "$tempdir/$tmpfile.wav")
}

30
lib/decode/decodeSox Normal file
View File

@ -0,0 +1,30 @@
decodeSox() {
commandline=(sox --single-threaded --temp "$tempdir")
soxoptions_in=''
soxoptions_out=''
if (( ${destinationnormalize["$destination"]} ))
then
commandline+=(--norm)
soxoptions_in+=' --norm'
fi
if [ -n "$1" ]
then
commandline+=("$1")
else
commandline+=("$sourcepath/$filename")
fi
if [ -n "${destinationfrequency["$destination"]}" ] \
&& (( ${rate:-0} != ${destinationfrequency["$destination"]} ))
then
commandline+=(-r ${destinationfrequency["$destination"]})
soxoptions_out+=" -r ${destinationfrequency["$destination"]}"
fi
if [ -n "${destinationchannels["$destination"]}" ] \
&& (( ${channels:-0} != ${destinationchannels["$destination"]} ))
then
commandline+=(-c ${destinationchannels["$destination"]})
soxoptions_out+=" -c ${destinationchannels["$destination"]}"
fi
tmpfile="$fileid${soxoptions_in// /}${soxoptions_out// /}"
commandline+=("$tempdir/$tmpfile.wav")
}

View File

@ -0,0 +1,16 @@
createDestinations() {
for destination in ${!destinationpath[@]}
do
if ! [ -d "${destinationpath["$destination"]}" ]
then
if ! mkdir -p "${destinationpath["$destination"]}"
then
echo "$destination: Could not create ${destinationpath["$destination"]}!"
exit $EINVDEST
fi
fi
destinationid["$destination"]=$(
InsertIfUnset destinations <<<"name $destination"
)
done
}

View File

@ -0,0 +1,31 @@
updateMimes() {
Update mime_actions action 1 <<<"action != 1"
for destination in ${!destinationskipmime[@]}
do
IFS='|'
for mime_type in ${destinationskipmime["$destination"]}
do
IFS="$oldIFS"
Update mime_type_actions action 0 >/dev/null < <(
cat <<-EOWhere
destination_id = ${destinationid["$destination"]}
mime_text LIKE ${mime_type//\*/%}
EOWhere
)
done
done
for destination in ${!destinationcopymime[@]}
do
IFS='|'
for mime_type in ${destinationcopymime["$destination"]}
do
IFS="$oldIFS"
Update mime_type_actions action 2 >/dev/null < <(
cat <<-EOWhere
destination_id = ${destinationid["$destination"]}
mime_text LIKE ${mime_type//\*/%}
EOWhere
)
done
done
}

View File

@ -0,0 +1,62 @@
encodeFile::mp3() {
lameopts=(lame --quiet -v --abr ${destinationquality[$destination]})
[ -n "$album" ] && lameopts+=(--tl "$album" )
[ -n "$artist" ] && lameopts+=(--ta "$artist")
[ -n "$genre" ] && lameopts+=(--tg "$genre")
[ -n "$title" ] && lameopts+=(--tt "$title")
[ -n "$track" ] && lameopts+=(--tn "$track")
[ -n "$year" ] && lameopts+=(--ty "$year")
if (( ${destinationnoresample[$destination]:-0} == 1 ))
then
# If 'rate' is not one of these value, it cannot be encoded to
# MP3, in which case we'd be better of letting lame decide which
# rate to use.
if [ -n "${destinationfrequency["$destination"]}" ]
then
case ${destinationfrequency["$destination"]} in
48000) lameopts+=(--resample 48) ;;
44100) lameopts+=(--resample 44.1) ;;
32000) lameopts+=(--resample 32) ;;
24000) lameopts+=(--resample 24) ;;
22050) lameopts+=(--resample 22.05) ;;
16000) lameopts+=(--resample 16) ;;
12000) lameopts+=(--resample 12) ;;
11025) lameopts+=(--resample 11.025) ;;
8000) lameopts+=(--resample 8) ;;
esac
else
case $rate in
48000) lameopts+=(--resample 48) ;;
44100) lameopts+=(--resample 44.1) ;;
32000) lameopts+=(--resample 32) ;;
24000) lameopts+=(--resample 24) ;;
22050) lameopts+=(--resample 22.05) ;;
16000) lameopts+=(--resample 16) ;;
12000) lameopts+=(--resample 12) ;;
11025) lameopts+=(--resample 11.025) ;;
8000) lameopts+=(--resample 8) ;;
esac
fi
fi
lameopts+=("$tempdir/$tmpfile.wav" "$destdir/$destfile.mp3")
encodetaskid=$(
Insert tasks <<-EOInsert
key ${fileid}lame$destination
requires ${soxtaskid:-$decodetaskid}
required ${soxtaskid:-$decodetaskid}
fileid $destfileid
filename $destdir/$destfile.mp3
$(
for key in ${!lameopts[@]}
do
echo "cmd_arg$key ${lameopts[key]}"
done
)
cleanup $tempdir/$tmpfile.wav
source_file $fileid
status 0
rename_pattern ${destinationrenamepath[$destination]}/${destinationrename[$destination]}:${destinationfat32compat["$destination"]}
EOInsert
)
progressSpin
}

View File

@ -0,0 +1,38 @@
encodeFile::opus() {
opusencopts=(opusenc --music --quiet)
opusencopts+=(--bitrate ${destinationquality[$destination]})
[ -n "${destinationloss["$destination"]}" ] \
&& opusencopts+=(--expect-loss "${destinationloss["$destination"]}")
[ -n "$albumartist" ] && opusencopts+=(--comment "ALBUMARTIST=$albumartist")
[ -n "$album" ] && opusencopts+=(--comment "ALBUM=$album")
[ -n "$artist" ] && opusencopts+=(--artist "$artist")
[ -n "$composer" ] && opusencopts+=(--comment "COMPOSER=$composer")
[ -n "$disc" ] && opusencopts+=(--comment "DISCNUMBER=$disc")
[ -n "$genre" ] && opusencopts+=(--comment "GENRE=$genre")
[ -n "$performer" ] && opusencopts+=(--comment "PERFORMER=$performer")
[ -n "$title" ] && opusencopts+=(--title "$title")
[ -n "$track" ] && opusencopts+=(--comment "TRACKNUMBER=${track%/*}")
[ -n "${track#*/}" ] && opusencopts+=(--comment "TRACKTOTAL=${track#*/}")
[ -n "$year" ] && opusencopts+=(--comment "DATE=$year")
opusencopts+=("$tempdir/$tmpfile.wav" "$destdir/$destfile.opus")
encodetaskid=$(
Insert tasks <<-EOInsert
key ${fileid}opusenc$destination
requires ${soxtaskid:-$decodetaskid}
required ${soxtaskid:-$decodetaskid}
fileid $destfileid
filename $destdir/$destfile.ogg
$(
for key in ${!opusencopts[@]}
do
echo "cmd_arg$key ${opusencopts[key]}"
done
)
cleanup $tempdir/$tmpfile.wav
source_file $fileid
status 0
rename_pattern ${destinationrenamepath[$destination]}/${destinationrename[$destination]}:${destinationfat32compat["$destination"]}
EOInsert
)
progressSpin
}

View File

@ -0,0 +1,34 @@
encodeFile::vorbis() {
oggencopts=(oggenc -Q -q ${destinationquality[$destination]})
[ -n "$albumartist" ] && oggencopts+=(-c "ALBUMARTIST=$albumartist")
[ -n "$album" ] && oggencopts+=(-l "$album")
[ -n "$artist" ] && oggencopts+=(-a "$artist")
[ -n "$composer" ] && oggencopts+=(-c "COMPOSER=$composer")
[ -n "$disc" ] && oggencopts+=(-c "DISCNUMBER=$disc")
[ -n "$genre" ] && oggencopts+=(-G "$genre")
[ -n "$performer" ] && oggencopts+=(-c "PERFORMER=$performer")
[ -n "$title" ] && oggencopts+=(-t "$title")
[ -n "$track" ] && oggencopts+=(-N "$track")
[ -n "$year" ] && oggencopts+=(-d "$year")
oggencopts+=(-o "$destdir/$destfile.ogg" "$tempdir/$tmpfile.wav")
encodetaskid=$(
Insert tasks <<-EOInsert
key ${fileid}oggenc$destination
requires ${soxtaskid:-$decodetaskid}
required ${soxtaskid:-$decodetaskid}
fileid $destfileid
filename $destdir/$destfile.ogg
$(
for key in ${!oggencopts[@]}
do
echo "cmd_arg$key ${oggencopts[key]}"
done
)
cleanup $tempdir/$tmpfile.wav
source_file $fileid
status 0
rename_pattern ${destinationrenamepath[$destination]}/${destinationrename[$destination]}:${destinationfat32compat["$destination"]}
EOInsert
)
progressSpin
}

34
lib/files/getDestDir Normal file
View File

@ -0,0 +1,34 @@
getDestDir() {
destdir="${destinationpath[$destination]}/"
if [ -n "${destinationrenamepath[$destination]}" ]
then
destdir+="${destinationrenamepath[$destination]//%\{album\}/$album}"
replace=$(sanitizeFile "$albumartist")
destdir="${destdir//%\{albumartist\}/$replace}"
replace=$(sanitizeFile "$artist")
destdir="${destdir//%\{artist\}/$replace}"
replace=$(sanitizeFile "$genre")
destdir="${destdir//%\{genre\}/$replace}"
replace=$(sanitizeFile "$title")
destdir="${destdir//%\{title\}/$replace}"
tracknumber="${track%/*}"
replace=$(sanitizeFile "$tracknumber")
destdir="${destdir//%\{track\}/$replace}"
replace=$(sanitizeFile "$year")
destdir="${destdir//%\{year\}/$replace}"
replace=$(sanitizeFile "$disc")
destdir="${destdir//%\{disc\}/$replace}"
else
destdir+=$(sanitizeFile "${filename%%/*}")
part=${filename#*/}
while [[ $part =~ / ]]
do
destdir+="/$(sanitizeFile "${part%%/*}")"
part=${part#*/}
done
fi
if ! [ -d "$destdir" ]
then
mkdir -p "$destdir"
fi
}

18
lib/files/getDestFile Normal file
View File

@ -0,0 +1,18 @@
getDestFile() {
if [ -n "${destinationrename[$destination]}" ]
then
destfile="${destinationrename[$destination]//%\{album\}/$album}"
destfile="${destfile//%\{albumartist\}/$albumartist}"
destfile="${destfile//%\{artist\}/$artist}"
destfile="${destfile//%\{genre\}/$genre}"
destfile="${destfile//%\{title\}/$title}"
tracknumber="${track%/*}"
destfile="${destfile//%\{track\}/$tracknumber}"
destfile="${destfile//%\{year\}/$year}"
destfile="${destfile//%\{disc\}/$disc}"
else
destfile="${filename##*/}"
destfile="${destfile%.*}"
fi
destfile=$(sanitizeFile "$destfile")
}

63
lib/files/getFiles Normal file
View File

@ -0,0 +1,63 @@
getFiles() {
scantime=$(date +%s)
for prune_expression in "${skippeddirectories[@]}"
do
prunes+="-path $sourcepath$prune_expression -prune -o "
done
echo -n "Scanning $sourcepath... "
# We probably have thousands of files, don't waste time on disk writes
echo 'BEGIN TRANSACTION;' >&3
while read time size filename
do
if ! Select source_files id >/dev/null <<-EOWhere
filename = $filename
mime_type > 0
last_change = $time
EOWhere
then
mimetype=$(file -b --mime-type "$sourcepath/$filename")
if [[ $mimetype == application/ogg ]]
then
case "$(head -n1 "$sourcepath/$filename")" in
*'vorbis'*)
mimetype+=' vorbis'
;;
*'OpusHead'*)
mimetype+=' opus'
;;
esac
fi
mimetypeid=$(
InsertIfUnset mime_types <<-EOInsert
mime_text $mimetype
EOInsert
)
InsertOrUpdate source_files \
last_change $time \
size $size \
last_seen $scantime \
mime_type $mimetypeid \
>/dev/null \
<<-EOWhere
filename $filename
EOWhere
(( ++new ))
if (( new % 1000 == 0 ))
then
echo 'COMMIT;BEGIN TRANSACTION;' >&3
(( debug )) \
&& echo -ne "\bCommitted $count files... "
fi
else
Update source_files last_seen $scantime <<-EOWhere
filename = $filename
EOWhere
fi
progressSpin
done < <(
find "$sourcepath" $prunes -type f -printf "%T@ %s %P\n"
)
echo 'COMMIT;' >&3
echo -e "\r${count:-0} files found, ${new:=0} new or changed."
unset count
}

View File

@ -0,0 +1,5 @@
removeObsoleteFiles() {
Delete source_files <<-EOWhere
last_seen < $scantime
EOWhere
}

23
lib/files/sanitizeFile Normal file
View File

@ -0,0 +1,23 @@
sanitizeFile() {
shopt -s extglob
string="$1"
# Filenames can't contain /
string="${string//\// }"
if (( ${destinationfat32compat[$destination]} ))
then
# Filenames can't contain:
string=${string//\?/ }
string=${string//\\/ }
string=${string//</ }
string=${string//>/ }
string=${string//:/ }
string=${string//\*/ }
string=${string//|/ }
string=${string//\"/ }
# Filenames can't begin or end with ' '
string=${string/#+( )/}
string=${string/%+( )/}
fi
echo "$string"
}

54
lib/tags/getInfos::APE Normal file
View File

@ -0,0 +1,54 @@
getInfosAPE_version='APE-1'
tagreaders+=( "$getInfosAPE_version" )
getInfos::APE() {
# I was not able to find a decent cli tool to read APE tags.
# This is raw but works for the very few MusePack files I got.
#
# Please tell me if you know of any good tool.
tagreader="$getInfosAPE_version"
IFS='='
while read tag value
do
IFS="$oldIFS"
case $tag in
[Aa][Ll][Bb][Uu][Mm]' '[Aa][Rr][Tt][Ii][Ss][Tt])
albumartist="$value"
;;
[Aa][Rr][Tt][Ii][Ss][Tt])
artist="$value"
;;
[Yy][Ee][Aa][Rr])
year="$value"
;;
[Aa][Ll][Bb][Uu][Mm])
album="$value"
;;
[Tt][Ii][Tt][Ll][Ee])
title="$value"
;;
[Tt][Rr][Aa][Cc][Kk])
tracknum="$value"
;;
[Gg][Ee][Nn][Rr][Ee])
genre="$value"
;;
[Cc][Oo][Mm][Pp][Oo][Ss][Ee][Rr])
composer="$value"
;;
[Pp][Ee][Rr][Ff][Oo][Rr][Mm][Ee][Rr])
performer="$value"
;;
*)
;;
esac
IFS='='
done < <(
IFS="$oldIFS"
sed \
's/APETAGEX/\n/;s/[\x00\-\x1F]\x00\+/\n/g;s/\x00/=/g' \
"$sourcepath/$filename" \
| egrep -i \
'^(Album Artist|Artist|Year|Album|Title|Track|Genre|Composer|Performer)='
)
IFS="$oldIFS"
}

44
lib/tags/getInfos::FLAC Normal file
View File

@ -0,0 +1,44 @@
getInfosFLAC_version='FLAC-1'
tagreaders+=( "$getInfosFLAC_version" )
getInfos::FLAC() {
tagreader="$getInfosFLAC_version"
infos=$(
metaflac \
--show-tag=ALBUM \
--show-tag=ALBUMARTIST \
--show-tag=ARTIST \
--show-tag=COMPOSER \
--show-tag=DATE \
--show-tag=DISCNUMBER \
--show-tag=GENRE \
--show-tag=PERFORMER \
--show-tag=TITLE \
--show-tag=TRACKNUMBER \
--show-tag=TRACKTOTAL \
"$sourcepath/$filename"
)
albumartist=$(gettag albumartist)
album=$(gettag album)
artist=$(gettag artist)
composer=$(gettag composer)
disc=$(gettag discnumber)
genre=$(gettag genre)
performer=$(gettag performer)
title=$(gettag title)
tracknum="$(gettag tracknumber)/$(gettag tracktotal)"
year=$(gettag date)
if [ -n "$tracknum" -a -n "$tracktotal" ]
then
tracknum="$tracknum/$tracktotal"
fi
year=$(gettag DATE)
{
read rate
read channels
} < <(
metaflac \
--show-sample-rate \
--show-channels \
"$sourcepath/$filename"
)
}

28
lib/tags/getInfos::MP3 Normal file
View File

@ -0,0 +1,28 @@
getInfosMP3_version='ID3-2'
tagreaders+=( "$getInfosMP3_version" )
getInfos::MP3() {
tagreader="$getInfosMP3_version"
infos=$(
soxi "$sourcepath/$filename" 2>/dev/null \
| sed 's/ *: /=/'
)
album=$(gettag album)
artist=$(gettag artist)
genre=$(gettag genre)
title=$(gettag title)
tracknum=$(gettag tracknumber)
year=$(gettag year)
expr='^\([0-9]*\)$'
if [[ $genre =~ $expr ]]
then
genre=${genre%)}
genre=${genre#(}
genre="${id3genres[$genre]}"
fi
infos="${infos/: /=}"
channels=$(gettag channels)
rate=$(gettag 'sample rate')
bitrate=$(gettag 'bit rate')
bitrate=${bitrate%%.*}
bitrate=${bitrate%k}
}

29
lib/tags/getInfos::Ogg Normal file
View File

@ -0,0 +1,29 @@
getInfosOgg_version='Ogg-1'
tagreaders+=( "$getInfosOgg_version" )
getInfos::Ogg() {
tagreader="$getInfosOgg_version"
infos=$(
ogginfo "$sourcepath/$filename" \
| sed 's/\t//'
)
albumartist=$(gettag albumartist)
album=$(gettag album)
artist=$(gettag artist)
composer=$(gettag composer)
disc=$(gettag discnumber)
genre=$(gettag genre)
performer=$(gettag performer)
title=$(gettag title)
tracknum=$(gettag tracknumber)
tracktotal=$(gettag tracktotal)
if [ -n "$tracknum" -a -n "$tracktotal" ]
then
tracknum="$tracknum/$tracktotal"
fi
year=$(gettag date)
infos="${infos/: /=}"
rate=$(gettag rate|head -n1)
channels=$(gettag channels|head -n1)
bitrate=$(gettag 'nominal bitrate')
bitrate=${bitrate%%,*}
}

29
lib/tags/getInfos::Opus Normal file
View File

@ -0,0 +1,29 @@
getInfosOpus_version='Opus-1'
tagreaders+=( "$getInfosOpus_version" )
getInfos::Opus() {
tagreader="$getInfosOpus_version"
infos=$(
opusinfo "$sourcepath/$filename" \
| sed 's/\t//'
)
albumartist=$(gettag albumartist)
album=$(gettag album)
artist=$(gettag artist)
composer=$(gettag composer)
disc=$(gettag discnumber)
genre=$(gettag genre)
performer=$(gettag performer)
title=$(gettag title)
tracknum=$(gettag tracknumber)
tracktotal=$(gettag tracktotal)
if [ -n "$tracknum" -a -n "$tracktotal" ]
then
tracknum="$tracknum/$tracktotal"
fi
year=$(gettag date)
infos="${infos/: /=}"
rate=$(gettag 'original sample rate'|head -n1)
channels=$(gettag channels|head -n1)
bitrate=$(gettag 'average bitrate')
bitrate=${bitrate%%.*}
}

View File

@ -0,0 +1,15 @@
getRateChannelMPC() {
while read key value garbage
do
case $key in
'samplerate:')
rate=$value
;;
'channels:')
channels=$value
;;
esac
done < <(
mpcdec "$sourcepath/$filename" -i 2>&1
)
}

View File

@ -0,0 +1,4 @@
getRateChannelSoxi() {
rate=$(soxi -r "$sourcepath/$filename" 2>/dev/null)
channels=$(soxi -c "$sourcepath/$filename" 2>/dev/null)
}

41
lib/tags/getTags Normal file
View File

@ -0,0 +1,41 @@
getTags_version='unknown-2'
tagreaders+=( "$getTags_version" )
getTags() {
unset type
case "$mimetype" in
audio/mpeg)
type=MP3
;;
'application/ogg opus')
type=Opus
;;
application/ogg*)
type=Ogg
;;
audio/x-flac)
type=FLAC
;;
*)
extendedtype=$(file -b "$sourcepath/$filename")
case "$extendedtype" in
*' ID3 '*)
type=MP3
;;
*'Musepack '*)
getRateChannelMPC
tryAPE
;;
*)
getRateChannelSoxi
tryAPE
;;
esac
;;
esac
if [ -n "$type" ]
then
getInfos::$type
else
tagreader=$getTags_version
fi
}

4
lib/tags/gettag Normal file
View File

@ -0,0 +1,4 @@
gettag() {
echo -e "$infos" \
| sed -n "/^${1}=/I{s/^${1}=//I;p;q}"
}

5
lib/tags/tryAPE Normal file
View File

@ -0,0 +1,5 @@
tryAPE() {
grep -q 'APETAGEX' \
"$sourcepath/$filename" \
&& type=APE
}

26
lib/tasks/gettaskinfos Normal file
View File

@ -0,0 +1,26 @@
gettaskinfos() {
echo '
SELECT
id,
source_file,
required,
cleanup,
fileid,
filename
FROM tasks
WHERE id='$1';
' >&3
read -u4 line
taskid=${line%%|*}
rest="${line#*|}|"
sourcefileid=${rest%%|*}
rest=${rest#*|}
required=${rest%%|*}
rest=${rest#*|}
cleanup=${rest%%|*}
rest=${rest#*|}
destfileid=${rest%%|*}
rest=${rest#*|}
destfilename=${rest%%|*}
rest=${rest#*|}
}

9
lib/tools/progressSpin Normal file
View File

@ -0,0 +1,9 @@
progressSpin() {
case $(( ++count % 40 )) in
0) echo -ne '\b|' ;;
10) echo -ne '\b/' ;;
20) echo -en '\b-' ;;
30) echo -ne '\b\\' ;;
*) ;;
esac
}

19
lib/workers/checkworkers Normal file
View File

@ -0,0 +1,19 @@
checkworkers() {
for key in ${!workers[@]}
do
if ! kill -0 ${workers[key]} 2>/dev/null
then
taskid=${workertasks[key]}
(( ++ran ))
(( active-- ))
if destroyworker $key
then
finishedtasks+=($taskid)
else
failedtasks+=($taskid)
(( ++failed ))
fi
unset workertasks[key]
fi
done
}

64
lib/workers/cleaner Normal file
View File

@ -0,0 +1,64 @@
cleaner() {
for key in ${!failedtasks[@]}
do
taskid=${failedtasks[key]}
gettaskinfos $taskid
faildepends=$(
Select tasks 'COUNT(*)' <<-EOWhere
requires = $taskid
EOWhere
)
(( failed+=faildepends ))
Update tasks status 2 <<<"id = $taskid"
Update tasks status 2 <<<"requires = $taskid"
echo "SELECT COUNT(*)
FROM tasks
WHERE ( status = 0 OR status = 1 )
AND required = $taskid;">&3
read -u4 count
if (( count == 0 ))
then
rm -f "$cleanup"
fi
unset failedtasks[key]
done
for key in ${!finishedtasks[@]}
do
taskid=${finishedtasks[key]}
gettaskinfos $taskid
if [ -n "$destfilename" ]
then
echo \
"UPDATE destination_files" \
"SET filename=\"${destfilename//\"/\"\"}\"," \
" last_change=(" \
" SELECT last_change" \
" FROM source_files" \
" WHERE id=$sourcefileid" \
" )," \
" old_filename=(" \
" SELECT filename" \
" FROM destination_files" \
" WHERE id=$destfileid" \
" )," \
" rename_pattern=(" \
" SELECT rename_pattern" \
" FROM tasks" \
" WHERE id=$taskid" \
" )" \
"WHERE id=$destfileid;" \
>&3
fi
echo "SELECT COUNT(*)
FROM tasks
WHERE ( status = 0 OR status = 1 )
AND required = $taskid;">&3
read -u4 count
if (( count == 0 ))
then
rm -f "$cleanup"
fi
Delete tasks <<<"id = $taskid"
unset finishedtasks[key]
done
}

4
lib/workers/createworker Normal file
View File

@ -0,0 +1,4 @@
createworker() {
worker $1 &
workers[$1]=$!
}

View File

@ -0,0 +1,5 @@
destroyworker() {
dyingworker=${workers[$1]}
unset workers[$1]
wait $dyingworker
}

14
lib/workers/getworkerid Normal file
View File

@ -0,0 +1,14 @@
getworkerid() {
local i
for (( i=0 ; i >= 0 ; i++ ))
do
if [ -z "${workers[i]}" ]
then
echo $i
return
fi
done
# If we reach this, we have reached the signed long limit
# (2^63 - 1 = 9223372036854775807 - Got a supercomputer?)
(( concurrency-- ))
}

153
lib/workers/master Normal file
View File

@ -0,0 +1,153 @@
master() {
if (( active >= concurrency)) || [ -n "$quit" ]
then
sleep 0.1
else
echo '
SELECT COUNT(*)
FROM tasks
WHERE status = 0;
SELECT COUNT(*)
FROM tasks
WHERE status = 0
AND requires is NULL;
SELECT
id,
source_file,
required,
cmd_arg0,
cmd_arg1,
cmd_arg2,
cmd_arg3,
cmd_arg4,
cmd_arg5,
cmd_arg6,
cmd_arg7,
cmd_arg8,
cmd_arg9,
cmd_arg10,
cmd_arg11,
cmd_arg12,
cmd_arg13,
cmd_arg14,
cmd_arg15,
cmd_arg16,
cmd_arg17,
cmd_arg18,
cmd_arg19,
cmd_arg20,
cmd_arg21,
cmd_arg22,
cmd_arg23,
cmd_arg24,
cmd_arg25,
cmd_arg26,
cmd_arg27,
cmd_arg28,
cmd_arg29,
cleanup,
fileid,
filename
FROM tasks
WHERE status = 0
AND requires is NULL
ORDER BY source_file
LIMIT 1;
' >&3
read -u4 remaining
read -u4 ready
if (( remaining == 0 ))
then
sleep 0.1
continue
elif (( ready == 0 ))
then
sleep 0.1
else
(( ++active ))
read -u4 line
taskid=${line%%|*}
rest="${line#*|}|"
sourcefileid=${rest%%|*}
rest=${rest#*|}
required=${rest%%|*}
rest=${rest#*|}
cmd_arg=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cmd_arg+=("${rest%%|*}")
rest=${rest#*|}
cleanup=${rest%%|*}
rest=${rest#*|}
destfileid=${rest%%|*}
rest=${rest#*|}
destfilename=${rest%%|*}
rest=${rest#*|}
for key in ${!cmd_arg[@]}
do
[ -z "${cmd_arg[key]}" ] && unset cmd_arg[key]
done
workerid=$(getworkerid)
workertasks[workerid]=$taskid
Update tasks status 1 <<<"id = $taskid"
createworker $workerid
fi
fi
}

14
lib/workers/worker Normal file
View File

@ -0,0 +1,14 @@
worker() {
exec 2>>"$tempdir/worker$1.log"
(( debug >= 2 )) && echo "${cmd_arg[@]}" >&2
"${cmd_arg[@]}" >/dev/null
}
createworker() {
worker $1 &
workers[$1]=$!
}
destroyworker() {
dyingworker=${workers[$1]}
unset workers[$1]
wait $dyingworker
}