1029 lines
19 KiB
Bash
Executable File
1029 lines
19 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
## Define exit codes
|
|
# General config errors [10-19]
|
|
ELOAD=10
|
|
EINTERVAL=11
|
|
ENOCFG=19
|
|
# Source cofig errors [20-29]
|
|
# Destination config errors [30-49]
|
|
EFORMAT=30
|
|
ECHANNEL=31
|
|
EFMTINVPARM=49
|
|
|
|
# config structures
|
|
declare -A \
|
|
destinationchannels \
|
|
destinationcopymime \
|
|
destinationformat \
|
|
destinationfrequency \
|
|
destinationid \
|
|
destinationpath \
|
|
destinationquality \
|
|
destinationrename \
|
|
destinationrenamepath \
|
|
destinationskipmime \
|
|
|| {
|
|
echo "Check your Bash version. You need >= 4.0" >&2
|
|
exit $EBASHVERS
|
|
}
|
|
|
|
declare -r \
|
|
DOCDIR=./doc \
|
|
SHAREDIR=./share
|
|
declare -r \
|
|
exampleconf=$DOCDIR/example.cfg \
|
|
schema=$SHAREDIR/schema.sql \
|
|
\
|
|
oldIFS="$IFS"
|
|
|
|
#parse arguments
|
|
OPTERR=0
|
|
while getopts ':c:l:T:F:hD' opt
|
|
do
|
|
case $opt in
|
|
c)
|
|
cffile="$OPTARG"
|
|
;;
|
|
l)
|
|
cliload="$OPTARG"
|
|
;;
|
|
T)
|
|
cliltimer="$OPTARG"
|
|
;;
|
|
F)
|
|
forceall+=("$OPTARG")
|
|
;;
|
|
h)
|
|
help
|
|
exit 0
|
|
;;
|
|
D)
|
|
(( debug++ ))
|
|
;;
|
|
:)
|
|
echo "-$OPTARG requires an argument"
|
|
help
|
|
exit 127
|
|
;;
|
|
*)
|
|
echo "Unrecognized option: -$OPTARG"
|
|
help
|
|
exit 127
|
|
;;
|
|
esac
|
|
done
|
|
|
|
#parse config
|
|
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
|
|
}
|
|
|
|
getConfigSource() {
|
|
case "$key" in
|
|
'path')
|
|
sourcepath="$value"
|
|
;;
|
|
'id3charset')
|
|
sourceid3charset="$value"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
getConfigDestination() {
|
|
case "$key" in
|
|
'path')
|
|
destinationpath["$destination"]="$value"
|
|
;;
|
|
'format')
|
|
case "$value" in
|
|
'mp3')
|
|
destinationformat["$destination"]=mp3
|
|
;;
|
|
'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
|
|
;;
|
|
'bitrate')
|
|
expr='^[0-9]*$'
|
|
if ! [[ $value =~ $expr ]]
|
|
then
|
|
echo "Invalid bitrate value: $value" >&2
|
|
exit $EQUALITY
|
|
fi
|
|
unset expr
|
|
case "${destinationformat["$destination"]}" in
|
|
'mp3')
|
|
destinationquality["$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
|
|
;;
|
|
'rename')
|
|
case "$value" in
|
|
*/*)
|
|
destinationrenamepath["$destination"]="${value%/*}"
|
|
;;
|
|
esac
|
|
destinationrename["$destination"]="${value##*/}"
|
|
;;
|
|
'skip_mime-type')
|
|
destinationskipmime[$destination]="${destinationskipmime[$destination]:+${destinationskipmime[$destination]}|}$value"
|
|
;;
|
|
'copy_mime-type')
|
|
destinationcopymime[$destination]="${destinationcopymime[$destination]:+${destinationcopymime[$destination]}|}$value"
|
|
;;
|
|
esac
|
|
}
|
|
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
|
|
}
|
|
|
|
#check sanity
|
|
|
|
openDatabase() {
|
|
if [ ! -d "$tempdir" ]
|
|
then
|
|
mkdir -p "$tempdir"
|
|
fi
|
|
mkfifo "$tempdir"/sqlite.{in,out}
|
|
if [ ! -f "$database" ]
|
|
then
|
|
if [ ! -d "${database%/*}" ]
|
|
then
|
|
mkdir -p "${database%/*}"
|
|
fi
|
|
sqlite3 "$database" < $schema
|
|
fi
|
|
sqlite3 "$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
|
|
}
|
|
|
|
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}
|
|
}
|
|
|
|
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
|
|
}
|
|
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+=","
|
|
if [[ $value == NULL ]]
|
|
then
|
|
insert_values+="NULL"
|
|
else
|
|
insert_values+='"'"${value//\"/\"\"}"'"'
|
|
fi
|
|
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"
|
|
}
|
|
}
|
|
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)
|
|
set_statement="${set_statement}="'"'"${argument//\"/\"\"}"'"'
|
|
what=key
|
|
;;
|
|
esac
|
|
done
|
|
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 "UPDATE '$table' SET" \
|
|
"$set_statement" \
|
|
"WHERE" \
|
|
"${where_statement[@]}" \
|
|
";" >&3
|
|
}
|
|
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"
|
|
}
|
|
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"
|
|
}
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
getFiles() {
|
|
scantime=$(date +%s)
|
|
# 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")
|
|
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 ))
|
|
else
|
|
Update source_files last_seen $scantime <<-EOWhere
|
|
filename = $filename
|
|
EOWhere
|
|
fi
|
|
case $(( ++count % 4 )) in
|
|
0) echo -ne '\r|' ;;
|
|
1) echo -ne '\r/' ;;
|
|
2) echo -en '\r-' ;;
|
|
3) echo -ne '\r\\' ;;
|
|
esac
|
|
done < <(
|
|
find "$sourcepath" -type f -printf "%T@ %s %P\n"
|
|
)
|
|
echo 'COMMIT;' >&3
|
|
echo -e "\r$count files found, ${new:=0} new or changed."
|
|
unset count
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
removeObsoleteFiles() {
|
|
Delete source_files <<-EOWhere
|
|
last_seen < $scantime
|
|
EOWhere
|
|
}
|
|
|
|
gettag() {
|
|
tagval=$(
|
|
echo -e "$infos" \
|
|
| egrep -i "^${1}="
|
|
)
|
|
echo "${tagval#${1^^}=}"
|
|
unset tagval
|
|
}
|
|
|
|
getInfos::MP3() {
|
|
# mp3info only supports ID3v1. Information may be incomplete.
|
|
infos=$(
|
|
mp3info -p '
|
|
ALBUM=%l
|
|
ARTIST=%a
|
|
GENRE=%g
|
|
TITLE=%t
|
|
TRACKNUM=%n
|
|
YEAR=%y' "$sourcepath/$filename" \
|
|
| iconv -f $sourceid3charset -t utf-8//TRANSLIT
|
|
)
|
|
album=$(gettag album)
|
|
artist=$(gettag artist)
|
|
genre=$(gettag genre)
|
|
title=$(gettag title)
|
|
tracknum=$(gettag tracknum)
|
|
year=$(gettag year)
|
|
[ -n "$album" \
|
|
-o -n "$artist" \
|
|
-o -n "$genre" \
|
|
-o -n "$title" \
|
|
-o -n "$tracknum" \
|
|
-o -n "$year" ] \
|
|
|| return 1
|
|
}
|
|
|
|
getInfos::Ogg() {
|
|
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)/$(gettag tracktotal)"
|
|
year=$(gettag date)
|
|
[ -n "$album" \
|
|
-o -n "$albumartist" \
|
|
-o -n "$artist" \
|
|
-o -n "$composer" \
|
|
-o -n "$disc" \
|
|
-o -n "$genre" \
|
|
-o -n "$performer" \
|
|
-o -n "$title" \
|
|
-o -n "$tracknum" \
|
|
-o -n "$year" ] \
|
|
|| return 1
|
|
}
|
|
|
|
getInfos::FLAC() {
|
|
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)
|
|
[ -n "$album" \
|
|
-o -n "$albumartist" \
|
|
-o -n "$artist" \
|
|
-o -n "$composer" \
|
|
-o -n "$disc" \
|
|
-o -n "$genre" \
|
|
-o -n "$performer" \
|
|
-o -n "$title" \
|
|
-o -n "$tracknum" \
|
|
-o -n "$year" ] \
|
|
|| return 1
|
|
}
|
|
|
|
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.
|
|
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])
|
|
date="$value"
|
|
;;
|
|
[Aa][Ll][Bb][Uu][Mm])
|
|
album="$value"
|
|
;;
|
|
[Tt][Ii][Tt][Ll][Ee])
|
|
title="$value"
|
|
;;
|
|
[Tt][Rr][Aa][Cc][Kk])
|
|
track="$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' \
|
|
"$1" \
|
|
| egrep -i \
|
|
'^(Album Artist|Artist|Year|Album|Title|Track|Genre|Composer|Performer)='
|
|
)
|
|
IFS="$oldIFS"
|
|
[ -n "$album" \
|
|
-o -n "$albumartist" \
|
|
-o -n "$artist" \
|
|
-o -n "$composer" \
|
|
-o -n "$disc" \
|
|
-o -n "$genre" \
|
|
-o -n "$performer" \
|
|
-o -n "$title" \
|
|
-o -n "$tracknum" \
|
|
-o -n "$year" ] \
|
|
|| return 1
|
|
}
|
|
|
|
getTags() {
|
|
unset type
|
|
case "$mimetype" in
|
|
audio/mpeg)
|
|
type=MP3
|
|
;;
|
|
application/ogg)
|
|
type=Ogg
|
|
;;
|
|
audio/x-flac)
|
|
type=FLAC
|
|
;;
|
|
*)
|
|
extendedtype=$(file -b "$sourcepath/$filename")
|
|
case "$extendedtype" in
|
|
*' ID3 '*)
|
|
type=MP3
|
|
;;
|
|
*)
|
|
grep -q 'APETAGEX' \
|
|
"$sourcepath/$filename" \
|
|
&& type=APE
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
if [ -n "$type" ]
|
|
then
|
|
getInfos::$type || return 1
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
encodeFile::MP3() {
|
|
#lame
|
|
:
|
|
}
|
|
|
|
encodeFile::Ogg() {
|
|
#oggenc
|
|
:
|
|
}
|
|
|
|
transcodeFile() {
|
|
#sox -> wav
|
|
for format in $targets["format"]
|
|
do
|
|
#encodeFile::$format
|
|
:
|
|
done
|
|
#signal and of encoding to parent
|
|
}
|
|
|
|
checkFinished() {
|
|
#retrieve info from finished transcodeFile()
|
|
#update counters / metadata
|
|
:
|
|
}
|
|
|
|
checkLoad() {
|
|
#if load > threshold
|
|
# decrease concurrency
|
|
#elif load < threshold
|
|
# increase concurrency
|
|
#fi
|
|
:
|
|
}
|
|
|
|
readUserInput() {
|
|
#read + / - / q(uit) / p(ause)
|
|
#initiate shutdown / change load threshold / SIGSTOP all children / SIGCONT all children
|
|
:
|
|
}
|
|
|
|
transcodeLauncher() {
|
|
checkLoad
|
|
checkFinished
|
|
#until running processes < max processes
|
|
#do
|
|
checkLoad
|
|
checkFinished
|
|
readUserInput
|
|
#done
|
|
transcodeFile &
|
|
#update counter / metadata
|
|
}
|
|
|
|
#UI
|
|
|
|
if [ ! -f ~/.atom/atom.cfg ]
|
|
then
|
|
if [ ! -d ~/.atom ]
|
|
then
|
|
mkdir -p ~/.atom
|
|
fi
|
|
sed "s:%HOME%:$HOME:" "$exampleconf" > ~/.atom/atom.cfg
|
|
cat >&2 <<-EOCfgNotice
|
|
No configuration file found!
|
|
An example file has been created as ~/.atom/atom.cfg.
|
|
You should change it to your likings using you favorite editor.
|
|
|
|
Bailing out.
|
|
EOCfgNotice
|
|
exit $ENOCFG
|
|
fi
|
|
getConfig
|
|
{
|
|
cat <<EOF
|
|
General|Load|$maxload
|
|
|Load Interval|$loadinterval
|
|
|Temp Dir|$tempdir
|
|
|Database|$database
|
|
|Debug|$debug
|
|
Source|Path|$sourcepath
|
|
|ID3 Charset|$sourceid3charset
|
|
EOF
|
|
for destination in ${!destinationpath[@]}
|
|
do
|
|
cat <<EOF
|
|
$destination|Path|${destinationpath[$destination]}
|
|
|Format|${destinationformat[$destination]}
|
|
|Quality|${destinationquality[$destination]}
|
|
|Channels|${destinationchannels[$destination]}
|
|
|Frequency|${destinationfrequency[$destination]}
|
|
|Path Change|${destinationrenamepath[$destination]}
|
|
|File Rename|${destinationrename[$destination]}
|
|
EOF
|
|
echo "|Skipped mime-type|${destinationskipmime[$destination]//\|/
|
|
|Skipped mime-type|}"
|
|
echo "|Copied mime-type|${destinationcopymime[$destination]//\|/
|
|
|Copied mime-type|}"
|
|
done
|
|
}|column -t -s'|' -n
|
|
|
|
openDatabase
|
|
|
|
createDestinations
|
|
|
|
getFiles
|
|
|
|
updateMimes
|
|
|
|
removeObsoleteFiles
|
|
|
|
# get files
|
|
echo '
|
|
SELECT COUNT(DISTINCT source_files.filename)
|
|
FROM source_files,
|
|
destinations,
|
|
destination_files,
|
|
mime_type_actions,
|
|
tags
|
|
WHERE destination_files.last_change < source_files.last_change
|
|
AND destinations.id = destination_files.destination_id
|
|
AND mime_type_actions.destination_id = destinations.id
|
|
AND mime_type_actions.id = source_files.mime_type
|
|
AND source_files.id = destination_files.source_file_id
|
|
AND source_files.id = tags.source_file
|
|
AND tags.last_change < source_files.last_change
|
|
AND mime_type_actions.action = 1;' >&3
|
|
read -u4 filecount
|
|
echo '
|
|
SELECT DISTINCT
|
|
source_files.id,
|
|
source_files.last_change,
|
|
mime_type_actions.mime_text,
|
|
source_files.filename
|
|
FROM source_files,
|
|
destinations,
|
|
destination_files,
|
|
mime_type_actions,
|
|
tags
|
|
WHERE destination_files.last_change < source_files.last_change
|
|
AND destinations.id = destination_files.destination_id
|
|
AND mime_type_actions.destination_id = destinations.id
|
|
AND mime_type_actions.id = source_files.mime_type
|
|
AND source_files.id = destination_files.source_file_id
|
|
AND source_files.id = tags.source_file
|
|
AND tags.last_change < source_files.last_change
|
|
AND mime_type_actions.action = 1;
|
|
|
|
SELECT "AtOM:NoMoreFiles";' >&3
|
|
read -u4 line
|
|
while ! [[ $line = AtOM:NoMoreFiles ]]
|
|
do
|
|
tagfiles+=("$line")
|
|
read -u4 line
|
|
done
|
|
echo 'BEGIN TRANSACTION;' >&3
|
|
for line in "${tagfiles[@]}"
|
|
do
|
|
sourcefileid=${line%%|*}
|
|
rest=${line#*|}
|
|
lastchange=${rest%%|*}
|
|
rest=${rest#*|}
|
|
mimetype=${rest%%|*}
|
|
filename=${rest#*|}
|
|
echo -en "\rTags: $((++count*100/filecount))%"
|
|
if getTags
|
|
then
|
|
Update tags \
|
|
album "$album" \
|
|
albumartist "$albumartist" \
|
|
artist "$artist" \
|
|
composer "$composer" \
|
|
disc "$disc" \
|
|
genre "$genre" \
|
|
performer "$performer" \
|
|
title "$title" \
|
|
track "$tracknum" \
|
|
year "$year" \
|
|
last_change "$lastchange" \
|
|
>/dev/null <<<"source_file $sourcefileid"
|
|
unset genre \
|
|
albumartist \
|
|
year \
|
|
album \
|
|
disc \
|
|
artist \
|
|
track \
|
|
title \
|
|
composer \
|
|
performer
|
|
fi
|
|
done
|
|
echo 'COMMIT;' >&3
|
|
echo -e "\rRead tags from $count files."
|
|
unset count tagfiles
|
|
closeDatabase
|
|
|
|
# vim:set ts=8 sw=8:
|