From 66e4b0c1a80db4dbc2cc3a939dcfe81635e5d75d Mon Sep 17 00:00:00 2001 From: Vincent Riquer Date: Sun, 7 Apr 2013 01:27:07 +0200 Subject: [PATCH 1/6] move eisting functions to separate files --- atom | 1604 +-------------------------- lib/config/getConfig | 27 + lib/config/getConfigDestination | 170 +++ lib/config/getConfigGeneral | 35 + lib/config/getConfigSource | 10 + lib/copy/checkCopy | 18 + lib/copy/copyFile | 28 + lib/database/Delete | 23 + lib/database/Insert | 37 + lib/database/InsertIfUnset | 42 + lib/database/InsertOrUpdate | 66 ++ lib/database/Select | 35 + lib/database/Update | 62 ++ lib/database/closeDatabase | 9 + lib/database/openDatabase | 27 + lib/decode/decodeFile | 48 + lib/decode/decodeMpcdec | 4 + lib/decode/decodeOpusdec | 4 + lib/decode/decodeSox | 30 + lib/destinations/createDestinations | 16 + lib/destinations/updateMimes | 31 + lib/encode/encodeFile::mp3 | 62 ++ lib/encode/encodeFile::opus | 38 + lib/encode/encodeFile::vorbis | 34 + lib/files/getDestDir | 34 + lib/files/getDestFile | 18 + lib/files/getFiles | 63 ++ lib/files/removeObsoleteFiles | 5 + lib/files/sanitizeFile | 23 + lib/tags/getInfos::APE | 54 + lib/tags/getInfos::FLAC | 44 + lib/tags/getInfos::MP3 | 28 + lib/tags/getInfos::Ogg | 29 + lib/tags/getInfos::Opus | 29 + lib/tags/getRateChannelMPC | 15 + lib/tags/getRateChannelSoxi | 4 + lib/tags/getTags | 41 + lib/tags/gettag | 4 + lib/tags/tryAPE | 5 + lib/tasks/gettaskinfos | 26 + lib/tools/progressSpin | 9 + lib/workers/checkworkers | 19 + lib/workers/cleaner | 64 ++ lib/workers/createworker | 4 + lib/workers/destroyworker | 5 + lib/workers/getworkerid | 14 + lib/workers/master | 153 +++ lib/workers/worker | 14 + 48 files changed, 1568 insertions(+), 1596 deletions(-) create mode 100644 lib/config/getConfig create mode 100644 lib/config/getConfigDestination create mode 100644 lib/config/getConfigGeneral create mode 100644 lib/config/getConfigSource create mode 100644 lib/copy/checkCopy create mode 100644 lib/copy/copyFile create mode 100644 lib/database/Delete create mode 100644 lib/database/Insert create mode 100644 lib/database/InsertIfUnset create mode 100644 lib/database/InsertOrUpdate create mode 100644 lib/database/Select create mode 100644 lib/database/Update create mode 100644 lib/database/closeDatabase create mode 100644 lib/database/openDatabase create mode 100644 lib/decode/decodeFile create mode 100644 lib/decode/decodeMpcdec create mode 100644 lib/decode/decodeOpusdec create mode 100644 lib/decode/decodeSox create mode 100644 lib/destinations/createDestinations create mode 100644 lib/destinations/updateMimes create mode 100644 lib/encode/encodeFile::mp3 create mode 100644 lib/encode/encodeFile::opus create mode 100644 lib/encode/encodeFile::vorbis create mode 100644 lib/files/getDestDir create mode 100644 lib/files/getDestFile create mode 100644 lib/files/getFiles create mode 100644 lib/files/removeObsoleteFiles create mode 100644 lib/files/sanitizeFile create mode 100644 lib/tags/getInfos::APE create mode 100644 lib/tags/getInfos::FLAC create mode 100644 lib/tags/getInfos::MP3 create mode 100644 lib/tags/getInfos::Ogg create mode 100644 lib/tags/getInfos::Opus create mode 100644 lib/tags/getRateChannelMPC create mode 100644 lib/tags/getRateChannelSoxi create mode 100644 lib/tags/getTags create mode 100644 lib/tags/gettag create mode 100644 lib/tags/tryAPE create mode 100644 lib/tasks/gettaskinfos create mode 100644 lib/tools/progressSpin create mode 100644 lib/workers/checkworkers create mode 100644 lib/workers/cleaner create mode 100644 lib/workers/createworker create mode 100644 lib/workers/destroyworker create mode 100644 lib/workers/getworkerid create mode 100644 lib/workers/master create mode 100644 lib/workers/worker diff --git a/atom b/atom index 6c9ae7b..fe2b3e2 100755 --- a/atom +++ b/atom @@ -35,6 +35,7 @@ declare -A \ declare -r \ DOCDIR=./doc \ + LIBDIR=./lib \ SHAREDIR=./share declare -r \ exampleconf=$DOCDIR/example.cfg \ @@ -44,9 +45,14 @@ declare -r \ LC_ALL=C +shopt -s extglob + source $SHAREDIR/id3genres -shopt -s extglob +for function in "$LIBDIR"/*/* +do + source "$function" +done #parse arguments OPTERR=0 @@ -85,1601 +91,7 @@ do 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" - ;; - 'skip') - skippeddirectories+=( "$value" ) - ;; - esac -} - -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 -} -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 - 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 -} - -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+="," - 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" - } -} -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 -} -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 -} - -progressSpin() { - case $(( ++count % 40 )) in - 0) echo -ne '\b|' ;; - 10) echo -ne '\b/' ;; - 20) echo -en '\b-' ;; - 30) echo -ne '\b\\' ;; - *) ;; - esac -} -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 -} - -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 -} - -getRateChannelSoxi() { - rate=$(soxi -r "$sourcepath/$filename" 2>/dev/null) - channels=$(soxi -c "$sourcepath/$filename" 2>/dev/null) -} - -getRateChannelMPC() { - while read key value garbage - do - case $key in - 'samplerate:') - rate=$value - ;; - 'channels:') - channels=$value - ;; - esac - done < <( - mpcdec "$sourcepath/$filename" -i 2>&1 - ) -} - -gettag() { - echo -e "$infos" \ - | sed -n "/^${1}=/I{s/^${1}=//I;p;q}" -} - -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} -} - -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%%,*} -} - -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%%.*} -} - -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" - ) -} - -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" -} - -tryAPE() { - grep -q 'APETAGEX' \ - "$sourcepath/$filename" \ - && type=APE -} - -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 -} - -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} - )) - ) - ) -} - -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") -} - -decodeMpcdec() { - tmpfile="${fileid}mpcdec" - commandline=(mpcdec "$sourcepath/$filename" "$tempdir/$tmpfile.wav") -} - -decodeOpusdec() { - tmpfile="${fileid}opusdec" - commandline=(opusdec "$sourcepath/$filename" "$tempdir/$tmpfile.wav") -} - -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 -} - -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 )) -} - -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//\"/ } - - # Filenames can't begin or end with ' ' - string=${string/#+( )/} - string=${string/%+( )/} - fi - echo "$string" -} - -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 -} - -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") -} - -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 -} - -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 -} - -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 -} - -worker() { - exec 2>>"$tempdir/worker$1.log" - (( debug >= 2 )) && echo "${cmd_arg[@]}" >&2 - "${cmd_arg[@]}" >/dev/null -} - -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 -} - -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-- )) -} - -createworker() { - worker $1 & - workers[$1]=$! -} - -destroyworker() { - dyingworker=${workers[$1]} - unset workers[$1] - wait $dyingworker -} - -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#*|} -} - -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 -} - -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 -} - -#UI +#FIXME: check sanity if [ ! -f ~/.atom/atom.cfg ] then diff --git a/lib/config/getConfig b/lib/config/getConfig new file mode 100644 index 0000000..6c14e36 --- /dev/null +++ b/lib/config/getConfig @@ -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 +} diff --git a/lib/config/getConfigDestination b/lib/config/getConfigDestination new file mode 100644 index 0000000..6195ed5 --- /dev/null +++ b/lib/config/getConfigDestination @@ -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 +} diff --git a/lib/config/getConfigGeneral b/lib/config/getConfigGeneral new file mode 100644 index 0000000..125b7b6 --- /dev/null +++ b/lib/config/getConfigGeneral @@ -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 +} diff --git a/lib/config/getConfigSource b/lib/config/getConfigSource new file mode 100644 index 0000000..9df19c3 --- /dev/null +++ b/lib/config/getConfigSource @@ -0,0 +1,10 @@ +getConfigSource() { + case "$key" in + 'path') + sourcepath="$value" + ;; + 'skip') + skippeddirectories+=( "$value" ) + ;; + esac +} diff --git a/lib/copy/checkCopy b/lib/copy/checkCopy new file mode 100644 index 0000000..7aa76d6 --- /dev/null +++ b/lib/copy/checkCopy @@ -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} + )) + ) + ) +} diff --git a/lib/copy/copyFile b/lib/copy/copyFile new file mode 100644 index 0000000..9e7b784 --- /dev/null +++ b/lib/copy/copyFile @@ -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 )) +} diff --git a/lib/database/Delete b/lib/database/Delete new file mode 100644 index 0000000..2cd5718 --- /dev/null +++ b/lib/database/Delete @@ -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 +} diff --git a/lib/database/Insert b/lib/database/Insert new file mode 100644 index 0000000..1e0338b --- /dev/null +++ b/lib/database/Insert @@ -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" + } +} diff --git a/lib/database/InsertIfUnset b/lib/database/InsertIfUnset new file mode 100644 index 0000000..c81ce33 --- /dev/null +++ b/lib/database/InsertIfUnset @@ -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" +} diff --git a/lib/database/InsertOrUpdate b/lib/database/InsertOrUpdate new file mode 100644 index 0000000..2fdf7c2 --- /dev/null +++ b/lib/database/InsertOrUpdate @@ -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" +} diff --git a/lib/database/Select b/lib/database/Select new file mode 100644 index 0000000..4a0ca7e --- /dev/null +++ b/lib/database/Select @@ -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 +} diff --git a/lib/database/Update b/lib/database/Update new file mode 100644 index 0000000..2e5f1c5 --- /dev/null +++ b/lib/database/Update @@ -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 +} diff --git a/lib/database/closeDatabase b/lib/database/closeDatabase new file mode 100644 index 0000000..91dccad --- /dev/null +++ b/lib/database/closeDatabase @@ -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} +} diff --git a/lib/database/openDatabase b/lib/database/openDatabase new file mode 100644 index 0000000..1b4f970 --- /dev/null +++ b/lib/database/openDatabase @@ -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 +} diff --git a/lib/decode/decodeFile b/lib/decode/decodeFile new file mode 100644 index 0000000..fb6591b --- /dev/null +++ b/lib/decode/decodeFile @@ -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 +} diff --git a/lib/decode/decodeMpcdec b/lib/decode/decodeMpcdec new file mode 100644 index 0000000..e1ee72c --- /dev/null +++ b/lib/decode/decodeMpcdec @@ -0,0 +1,4 @@ +decodeMpcdec() { + tmpfile="${fileid}mpcdec" + commandline=(mpcdec "$sourcepath/$filename" "$tempdir/$tmpfile.wav") +} diff --git a/lib/decode/decodeOpusdec b/lib/decode/decodeOpusdec new file mode 100644 index 0000000..4721b89 --- /dev/null +++ b/lib/decode/decodeOpusdec @@ -0,0 +1,4 @@ +decodeOpusdec() { + tmpfile="${fileid}opusdec" + commandline=(opusdec "$sourcepath/$filename" "$tempdir/$tmpfile.wav") +} diff --git a/lib/decode/decodeSox b/lib/decode/decodeSox new file mode 100644 index 0000000..84c29c6 --- /dev/null +++ b/lib/decode/decodeSox @@ -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") +} diff --git a/lib/destinations/createDestinations b/lib/destinations/createDestinations new file mode 100644 index 0000000..f2c2d51 --- /dev/null +++ b/lib/destinations/createDestinations @@ -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 +} diff --git a/lib/destinations/updateMimes b/lib/destinations/updateMimes new file mode 100644 index 0000000..8fc433d --- /dev/null +++ b/lib/destinations/updateMimes @@ -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 +} diff --git a/lib/encode/encodeFile::mp3 b/lib/encode/encodeFile::mp3 new file mode 100644 index 0000000..b49bd05 --- /dev/null +++ b/lib/encode/encodeFile::mp3 @@ -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 +} diff --git a/lib/encode/encodeFile::opus b/lib/encode/encodeFile::opus new file mode 100644 index 0000000..50d8a1c --- /dev/null +++ b/lib/encode/encodeFile::opus @@ -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 +} diff --git a/lib/encode/encodeFile::vorbis b/lib/encode/encodeFile::vorbis new file mode 100644 index 0000000..7ff4acd --- /dev/null +++ b/lib/encode/encodeFile::vorbis @@ -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 +} diff --git a/lib/files/getDestDir b/lib/files/getDestDir new file mode 100644 index 0000000..4f0baae --- /dev/null +++ b/lib/files/getDestDir @@ -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 +} diff --git a/lib/files/getDestFile b/lib/files/getDestFile new file mode 100644 index 0000000..14f4874 --- /dev/null +++ b/lib/files/getDestFile @@ -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") +} diff --git a/lib/files/getFiles b/lib/files/getFiles new file mode 100644 index 0000000..977bc4a --- /dev/null +++ b/lib/files/getFiles @@ -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 +} diff --git a/lib/files/removeObsoleteFiles b/lib/files/removeObsoleteFiles new file mode 100644 index 0000000..6cf0f54 --- /dev/null +++ b/lib/files/removeObsoleteFiles @@ -0,0 +1,5 @@ +removeObsoleteFiles() { + Delete source_files <<-EOWhere + last_seen < $scantime + EOWhere +} diff --git a/lib/files/sanitizeFile b/lib/files/sanitizeFile new file mode 100644 index 0000000..90a3ebc --- /dev/null +++ b/lib/files/sanitizeFile @@ -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//\"/ } + + # Filenames can't begin or end with ' ' + string=${string/#+( )/} + string=${string/%+( )/} + fi + echo "$string" +} diff --git a/lib/tags/getInfos::APE b/lib/tags/getInfos::APE new file mode 100644 index 0000000..181c668 --- /dev/null +++ b/lib/tags/getInfos::APE @@ -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" +} diff --git a/lib/tags/getInfos::FLAC b/lib/tags/getInfos::FLAC new file mode 100644 index 0000000..04533fa --- /dev/null +++ b/lib/tags/getInfos::FLAC @@ -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" + ) +} diff --git a/lib/tags/getInfos::MP3 b/lib/tags/getInfos::MP3 new file mode 100644 index 0000000..62290dc --- /dev/null +++ b/lib/tags/getInfos::MP3 @@ -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} +} diff --git a/lib/tags/getInfos::Ogg b/lib/tags/getInfos::Ogg new file mode 100644 index 0000000..175fe00 --- /dev/null +++ b/lib/tags/getInfos::Ogg @@ -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%%,*} +} diff --git a/lib/tags/getInfos::Opus b/lib/tags/getInfos::Opus new file mode 100644 index 0000000..61198ba --- /dev/null +++ b/lib/tags/getInfos::Opus @@ -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%%.*} +} diff --git a/lib/tags/getRateChannelMPC b/lib/tags/getRateChannelMPC new file mode 100644 index 0000000..05fdbc9 --- /dev/null +++ b/lib/tags/getRateChannelMPC @@ -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 + ) +} diff --git a/lib/tags/getRateChannelSoxi b/lib/tags/getRateChannelSoxi new file mode 100644 index 0000000..a8613cb --- /dev/null +++ b/lib/tags/getRateChannelSoxi @@ -0,0 +1,4 @@ +getRateChannelSoxi() { + rate=$(soxi -r "$sourcepath/$filename" 2>/dev/null) + channels=$(soxi -c "$sourcepath/$filename" 2>/dev/null) +} diff --git a/lib/tags/getTags b/lib/tags/getTags new file mode 100644 index 0000000..dbc08e3 --- /dev/null +++ b/lib/tags/getTags @@ -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 +} diff --git a/lib/tags/gettag b/lib/tags/gettag new file mode 100644 index 0000000..d6f6a2d --- /dev/null +++ b/lib/tags/gettag @@ -0,0 +1,4 @@ +gettag() { + echo -e "$infos" \ + | sed -n "/^${1}=/I{s/^${1}=//I;p;q}" +} diff --git a/lib/tags/tryAPE b/lib/tags/tryAPE new file mode 100644 index 0000000..3cf2db2 --- /dev/null +++ b/lib/tags/tryAPE @@ -0,0 +1,5 @@ +tryAPE() { + grep -q 'APETAGEX' \ + "$sourcepath/$filename" \ + && type=APE +} diff --git a/lib/tasks/gettaskinfos b/lib/tasks/gettaskinfos new file mode 100644 index 0000000..a92fe91 --- /dev/null +++ b/lib/tasks/gettaskinfos @@ -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#*|} +} diff --git a/lib/tools/progressSpin b/lib/tools/progressSpin new file mode 100644 index 0000000..ba41f8e --- /dev/null +++ b/lib/tools/progressSpin @@ -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 +} diff --git a/lib/workers/checkworkers b/lib/workers/checkworkers new file mode 100644 index 0000000..f8b711e --- /dev/null +++ b/lib/workers/checkworkers @@ -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 +} diff --git a/lib/workers/cleaner b/lib/workers/cleaner new file mode 100644 index 0000000..c1bc581 --- /dev/null +++ b/lib/workers/cleaner @@ -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 +} diff --git a/lib/workers/createworker b/lib/workers/createworker new file mode 100644 index 0000000..a2b955a --- /dev/null +++ b/lib/workers/createworker @@ -0,0 +1,4 @@ +createworker() { + worker $1 & + workers[$1]=$! +} diff --git a/lib/workers/destroyworker b/lib/workers/destroyworker new file mode 100644 index 0000000..707c574 --- /dev/null +++ b/lib/workers/destroyworker @@ -0,0 +1,5 @@ +destroyworker() { + dyingworker=${workers[$1]} + unset workers[$1] + wait $dyingworker +} diff --git a/lib/workers/getworkerid b/lib/workers/getworkerid new file mode 100644 index 0000000..0d7ddc3 --- /dev/null +++ b/lib/workers/getworkerid @@ -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-- )) +} diff --git a/lib/workers/master b/lib/workers/master new file mode 100644 index 0000000..518dc72 --- /dev/null +++ b/lib/workers/master @@ -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 +} diff --git a/lib/workers/worker b/lib/workers/worker new file mode 100644 index 0000000..4dd40b5 --- /dev/null +++ b/lib/workers/worker @@ -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 +} From 0f52325ac08aa259b5254fb66748833c0a21cfcc Mon Sep 17 00:00:00 2001 From: Vincent Riquer Date: Mon, 8 Apr 2013 01:54:16 +0200 Subject: [PATCH 2/6] move files copy --- atom | 74 +---------------------- lib/copy/copyFiles_action | 73 ++++++++++++++++++++++ lib/copy/{copyFile => copyFiles_matching} | 2 +- 3 files changed, 76 insertions(+), 73 deletions(-) create mode 100644 lib/copy/copyFiles_action rename lib/copy/{copyFile => copyFiles_matching} (97%) diff --git a/atom b/atom index 4023d92..d3c0dd2 100755 --- a/atom +++ b/atom @@ -518,7 +518,7 @@ do getDestFile if (( copied )) then - copyFile + copyFiles_matching else encodeFile::${destinationformat[$destination]} fi @@ -790,77 +790,7 @@ do unset count done -echo ' - SELECT - source_files.filename, - source_files.last_change, - destinations.id, - destination_files.id - FROM source_files - INNER JOIN destination_files - ON source_files.id - = destination_files.source_file_id - INNER JOIN destinations - ON destination_files.destination_id=destinations.id - INNER JOIN mime_type_actions - ON mime_type_actions.id = source_files.mime_type - WHERE CAST(destination_files.last_change AS TEXT) - <> CAST(source_files.last_change AS TEXT) - AND mime_type_actions.destination_id = destinations.id - AND mime_type_actions.action = 2; - - SELECT "AtOM:NoMoreFiles";' >&3 -read -u4 line -while ! [[ $line = AtOM:NoMoreFiles ]] -do - copyfiles+=("$line") - read -u4 line -done - -for copyfile in "${copyfiles[@]}" -do - sourcefilename=${copyfile%%|*} - rest="${copyfile#*|}|" - lastchange=${rest%%|*} - rest=${rest#*|} - destinationid=${rest%%|*} - rest=${rest#*|} - destfileid=${rest%%|*} - rest=${rest#*|} - echo 'SELECT IFNULL( ( - SELECT destination_files.filename - FROM destination_files - INNER JOIN source_files - ON destination_files.source_file_id=source_files.id - INNER JOIN mime_type_actions - ON - mime_type_actions.id=source_files.mime_type - INNER JOIN destinations - ON destinations.id=destination_files.destination_id - WHERE destinations.id = '$destinationid' - AND source_files.filename LIKE "'"${sourcefilename%/*}"'/%" - AND mime_type_actions.action = 1 - LIMIT 1 - ),"AtOM:NotFound"); - '>&3 - read -u4 filename - if [[ $filename != AtOM:NotFound ]] - then - destdir=${filename%/*} - if cp -al "$sourcepath/$sourcefilename" "$destdir" 2>/dev/null\ - || cp -a "$sourcepath/$sourcefilename" "$destdir" - then - Update destination_files \ - filename "$destdir/${sourcefilename##*/}"\ - rename_pattern "${destinationrenamepath[$destination]}/${destinationrename[$destination]}:${destinationfat32compat["$destination"]}"\ - <<-EOWhere - id = $destfileid - last_change = $lastchange - EOWhere - progressSpin - fi - fi -done +copyFiles_action echo ' SELECT id, diff --git a/lib/copy/copyFiles_action b/lib/copy/copyFiles_action new file mode 100644 index 0000000..5124b42 --- /dev/null +++ b/lib/copy/copyFiles_action @@ -0,0 +1,73 @@ +copyFiles_action() { + echo ' + SELECT + source_files.filename, + source_files.last_change, + destinations.id, + destination_files.id + FROM source_files + INNER JOIN destination_files + ON source_files.id + = destination_files.source_file_id + INNER JOIN destinations + ON destination_files.destination_id=destinations.id + INNER JOIN mime_type_actions + ON mime_type_actions.id = source_files.mime_type + WHERE CAST(destination_files.last_change AS TEXT) + <> CAST(source_files.last_change AS TEXT) + AND mime_type_actions.destination_id = destinations.id + AND mime_type_actions.action = 2; + + SELECT "AtOM:NoMoreFiles";' >&3 + read -u4 line + while ! [[ $line = AtOM:NoMoreFiles ]] + do + copyfiles+=("$line") + read -u4 line + done + + for copyfile in "${copyfiles[@]}" + do + sourcefilename=${copyfile%%|*} + rest="${copyfile#*|}|" + lastchange=${rest%%|*} + rest=${rest#*|} + destinationid=${rest%%|*} + rest=${rest#*|} + destfileid=${rest%%|*} + rest=${rest#*|} + echo 'SELECT IFNULL( ( + SELECT destination_files.filename + FROM destination_files + INNER JOIN source_files + ON destination_files.source_file_id=source_files.id + INNER JOIN mime_type_actions + ON + mime_type_actions.id=source_files.mime_type + INNER JOIN destinations + ON destinations.id=destination_files.destination_id + WHERE destinations.id = '$destinationid' + AND source_files.filename LIKE "'"${sourcefilename%/*}"'/%" + AND mime_type_actions.action = 1 + LIMIT 1 + ),"AtOM:NotFound"); + '>&3 + read -u4 filename + if [[ $filename != AtOM:NotFound ]] + then + destdir=${filename%/*} + if cp -al "$sourcepath/$sourcefilename" "$destdir" 2>/dev/null\ + || cp -a "$sourcepath/$sourcefilename" "$destdir" + then + Update destination_files \ + filename "$destdir/${sourcefilename##*/}"\ + rename_pattern "${destinationrenamepath[$destination]}/${destinationrename[$destination]}:${destinationfat32compat["$destination"]}"\ + <<-EOWhere + id = $destfileid + last_change = $lastchange + EOWhere + progressSpin + fi + fi + done +} diff --git a/lib/copy/copyFile b/lib/copy/copyFiles_matching similarity index 97% rename from lib/copy/copyFile rename to lib/copy/copyFiles_matching index 9e7b784..194fc88 100644 --- a/lib/copy/copyFile +++ b/lib/copy/copyFiles_matching @@ -1,4 +1,4 @@ -copyFile() { +copyFiles_matching() { extension="${filename##*.}" cp -al \ "$sourcepath/$filename" \ From 9fe99d98dd3615a4e07ee3d46d7a9d337da108ef Mon Sep 17 00:00:00 2001 From: Vincent Riquer Date: Mon, 8 Apr 2013 02:40:56 +0200 Subject: [PATCH 3/6] print config --- atom | 39 ++++++------------------------------ lib/config/print | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 33 deletions(-) create mode 100644 lib/config/print diff --git a/atom b/atom index d3c0dd2..ce5ceb6 100755 --- a/atom +++ b/atom @@ -56,12 +56,15 @@ done #parse arguments OPTERR=0 -while getopts ':c:l:T:F:hD' opt +while getopts ':c:Cl:T:F:hD' opt do case $opt in c) cffile="$OPTARG" ;; + C) + cfgdump=1 + ;; l) cliload="$OPTARG" ;; @@ -112,38 +115,8 @@ fi getConfig set +H -if (( debug )) -then - cat <<-EOF - General|Load|$maxload - |Load Interval|$loadinterval - |Temp Dir|$tempdir - |Database|$database - |Debug|$debug - Source|Path|$sourcepath - EOF - for prune_expression in "${skippeddirectories[@]}" - do - echo "|Skipped directory|$prune_expression" - done - for destination in ${!destinationpath[@]} - do - cat <<-EOF - $destination|Path|${destinationpath[$destination]} - |Format|${destinationformat[$destination]} - |Quality|${destinationquality[$destination]} - |Normalize|${destinationnormalize[$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 -fi |column -t -s'|' -n +(( debug || cfgdump )) && printConfig +(( cfgdump )) && exit openDatabase diff --git a/lib/config/print b/lib/config/print new file mode 100644 index 0000000..f466547 --- /dev/null +++ b/lib/config/print @@ -0,0 +1,51 @@ +printConfig() { + { + cat <<-EOF + General|Load|$maxload + |Load Interval|$loadinterval + |Temp Dir|$tempdir + |Database|$database + |Debug|$debug + Source|Path|$sourcepath + EOF + for prune_expression in "${skippeddirectories[@]}" + do + (( printed )) \ + && echo -n '||' \ + || echo -n '|Skipped directories|' + echo "$prune_expression" + printed=1 + done + unset printed + for destination in ${!destinationpath[@]} + do + cat <<-EOF + $destination|Path|${destinationpath["$destination"]} + |Format|${destinationformat["$destination"]} + |Quality|${destinationquality["$destination"]} + EOF + if [[ ${destinationformat["$destination"]} == opus ]] + then + echo "|Expected loss|${destinationloss["$destination"]}" + elif [[ ${destinationformat["$destination"]} == mp3 ]] + then + echo "|Prevent resampling|${destinationnoresample["$destination"]}" + fi + cat <<-EOF + |Normalize|${destinationnormalize["$destination"]} + |Channels|${destinationchannels["$destination"]} + |Frequency|${destinationfrequency["$destination"]} + |Higher than|${destinationmaxbps["$destination"]} + |Fat32 Compat.|${destinationfat32compat["$destination"]} + |Path Change|${destinationrenamepath["$destination"]} + |File Rename|${destinationrename["$destination"]} + EOF + [ -n "${destinationskipmime["$destination"]}" ] \ + && echo "|Skipped mime-types|${destinationskipmime["$destination"]//\|/ +||}" + [ -n "${destinationmskipime["$destination"]}" ] \ + && echo "|Copied mime-types|${destinationcopymime["$destination"]//\|/ +||}" + done + }|column -t -s'|' -n +} From 1a9758a251618beb3dfca5ec755f980ebb2bcaee Mon Sep 17 00:00:00 2001 From: Vincent Riquer Date: Mon, 8 Apr 2013 02:52:56 +0200 Subject: [PATCH 4/6] add shebang on lib files --- lib/config/getConfig | 1 + lib/config/getConfigDestination | 1 + lib/config/getConfigGeneral | 1 + lib/config/getConfigSource | 1 + lib/config/print | 1 + lib/copy/checkCopy | 1 + lib/copy/copyFiles_action | 1 + lib/copy/copyFiles_matching | 1 + lib/database/Delete | 1 + lib/database/Insert | 1 + lib/database/InsertIfUnset | 1 + lib/database/InsertOrUpdate | 1 + lib/database/Select | 1 + lib/database/Update | 1 + lib/database/closeDatabase | 1 + lib/database/openDatabase | 1 + lib/decode/decodeFile | 1 + lib/decode/decodeMpcdec | 1 + lib/decode/decodeOpusdec | 1 + lib/decode/decodeSox | 1 + lib/destinations/createDestinations | 1 + lib/destinations/updateMimes | 1 + lib/encode/encodeFile::mp3 | 1 + lib/encode/encodeFile::opus | 1 + lib/encode/encodeFile::vorbis | 1 + lib/files/getDestDir | 1 + lib/files/getDestFile | 1 + lib/files/getFiles | 1 + lib/files/removeObsoleteFiles | 1 + lib/files/sanitizeFile | 1 + lib/tags/getInfos::APE | 1 + lib/tags/getInfos::FLAC | 1 + lib/tags/getInfos::MP3 | 1 + lib/tags/getInfos::Ogg | 1 + lib/tags/getInfos::Opus | 1 + lib/tags/getRateChannelMPC | 1 + lib/tags/getRateChannelSoxi | 1 + lib/tags/getTags | 1 + lib/tags/gettag | 1 + lib/tags/tryAPE | 1 + lib/tasks/gettaskinfos | 1 + lib/tools/progressSpin | 1 + lib/workers/checkworkers | 1 + lib/workers/cleaner | 1 + lib/workers/createworker | 1 + lib/workers/destroyworker | 1 + lib/workers/getworkerid | 1 + lib/workers/master | 1 + lib/workers/worker | 1 + 49 files changed, 49 insertions(+) diff --git a/lib/config/getConfig b/lib/config/getConfig index 6c14e36..9243fb1 100644 --- a/lib/config/getConfig +++ b/lib/config/getConfig @@ -1,3 +1,4 @@ +#!/bin/bash getConfig() { while read key value do diff --git a/lib/config/getConfigDestination b/lib/config/getConfigDestination index 6195ed5..a6fa6a8 100644 --- a/lib/config/getConfigDestination +++ b/lib/config/getConfigDestination @@ -1,3 +1,4 @@ +#!/bin/bash getConfigDestination() { case "$key" in 'path') diff --git a/lib/config/getConfigGeneral b/lib/config/getConfigGeneral index 125b7b6..9de2888 100644 --- a/lib/config/getConfigGeneral +++ b/lib/config/getConfigGeneral @@ -1,3 +1,4 @@ +#!/bin/bash getConfigGeneral() { case $key in 'max-load') diff --git a/lib/config/getConfigSource b/lib/config/getConfigSource index 9df19c3..1042969 100644 --- a/lib/config/getConfigSource +++ b/lib/config/getConfigSource @@ -1,3 +1,4 @@ +#!/bin/bash getConfigSource() { case "$key" in 'path') diff --git a/lib/config/print b/lib/config/print index f466547..6722de6 100644 --- a/lib/config/print +++ b/lib/config/print @@ -1,3 +1,4 @@ +#!/bin/bash printConfig() { { cat <<-EOF diff --git a/lib/copy/checkCopy b/lib/copy/checkCopy index 7aa76d6..45cd185 100644 --- a/lib/copy/checkCopy +++ b/lib/copy/checkCopy @@ -1,3 +1,4 @@ +#!/bin/bash checkCopy() { ( [ -z "${destinationfrequency[$destination]}" ] \ diff --git a/lib/copy/copyFiles_action b/lib/copy/copyFiles_action index 5124b42..7482836 100644 --- a/lib/copy/copyFiles_action +++ b/lib/copy/copyFiles_action @@ -1,3 +1,4 @@ +#!/bin/bash copyFiles_action() { echo ' SELECT diff --git a/lib/copy/copyFiles_matching b/lib/copy/copyFiles_matching index 194fc88..7870cfe 100644 --- a/lib/copy/copyFiles_matching +++ b/lib/copy/copyFiles_matching @@ -1,3 +1,4 @@ +#!/bin/bash copyFiles_matching() { extension="${filename##*.}" cp -al \ diff --git a/lib/database/Delete b/lib/database/Delete index 2cd5718..01933ce 100644 --- a/lib/database/Delete +++ b/lib/database/Delete @@ -1,3 +1,4 @@ +#!/bin/bash Delete() { #Delete table < where_key where_operator where_value # [where_key where_operator where_value diff --git a/lib/database/Insert b/lib/database/Insert index 1e0338b..77f7ec2 100644 --- a/lib/database/Insert +++ b/lib/database/Insert @@ -1,3 +1,4 @@ +#!/bin/bash Insert() { #Insert table [no_id] < key value # [key value diff --git a/lib/database/InsertIfUnset b/lib/database/InsertIfUnset index c81ce33..4277a62 100644 --- a/lib/database/InsertIfUnset +++ b/lib/database/InsertIfUnset @@ -1,3 +1,4 @@ +#!/bin/bash InsertIfUnset() { #InsertIfUnset table [no_id] < key value \n key value local \ diff --git a/lib/database/InsertOrUpdate b/lib/database/InsertOrUpdate index 2fdf7c2..4f25110 100644 --- a/lib/database/InsertOrUpdate +++ b/lib/database/InsertOrUpdate @@ -1,3 +1,4 @@ +#!/bin/bash InsertOrUpdate() { #InsertOrUpdate table set_key set_value [set_key set_value […]] < where_key where_value # [where_key where_value diff --git a/lib/database/Select b/lib/database/Select index 4a0ca7e..b096953 100644 --- a/lib/database/Select +++ b/lib/database/Select @@ -1,3 +1,4 @@ +#!/bin/bash Select() { #Select table [col1 [col2 [..]]] < WHERE_key WHERE_operator WHERE_value # [WHERE_key WHERE_operator WHERE_value diff --git a/lib/database/Update b/lib/database/Update index 2e5f1c5..0fe5db5 100644 --- a/lib/database/Update +++ b/lib/database/Update @@ -1,3 +1,4 @@ +#!/bin/bash Update() { #Update table set_key set_value [set_key set_value […]] < where_key where_operator where_value # [where_key where_operator where_value diff --git a/lib/database/closeDatabase b/lib/database/closeDatabase index 91dccad..2d6e1a2 100644 --- a/lib/database/closeDatabase +++ b/lib/database/closeDatabase @@ -1,3 +1,4 @@ +#!/bin/bash closeDatabase() { echo .quit >&3 (( debug )) && echo -n "Waiting for SQLite to terminate... " diff --git a/lib/database/openDatabase b/lib/database/openDatabase index 1b4f970..74f4745 100644 --- a/lib/database/openDatabase +++ b/lib/database/openDatabase @@ -1,3 +1,4 @@ +#!/bin/bash openDatabase() { if [ ! -d "$tempdir" ] then diff --git a/lib/decode/decodeFile b/lib/decode/decodeFile index fb6591b..b66c6ef 100644 --- a/lib/decode/decodeFile +++ b/lib/decode/decodeFile @@ -1,3 +1,4 @@ +#!/bin/bash decodeFile() { if ! decodetaskid=$( Select tasks id <<<"key = $tmpfile" diff --git a/lib/decode/decodeMpcdec b/lib/decode/decodeMpcdec index e1ee72c..fd92a4f 100644 --- a/lib/decode/decodeMpcdec +++ b/lib/decode/decodeMpcdec @@ -1,3 +1,4 @@ +#!/bin/bash decodeMpcdec() { tmpfile="${fileid}mpcdec" commandline=(mpcdec "$sourcepath/$filename" "$tempdir/$tmpfile.wav") diff --git a/lib/decode/decodeOpusdec b/lib/decode/decodeOpusdec index 4721b89..9f9d0ef 100644 --- a/lib/decode/decodeOpusdec +++ b/lib/decode/decodeOpusdec @@ -1,3 +1,4 @@ +#!/bin/bash decodeOpusdec() { tmpfile="${fileid}opusdec" commandline=(opusdec "$sourcepath/$filename" "$tempdir/$tmpfile.wav") diff --git a/lib/decode/decodeSox b/lib/decode/decodeSox index 84c29c6..69f8053 100644 --- a/lib/decode/decodeSox +++ b/lib/decode/decodeSox @@ -1,3 +1,4 @@ +#!/bin/bash decodeSox() { commandline=(sox --single-threaded --temp "$tempdir") soxoptions_in='' diff --git a/lib/destinations/createDestinations b/lib/destinations/createDestinations index f2c2d51..fc6e3bc 100644 --- a/lib/destinations/createDestinations +++ b/lib/destinations/createDestinations @@ -1,3 +1,4 @@ +#!/bin/bash createDestinations() { for destination in ${!destinationpath[@]} do diff --git a/lib/destinations/updateMimes b/lib/destinations/updateMimes index 8fc433d..189572b 100644 --- a/lib/destinations/updateMimes +++ b/lib/destinations/updateMimes @@ -1,3 +1,4 @@ +#!/bin/bash updateMimes() { Update mime_actions action 1 <<<"action != 1" for destination in ${!destinationskipmime[@]} diff --git a/lib/encode/encodeFile::mp3 b/lib/encode/encodeFile::mp3 index b49bd05..34aa133 100644 --- a/lib/encode/encodeFile::mp3 +++ b/lib/encode/encodeFile::mp3 @@ -1,3 +1,4 @@ +#!/bin/bash encodeFile::mp3() { lameopts=(lame --quiet -v --abr ${destinationquality[$destination]}) [ -n "$album" ] && lameopts+=(--tl "$album" ) diff --git a/lib/encode/encodeFile::opus b/lib/encode/encodeFile::opus index 50d8a1c..bc7d9f3 100644 --- a/lib/encode/encodeFile::opus +++ b/lib/encode/encodeFile::opus @@ -1,3 +1,4 @@ +#!/bin/bash encodeFile::opus() { opusencopts=(opusenc --music --quiet) opusencopts+=(--bitrate ${destinationquality[$destination]}) diff --git a/lib/encode/encodeFile::vorbis b/lib/encode/encodeFile::vorbis index 7ff4acd..e3d87d4 100644 --- a/lib/encode/encodeFile::vorbis +++ b/lib/encode/encodeFile::vorbis @@ -1,3 +1,4 @@ +#!/bin/bash encodeFile::vorbis() { oggencopts=(oggenc -Q -q ${destinationquality[$destination]}) [ -n "$albumartist" ] && oggencopts+=(-c "ALBUMARTIST=$albumartist") diff --git a/lib/files/getDestDir b/lib/files/getDestDir index 4f0baae..d433cb3 100644 --- a/lib/files/getDestDir +++ b/lib/files/getDestDir @@ -1,3 +1,4 @@ +#!/bin/bash getDestDir() { destdir="${destinationpath[$destination]}/" if [ -n "${destinationrenamepath[$destination]}" ] diff --git a/lib/files/getDestFile b/lib/files/getDestFile index 14f4874..7730b48 100644 --- a/lib/files/getDestFile +++ b/lib/files/getDestFile @@ -1,3 +1,4 @@ +#!/bin/bash getDestFile() { if [ -n "${destinationrename[$destination]}" ] then diff --git a/lib/files/getFiles b/lib/files/getFiles index 977bc4a..1b53e6f 100644 --- a/lib/files/getFiles +++ b/lib/files/getFiles @@ -1,3 +1,4 @@ +#!/bin/bash getFiles() { scantime=$(date +%s) for prune_expression in "${skippeddirectories[@]}" diff --git a/lib/files/removeObsoleteFiles b/lib/files/removeObsoleteFiles index 6cf0f54..69bd04b 100644 --- a/lib/files/removeObsoleteFiles +++ b/lib/files/removeObsoleteFiles @@ -1,3 +1,4 @@ +#!/bin/bash removeObsoleteFiles() { Delete source_files <<-EOWhere last_seen < $scantime diff --git a/lib/files/sanitizeFile b/lib/files/sanitizeFile index 90a3ebc..34bf8e3 100644 --- a/lib/files/sanitizeFile +++ b/lib/files/sanitizeFile @@ -1,3 +1,4 @@ +#!/bin/bash sanitizeFile() { shopt -s extglob string="$1" diff --git a/lib/tags/getInfos::APE b/lib/tags/getInfos::APE index 181c668..66f2a29 100644 --- a/lib/tags/getInfos::APE +++ b/lib/tags/getInfos::APE @@ -1,3 +1,4 @@ +#!/bin/bash getInfosAPE_version='APE-1' tagreaders+=( "$getInfosAPE_version" ) getInfos::APE() { diff --git a/lib/tags/getInfos::FLAC b/lib/tags/getInfos::FLAC index 04533fa..288eac7 100644 --- a/lib/tags/getInfos::FLAC +++ b/lib/tags/getInfos::FLAC @@ -1,3 +1,4 @@ +#!/bin/bash getInfosFLAC_version='FLAC-1' tagreaders+=( "$getInfosFLAC_version" ) getInfos::FLAC() { diff --git a/lib/tags/getInfos::MP3 b/lib/tags/getInfos::MP3 index 62290dc..c98de74 100644 --- a/lib/tags/getInfos::MP3 +++ b/lib/tags/getInfos::MP3 @@ -1,3 +1,4 @@ +#!/bin/bash getInfosMP3_version='ID3-2' tagreaders+=( "$getInfosMP3_version" ) getInfos::MP3() { diff --git a/lib/tags/getInfos::Ogg b/lib/tags/getInfos::Ogg index 175fe00..c376496 100644 --- a/lib/tags/getInfos::Ogg +++ b/lib/tags/getInfos::Ogg @@ -1,3 +1,4 @@ +#!/bin/bash getInfosOgg_version='Ogg-1' tagreaders+=( "$getInfosOgg_version" ) getInfos::Ogg() { diff --git a/lib/tags/getInfos::Opus b/lib/tags/getInfos::Opus index 61198ba..2f99289 100644 --- a/lib/tags/getInfos::Opus +++ b/lib/tags/getInfos::Opus @@ -1,3 +1,4 @@ +#!/bin/bash getInfosOpus_version='Opus-1' tagreaders+=( "$getInfosOpus_version" ) getInfos::Opus() { diff --git a/lib/tags/getRateChannelMPC b/lib/tags/getRateChannelMPC index 05fdbc9..14b28ba 100644 --- a/lib/tags/getRateChannelMPC +++ b/lib/tags/getRateChannelMPC @@ -1,3 +1,4 @@ +#!/bin/bash getRateChannelMPC() { while read key value garbage do diff --git a/lib/tags/getRateChannelSoxi b/lib/tags/getRateChannelSoxi index a8613cb..91db5fe 100644 --- a/lib/tags/getRateChannelSoxi +++ b/lib/tags/getRateChannelSoxi @@ -1,3 +1,4 @@ +#!/bin/bash getRateChannelSoxi() { rate=$(soxi -r "$sourcepath/$filename" 2>/dev/null) channels=$(soxi -c "$sourcepath/$filename" 2>/dev/null) diff --git a/lib/tags/getTags b/lib/tags/getTags index dbc08e3..6a735df 100644 --- a/lib/tags/getTags +++ b/lib/tags/getTags @@ -1,3 +1,4 @@ +#!/bin/bash getTags_version='unknown-2' tagreaders+=( "$getTags_version" ) getTags() { diff --git a/lib/tags/gettag b/lib/tags/gettag index d6f6a2d..b6f5409 100644 --- a/lib/tags/gettag +++ b/lib/tags/gettag @@ -1,3 +1,4 @@ +#!/bin/bash gettag() { echo -e "$infos" \ | sed -n "/^${1}=/I{s/^${1}=//I;p;q}" diff --git a/lib/tags/tryAPE b/lib/tags/tryAPE index 3cf2db2..53f963e 100644 --- a/lib/tags/tryAPE +++ b/lib/tags/tryAPE @@ -1,3 +1,4 @@ +#!/bin/bash tryAPE() { grep -q 'APETAGEX' \ "$sourcepath/$filename" \ diff --git a/lib/tasks/gettaskinfos b/lib/tasks/gettaskinfos index a92fe91..f0023bd 100644 --- a/lib/tasks/gettaskinfos +++ b/lib/tasks/gettaskinfos @@ -1,3 +1,4 @@ +#!/bin/bash gettaskinfos() { echo ' SELECT diff --git a/lib/tools/progressSpin b/lib/tools/progressSpin index ba41f8e..cecff55 100644 --- a/lib/tools/progressSpin +++ b/lib/tools/progressSpin @@ -1,3 +1,4 @@ +#!/bin/bash progressSpin() { case $(( ++count % 40 )) in 0) echo -ne '\b|' ;; diff --git a/lib/workers/checkworkers b/lib/workers/checkworkers index f8b711e..fa52afb 100644 --- a/lib/workers/checkworkers +++ b/lib/workers/checkworkers @@ -1,3 +1,4 @@ +#!/bin/bash checkworkers() { for key in ${!workers[@]} do diff --git a/lib/workers/cleaner b/lib/workers/cleaner index c1bc581..3db4e0a 100644 --- a/lib/workers/cleaner +++ b/lib/workers/cleaner @@ -1,3 +1,4 @@ +#!/bin/bash cleaner() { for key in ${!failedtasks[@]} do diff --git a/lib/workers/createworker b/lib/workers/createworker index a2b955a..16d8e24 100644 --- a/lib/workers/createworker +++ b/lib/workers/createworker @@ -1,3 +1,4 @@ +#!/bin/bash createworker() { worker $1 & workers[$1]=$! diff --git a/lib/workers/destroyworker b/lib/workers/destroyworker index 707c574..3e34426 100644 --- a/lib/workers/destroyworker +++ b/lib/workers/destroyworker @@ -1,3 +1,4 @@ +#!/bin/bash destroyworker() { dyingworker=${workers[$1]} unset workers[$1] diff --git a/lib/workers/getworkerid b/lib/workers/getworkerid index 0d7ddc3..4e6affc 100644 --- a/lib/workers/getworkerid +++ b/lib/workers/getworkerid @@ -1,3 +1,4 @@ +#!/bin/bash getworkerid() { local i for (( i=0 ; i >= 0 ; i++ )) diff --git a/lib/workers/master b/lib/workers/master index 518dc72..4c22abe 100644 --- a/lib/workers/master +++ b/lib/workers/master @@ -1,3 +1,4 @@ +#!/bin/bash master() { if (( active >= concurrency)) || [ -n "$quit" ] then diff --git a/lib/workers/worker b/lib/workers/worker index 4dd40b5..c4b5954 100644 --- a/lib/workers/worker +++ b/lib/workers/worker @@ -1,3 +1,4 @@ +#!/bin/bash worker() { exec 2>>"$tempdir/worker$1.log" (( debug >= 2 )) && echo "${cmd_arg[@]}" >&2 From 37df18f45df5c99111ea1a3c3be345519c3fcc40 Mon Sep 17 00:00:00 2001 From: Vincent Riquer Date: Mon, 8 Apr 2013 02:57:10 +0200 Subject: [PATCH 5/6] implement -c --- atom | 8 +++++--- lib/config/getConfig | 2 +- lib/config/print | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/atom b/atom index ce5ceb6..23d8478 100755 --- a/atom +++ b/atom @@ -43,6 +43,8 @@ declare -r \ \ oldIFS="$IFS" +cffile="$HOME/.atom/atom.cfg" + LC_ALL=C shopt -s extglob @@ -96,16 +98,16 @@ done #FIXME: check sanity -if [ ! -f ~/.atom/atom.cfg ] +if [ ! -f "$cffile" ] then if [ ! -d ~/.atom ] then mkdir -p ~/.atom fi - sed "s:%HOME%:$HOME:" "$exampleconf" > ~/.atom/atom.cfg + sed "s:%HOME%:$HOME:" "$exampleconf" > "$cffile" cat >&2 <<-EOCfgNotice No configuration file found! - An example file has been created as ~/.atom/atom.cfg. + An example file has been created as "${cffile/$HOME/~}". You should change it to your likings using you favorite editor. Bailing out. diff --git a/lib/config/getConfig b/lib/config/getConfig index 9243fb1..cbc9b5f 100644 --- a/lib/config/getConfig +++ b/lib/config/getConfig @@ -24,5 +24,5 @@ getConfig() { getConfig$context ;; esac - done < ~/.atom/atom.cfg + done < "$cffile" } diff --git a/lib/config/print b/lib/config/print index 6722de6..bc189a0 100644 --- a/lib/config/print +++ b/lib/config/print @@ -2,7 +2,8 @@ printConfig() { { cat <<-EOF - General|Load|$maxload + General|Config file|$cffile + |Load|$maxload |Load Interval|$loadinterval |Temp Dir|$tempdir |Database|$database From 3d4a7579175877ec8c838233fd1f507282a67556 Mon Sep 17 00:00:00 2001 From: Vincent Riquer Date: Mon, 8 Apr 2013 11:28:26 +0200 Subject: [PATCH 6/6] ionice --- doc/config | 2 ++ doc/example.cfg | 1 + lib/config/getConfigGeneral | 43 +++++++++++++++++++++++++++++++++++ lib/config/print | 3 ++- lib/decode/decodeMpcdec | 3 ++- lib/decode/decodeOpusdec | 3 ++- lib/decode/decodeSox | 2 +- lib/encode/encodeFile::mp3 | 3 ++- lib/encode/encodeFile::opus | 2 +- lib/encode/encodeFile::vorbis | 2 +- 10 files changed, 57 insertions(+), 7 deletions(-) diff --git a/doc/config b/doc/config index a32eead..4aa2f32 100644 --- a/doc/config +++ b/doc/config @@ -26,6 +26,8 @@ Sections: too quickly. Set this too high, and AtOM will not adapt quickly enough to load increase. In both cases, your hard drive will suffer. In my experience, 30 seconds is a good value. + * ionice [niceness]: IO-hungry processes will be run with ionice class + and niceness [niceness] (if applicable). See man ionice for details. * temporary-directory : String. Name speaks for itself: this is where FIFOs (for communicating with sqlite) and temporary WAVE files will be created. Note that debug logs (if enabled) will go there too. diff --git a/doc/example.cfg b/doc/example.cfg index 1ffdb39..19b2b14 100644 --- a/doc/example.cfg +++ b/doc/example.cfg @@ -1,4 +1,5 @@ [general] +ionice 3 max-load 6 load-interval 30 temporary-directory %HOME%/.atom/tmp diff --git a/lib/config/getConfigGeneral b/lib/config/getConfigGeneral index 9de2888..ec183ab 100644 --- a/lib/config/getConfigGeneral +++ b/lib/config/getConfigGeneral @@ -23,6 +23,49 @@ getConfigGeneral() { fi unset expr ;; + 'ionice') + read class niceness <<<"$value" + case $class in + 1) + # real-time class, only root can do that + if (( UID )) + then + echo "IO class 'realtime' is"\ + "not available to unprivileged"\ + "users" >&2 + exit $EIONICE + fi + if [ -n "$niceness" ] \ + && (( niceness >= 0 && niceness <= 7 )) + then + ionice="ionice -c1 -n$niceness " + else + echo "Invalid IO priority"\ + "'$niceness'" >&2 + exit $EIONICE + fi + ;; + 2) + if [ -n "$niceness" ] \ + && (( niceness >= 0 && niceness <= 7 )) + then + ionice="ionice -c2 -n$niceness " + else + echo "Invalid IO priority"\ + "'$niceness'" >&2 + exit $EIONICE + fi + ;; + 3) + ionice="ionice -c3 " + ;; + *) + echo "Invalid ionice parameters $value"\ + >&2 + exit $EIONICE + ;; + esac + ;; 'temporary-directory') tempdir="$value" ;; diff --git a/lib/config/print b/lib/config/print index bc189a0..0a4fea4 100644 --- a/lib/config/print +++ b/lib/config/print @@ -1,8 +1,9 @@ #!/bin/bash printConfig() { { + echo "General|Config file|$cffile" + [ -n "$ionice" ] && echo "|IO Nice|$ionice" cat <<-EOF - General|Config file|$cffile |Load|$maxload |Load Interval|$loadinterval |Temp Dir|$tempdir diff --git a/lib/decode/decodeMpcdec b/lib/decode/decodeMpcdec index fd92a4f..3bbd71c 100644 --- a/lib/decode/decodeMpcdec +++ b/lib/decode/decodeMpcdec @@ -1,5 +1,6 @@ #!/bin/bash decodeMpcdec() { tmpfile="${fileid}mpcdec" - commandline=(mpcdec "$sourcepath/$filename" "$tempdir/$tmpfile.wav") + commandline=(${ionice}mpcdec) + commandline+=("$sourcepath/$filename" "$tempdir/$tmpfile.wav") } diff --git a/lib/decode/decodeOpusdec b/lib/decode/decodeOpusdec index 9f9d0ef..46f08bd 100644 --- a/lib/decode/decodeOpusdec +++ b/lib/decode/decodeOpusdec @@ -1,5 +1,6 @@ #!/bin/bash decodeOpusdec() { tmpfile="${fileid}opusdec" - commandline=(opusdec "$sourcepath/$filename" "$tempdir/$tmpfile.wav") + commandline=(${ionice}opusdec) + commandline+=("$sourcepath/$filename" "$tempdir/$tmpfile.wav") } diff --git a/lib/decode/decodeSox b/lib/decode/decodeSox index 69f8053..5d7e6df 100644 --- a/lib/decode/decodeSox +++ b/lib/decode/decodeSox @@ -1,6 +1,6 @@ #!/bin/bash decodeSox() { - commandline=(sox --single-threaded --temp "$tempdir") + commandline=(${ionice}sox --single-threaded --temp "$tempdir") soxoptions_in='' soxoptions_out='' if (( ${destinationnormalize["$destination"]} )) diff --git a/lib/encode/encodeFile::mp3 b/lib/encode/encodeFile::mp3 index 34aa133..c958f2a 100644 --- a/lib/encode/encodeFile::mp3 +++ b/lib/encode/encodeFile::mp3 @@ -1,6 +1,7 @@ #!/bin/bash encodeFile::mp3() { - lameopts=(lame --quiet -v --abr ${destinationquality[$destination]}) + lameopts=(${ionice}lame --quiet) + lameopts+=(-v --abr ${destinationquality[$destination]}) [ -n "$album" ] && lameopts+=(--tl "$album" ) [ -n "$artist" ] && lameopts+=(--ta "$artist") [ -n "$genre" ] && lameopts+=(--tg "$genre") diff --git a/lib/encode/encodeFile::opus b/lib/encode/encodeFile::opus index bc7d9f3..2794a05 100644 --- a/lib/encode/encodeFile::opus +++ b/lib/encode/encodeFile::opus @@ -1,6 +1,6 @@ #!/bin/bash encodeFile::opus() { - opusencopts=(opusenc --music --quiet) + opusencopts=(${ionice}opusenc --music --quiet) opusencopts+=(--bitrate ${destinationquality[$destination]}) [ -n "${destinationloss["$destination"]}" ] \ && opusencopts+=(--expect-loss "${destinationloss["$destination"]}") diff --git a/lib/encode/encodeFile::vorbis b/lib/encode/encodeFile::vorbis index e3d87d4..c3a72e3 100644 --- a/lib/encode/encodeFile::vorbis +++ b/lib/encode/encodeFile::vorbis @@ -1,6 +1,6 @@ #!/bin/bash encodeFile::vorbis() { - oggencopts=(oggenc -Q -q ${destinationquality[$destination]}) + oggencopts=(${ionice}oggenc -Q -q ${destinationquality[$destination]}) [ -n "$albumartist" ] && oggencopts+=(-c "ALBUMARTIST=$albumartist") [ -n "$album" ] && oggencopts+=(-l "$album") [ -n "$artist" ] && oggencopts+=(-a "$artist")