Compare commits

..

1 Commits

Author SHA1 Message Date
Vincent Riquer
96d23d9a7d Populate all available worker slots on each main loop run 2025-03-14 02:54:33 +01:00
56 changed files with 554 additions and 1046 deletions

View File

@ -1,62 +1,16 @@
# 1.0.6
### BUGS
* Support for newline character in filenames
* Fix obsolete files deletion
### BUGS (Minor)
* Fix timing information
* Fix level 3+ debug levels (hanging waiting for child processes)
# 1.0.5
### BUGS (Minor)
* `toys/createindex`: handle empty channel count, bitdepth and sampling rate cleanly
### Enhancements
* Allow ignoring microsecond precision in timestamps
* Don't print useless information (stuff that handled 0 files and such)
* Store transcoded file paths relative to their destination's root (db version 7)
* Add missing (used but not declared) error codes
# 1.0.4
## `BREAKING CHANGES`
* ffmpeg parser was fixed. All files previously parsed by it and parsers depending on it will be reprocessed. Running in batches (`-B <batchsize>`) is recommended.
### BUGS
* `ffmpeg` output parsed incorrectly
* `toys/createindex`: incorrect path for ID3v1 tag index file
### Enhancements
* Use bash builtin `printf` instead of command `date`
* Commit to database more frequently when reading tags
# 1.0.3
## `BREAKING CHANGES`
* Implementing replaygain copy meant bumping versions of every tag parser. All file tags will be read again. Running in batches (`-B <batchsize>`) is recommended.
### Bugs
* Fix a number of issues with `releasecountry` tag
### Enhancements
* Spawn tasks faster when slots are available.
* Copy replaygain tags.
* Add `copy_extension` to setup
# 1.0.2 # 1.0.2
### Bugs ## Bugs
* Tag reading hangs on vorbis files with embedded images. * Tag reading hangs on vorbis files with embedded images
* Recreate destination files on releasecountry tag change (bump database schema to 5). * Recreate destination files on releasecountry tag change (bump database schema to 5)
# 1.0.1 # 1.0.1
## `BREAKING CHANGES` ## BREAKING CHANGES
* Implementing releasecountry meant bumping versions of every tag parser. All file tags will be read again. Running in batches (`-B <batchsize>`) is recommended. Implementing releasecountry meant bumping versions of every tag parser. All file tags will be read again. Running in batches (`-B <batchsize>`) is recommended.
### Enhancements ## Bugs
* Copy releasecountry tag ("MusicBrainz Album Release Country" for MP3). Opus tags were not actually parsed due to an issue with the tag reader selector.
### Bugs ## Enhancements
* Opus tags were not actually parsed due to an issue with the tag reader selector.
### Enhancements
* Fetch releasecountry tag from files * Fetch releasecountry tag from files
* Add `%{releasecountry}` placeholder in `rename` * Add `%{releasecountry}` placeholder in `rename`
* Add `-r` (show release country) to toys/createindex * Add `-r` (show release country) to toys/createindex

View File

@ -20,19 +20,19 @@ in the same format, it will want a constant sample-rate and bitrate. You can
have AtOM do that! have AtOM do that!
Here's what I have for my tests: Here's what I have for my tests:
| Directory | Format | Sample rate | Bitrate | Channels | FAT32 compat. | ASCII | Size | | Directory | Format | Frequency | Bitrate | Channels | FAT32 compat. | ASCII | Size |
| --------- | ------ | ----------- | --------- | -------- | ------------- | ----- | ---- | | --------- | ------ | --------- | ------- | -------- | ------------- | ----- | ---- |
| 0-Full | Mixed | Mixed | Mixed | Mixed | No | No | 568G | | 0-Full | Mixed | Mixed | Mixed | Mixed | No | No | 508G |
| 1-High | Vorbis | Same | Quality 5 | Same | Yes | No | 143G | | 1-High | Opus | Same | 128 | Same | Yes | No | 101G |
| 2-Medium | Opus | Same | 64 | Same | Yes | No | 60G | | 2-Medium | Opus | Same | 64 | Same | Yes | No | 59G |
| 3-Small | Opus | Same | 32 | Same | Yes | No | 31G | | 3-Small | Opus | Same | 32 | Same | Yes | No | 30G |
| 4-MP3 | MP3 | 44100 | 128 | 2 | Yes | Yes | 119G | | 4-MP3 | MP3 | 44100 | 128 | 2 | Yes | Yes | 114G |
* URL: https://framagit.org/atom/AtOM/ * URL: https://framagit.org/ScriptFanix/AtOM/
* Author: Vincent Riquer <vincent+prog.atom@riquer.fr> * Author: Vincent Riquer <vincent+prog.atom@riquer.fr>
* Copyright/left: 2012-2013,2015,2025-2026 Vincent Riquer - GPLv3 * Copyright/left: 2012-2013,2015,2025 Vincent Riquer - GPLv3
## Dependencies ## Dependencies
### Required: ### Required:

5
TODO Normal file
View File

@ -0,0 +1,5 @@
Tag Guessing
------------
From a user-defined pattern, guess tags for unsupported formats/untagged files
from the file path and file name.

274
atom
View File

@ -1,17 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
## Define exit codes ## Define exit codes
source "$SHAREDIR"/errorcodes # General config errors [10-19]
ELOAD=10
EINTERVAL=11
ENOCFG=19
# Source cofig errors [20-29]
# Destination config errors [30-49]
EFORMAT=30
ECHANNEL=31
EFMTINVPARM=49
# config structures # config structures
declare -A \ declare -A \
@ -38,11 +36,21 @@ declare -A \
exit $EBASHVERS exit $EBASHVERS
} }
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
LC_ALL=C LC_ALL=C
shopt -s extglob shopt -s extglob
source "$SHAREDIR"/id3genres source $SHAREDIR/id3genres
for function in "$LIBDIR"/*/* for function in "$LIBDIR"/*/*
do do
@ -117,12 +125,12 @@ do
:) :)
echo "-$OPTARG requires an argument" echo "-$OPTARG requires an argument"
help help
exit $EINVARG exit 127
;; ;;
*) *)
echo "Unrecognized option: -$OPTARG" echo "Unrecognized option: -$OPTARG"
help help
exit $EINVARG exit 127
;; ;;
esac esac
done done
@ -202,44 +210,36 @@ echo '
FROM destination_files FROM destination_files
WHERE source_file_id is NULL;' >&3 WHERE source_file_id is NULL;' >&3
read -u4 -r -d $'\0' removecount read -u4 removecount
until (( ${#removefile[@]} == removecount )) until (( ${#removefile[@]} == removecount ))
do do
echo ' echo '
SELECT destination_files.id, SELECT id,
destinations.name, filename
destination_files.filename
FROM destination_files FROM destination_files
INNER JOIN destinations
ON destination_files.destination_id
= destinations.id
WHERE source_file_id is NULL WHERE source_file_id is NULL
LIMIT 500 OFFSET '${#removefile[@]}'; LIMIT 500 OFFSET '${#removefile[@]}';
SELECT "AtOM:NoMoreFiles"; SELECT "AtOM:NoMoreFiles";
' >&3 ' >&3
read -u4 -r -d $'\0' line read -u4 line
until [[ $line == AtOM:NoMoreFiles ]] until [[ $line == AtOM:NoMoreFiles ]]
do do
removeFileId=${line%%::AtOM:SQL:Sep::*} removefile[${line%::AtOM:SQL:Sep::*}]="${line#*::AtOM:SQL:Sep::}"
rest=${line#*::AtOM:SQL:Sep::} read -u4 line
removeFileDestName=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
removefile[$removeFileId]="${destinationpath["$removeFileDestName"]}/${rest%%::AtOM:SQL:Sep::*}"
read -u4 -r -d $'\0' line
done done
done done
unset deleted deleted=0
unset removed removed=0
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
for id in ${!removefile[@]} for id in ${!removefile[@]}
do do
filename=${removefile[id]} filename=${removefile[id]}
if [ -n "$filename" ] if [ -n "$filename" ]
then then
if rm "$filename" if rm -f "$filename"
then then
Delete destination_files <<<"id = $id" Delete destination_files <<<"id = $id"
(( ++deleted )) (( ++deleted ))
@ -256,9 +256,9 @@ do
done done
echo 'COMMIT;' >&3 echo 'COMMIT;' >&3
(( cron )) || echo -n $'\r' (( cron )) || echo -n $'\r'
echo -n "${deleted+$deleted files deleted${removed:+, }}${removed:+$removed removed from database}" echo -n "Suppressed $deleted files, $removed removed from database"
(( cron )) || echo -ne "\033[K" (( cron )) || echo -ne "\033[K"
(( deleted || removed )) && echo echo
unset removecount deleted removed removefile unset removecount deleted removed removefile
updateTags updateTags
@ -272,8 +272,7 @@ do
Update destination_files last_change 0 \ Update destination_files last_change 0 \
<<<"destination_id = $forcedestid" <<<"destination_id = $forcedestid"
else else
echo "Full rebuild of destination $forcedest was requested," \ echo "Destination $forcedest does not exist!" >&2
"but it does not exist!" >&2
fi fi
done done
@ -289,7 +288,6 @@ echo '
ascii INTEGER, ascii INTEGER,
source_file INTEGER, source_file INTEGER,
fileid INTEGER, fileid INTEGER,
destdir TEXT,
filename TEXT, filename TEXT,
cmd_arg0 TEXT, cmd_arg0 TEXT,
cmd_arg1 TEXT, cmd_arg1 TEXT,
@ -387,7 +385,7 @@ echo '
<> CAST(source_files.last_change AS TEXT) <> CAST(source_files.last_change AS TEXT)
AND mime_type_actions.destination_id = destinations.id AND mime_type_actions.destination_id = destinations.id
AND mime_type_actions.action = 1;' >&3 AND mime_type_actions.action = 1;' >&3
read -u4 -r -d $'\0' filecount read -u4 filecount
if [ -n "$maxbatch" ] && (( maxbatch < filecount )) if [ -n "$maxbatch" ] && (( maxbatch < filecount ))
then then
(( togo = filecount - maxbatch )) (( togo = filecount - maxbatch ))
@ -400,23 +398,20 @@ echo '
mime_type_actions.mime_text, mime_type_actions.mime_text,
destinations.name, destinations.name,
destination_files.id, destination_files.id,
tags.album,
tags.albumartist,
tags.artist,
tags.bitrate,
tags.channels,
tags.composer,
tags.depth, tags.depth,
tags.disc,
tags.genre,
tags.performer,
tags.rate, tags.rate,
tags.releasecountry, tags.channels,
tags.replaygain_alb, tags.bitrate,
tags.replaygain_trk, tags.genre,
tags.title, tags.albumartist,
tags.year,
tags.album,
tags.disc,
tags.artist,
tags.track, tags.track,
tags.year tags.title,
tags.composer,
tags.performer
FROM source_files FROM source_files
INNER JOIN destination_files INNER JOIN destination_files
ON source_files.id ON source_files.id
@ -436,13 +431,13 @@ echo '
(( maxbatch )) && echo "LIMIT $maxbatch" >&3 (( maxbatch )) && echo "LIMIT $maxbatch" >&3
echo '; echo ';
SELECT "AtOM:NoMoreFiles";' >&3 SELECT "AtOM:NoMoreFiles";' >&3
read -u4 -r -d $'\0' line read -u4 line
while ! [[ $line = AtOM:NoMoreFiles ]] while ! [[ $line = AtOM:NoMoreFiles ]]
do do
decodefiles+=("$line::AtOM:SQL:Sep::") decodefiles+=("$line::AtOM:SQL:Sep::")
read -u4 -r -d $'\0' line read -u4 line
done done
(( cron )) || echo -n $'Creating tasks...\033[K' (( cron )) || echo -n 'Creating tasks... '
(( textunidecodeneeded )) && ascii (( textunidecodeneeded )) && ascii
@ -459,39 +454,33 @@ do
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
destfileid=${rest%%::AtOM:SQL:Sep::*} destfileid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
album=${rest%%::AtOM:SQL:Sep::*} bitdepth=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
albumartist=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
artist=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
bitrate=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
channels=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
composer=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
depth=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
disc=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
genre=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
performer=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
rate=${rest%%::AtOM:SQL:Sep::*} rate=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
releasecountry=${rest%%::AtOM:SQL:Sep::*} channels=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
replaygain_alb=${rest%%::AtOM:SQL:Sep::*} bitrate=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
replaygain_trk=${rest%%::AtOM:SQL:Sep::*} genre=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
title=${rest%%::AtOM:SQL:Sep::*} albumartist=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
year=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
album=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
disc=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
artist=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
track=${rest%%::AtOM:SQL:Sep::*} track=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
year=${rest%%::AtOM:SQL:Sep::*} title=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
composer=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
performer=${rest%%::AtOM:SQL:Sep::*}
unset rest unset rest
case ${destinationformat["$destination"]} in case ${destinationformat["$destination"]} in
vorbis) (( disableoggenc )) && continue ;; vorbis) (( disableoggenc )) && continue ;;
@ -503,7 +492,7 @@ do
getDestFile getDestFile
for copy_ext in "${destinationcopyext[@]}" for copy_ext in "${destinationcopyext[@]}"
do do
if [[ $filename =~ '.*\.'"$copy_ext"'$' ]] if [[ $filename =~ '.*'"$copy_ext" ]]
then then
copied=1 copied=1
break break
@ -519,13 +508,13 @@ do
album \ album \
albumartist \ albumartist \
artist \ artist \
bitdepth \
bitrate \ bitrate \
channels \ channels \
commandline \ commandline \
composer \ composer \
copied \ copied \
decodetaskid \ decodetaskid \
depth \
destfileid \ destfileid \
destination \ destination \
disc \ disc \
@ -534,9 +523,6 @@ do
mimetype \ mimetype \
performer \ performer \
rate \ rate \
releasecountry \
replaygain_alb \
replaygain_trk \
rest \ rest \
sox_needed \ sox_needed \
soxoptions_in \ soxoptions_in \
@ -548,11 +534,8 @@ do
tmpfile tmpfile
done done
echo 'COMMIT;' >&3 echo 'COMMIT;' >&3
(( cron )) || echo -n $'\r\033[K' (( cron )) || echo -n $'\r'
(( count )) \ echo "Created ${count:-0} tasks for $filecount files ${togo:+($togo left) }(${copies:-0} immediate copies)"
&& echo "Created $count tasks for $filecount files" \
"${togo:+($togo left) }" \
"${copies:+($copies immediate copies)}"
# remove perl unicode to ascii coprocess # remove perl unicode to ascii coprocess
(( textunidecodeneeded )) && eval exec "${toascii[1]}>&-" (( textunidecodeneeded )) && eval exec "${toascii[1]}>&-"
@ -560,16 +543,16 @@ echo 'COMMIT;' >&3
concurrency=$(( maxload / 2 )) concurrency=$(( maxload / 2 ))
(( concurrency )) || concurrency=1 (( concurrency )) || concurrency=1
active=0 active=0
concurrencychange=$EPOCHSECONDS concurrencychange=$(date +%s)
starttime=$concurrencychange starttime=$concurrencychange
taskcount=$count taskcount=$count
remaining=$taskcount remaining=$taskcount
failed=0 failed=0
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
committime=$EPOCHSECONDS committime=$(date +%s)
while (( remaining || ${#workers[@]} )) while (( (remaining || ${#workers[@]}) && ! quit ))
do do
timestamp=$EPOCHSECONDS timestamp=$(date +%s)
if (( $timestamp - committime >= 60 )) if (( $timestamp - committime >= 60 ))
then then
echo $'COMMIT;\nBEGIN TRANSACTION;' >&3 echo $'COMMIT;\nBEGIN TRANSACTION;' >&3
@ -581,15 +564,16 @@ do
then then
concurrency="$fixed_workers" concurrency="$fixed_workers"
else else
if (( timestamp - concurrencychange >= loadinterval )) if [ -z "$quit" ] \
&& (( ! pause )) \
&& (( timestamp - concurrencychange >= loadinterval ))
then then
if (( concurrency > 1 || allow_zero_running )) \ if (( concurrency > 1 )) \
&& (( load > maxload && concurrency )) && (( load > maxload ))
then then
concurrencychange=$timestamp concurrencychange=$timestamp
(( --concurrency )) (( --concurrency ))
elif (( load < maxload )) \ elif (( load < maxload )) && (( active > concurrency - 1 ))
&& (( active > concurrency - 1 ))
then then
concurrencychange=$timestamp concurrencychange=$timestamp
(( ++concurrency )) (( ++concurrency ))
@ -598,11 +582,16 @@ do
fi fi
checkworkers checkworkers
cleaner cleaner
master (( pause )) || master
if (( ran - failed )) if (( ran - failed ))
then then
currenttime=$timestamp currenttime=$timestamp
(( runtime = currenttime - starttime )) if (( pause ))
then
(( runtime = pausestart - starttime - pausedtime ))
else
(( runtime = currenttime - starttime - pausedtime ))
fi
avgduration=$(( avgduration=$((
( runtime * 1000) ( runtime * 1000)
/ /
@ -638,7 +627,11 @@ do
fmtprogress="T:%${#taskcount}i/%i (F:%i) %3i%%" fmtprogress="T:%${#taskcount}i/%i (F:%i) %3i%%"
fmttime='%2id %2ih%02im%02is (A:%4.1fs/task)' fmttime='%2id %2ih%02im%02is (A:%4.1fs/task)'
eta="ETA:$( eta="ETA:$(
printf "%(%c)T" "$(( currenttime + secsremaining ))" date -d "${days:-0} days
${hours:-0} hours
${minutes:-0} minutes
${seconds:-0} seconds" \
+'%d/%m %H:%M:%S'
)" )"
(( cron )) || printf \ (( cron )) || printf \
"\r$fmtload $fmtworkers $fmtprogress $fmttime $eta\033[K"\ "\r$fmtload $fmtworkers $fmtprogress $fmttime $eta\033[K"\
@ -655,7 +648,7 @@ do
${minutes:-0} \ ${minutes:-0} \
${seconds:-0} \ ${seconds:-0} \
${avgdsec:-0}.${avgdmsec:-0} ${avgdsec:-0}.${avgdmsec:-0}
if ! (( concurrency )) && ! (( cron )) if (( pause ))
then then
if (( active )) if (( active ))
then then
@ -668,24 +661,24 @@ done
echo 'COMMIT;' >&3 echo 'COMMIT;' >&3
unset count unset count
endtime=$EPOCHSECONDS endtime=$(date +%s)
(( elapsedseconds = endtime - starttime )) (( elapsedseconds = endtime - starttime - pausedtime ))
(( days = (( days =
elapsedseconds elapsedseconds
/ /
( 24*60*60 ) ( 24*60*60 )
)) || unset days )) || true
(( hours = (( hours =
( elapsedseconds - ( days*24*60*60 ) ) ( elapsedseconds - ( days*24*60*60 ) )
/ /
( 60*60 ) ( 60*60 )
)) || (( days )) || unset hours )) || true
(( minutes = (( minutes =
( elapsedseconds - ( ( days*24 + hours ) *60*60 ) ) ( elapsedseconds - ( ( days*24 + hours ) *60*60 ) )
/ /
60 60
)) || (( days || hours )) || unset minutes )) || true
(( seconds = (( seconds =
elapsedseconds elapsedseconds
- -
@ -693,15 +686,10 @@ endtime=$EPOCHSECONDS
)) || true )) || true
(( cron )) || echo -n $'\r' (( cron )) || echo -n $'\r'
(( ran )) \ echo -n "Ran ${ran:=0} tasks, $failed of which failed, in $days" \
&& echo -n "Ran $ran tasks${failed:+, $failed of which failed,}" \ "days, $hours hours, $minutes minutes and $seconds seconds."
"in ${days:+$days days,}" \
"${hours:+$hours hours,}" \
"${minutes:+$minutes minutes and}" \
"$seconds seconds."
(( cron )) || echo -en "\033[K" (( cron )) || echo -en "\033[K"
(( ran )) && echo echo
if (( failed )) if (( failed ))
then then
echo $'\nFailed tasks:\n' echo $'\nFailed tasks:\n'
@ -773,21 +761,28 @@ then
WHERE tasks.status = 2; WHERE tasks.status = 2;
SELECT "AtOM:NoMoreFiles";' >&3 SELECT "AtOM:NoMoreFiles";' >&3
read -u4 -r -d $'\0' line read -u4 line
while ! [[ $line = AtOM:NoMoreFiles ]] while ! [[ $line = AtOM:NoMoreFiles ]]
do do
failedtasks+=("$line") failedtasks+=("$line")
read -u4 -r -d $'\0' line read -u4 line
done done
for line in "${failedtasks[@]}" for line in "${failedtasks[@]}"
do do
echo "${line%%::AtOM:SQL:Sep::*}" echo "${line%%::AtOM:SQL:Sep::*}"
line="${line#*::AtOM:SQL:Sep::}" line="${line#*::AtOM:SQL:Sep::}"
line="${line//::AtOM:SQL:Sep::/ }" line="${line//::AtOM:SQL:Sep::/ }"
echo $'\t'"${line/+( )$/}"$'\n' echo $'\t'"${line/+( )$/}"
echo
done done
fi fi
if [ -n "$quit" ]
then
closeDatabase
exit
fi
for destination in "${!destinationpath[@]}" for destination in "${!destinationpath[@]}"
do do
echo ' echo '
@ -802,7 +797,6 @@ do
tags.disc, tags.disc,
tags.genre, tags.genre,
tags.performer, tags.performer,
tags.releasecountry,
tags.title, tags.title,
tags.track, tags.track,
tags.year tags.year
@ -833,11 +827,11 @@ do
SELECT "AtOM:NoMoreFiles"; SELECT "AtOM:NoMoreFiles";
' >&3 ' >&3
read -u4 -r -d $'\0' line read -u4 line
while [[ $line != AtOM:NoMoreFiles ]] while [[ $line != AtOM:NoMoreFiles ]]
do do
renamefiles+=("$line") renamefiles+=("$line")
read -u4 -r -d $'\0' line read -u4 line
done done
if (( ${#renamefiles[@]} )) if (( ${#renamefiles[@]} ))
then then
@ -871,8 +865,6 @@ do
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
performer=${rest%%::AtOM:SQL:Sep::*} performer=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
releasecountry=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
title=${rest%%::AtOM:SQL:Sep::*} title=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
track=${rest%%::AtOM:SQL:Sep::*} track=${rest%%::AtOM:SQL:Sep::*}
@ -887,9 +879,7 @@ do
progressSpin progressSpin
if [[ "$oldfilename" != "$destfilename" ]] if [[ "$oldfilename" != "$destfilename" ]]
then then
mv \ mv "$oldfilename" "$destfilename"
"${destinationpath[$destination]}/$oldfilename" \
"${destinationpath[$destination]}/$destfilename"
(( changedcount++ )) (( changedcount++ ))
commit=1 commit=1
fi fi
@ -914,10 +904,9 @@ do
(( textunidecodeneeded )) && eval exec "${toascii[1]}>&-" (( textunidecodeneeded )) && eval exec "${toascii[1]}>&-"
echo 'COMMIT;' >&3 echo 'COMMIT;' >&3
(( cron )) || echo -n $'\r' (( cron )) || echo -n $'\r'
(( changedcount )) \ echo -n "$destination: Renamed ${changedcount:-0} files"
&& echo -n "$destination: Renamed $changedcount files"
(( cron )) || echo -en "\033[K" (( cron )) || echo -en "\033[K"
(( changedcount )) && echo echo
fi fi
unset count changedcount renamefiles unset count changedcount renamefiles
done done
@ -925,26 +914,22 @@ done
copyFiles_action copyFiles_action
echo ' echo '
SELECT destination_files.id, SELECT id,
destination_files.filename, filename,
destination_files.old_filename, old_filename
destinations.name
FROM destination_files FROM destination_files
INNER JOIN destinations
ON destination_files.destination_id
= destinations.id
WHERE old_filename IS NOT NULL; WHERE old_filename IS NOT NULL;
SELECT "AtOM:NoMoreFiles"; SELECT "AtOM:NoMoreFiles";
' >&3 ' >&3
(( cron )) || echo -n 'Removing obsolete files...'$'\033[K' (( cron )) || echo -n 'Removing obsolete files... '
lines=() lines=()
read -u4 -r -d $'\0' line read -u4 line
while [[ $line != AtOM:NoMoreFiles ]] while [[ $line != AtOM:NoMoreFiles ]]
do do
lines+=("$line") lines+=("$line")
read -u4 -r -d $'\0' line read -u4 line
done done
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
for line in "${lines[@]}" for line in "${lines[@]}"
@ -952,12 +937,10 @@ do
id=${line%%::AtOM:SQL:Sep::*} id=${line%%::AtOM:SQL:Sep::*}
rest=${line#*::AtOM:SQL:Sep::} rest=${line#*::AtOM:SQL:Sep::}
filename=${rest%%::AtOM:SQL:Sep::*} filename=${rest%%::AtOM:SQL:Sep::*}
rest=${line#*::AtOM:SQL:Sep::} oldfilename=${rest#*::AtOM:SQL:Sep::}
oldfilename=${rest%%::AtOM:SQL:Sep::*}
destination=${rest#*::AtOM:SQL:Sep::}
if [[ $oldfilename != "$filename" ]] && [ -f "$oldfilename" ] if [[ $oldfilename != "$filename" ]] && [ -f "$oldfilename" ]
then then
rm -f "${destinationpath[$destination]}/$oldfilename" rm -f "$oldfilename"
fi fi
Update destination_files old_filename NULL <<<"id = $id" Update destination_files old_filename NULL <<<"id = $id"
(( count++ )) (( count++ ))
@ -965,12 +948,11 @@ do
done done
echo 'COMMIT;' >&3 echo 'COMMIT;' >&3
(( cron )) || echo -n $'\r' (( cron )) || echo -n $'\r'
(( count )) \ echo -n "Removed ${count:-0} obsolete files."
&& echo -n "Removed $count obsolete files."
(( cron )) || echo -en "\033[K" (( cron )) || echo -en "\033[K"
(( count )) && echo echo
(( debug )) && echo "Purging empty directories..." echo "Purging empty directories."
for path in "${destinationpath[@]}" for path in "${destinationpath[@]}"
do do
find "$path" -type d -empty -delete find "$path" -type d -empty -delete

View File

@ -39,11 +39,6 @@ Sections:
* debug <level>: Integer. Currently defined values: * debug <level>: Integer. Currently defined values:
* 1: few additional status informations. * 1: few additional status informations.
* 3: log SQL queries. * 3: log SQL queries.
* skip-timestamp-microsec <bool>: Ignore microsecond precision in timestamps.
Microsec precise timestamps are still stored as-is in the DB, this
setting just impacts comparisons when determining if a file has been
changed.
[source] [source]
This section defines where are the files you want transcoded. This section defines where are the files you want transcoded.

View File

@ -1,177 +1,44 @@
[general] [general]
# This section contains parameters of the program itself.
# * max-load <load>: Integer. Defines how parallel processing will behave. AtOM
# will try to keep the 1 minute load average between <load> and <load>+1 by
# adjusting concurrency.
# Initial concurrency will be set to half of that value.
max-load 16
# * load-interval <seconds>: Integer. How often should we check the load average
# and adjust concurrency. Set this too low, and concurrency may be increased
# 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.
load-interval 10
# * ionice <class> [niceness]: IO-hungry processes will be run with ionice class
# <class> and niceness [niceness] (if applicable). See man ionice for details.
ionice 3 ionice 3
max-load 6
# * temporary-directory <directory>: String. Name speaks for itself: this is load-interval 30
# where FIFOs (for communicating with sqlite) and temporary WAVE files will temporary-directory %HOME%/.atom/tmp
# be created. Note that debug logs (if enabled) will go there too. database %HOME%/.atom/atom.db
temporary-directory /tmp/AtOM-user/ debug 0
# * database <file>: String. Where the SQLite database should be stored.
database /home/user/.local/share/AtOM/atom.db
# * debug <level>: Integer.
# 0: No debug output, encoding and decoding errors logged to
# <temporary-directory>/workerN.log
# 1: Print some debug to stdout, log decoding and encoding commands to
# <temporary-directory>/workerN.log
# 3: log SQL queries to <temporary-directory>/debug.log
#debug 1
# * skip-timestamp-microsec: Ignore microsecond precision in timestamps.
skip-timestamp-microsec 0
[source] [source]
# This section defines where are the files you want transcoded. path /var/lib/mpd/music
# * path <directory>: String. The root of your collection.
# Default: /var/lib/mpd/music
path /mnt/Musique
# * skip <directory>: String. Files in <directory> will be ignored. Note that
# <directory> can be any expression accepted by find.
skip /lost+found
skip /last skip /last
skip /lastfm skip /lastfm
skip /zzz-atrier skip /zzz-atrier
[Vorbis] [Ogg]
# Each section not named 'general' or 'source' will define a new destination.
# Common parameters:
# Mandatory parameters:
# * enabled: Whether or not to treat this destination (1=true/0=false)
enabled 1
# * path: Where files will be written
path /mnt/Musique-OggQ2 path /mnt/Musique-OggQ2
# * format: copy, ogg, opus or mp3. Other formats may appear in the future -
# feel free to implement your preferred format.
format vorbis format vorbis
# Ogg parameters:
# * quality <quality>: The quality parameter of oggenc. See man oggenc for
# more info. This is the only mode supported and planned. Still, if you want
# to be able to use bitrate settings, feel free to fork and file a pull
# request.
quality 1 quality 1
# Optional parameters:
# * normalize <yes>/<no>: Normalize output files.
normalize yes normalize yes
channels 2
# * rename <string>: Destination files will be named according to <string>, frequency 44100
# after expansion of special strings:
# %{album},
# %{albumartist},
# %{artist},
# %{disc},
# %{genre},
# %{releasecountry},
# %{title},
# %{track},
# %{year}.
# Untagged files or files in unrecognized formats will not be changed.
rename %{genre}/%{albumartist}/%{year}-%{album}-%{releasecountry}/%{disc}%{track}--%{artist}-%{title}
# * fat32compat <yes>/<no>: Rename files for compatibility with FAT32
# filesystems.
fat32compat yes
# * ascii-only <yes>/<no>: Rename files for compatibility with ASCII-only
# systems.
ascii-only no
# you should not skip or copy application/octet-stream, they could be something # you should not skip or copy application/octet-stream, they could be something
# similar to "Audio file with ID3 version 2.4.0, unsynchronized frames" # similar to "Audio file with ID3 version 2.4.0, unsynchronized frames"
# * skip_mime-type <mime-type>: Files with mime-type <mime-type> will not
# be included in that destination. For more than one mime-type, use multiple
# times, as needed. The '*' character is a wildcard.
skip_mime-type inode/x-empty
skip_mime-type audio/midi
skip_mime-type image/*
skip_mime-type text/*
skip_mime-type application/pdf
skip_mime-type application/javascript*
skip_mime-type very short file (no magic)
# * copy_mime-type <mime-type>: Same as skip_mime-type, except that files
# matching will be copied as-is to the destination. E.g. image/* will copy
# covers and other images to the destination. In fact, AtOM will try to use
# hard links instead of copies.
copy_mime-type image/* copy_mime-type image/*
copy_mime-type text/*
# * copy_extension <extension>: Copy files whose name and with ".<extension>"
copy_extension txt
# * channels <number>: Files with more than <number> channels will be
# downmixed. Useful if you create files for telephony music-on-hold.
channels 2
# * frequency <hertz>: Files will be resampled as needed to <hertz>Hz
# sampling-rate. Shoutcast/Icecast streams require a constant sampling-rate.
# Telephony systems often require a sample rate of 8000Hz.
frequency 44100
# * higher-than <bitrate>: Integer. Only reencode files with bitrates higher
# then <bitrate>kbps. This only applies if sample-rate, channel count and of
# course format are equal. If unset, only files with bitrates equal to that
# of the target will be copied (actually, hardlinking will be attempted
# first). As Ogg Vorbis target quality is not defined by its bitrate, Ogg
# Vorbis files will always be reencoded if unset.
higher-than 200
[Opus] [Opus]
enabled 1
path /mnt/Musique-opus path /mnt/Musique-opus
format opus format opus
# Opus parameters:
# * bitrate <bitrate>: Set (VBR) bitrate to <bitrate>. Note that while Opus
# allows for decimal values, AtOM does not. The reason for this is simple:
# we do numeric comparisons, and Bash only manipulates integers.
bitrate 96 bitrate 96
normalize yes normalize yes
frequency 48000 frequency 48000
copy_mime-type image/* copy_mime-type image/*
copy_mime-type text/* copy_mime-type text/*
[MP3] [MP3]
enabled 1 path /mnt/Musique-mp3.test
path /mnt/Musique-mp3
format mp3 format mp3
# MP3 parameters:
# * bitrate <bitrate>: Set ABR to <bitrate>. Again, if you want CBR or any
# other mode supported by lame, please fork and file a pull request.
bitrate 96 bitrate 96
# * noresample <yes>/<no>: LAME may decide to encode your file to a lower
# sampling-rate if you use a low bitrate. Setting this to yes will
# append --resample <original file's rate>, preventing any resampling from
# happening.
noresample yes noresample yes
normalize yes normalize yes
higher-than 128 higher-than 128
@ -183,7 +50,6 @@ skip_mime-type image/*
skip_mime-type text/* skip_mime-type text/*
[asterisk] [asterisk]
enabled 1
path /mnt/Musique-asterisk path /mnt/Musique-asterisk
format vorbis format vorbis
quality 0 quality 0

View File

@ -5,7 +5,7 @@ getConfigDestination() {
destinationenabled["$destination"]="$value" destinationenabled["$destination"]="$value"
;; ;;
'path') 'path')
destinationpath["$destination"]="${value%/}" destinationpath["$destination"]="$value"
;; ;;
'format') 'format')
case "$value" in case "$value" in
@ -186,7 +186,7 @@ getConfigDestination() {
destinationcopymime[$destination]="${destinationcopymime[$destination]:+${destinationcopymime[$destination]}|}$value" destinationcopymime[$destination]="${destinationcopymime[$destination]:+${destinationcopymime[$destination]}|}$value"
;; ;;
'copy_extension') 'copy_extension')
destinationcopyext[$destination]="${destinationcopyext[$destination]:+${destinationcopyext[$destination]}|}$value" destinationcopyext[$destination]="${destinationcopyext[$destination]:+${destinationcopyext[$destination]}|}\.$value$"
;; ;;
'higher-than') 'higher-than')
expr='^[0-9]*$' expr='^[0-9]*$'

View File

@ -75,9 +75,6 @@ getConfigGeneral() {
'database') 'database')
database="$value" database="$value"
;; ;;
'skip-timestamp-microsec')
skip_us_timestamp="$value"
;;
debug) debug)
(( value > debug )) && debug=$value (( value > debug )) && debug=$value
;; ;;

View File

@ -49,13 +49,13 @@ printConfig() {
EOF EOF
[ -n "${destinationskipmime["$destination"]}" ] \ [ -n "${destinationskipmime["$destination"]}" ] \
&& echo " |Skipped mime-types|${destinationskipmime["$destination"]//\|/ && echo " |Skipped mime-types|${destinationskipmime["$destination"]//\|/
| |}" | | |}"
[ -n "${destinationcopymime["$destination"]}" ] \ [ -n "${destinationcopymime["$destination"]}" ] \
&& echo " |Copied mime-types|${destinationcopymime["$destination"]//\|/ && echo " |Copied mime-types|${destinationcopymime["$destination"]//\|/
| |}" | | |}"
[ -n "${destinationcopyext["$destination"]}" ] \ [ -n "${destinationcopyext["$destination"]}" ] \
&& echo " |Copied extensions|${destinationcopyext["$destination"]//\|/ && echo " |Copied extensions|${destinationcopyext["$destination"]//\|/
| |}" | | |}"
done done
}|column -t -s'|' }|column -t -s'|'
} }

View File

@ -33,9 +33,6 @@ database $database
# * debug <level>: Integer. # * debug <level>: Integer.
#debug 1 #debug 1
# * skip-timestamp-microsec: Ignore microsecond precision in timestamps.
skip-timestamp-microsec ${skip_us_timestamp:-0}
[source] [source]
# This section defines where are the files you want transcoded. # This section defines where are the files you want transcoded.
@ -63,7 +60,7 @@ path $sourcepath
# Common parameters: # Common parameters:
# Mandatory parameters: # Mandatory parameters:
# * enabled: Whether or not to treat this destination (1=true/0=false) # * enabled: Whether or not to treat this destination (1=tue/0=false)
enabled 1 enabled 1
# * path: Where files will be written # * path: Where files will be written
@ -147,7 +144,6 @@ bitrate ${destinationquality["$destination"]}
# %{artist}, # %{artist},
# %{disc}, # %{disc},
# %{genre}, # %{genre},
# %{releasecountry},
# %{title}, # %{title},
# %{track}, # %{track},
# %{year}. # %{year}.
@ -211,7 +207,8 @@ bitrate ${destinationquality["$destination"]}
done done
cat <<-EOCfg cat <<-EOCfg
# * copy_extension <extension>: Copy files whose name and with ".<extension>" # * copy_extension <extension>: Same as skip_extension, except that files
# matching will be copied as-is to the destination.
EOCfg EOCfg
destinationcopyext["$destination"]="${destinationcopyext["$destination"]}|" destinationcopyext["$destination"]="${destinationcopyext["$destination"]}|"
while [[ ${destinationcopyext["$destination"]} =~ \| ]] while [[ ${destinationcopyext["$destination"]} =~ \| ]]

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
copyFiles_action() { copyFiles_action() {
(( cron )) || echo -n $'Copying files...\033[K' (( cron )) || echo -n "Copying files... "
echo ' echo '
SELECT SELECT
source_files.filename, source_files.filename,
@ -22,11 +22,11 @@ copyFiles_action() {
AND mime_type_actions.action = 2; AND mime_type_actions.action = 2;
SELECT "AtOM:NoMoreFiles";' >&3 SELECT "AtOM:NoMoreFiles";' >&3
read -u4 -r -d $'\0' line read -u4 line
while ! [[ $line = AtOM:NoMoreFiles ]] while ! [[ $line = AtOM:NoMoreFiles ]]
do do
copyfiles+=("$line") copyfiles+=("$line")
read -u4 -r -d $'\0' line read -u4 line
done done
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
@ -79,30 +79,15 @@ copyFiles_action() {
fi fi
fi fi
fi fi
if cp -a --reflink=always \ if cp -al "$sourcepath/$sourcefilename" "$destdir" 2>/dev/null\
"$sourcepath/$sourcefilename" \ || cp -a "$sourcepath/$sourcefilename" "$destdir"
"$destdir" \
2>/dev/null \
|| cp -al \
"$sourcepath/$sourcefilename" \
"$destdir" \
2>/dev/null \
|| cp -a \
"$sourcepath/$sourcefilename" \
"$destdir"
then then
destfilename=${sourcefilename//$'\n'/::AtOM:NewLine:SQL:Inline::}
Update destination_files \ Update destination_files \
filename \ filename "$destdir/${sourcefilename##*/}"\
"$destdir/${destfilename##*/}"\ rename_pattern "${destinationrenamepath[$destination]}/${destinationrename[$destination]}"\
rename_pattern \ fat32compat ${destinationfat32compat["$destination"]}\
"${destinationrenamepath[$destination]}/${destinationrename[$destination]}"\ ascii ${destinationascii["$destination"]}\
fat32compat \ last_change $lastchange \
${destinationfat32compat["$destination"]}\
ascii \
${destinationascii["$destination"]}\
last_change \
$lastchange \
<<-EOWhere <<-EOWhere
id = $destfileid id = $destfileid
EOWhere EOWhere
@ -118,7 +103,7 @@ copyFiles_action() {
(( cron )) || echo -en "\033[K" (( cron )) || echo -en "\033[K"
echo echo
else else
(( cron )) || echo -n $'\r\033[K' (( cron )) || echo -e "\rNothing to copy.\033[K"
fi fi
unset count done unset count done
} }

View File

@ -21,7 +21,7 @@ guessPath() {
LIMIT 1 LIMIT 1
),"0.0"); ),"0.0");
'>&3 '>&3
read -u4 -r -d $'\0' timestamp read -u4 timestamp
if (( ${timestamp/./} == 0 )) if (( ${timestamp/./} == 0 ))
then then
return 2 return 2
@ -46,7 +46,7 @@ guessPath() {
LIMIT 1 LIMIT 1
),"AtOM:NotFound"); ),"AtOM:NotFound");
'>&3 '>&3
read -u4 -r -d $'\0' filename read -u4 filename
if [[ $filename != AtOM:NotFound ]] if [[ $filename != AtOM:NotFound ]]
then then
echo "${filename%/*}" echo "${filename%/*}"

View File

@ -1,15 +1,13 @@
#!/bin/bash #!/bin/bash
copyFiles_matching() { copyFiles_matching() {
local extension="${filename##*.}" extension="${filename##*.}"
if \
cp -al \ cp -al \
"$sourcepath/$filename" \ "$sourcepath/$filename" \
"${destinationpath[$destination]}/$destdir/$destfile.$extension" \ "$destdir/$destfile.$extension" \
2>/dev/null \ 2>/dev/null \
|| cp -a \ || cp -a \
"$sourcepath/$filename" \ "$sourcepath/$filename" \
"${destinationpath[$destination]}/$destdir/$destfile.$extension" "$destdir/$destfile.$extension"
then
echo \ echo \
"UPDATE destination_files" \ "UPDATE destination_files" \
"SET filename=" \ "SET filename=" \
@ -25,12 +23,11 @@ copyFiles_matching() {
" WHERE id=$destfileid" \ " WHERE id=$destfileid" \
" )," \ " )," \
" rename_pattern=" \ " rename_pattern=" \
"\"${destinationrenamepath[$destination]}/${destinationrename[$destination]}\","\ "\"${destinationrenamepath[$destination]}/${destinationrename[$destination]}\","\
" fat32compat=" \ " fat32compat=" \
"${destinationfat32compat["$destination"]}," \ "${destinationfat32compat["$destination"]}," \
" ascii=${destinationascii["$destination"]}"\ " ascii=${destinationascii["$destination"]}" \
"WHERE id=$destfileid;" \ "WHERE id=$destfileid;" \
>&3 >&3
(( ++copies )) (( ++copies ))
fi
} }

View File

@ -26,7 +26,6 @@ Insert() {
insert_values+=$value insert_values+=$value
;; ;;
*) *)
value=${value//::AtOM:NewLine:SQL:Inline::/$'\n'}
insert_values+='"'"${value//\"/\"\"}"'"' insert_values+='"'"${value//\"/\"\"}"'"'
;; ;;
esac esac
@ -37,7 +36,7 @@ Insert() {
"( $insert_values );" >&3 "( $insert_values );" >&3
(( no_id )) || { (( no_id )) || {
echo 'SELECT LAST_INSERT_ROWID();' >&3 echo 'SELECT LAST_INSERT_ROWID();' >&3
read -u4 -r -d $'\0' results read -u 4 results
echo "$results" echo "$results"
} }
} }

View File

@ -19,7 +19,6 @@ Select() {
while read key operator value while read key operator value
do do
(( ${#where_statement} )) && where_statement+=( "AND" ) (( ${#where_statement} )) && where_statement+=( "AND" )
value=${value//::AtOM:NewLine:SQL:Inline::/$'\n'}
where_statement+=( "$key $operator "'"'"${value//\"/\"\"}"'"' ) where_statement+=( "$key $operator "'"'"${value//\"/\"\"}"'"' )
done done
echo "SELECT IFNULL(" \ echo "SELECT IFNULL(" \
@ -27,7 +26,7 @@ Select() {
"WHERE ${where_statement[@]})" \ "WHERE ${where_statement[@]})" \
",'SQL::Select:not found'" \ ",'SQL::Select:not found'" \
");" >&3 ");" >&3
read -u 4 -r -d $'\0' results read -u 4 results
if ! [[ $results == "SQL::Select:not found" ]] if ! [[ $results == "SQL::Select:not found" ]]
then then
echo "$results" echo "$results"

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/bin/bash
currentdbversion=8 currentdbversion=5
checkDatabaseVersion() { checkDatabaseVersion() {
local dbversion local dbversion
if dbversion=$(Select atom version <<<"\"1\" = 1") if dbversion=$(Select atom version <<<"\"1\" = 1")
@ -15,10 +15,10 @@ checkDatabaseVersion() {
dbversion=$(Select atom version <<<"\"1\" = 1") dbversion=$(Select atom version <<<"\"1\" = 1")
done done
else else
echo "Database schema version $dbversion is" \ echo "Database schema version $dbversion is higher than
"higher thanthat of this version of" \ that of this version of AtOM
"AtOM ($currentdbversion). Bailing out." >&2 ($currentdbversion). Bailing out." >&2
exit $EDBVERSION exit 1
fi fi
else else
Insert atom 1 <<<"version $currentdbversion" Insert atom 1 <<<"version $currentdbversion"

View File

@ -3,8 +3,7 @@ closeDatabase() {
echo 'vacuum;' >&3 echo 'vacuum;' >&3
echo .quit >&3 echo .quit >&3
(( debug )) && echo -n "Waiting for SQLite to terminate... " (( debug )) && echo -n "Waiting for SQLite to terminate... "
(( debug > 2 )) && exec 5>&- wait
wait $db_pid
(( debug )) && echo OK (( debug )) && echo OK
exec 3>&- exec 3>&-
exec 4<&- exec 4<&-

View File

@ -1,34 +1,25 @@
#!/usr/bin/env bash #!/bin/bash
openDatabase() { openDatabase() {
local \
populate_db
[[ -f "$database" ]] || populate_db=1
rm -f "$tempdir"/sqlite.{in,out} rm -f "$tempdir"/sqlite.{in,out}
mkfifo "$tempdir"/sqlite.{in,out} mkfifo "$tempdir"/sqlite.{in,out}
stdbuf -o0 sqlite3 -bail \ sqlite3 -bail "$database" \
-newline $'::AtOM:SQL:EOL::\n' \
"$database" \
< "$tempdir/sqlite.in" \ < "$tempdir/sqlite.in" \
| stdbuf -o0 \
sed 's/::AtOM:SQL:EOL::/\x0/g;s/\(\x0\)\xA/\1/g' \
> "$tempdir/sqlite.out" & > "$tempdir/sqlite.out" &
db_pid=$!
exec 3> "$tempdir"/sqlite.in exec 3> "$tempdir"/sqlite.in
exec 4< "$tempdir"/sqlite.out exec 4< "$tempdir"/sqlite.out
rm "$tempdir"/sqlite.{in,out} rm "$tempdir"/sqlite.in "$tempdir"/sqlite.out
if (( debug > 2 )) if (( debug > 2 ))
then then
exec 5>&3 exec 5>&3
exec 3> >(tee -a "$tempdir/debug.log" >&5) exec 3> >(tee -a "$tempdir/debug.log" >&5)
fi fi
(( populate_db )) && cat $schema >&3 cat $schema >&3
echo '.separator ::AtOM:SQL:Sep::' >&3 echo '.separator ::AtOM:SQL:Sep::' >&3
echo 'PRAGMA foreign_keys = ON;' >&3 echo 'PRAGMA foreign_keys = ON;' >&3
echo 'PRAGMA recursive_triggers = ON;' >&3 echo 'PRAGMA recursive_triggers = ON;' >&3
echo 'PRAGMA temp_store = 2;' >&3 echo 'PRAGMA temp_store = 2;' >&3
echo 'PRAGMA locking_mode = EXCLUSIVE;' >&3 echo 'PRAGMA locking_mode = EXCLUSIVE;' >&3
read -u4 -r -d $'\0' read -u4
unset REPLY unset REPLY
checkDatabaseVersion checkDatabaseVersion
} }

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
upgradedatabase_1_2() { upgradedatabase_1_2() {
local data \ local data \
@ -18,11 +18,11 @@ FROM destination_files;
SELECT "AtOM::NoMoreData";' >&3 SELECT "AtOM::NoMoreData";' >&3
read -u4 -r -d $'\0' data read -u4 data
while [[ $data != AtOM::NoMoreData ]] while [[ $data != AtOM::NoMoreData ]]
do do
datas+=( "$data" ) datas+=( "$data" )
read -u4 -r -d $'\0' data read -u4 data
done done
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
for data in "${datas[@]}" for data in "${datas[@]}"

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
upgradedatabase_2_3() { upgradedatabase_2_3() {
local data \ local data \

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
upgradedatabase_3_4() { upgradedatabase_3_4() {
echo "Upgrading database to version 4... (backup is $database.bak_v3)" echo "Upgrading database to version 4... (backup is $database.bak_v3)"

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
upgradedatabase_4_5() { upgradedatabase_4_5() {
echo "Upgrading database to version 5... (backup is $database.bak_v4)" echo "Upgrading database to version 5... (backup is $database.bak_v4)"
@ -21,7 +21,7 @@ upgradedatabase_4_5() {
rate, rate,
channels, channels,
bitrate, bitrate,
depth bitdepth
ON tags ON tags
BEGIN BEGIN
UPDATE destination_files SET last_change=0 UPDATE destination_files SET last_change=0

View File

@ -1,37 +0,0 @@
#!/usr/bin/env bash
upgradedatabase_5_6() {
echo "Upgrading database to version 6... (backup is $database.bak_v5)"
cp "$database" "$database.bak_v5"
echo '
ALTER TABLE tags ADD COLUMN replaygain_alb TEXT;
ALTER TABLE tags ADD COLUMN replaygain_trk TEXT;
DROP TRIGGER force_destination_update_on_tag_update;
CREATE TRIGGER IF NOT EXISTS force_destination_update_on_tag_update
AFTER UPDATE OF
genre,
albumartist,
year,
album,
disc,
artist,
track,
title,
composer,
performer,
releasecountry,
replaygain_alb,
replaygain_trk,
rate,
channels,
bitrate,
depth
ON tags
BEGIN
UPDATE destination_files SET last_change=0
WHERE source_file_id=old.source_file;
END;
' >&3
Update atom version 6 <<<"1 = 1"
}

View File

@ -1,11 +0,0 @@
#!/usr/bin/env bash
upgradedatabase_6_7() {
echo "Upgrading database to version 7... (backup is $database.bak_v6)"
cp "$database" "$database.bak_v6"
for destination in "${destinations[@]}"
do
echo "UPDATE destination_files SET filename = REPLACE(filename,'${destinationpath[$destination]}/','') WHERE filename LIKE '${destinationpath[$destination]}/%';" >&3
done
Update atom version 7 <<<"1 = 1"
}

View File

@ -1,9 +0,0 @@
#!/usr/bin/env bash
upgradedatabase_7_8() {
echo "Upgrading database to version 8... (backup is $database.bak_v7)"
cp "$database" "$database.bak_v7"
echo 'Deletion of old files was failing. Users of previous versions (YOU!) are strongly advised to run cleandestinations with the "-r" flag.'
read -p "Press Enter to continue..."
Update atom version 8 <<<"1 = 1"
}

View File

@ -2,5 +2,5 @@
decodeMpcdec() { decodeMpcdec() {
tmpfile="${fileid}mpcdec" tmpfile="${fileid}mpcdec"
commandline=(${ionice}mpcdec) commandline=(${ionice}mpcdec)
commandline+=("$sourcepath/${filename//$'\n'/::AtOM:NewLine:SQL:Inline::}" "$tempdir/$tmpfile.wav") commandline+=("$sourcepath/$filename" "$tempdir/$tmpfile.wav")
} }

View File

@ -2,5 +2,5 @@
decodeOpusdec() { decodeOpusdec() {
tmpfile="${fileid}opusdec" tmpfile="${fileid}opusdec"
commandline=(${ionice}opusdec) commandline=(${ionice}opusdec)
commandline+=("$sourcepath/${filename//$'\n'/::AtOM:NewLine:SQL:Inline::}" "$tempdir/$tmpfile.wav") commandline+=("$sourcepath/$filename" "$tempdir/$tmpfile.wav")
} }

View File

@ -12,7 +12,7 @@ decodeSox() {
then then
commandline+=("$1") commandline+=("$1")
else else
commandline+=("$sourcepath/${filename//$'\n'/::AtOM:NewLine:SQL:Inline::}") commandline+=("$sourcepath/$filename")
fi fi
if [ -n "${destinationfrequency["$destination"]}" ] \ if [ -n "${destinationfrequency["$destination"]}" ] \
&& (( ${rate:-0} != ${destinationfrequency["$destination"]} )) && (( ${rate:-0} != ${destinationfrequency["$destination"]} ))
@ -26,7 +26,7 @@ decodeSox() {
commandline+=(-c ${destinationchannels["$destination"]}) commandline+=(-c ${destinationchannels["$destination"]})
soxoptions_out+=" -c ${destinationchannels["$destination"]}" soxoptions_out+=" -c ${destinationchannels["$destination"]}"
fi fi
if (( ${depth:-0} > 16 )) if (( ${bitdepth:-0} > 16 ))
then then
commandline+=(-b 16) commandline+=(-b 16)
soxoptions_out+=" -b 16" soxoptions_out+=" -b 16"

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/bin/bash
encodeFile::mp3() { encodeFile::mp3() {
lameopts=(${ionice}lame --quiet --noreplaygain) lameopts=(${ionice}lame --quiet)
lameopts+=(-v --abr ${destinationquality[$destination]}) lameopts+=(-v --abr ${destinationquality[$destination]})
[ -n "$album" ] && lameopts+=(--tl "$album" ) [ -n "$album" ] && lameopts+=(--tl "$album" )
[ -n "$artist" ] && lameopts+=(--ta "$artist") [ -n "$artist" ] && lameopts+=(--ta "$artist")
@ -13,10 +13,6 @@ encodeFile::mp3() {
[ -n "$performer" ] && lameopts+=(--tv TOPE="$performer") [ -n "$performer" ] && lameopts+=(--tv TOPE="$performer")
[ -n "$releasecountry" ] \ [ -n "$releasecountry" ] \
&& lameopts+=(--tv TXXX="MusicBrainz Album Release Country=$releasecountry") && lameopts+=(--tv TXXX="MusicBrainz Album Release Country=$releasecountry")
[ -n "$replaygain_alb" ] \
&& lameopts+=(--tv "TXXX=REPLAYGAIN_ALBUM_GAIN=$replaygain_alb")
[ -n "$replaygain_trk" ] \
&& lameopts+=(--tv "TXXX=REPLAYGAIN_TRACK_GAIN=$replaygain_trk")
[ -n "$disc" ] && lameopts+=(--tv TPOS="$disc") [ -n "$disc" ] && lameopts+=(--tv TPOS="$disc")
if (( ${destinationnoresample[$destination]:-0} == 1 )) if (( ${destinationnoresample[$destination]:-0} == 1 ))
then then
@ -53,7 +49,7 @@ encodeFile::mp3() {
esac esac
fi fi
fi fi
lameopts+=("$tempdir/$tmpfile.wav" "${destinationpath[$destination]}/$destdir/$destfile.mp3") lameopts+=("$tempdir/$tmpfile.wav" "$destdir/$destfile.mp3")
encodetaskid=$( encodetaskid=$(
Insert tasks <<-EOInsert Insert tasks <<-EOInsert
key ${fileid}lame$destination key ${fileid}lame$destination

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
encodeFile::opus() { encodeFile::opus() {
opusencopts=(${ionice}opusenc --quiet) opusencopts=(${ionice}opusenc --quiet)
opusencopts+=(--bitrate ${destinationquality[$destination]}) opusencopts+=(--bitrate ${destinationquality[$destination]})
@ -13,17 +13,11 @@ encodeFile::opus() {
[ -n "$performer" ] && opusencopts+=(--comment "PERFORMER=$performer") [ -n "$performer" ] && opusencopts+=(--comment "PERFORMER=$performer")
[ -n "$releasecountry" ] \ [ -n "$releasecountry" ] \
&& opusencopts+=(--comment "RELEASECOUNTRY=$releasecountry") && opusencopts+=(--comment "RELEASECOUNTRY=$releasecountry")
[ -n "$replaygain_alb" ] \
&& opusencopts+=(--comment) \
&& opusencopts+=("REPLAYGAIN_ALBUM_GAIN=$replaygain_alb")
[ -n "$replaygain_trk" ] \
&& opusencopts+=(--comment) \
&& opusencopts+=("REPLAYGAIN_TRACK_GAIN=$replaygain_trk")
[ -n "$title" ] && opusencopts+=(--title "$title") [ -n "$title" ] && opusencopts+=(--title "$title")
[ -n "$track" ] && opusencopts+=(--comment "TRACKNUMBER=${track%/*}") [ -n "$track" ] && opusencopts+=(--comment "TRACKNUMBER=${track%/*}")
[ -n "${track#*/}" ] && opusencopts+=(--comment "TRACKTOTAL=${track#*/}") [ -n "${track#*/}" ] && opusencopts+=(--comment "TRACKTOTAL=${track#*/}")
[ -n "$year" ] && opusencopts+=(--comment "DATE=$year") [ -n "$year" ] && opusencopts+=(--comment "DATE=$year")
opusencopts+=("$tempdir/$tmpfile".wav "${destinationpath[$destination]}/$destdir/$destfile.opus") opusencopts+=("$tempdir/$tmpfile".wav "$destdir/$destfile.opus")
encodetaskid=$( encodetaskid=$(
Insert tasks <<-EOInsert Insert tasks <<-EOInsert
key ${fileid}opusenc$destination key ${fileid}opusenc$destination

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
encodeFile::vorbis() { encodeFile::vorbis() {
oggencopts=(${ionice}oggenc -Q -q ${destinationquality[$destination]}) oggencopts=(${ionice}oggenc -Q -q ${destinationquality[$destination]})
[ -n "$albumartist" ] && oggencopts+=(-c "ALBUMARTIST=$albumartist") [ -n "$albumartist" ] && oggencopts+=(-c "ALBUMARTIST=$albumartist")
@ -9,15 +9,11 @@ encodeFile::vorbis() {
[ -n "$genre" ] && oggencopts+=(-G "$genre") [ -n "$genre" ] && oggencopts+=(-G "$genre")
[ -n "$performer" ] && oggencopts+=(-c "PERFORMER=$performer") [ -n "$performer" ] && oggencopts+=(-c "PERFORMER=$performer")
[ -n "$releasecountry" ] \ [ -n "$releasecountry" ] \
&& oggencopts+=(-c "RELEASECOUNTRY=$releasecountry") && oggencopts+=(--comment "RELEASECOUNTRY=$releasecountry")
[ -n "$replaygain_alb" ] \
&& oggencopts+=(-c "REPLAYGAIN_ALBUM_GAIN=$replaygain_alb")
[ -n "$replaygain_trk" ] \
&& oggencopts+=(-c "REPLAYGAIN_TRACK_GAIN=$replaygain_trk")
[ -n "$title" ] && oggencopts+=(-t "$title") [ -n "$title" ] && oggencopts+=(-t "$title")
[ -n "$track" ] && oggencopts+=(-N "$track") [ -n "$track" ] && oggencopts+=(-N "$track")
[ -n "$year" ] && oggencopts+=(-d "$year") [ -n "$year" ] && oggencopts+=(-d "$year")
oggencopts+=(-o "${destinationpath[$destination]}/$destdir/$destfile.ogg" "$tempdir/$tmpfile.wav") oggencopts+=(-o "$destdir/$destfile.ogg" "$tempdir/$tmpfile.wav")
encodetaskid=$( encodetaskid=$(
Insert tasks <<-EOInsert Insert tasks <<-EOInsert
key ${fileid}oggenc$destination key ${fileid}oggenc$destination

View File

@ -41,7 +41,7 @@ getDestDir() {
) )
) )
then then
destdir="" destdir="${destinationpath[$destination]}/"
if (( ${destinationascii["$destination"]} )) if (( ${destinationascii["$destination"]} ))
then then
echo "$album" >&${toascii[1]} echo "$album" >&${toascii[1]}
@ -83,12 +83,12 @@ getDestDir() {
replace=$(sanitizeFile "$disc" dir) replace=$(sanitizeFile "$disc" dir)
destdir="${destdir//?(\[)%\{disc\}?(\])/$replace}" destdir="${destdir//?(\[)%\{disc\}?(\])/$replace}"
else else
destdir=$(sanitizeFile "${filename%%/*}" dir) destdir="${destinationpath[$destination]}/"
destdir+=$(sanitizeFile "${filename%%/*}" dir)
part=${filename#*/} part=${filename#*/}
while [[ $part =~ / ]] while [[ $part =~ / ]]
do do
thispart="${part%%/*}" thispart="${part%%/*}"
thispart=${thispart//$'\n'/::AtOM:NewLine:SQL:Inline::}
if (( ${destinationascii["$destination"]} )) if (( ${destinationascii["$destination"]} ))
then then
echo "$thispart" >&${toascii[1]} echo "$thispart" >&${toascii[1]}
@ -99,9 +99,9 @@ getDestDir() {
part=${part#*/} part=${part#*/}
done done
fi fi
if ! [ -d "${destinationpath[$destination]}/$destdir" ] if ! [ -d "$destdir" ]
then then
mkdir -p "${destinationpath[$destination]}/${destdir//::AtOM:NewLine:SQL:Inline::/$'\n'}" mkdir -p "$destdir"
fi fi
destdir="${destdir//+(\/)//}" destdir="${destdir//+(\/)//}"
} }

View File

@ -55,11 +55,10 @@ getDestFile() {
destfile="${filename##*/}" destfile="${filename##*/}"
destfile="${destfile%.*}" destfile="${destfile%.*}"
fi fi
destfile=$(sanitizeFile "$destfile")
destfile=${destfile//$'\n'/::AtOM:NewLine:SQL:Inline::}
if (( ${destinationascii["$destination"]} )) if (( ${destinationascii["$destination"]} ))
then then
echo "$destfile" >&${toascii[1]} echo "$destfile" >&${toascii[1]}
read -r -u${toascii[0]} destfile read -r -u${toascii[0]} destfile
fi fi
destfile=$(sanitizeFile "$destfile")
} }

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
getFiles() { getFiles() {
scantime=$EPOCHSECONDS scantime=$(date +%s)
for prune_expression in "${skippeddirectories[@]}" for prune_expression in "${skippeddirectories[@]}"
do do
prunes+=( -path "$sourcepath$prune_expression" -prune -o ) prunes+=( -path "$sourcepath$prune_expression" -prune -o )
@ -8,19 +8,12 @@ getFiles() {
(( cron )) || echo -n "Scanning $sourcepath... " (( cron )) || echo -n "Scanning $sourcepath... "
# We probably have thousands of files, don't waste time on disk writes # We probably have thousands of files, don't waste time on disk writes
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
while read -d $'\0' time size filename while read time size filename
do do
if (( skip_us_timestamp ))
then
compare_time=${time%.*}.%
else
compare_time=$time
fi
if ! Select source_files id >/dev/null <<-EOWhere if ! Select source_files id >/dev/null <<-EOWhere
filename = ${filename//$'\n'/::AtOM:NewLine:SQL:Inline::} filename = $filename
mime_type > 0 mime_type > 0
last_change LIKE $compare_time last_change = $time
size = $size
EOWhere EOWhere
then then
mimetype=$(file -b --mime-type "$sourcepath/$filename") mimetype=$(file -b --mime-type "$sourcepath/$filename")
@ -47,7 +40,7 @@ getFiles() {
mime_type $mimetypeid \ mime_type $mimetypeid \
>/dev/null \ >/dev/null \
<<-EOWhere <<-EOWhere
filename ${filename//$'\n'/::AtOM:NewLine:SQL:Inline::} filename $filename
EOWhere EOWhere
(( ++new )) (( ++new ))
if (( new % 1000 == 0 )) if (( new % 1000 == 0 ))
@ -58,19 +51,15 @@ getFiles() {
fi fi
else else
Update source_files last_seen $scantime <<-EOWhere Update source_files last_seen $scantime <<-EOWhere
filename = ${filename//$'\n'/::AtOM:NewLine:SQL:Inline::} filename = $filename
EOWhere EOWhere
fi fi
progressSpin progressSpin
done < <( done < <(
find "$sourcepath" "${prunes[@]}" -type f -not -name '.*' -printf "%T@ %s %P\0" find "$sourcepath" "${prunes[@]}" -type f -not -name '.*' -printf "%T@ %s %P\n"
) )
echo 'COMMIT;' >&3 echo 'COMMIT;' >&3
(( cron )) || echo -n $'\r' (( cron )) || echo -n $'\r'
if (( count )) echo "${count:-0} files found, ${new:=0} new or changed."$'\033[K'
then
echo "$count files found${new:+, $new new or changed}." \
$'\033[K'
fi
unset count unset count
} }

View File

@ -251,7 +251,6 @@ setupDestination() {
%{artist}, %{artist},
%{disc}, %{disc},
%{genre}, %{genre},
%{releasecountry},
%{title}, %{title},
%{track}, %{track},
%{year}. %{year}.
@ -410,44 +409,6 @@ setupDestination() {
fi fi
done done
unset copiedmimes unset copiedmimes
cat <<-EODesc
Copy extensions (extension, string):
Files with extension <extension> will be copied as-is to the
destination. E.g. .jpg will copy covers and other images to the
destination.
This prompt will loop until an empty string is encountered.
EODesc
while [[ ${destinationcopyext["$destination"]} =~ \| ]]
do
copiedexts+=("${destinationcopyext["$destination"]%%|*}")
destinationcopyext["$destination"]="${destinationcopyext["$destination"]#*|}"
done
[ -n "${destinationcopyext["$destination"]}" ] \
&& copiedexts+=("${destinationcopyext["$destination"]}")
count=${#copiedexts[@]}
unset destinationcopyext["$destination"]
for (( i=0 ; 1 ; i++ ))
do
read \
-e \
${copiedexts[i]+-i"${copiedexts[i]}"} \
-p 'Copy extension: ' \
value
if [ -n "$value" ]
then
destinationcopyext[$destination]="${destinationcopyext[$destination]:+${destinationcopyext[$destination]}|}$value"
elif (( i < count ))
then
continue
else
break
fi
done
unset copiedexts
cat <<-EODesc cat <<-EODesc
Channels (integer): Channels (integer):

View File

@ -24,7 +24,6 @@ Completion is available for prompts asking for a paths or filenames.
destinationchannels \ destinationchannels \
destinationfat32compat \ destinationfat32compat \
destinationcopymime \ destinationcopymime \
destinationcopyext \
destinationformat \ destinationformat \
destinationfrequency \ destinationfrequency \
destinationid \ destinationid \
@ -41,7 +40,6 @@ Completion is available for prompts asking for a paths or filenames.
destinationchannels \ destinationchannels \
destinationfat32compat \ destinationfat32compat \
destinationcopymime \ destinationcopymime \
destinationcopyext \
destinationformat \ destinationformat \
destinationfrequency \ destinationfrequency \
destinationid \ destinationid \
@ -76,7 +74,7 @@ Completion is available for prompts asking for a paths or filenames.
esac esac
;; ;;
esac esac
read -p'Run index and conversion now? [Y/n] ' do_run read -p'Run now? [Y/n] ' do_run
case $do_run in case $do_run in
n) exit ;; n) exit ;;
*) ;; *) ;;

65
lib/tags/ffmpeg Normal file
View File

@ -0,0 +1,65 @@
#!/bin/bash
getInfosffmpeg_version='ffmpeg-7'
tagreaders+=( "$getInfosffmpeg_version" )
getInfos::ffmpeg() {
tagreader="$getInfosffmpeg_version"
local allinfos=$(
ffprobe -show_streams \
-i "$sourcepath/$filename" 2>&1 \
|sed '
/^Input/,/.* Audio: /{s/ *: */=/}
s/^[[:space:]]*//
s/\0//g'
)
local metadata=$(
echo -e "$allinfos" \
|sed -n '/Metadata=/,/\[STREAM\]/p'
)
local fmt_infos=$(
echo -e "$allinfos" \
|sed -n \
'/codec_type=audio/,/\[STREAM\]/{
/^\(sample_fmt\|sample_rate\|bit_rate\|channels\)=/{
p
}
}'
)
local infos="$metadata"
albumartist=$(gettag album_artist)
album=$(gettag album)
artist=$(gettag artist)
composer=$(gettag composer)
disc=$(gettag disc)
genre=$(gettag genre)
performer=$(gettag TOPE)
releasecountry=$(gettag releasecountry)
[[ -z "$releasecountry" ]] && releasecountry=$(gettag "MusicBrainz Album Release Country")
title=$(gettag title)
tracknum=$(gettag track)
year=$(gettag date)
expr='^[0-9]*$'
if [ -n "$genre" ] && [[ $genre =~ $expr ]]
then
genre="${id3genres[$genre]}"
fi
infos="$fmt_infos"
channels=$(gettag channels)
rate=$(gettag 'sample_rate')
case $rate in
96) rate=96000;;
48) rate=48000;;
441) rate=44100;;
32) rate=32000;;
24) rate=24000;;
225) rate=22500;;
esac
bitrate=$(gettag 'bit_rate')
bitdepth=$(gettag 'sample_fmt')
bitdepth=${bitdepth//[A-z]/}
if [[ $bitrate == N/A ]]
then
unset bitrate
else
bitrate=$((bitrate / 1000))
fi
}

50
lib/tags/flac Normal file
View File

@ -0,0 +1,50 @@
#!/bin/bash
getInfosFLAC_version='FLAC-4'
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=RELEASECOUNTRY \
--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)
releasecountry=$(gettag releasecountry)
title=$(gettag title)
tracknum=$(gettag tracknumber)
tracktotal=$(gettag tracktotal)
year=$(gettag date)
if [ -n "$tracknum" -a -n "$tracktotal" ]
then
tracknum="$tracknum/$tracktotal"
fi
year=$(gettag DATE)
{
read rate
read channels
read bitdepth
} < <(
metaflac \
--show-sample-rate \
--show-channels \
--show-bps \
"$sourcepath/$filename"
)
}

View File

@ -1,54 +0,0 @@
#!/usr/bin/env bash
getInfosFLAC_version='FLAC-5'
tagreaders+=( "$getInfosFLAC_version" )
getInfos::FLAC() {
local \
infos \
tagreader="$getInfosFLAC_version"
infos=$(
metaflac \
--show-sample-rate \
--show-channels \
--show-bps \
--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=RELEASECOUNTRY \
--show-tag=REPLAYGAIN_ALBUM_GAIN \
--show-tag=REPLAYGAIN_TRACK_GAIN \
--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)
releasecountry=$(gettag releasecountry)
replaygain_alb=$(gettag replaygain_album_gain)
replaygain_trk=$(gettag replaygain_track_gain)
title=$(gettag title)
tracknum=$(gettag tracknumber)
tracktotal=$(gettag tracktotal)
year=$(gettag date)
if [ -n "$tracknum" -a -n "$tracktotal" ]
then
tracknum="$tracknum/$tracktotal"
fi
year=$(gettag DATE)
{
read rate
read channels
read depth
} <<<"$infos"
}

View File

@ -1,75 +0,0 @@
#!/usr/bin/env bash
getInfosffmpeg_version='ffmpeg-9'
tagreaders+=( "$getInfosffmpeg_version" )
getInfos::ffmpeg() {
tagreader="$getInfosffmpeg_version"
local \
infos \
infos=$(
ffprobe -v error \
-show_entries " \
format_tags= \
album_artist, \
album, \
artist, \
composer, \
disc, \
genre, \
TOPE, \
releasecountry, \
'MusicBrainz Album Release Country',\
title, \
track, \
date, \
replaygain_track_gain, \
replaygain_album_gain \
:stream= \
bit_rate, \
channels, \
sample_rate, \
" \
-of default=noprint_wrappers=1 \
-i "$sourcepath/$filename" \
| egrep -v '=N/A$'
)
albumartist=$(gettag TAG:album_artist)
album=$(gettag TAG:album)
artist=$(gettag TAG:artist)
composer=$(gettag TAG:composer)
disc=$(gettag TAG:disc)
genre=$(gettag TAG:genre)
performer=$(gettag TAG:TOPE)
releasecountry=$(gettag TAG:releasecountry)
[[ -z "$releasecountry" ]] \
&& releasecountry=$(gettag "TAG:MusicBrainz Album Release Country")
replaygain_alb=$(gettag TAG:replaygain_album_gain)
replaygain_trk=$(gettag TAG:replaygain_track_gain)
title=$(gettag TAG:title)
tracknum=$(gettag TAG:track)
year=$(gettag TAG:date)
expr='^[0-9]*$'
if [ -n "$genre" ] && [[ $genre =~ $expr ]]
then
genre="${id3genres[$genre]}"
fi
channels=$(gettag channels)
rate=$(gettag 'sample_rate')
case $rate in
96) rate=96000;;
48) rate=48000;;
441) rate=44100;;
32) rate=32000;;
24) rate=24000;;
225) rate=22500;;
esac
bitrate=$(gettag 'bit_rate')
depth=$(gettag 'sample_fmt')
depth=${depth//[A-z]/}
if [[ $bitrate == N/A ]]
then
unset bitrate
else
bitrate=$((bitrate / 1000))
fi
}

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
getTags_version='unknown-4' getTags_version='unknown-4'
getTags() { getTags() {
local type unset type
case "$mimetype" in case "$mimetype" in
audio/mpeg) audio/mpeg)
type=ffmpeg type=ffmpeg

View File

@ -1,11 +1,8 @@
#!/usr/bin/env bash #!/bin/bash
getInfosOpus_version='Opus-4' getInfosOpus_version='Opus-3'
tagreaders+=( "$getInfosOpus_version" ) tagreaders+=( "$getInfosOpus_version" )
getInfos::Opus() { getInfos::Opus() {
tagreader="$getInfosOpus_version" tagreader="$getInfosOpus_version"
local \
infos \
infos=$( infos=$(
opusinfo "$sourcepath/$filename" \ opusinfo "$sourcepath/$filename" \
| sed 's/\t//' | sed 's/\t//'
@ -18,8 +15,6 @@ getInfos::Opus() {
genre=$(gettag genre) genre=$(gettag genre)
performer=$(gettag performer) performer=$(gettag performer)
releasecountry=$(gettag releasecountry) releasecountry=$(gettag releasecountry)
replaygain_alb=$(gettag replaygain_album_gain)
replaygain_trk=$(gettag replaygain_track_gain)
title=$(gettag title) title=$(gettag title)
tracknum=$(gettag tracknumber) tracknum=$(gettag tracknumber)
tracktotal=$(gettag tracktotal) tracktotal=$(gettag tracktotal)

View File

@ -1,11 +1,8 @@
#!/usr/bin/env bash #!/bin/bash
getInfosSoxi_version='soxi-3' getInfosSoxi_version='soxi-2'
tagreaders+=( "$getInfosSoxi_version" ) tagreaders+=( "$getInfosSoxi_version" )
getInfos::soxi() { getInfos::soxi() {
tagreader="$getInfosSoxi_version" tagreader="$getInfosSoxi_version"
local \
infos \
infos=$( infos=$(
soxi "$sourcepath/$filename" \ soxi "$sourcepath/$filename" \
| grep -v METADATA_BLOCK_PICTURE | grep -v METADATA_BLOCK_PICTURE
@ -18,8 +15,6 @@ getInfos::soxi() {
genre=$(gettag genre) genre=$(gettag genre)
performer=$(gettag performer) performer=$(gettag performer)
releasecountry=$(gettag releasecountry) releasecountry=$(gettag releasecountry)
replaygain_alb=$(gettag replaygain_album_gain)
replaygain_trk=$(gettag replaygain_track_gain)
title=$(gettag title) title=$(gettag title)
tracknum=$(gettag tracknumber) tracknum=$(gettag tracknumber)
tracktotal=$(gettag tracktotal) tracktotal=$(gettag tracktotal)
@ -34,6 +29,6 @@ getInfos::soxi() {
bitrate=$(gettag 'bit rate') bitrate=$(gettag 'bit rate')
bitrate=${bitrate%k} bitrate=${bitrate%k}
bitrate=${bitrate%%.*} bitrate=${bitrate%%.*}
depth=$(gettag precision) bitdepth=$(gettag precision)
depth=${depth%-bit} bitdepth=${bitdepth%-bit}
} }

View File

@ -1,7 +1,5 @@
#!/usr/bin/env bash #!/bin/bash
updateTags() { updateTags() {
local reader \
for reader in "${tagreaders[@]}" for reader in "${tagreaders[@]}"
do do
tagreaderclause+="${tagreaderclause:+ AND }NOT tags.tagreader = \"$reader\"" tagreaderclause+="${tagreaderclause:+ AND }NOT tags.tagreader = \"$reader\""
@ -21,8 +19,6 @@ updateTags() {
tags.genre, tags.genre,
tags.performer, tags.performer,
tags.releasecountry, tags.releasecountry,
tags.replaygain_alb,
tags.replaygain_trk,
tags.title, tags.title,
tags.track, tags.track,
tags.year, tags.year,
@ -52,12 +48,12 @@ echo '
; ;
SELECT "AtOM:NoMoreFiles";' >&3 SELECT "AtOM:NoMoreFiles";' >&3
read -u4 -r -d $'\0' line read -u4 line
while ! [[ $line = AtOM:NoMoreFiles ]] while ! [[ $line = AtOM:NoMoreFiles ]]
do do
tagfiles+=("$line") tagfiles+=("$line")
(( filecount++ )) (( filecount++ ))
read -u4 -r -d $'\0' line read -u4 line
done done
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
for line in "${tagfiles[@]}" for line in "${tagfiles[@]}"
@ -88,10 +84,6 @@ echo '
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
oldreleasecountry=${rest%%::AtOM:SQL:Sep::*} oldreleasecountry=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
oldreplaygain_alb=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldreplaygain_trk=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldtitle=${rest%%::AtOM:SQL:Sep::*} oldtitle=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::} rest=${rest#*::AtOM:SQL:Sep::}
oldtrack=${rest%%::AtOM:SQL:Sep::*} oldtrack=${rest%%::AtOM:SQL:Sep::*}
@ -105,7 +97,7 @@ echo '
oldbitrate=${rest%%::AtOM:SQL:Sep::*} oldbitrate=${rest%%::AtOM:SQL:Sep::*}
((++count)) ((++count))
(( cron )) || echo -en "\rTags: $((count*100/filecount))%" (( cron )) || echo -en "\rTags: $((count*100/filecount))%"
if (( count % 100 == 0 )) if (( count % 1000 == 0 ))
then then
echo 'COMMIT;BEGIN TRANSACTION;' >&3 echo 'COMMIT;BEGIN TRANSACTION;' >&3
(( debug )) \ (( debug )) \
@ -117,13 +109,11 @@ echo '
[[ $oldalbumartist != "$albumartist" ]]&&uaa=1 [[ $oldalbumartist != "$albumartist" ]]&&uaa=1
[[ $oldartist != "$artist" ]]&& uar=1 [[ $oldartist != "$artist" ]]&& uar=1
[[ $oldcomposer != "$composer" ]]&& uco=1 [[ $oldcomposer != "$composer" ]]&& uco=1
[[ $olddepth != "$depth" ]]&& ude=1 [[ $olddepth != "$bitdepth" ]]&& ude=1
[[ $olddisc != "$disc" ]]&& udi=1 [[ $olddisc != "$disc" ]]&& udi=1
[[ $oldgenre != "$genre" ]]&& uge=1 [[ $oldgenre != "$genre" ]]&& uge=1
[[ $oldperformer != "$performer" ]]&& upe=1 [[ $oldperformer != "$performer" ]]&& upe=1
[[ $oldreleasecountry != "$releasecountry" ]]&& urc=1 [[ $oldreleasecountry != "$releasecountry" ]]&& urc=1
[[ $oldreplaygain_alb != "$replaygain_alb" ]]&& urpa=1
[[ $oldreplaygain_trk != "$replaygain_trk" ]]&& urpt=1
[[ $oldtitle != "$title" ]]&& uti=1 [[ $oldtitle != "$title" ]]&& uti=1
[[ $oldtrack != "$tracknum" ]]&& utr=1 [[ $oldtrack != "$tracknum" ]]&& utr=1
[[ $oldyear != "$year" ]]&& uye=1 [[ $oldyear != "$year" ]]&& uye=1
@ -135,13 +125,11 @@ echo '
${uaa:+albumartist "${albumartist:+::AtOM:FT::}${albumartist:-NULL}"}\ ${uaa:+albumartist "${albumartist:+::AtOM:FT::}${albumartist:-NULL}"}\
${uar:+artist "${artist:+::AtOM:FT::}${artist:-NULL}"}\ ${uar:+artist "${artist:+::AtOM:FT::}${artist:-NULL}"}\
${uco:+composer "${composer:+::AtOM:FT::}${composer:-NULL}"}\ ${uco:+composer "${composer:+::AtOM:FT::}${composer:-NULL}"}\
${ude:+depth "${depth:-NULL}"} \ ${ude:+depth "${bitdepth:-NULL}"} \
${udi:+disc "${disc:-NULL}"} \ ${udi:+disc "${disc:-NULL}"} \
${uge:+genre "${genre:-NULL}"} \ ${uge:+genre "${genre:-NULL}"} \
${upe:+performer "${performer:+::AtOM:FT::}${performer:-NULL}"}\ ${upe:+performer "${performer:+::AtOM:FT::}${performer:-NULL}"}\
${urc:+releasecountry "${releasecountry:+::AtOM:FT::}${releasecountry:-NULL}"}\ ${urc:+releasecountry "${releasecountry:+::AtOM:FT::}${releasecountry:-NULL}"}\
${urpa:+replaygain_alb "${replaygain_alb:-NULL}"}\
${urpt:+replaygain_trk "${replaygain_trk:-NULL}"}\
${uti:+title "${title:+::AtOM:FT::}${title:-NULL}"}\ ${uti:+title "${title:+::AtOM:FT::}${title:-NULL}"}\
${utr:+track "${track:+::AtOM:FT::}${tracknum:-NULL}"}\ ${utr:+track "${track:+::AtOM:FT::}${tracknum:-NULL}"}\
${uye:+year "${year:-NULL}"} \ ${uye:+year "${year:-NULL}"} \
@ -162,29 +150,10 @@ echo '
composer \ composer \
performer \ performer \
releasecountry \ releasecountry \
replaygain_alb \
replaygain_trk \
rate \ rate \
depth \ bitdepth \
bitrate \ bitrate \
channels \ channels \
oldgenre \
oldalbumartist \
oldyear \
oldalbum \
olddisc \
oldartist \
oldtracknum \
oldtitle \
oldcomposer \
oldperformer \
oldreleasecountry \
oldreplaygain_alb \
oldreplaygain_trk \
oldrate \
olddepth \
oldbitrate \
oldchannels \
ual \ ual \
uaa \ uaa \
uar \ uar \
@ -194,8 +163,6 @@ echo '
uge \ uge \
upe \ upe \
urc \ urc \
urpa \
urpt \
uti \ uti \
utr \ utr \
uye \ uye \
@ -206,8 +173,8 @@ echo '
done done
echo 'COMMIT;' >&3 echo 'COMMIT;' >&3
(( cron )) || echo -n $'\r' (( cron )) || echo -n $'\r'
(( count )) && echo -n "Read tags from $count files." echo -n "Read tags from ${count:-0} files."
(( cron )) || echo -n $'\033[K' (( cron )) || echo -ne "\033[K"
(( count )) && echo echo
unset count tagfiles unset count tagfiles
} }

View File

@ -10,7 +10,7 @@ gettaskinfos() {
FROM tasks FROM tasks
WHERE id='$1'; WHERE id='$1';
' >&3 ' >&3
read -u4 -r -d $'\0' line read -u4 line
taskid=${line%%::AtOM:SQL:Sep::*} taskid=${line%%::AtOM:SQL:Sep::*}
rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::" rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::"
sourcefileid=${rest%%::AtOM:SQL:Sep::*} sourcefileid=${rest%%::AtOM:SQL:Sep::*}

View File

@ -2,7 +2,7 @@
createworker() { createworker() {
(( ++active )) (( ++active ))
read -u4 -r -d $'\0' line read -u4 line
taskid=${line%%::AtOM:SQL:Sep::*} taskid=${line%%::AtOM:SQL:Sep::*}
rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::" rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::"
sourcefileid=${rest%%::AtOM:SQL:Sep::*} sourcefileid=${rest%%::AtOM:SQL:Sep::*}

View File

@ -10,7 +10,7 @@ master() {
WHERE status = 0; WHERE status = 0;
'>&3 '>&3
read -u4 -r -d $'\0' remaining read -u4 remaining
if (( remaining == 0 )) if (( remaining == 0 ))
then then
sleep 0.1 sleep 0.1
@ -108,7 +108,7 @@ master() {
LIMIT 1; LIMIT 1;
'>&3 '>&3
read -u4 -r -d $'\0' ready read -u4 ready
if (( ready > 0 )) if (( ready > 0 ))
then then
createworker createworker
@ -194,7 +194,7 @@ master() {
LIMIT 1; LIMIT 1;
' >&3 ' >&3
read -u4 -r -d $'\0' ready read -u4 ready
if (( active == 0 && ready == 0 )) if (( active == 0 && ready == 0 ))
then then
@ -220,11 +220,10 @@ master() {
echo "Waiting for children to come back home..." echo "Waiting for children to come back home..."
wait wait
echo $'\nGood luck!' echo $'\nGood luck!'
exit $ETASKLEFT exit 1
elif (( ready == 0 )) elif (( ready == 0 ))
then then
sleep 0.1 sleep 0.1
break
else else
createworker createworker
fi fi
@ -234,7 +233,7 @@ master() {
WHERE status = 0; WHERE status = 0;
'>&3 '>&3
read -u4 -r -d $'\0' remaining read -u4 remaining
done done
fi fi
} }

View File

@ -1,24 +0,0 @@
#!/usr/bin/env bash
## Define exit codes
# General config errors [10-19]
EDBVERSION=10
ELOAD=11
EINTERVAL=12
ELOAD=13
EIONICE=14
ENOCFG=19
# Source cofig errors [20-29]
# Destination config errors [30-49]
EFORMAT=30
ECHANNEL=31
EQUALITY=32
EMAXBPS=33
EINVDEST=34
EFMTINVPARM=49
# Tasks
ETASKLEFT=50
# Sanity
ESANITY=60
# Invalid arguments
EINVARG=127

View File

@ -59,8 +59,6 @@ CREATE TABLE IF NOT EXISTS tags (
composer TEXT, composer TEXT,
performer TEXT, performer TEXT,
releasecountry TEXT, releasecountry TEXT,
replaygain_alb TEXT,
replaygain_trk TEXT,
depth INTEGER, depth INTEGER,
rate INTEGER, rate INTEGER,
channels INTEGER, channels INTEGER,
@ -133,12 +131,10 @@ CREATE TRIGGER IF NOT EXISTS force_destination_update_on_tag_update
composer, composer,
performer, performer,
releasecountry, releasecountry,
replaygain_alb,
replaygain_trk,
rate, rate,
channels, channels,
bitrate, bitrate,
depth bitdepth
ON tags ON tags
BEGIN BEGIN
UPDATE destination_files SET last_change=0 UPDATE destination_files SET last_change=0

View File

@ -1,26 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cat <<-EOWarn
This script is unmaintained and provided as-is. It may or may not work.
Use at your own risk!
EOWarn
read -p "Press Enter to continue or Ctrl-C to abort"
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
## Define exit codes
source "$SHAREDIR"/errorcodes
# config structures # config structures
declare -A \ declare -A \
destinationchannels \ destinationchannels \
@ -43,6 +22,16 @@ declare -A \
exit $EBASHVERS exit $EBASHVERS
} }
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
LC_ALL=C LC_ALL=C
shopt -s extglob shopt -s extglob
@ -121,7 +110,7 @@ getdstfiles() {
; ;
SELECT "AtOM:NoMoreFiles"; SELECT "AtOM:NoMoreFiles";
'>&3 '>&3
while read -u4 -r -d $'\0' line while read -u4 line
do do
if [[ $line == AtOM:NoMoreFiles ]] if [[ $line == AtOM:NoMoreFiles ]]
then then
@ -159,7 +148,7 @@ renameFile() {
fi fi
} }
while read -u4 -r -d $'\0' line while read -u4 line
do do
if [[ $line == AtOM:NoMoreFiles ]] if [[ $line == AtOM:NoMoreFiles ]]
then then

View File

@ -1,18 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
## Define exit codes
source "$SHAREDIR"/errorcodes
# config structures # config structures
declare -A \ declare -A \
destinationchannels \ destinationchannels \
@ -35,6 +22,16 @@ declare -A \
exit $EBASHVERS exit $EBASHVERS
} }
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
LC_ALL=C LC_ALL=C
shopt -s extglob shopt -s extglob

View File

@ -1,18 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
## Define exit codes
source "$SHAREDIR"/errorcodes
# config structures # config structures
declare -A \ declare -A \
destinationchannels \ destinationchannels \
@ -35,6 +22,16 @@ declare -A \
exit $EBASHVERS exit $EBASHVERS
} }
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
LC_ALL=C LC_ALL=C
shopt -s extglob shopt -s extglob
@ -65,42 +62,27 @@ getConfig
sanityCheck sanityCheck
openDatabase openDatabase
echo -n "Checking for missing files... " echo 'SELECT id,filename FROM destination_files WHERE filename IS NOT NULL;' >&3
echo '
SELECT
destination_files.id,
destinations.name,
destination_files.filename
FROM destinations
INNER JOIN destination_files
ON destinations.id=destination_files.destination_id
WHERE filename IS NOT NULL;' >&3
echo 'SELECT "AtOM:NoMoreFiles";' >&3 echo 'SELECT "AtOM:NoMoreFiles";' >&3
declare -a \ read -u4 filename
destination_names \ until [[ $filename == AtOM:NoMoreFiles ]]
files
read -u4 -r -d $'\0' line
until [[ $line == AtOM:NoMoreFiles ]]
do do
id=${line%%::AtOM:SQL:Sep::*} files+=("$filename")
rest=${line#*::AtOM:SQL:Sep::} read -u4 filename
destination_names[id]=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
files[id]=${rest}
read -u4 -r -d $'\0' line
done done
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
for index in "${!files[@]}" echo -n "Checking for missing files... "
for filename in "${files[@]}"
do do
destination=${destination_names[index]} id=${filename%%::AtOM:SQL:Sep::*}
filename="${destinationpath[$destination]}/${files[index]}" filename=${filename#*::AtOM:SQL:Sep::}
if ! [ -f "$filename" ] if ! [ -f "$filename" ]
then then
echo -e "\r$filename\033[K" echo -e "\r$filename\033[K"
((regen))&&Update destination_files last_change 0 <<<"id = $index" ((regen))&&Update destination_files last_change 0 <<<"id = $id"
echo -n "Checking for missing files... " echo -n "Checking for missing files... "
(( missing++ )) (( missing++ ))
fi fi
@ -109,6 +91,6 @@ done
echo 'COMMIT;' >&3 echo 'COMMIT;' >&3
echo -e "\r${missing:-No} missing files\033[K" echo -e "\r${missing:-0} missing files\033[K"
closeDatabase closeDatabase

View File

@ -1,18 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
## Define exit codes
source "$SHAREDIR"/errorcodes
# config structures # config structures
declare -A \ declare -A \
destinationchannels \ destinationchannels \
@ -35,6 +22,16 @@ declare -A \
exit $EBASHVERS exit $EBASHVERS
} }
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
LC_ALL=C LC_ALL=C
shopt -s extglob shopt -s extglob
@ -65,22 +62,25 @@ getConfig
sanityCheck sanityCheck
openDatabase openDatabase
checkwanted() {
Select id <<<"filename = $1"
}
for destination in "${!destinationpath[@]}" for destination in "${!destinationpath[@]}"
do do
echo -ne "\rScanning destination $destination... \033[K" echo -ne "\rScanning destination $destination... \033[K"
while read -r -d $'\0' filename while read -r filename
do do
sqlfile=${filename//$'\n'/::AtOM:NewLine:SQL:Inline::}
if ! Select destination_files id \ if ! Select destination_files id \
>/dev/null \ >/dev/null \
<<<"filename = ${sqlfile#${destinationpath["$destination"]}/}" <<<"filename = $filename"
then then
echo -e $'\r'"$filename\033[K" echo -e $'\r'"$filename\033[K"
(( remove )) && rm "$filename" (( remove )) && rm -f "$filename"
echo -n "Scanning destination $destination... " echo -n "Scanning destination $destination... "
fi fi
progressSpin progressSpin
done < <(find "${destinationpath["$destination"]}" -type f -print0) done < <(find "${destinationpath["$destination"]}" -type f)
done done
echo -en "\r\033[K" echo -en "\r\033[K"

View File

@ -1,17 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
declare -r \ #!/bin/bash
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
## Define exit codes
source "$SHAREDIR"/errorcodes
# config structures # config structures
declare -A \ declare -A \
@ -35,11 +24,21 @@ declare -A \
exit $EBASHVERS exit $EBASHVERS
} }
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
LC_ALL=C LC_ALL=C
shopt -s extglob shopt -s extglob
source "$SHAREDIR"/id3genres source ./share/id3genres
for function in "$LIBDIR"/*/* for function in "$LIBDIR"/*/*
do do
@ -274,7 +273,7 @@ fi
echo 'SELECT IFNULL( echo 'SELECT IFNULL(
(SELECT last_seen FROM source_files ORDER BY last_seen DESC LIMIT 1), (SELECT last_seen FROM source_files ORDER BY last_seen DESC LIMIT 1),
0);' >&3 0);' >&3
read -u4 -r -d $'\0' lastupdate read -u4 lastupdate
if ! [[ "$output" == - ]] if ! [[ "$output" == - ]]
then then
@ -288,12 +287,12 @@ cat <<-EOBrag
# #
# $0 $args # $0 $args
# #
# Last database update: $(printf "%(%x %X)T" "$lastupdate") # Last database update: $(date -d @$lastupdate +'%x %X')
EOBrag EOBrag
printDate() { printDate() {
printf "%("${timeformat:-%x %X}")T" "$1" date -d"@$1" +"${timeformat:-%x %X}"
} }
for index in ${!show[@]} for index in ${!show[@]}
@ -369,11 +368,11 @@ COLLATE NOCASE;
SELECT "AtOM:NoMoreFiles";' >&3 SELECT "AtOM:NoMoreFiles";' >&3
read -u4 -r -d $'\0' line read -u4 line
until [[ $line == AtOM:NoMoreFiles ]] until [[ $line == AtOM:NoMoreFiles ]]
do do
files+=("$line") files+=("$line")
read -u4 -r -d $'\0' line read -u4 line
done done
for line in "${files[@]}" for line in "${files[@]}"
@ -453,10 +452,7 @@ do
expr2='(,|$)' expr2='(,|$)'
if ! [[ $channelss =~ $expr1"$channels"$expr2 ]] if ! [[ $channelss =~ $expr1"$channels"$expr2 ]]
then then
if [[ -n "$channels" ]] if [ -n "$channelss" ] \
then
:
elif [ -n "$channelss" ] \
&& (( channels < ${channelss%%,*} )) && (( channels < ${channelss%%,*} ))
then then
channelss="$channels,$channelss" channelss="$channels,$channelss"
@ -466,10 +462,7 @@ do
fi fi
if ! [[ $rates =~ $expr1"$rate"$expr2 ]] if ! [[ $rates =~ $expr1"$rate"$expr2 ]]
then then
if [[ -n "$rate" ]] if [ -n "$rates" ] \
then
:
elif [ -n "$rates" ] \
&& (( rate < ${rates%%,*} )) && (( rate < ${rates%%,*} ))
then then
rates="$rate,$rates" rates="$rate,$rates"
@ -479,10 +472,7 @@ do
fi fi
if [ -n "$depth" ] && ! [[ $depths =~ $expr1"$depth"$expr2 ]] if [ -n "$depth" ] && ! [[ $depths =~ $expr1"$depth"$expr2 ]]
then then
if [[ -n "$depth" ]] if [ -n "$depths" ] \
then
:
elif [ -n "$depths" ] \
&& (( depth < ${depths%%,*} )) && (( depth < ${depths%%,*} ))
then then
depths="$depth,$depths" depths="$depth,$depths"
@ -675,7 +665,7 @@ echo '
SELECT "AtOM:NoMoreFiles";' >&3 SELECT "AtOM:NoMoreFiles";' >&3
read -u4 -r -d $'\0' line read -u4 line
until [[ $line == AtOM:NoMoreFiles ]] until [[ $line == AtOM:NoMoreFiles ]]
do do
artist="${line%%::AtOM:SQL:Sep::*}" artist="${line%%::AtOM:SQL:Sep::*}"
@ -685,7 +675,7 @@ do
artists+=( "$artist" ) artists+=( "$artist" )
maxcountlen=$(( ${#count} > maxcountlen ? ${#count} : maxcountlen )) maxcountlen=$(( ${#count} > maxcountlen ? ${#count} : maxcountlen ))
maxartistlen=$(( ${#artist} > maxartistlen ? ${#artist} : maxartistlen )) maxartistlen=$(( ${#artist} > maxartistlen ? ${#artist} : maxartistlen ))
read -u4 -r -d $'\0' line read -u4 line
done done
head=$( head=$(
printf "| # | %'${maxcoutlen}s | %-${maxartistlen}s |" \ printf "| # | %'${maxcoutlen}s | %-${maxartistlen}s |" \
@ -714,7 +704,7 @@ echo '
FROM source_files FROM source_files
INNER JOIN mime_types INNER JOIN mime_types
ON source_files.mime_type=mime_types.id;' >&3 ON source_files.mime_type=mime_types.id;' >&3
read -u4 -r -d $'\0' line read -u4 line
totalcount="${line%%::AtOM:SQL:Sep::*}" totalcount="${line%%::AtOM:SQL:Sep::*}"
maxcountlen=$(printf "%'i" $totalcount) maxcountlen=$(printf "%'i" $totalcount)
maxcountlen=${#maxcountlen} maxcountlen=${#maxcountlen}
@ -743,7 +733,7 @@ do
INNER JOIN mime_types INNER JOIN mime_types
ON source_files.mime_type=mime_types.id ON source_files.mime_type=mime_types.id
WHERE mime_text LIKE "'"$format"'";' >&3 WHERE mime_text LIKE "'"$format"'";' >&3
read -u4 -r -d $'\0' line read -u4 line
count="${line%%::AtOM:SQL:Sep::*}" count="${line%%::AtOM:SQL:Sep::*}"
rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::" rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::"
size="${rest%%::AtOM:SQL:Sep::*}" size="${rest%%::AtOM:SQL:Sep::*}"

View File

@ -1,18 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
## Define exit codes
source "$SHAREDIR"/errorcodes
# config structures # config structures
declare -A \ declare -A \
destinationchannels \ destinationchannels \
@ -35,6 +22,16 @@ declare -A \
exit $EBASHVERS exit $EBASHVERS
} }
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
LC_ALL=C LC_ALL=C
shopt -s extglob shopt -s extglob
@ -131,11 +128,11 @@ echo ') ORDER BY bitrate;' >&3
echo 'SELECT "AtOM:NoMoreFiles";' >&3 echo 'SELECT "AtOM:NoMoreFiles";' >&3
read -u4 -r -d $'\0' line read -u4 line
until [[ $line == AtOM:NoMoreFiles ]] until [[ $line == AtOM:NoMoreFiles ]]
do do
echo "${line//::AtOM:SQL:Sep::/$'\t'}" echo "${line//::AtOM:SQL:Sep::/$'\t'}"
read -u4 -r -d $'\0' line read -u4 line
done done
closeDatabase closeDatabase

View File

@ -1,18 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
## Define exit codes
source "$SHAREDIR"/errorcodes
# config structures # config structures
declare -A \ declare -A \
destinationchannels \ destinationchannels \
@ -36,6 +23,16 @@ declare -A \
exit $EBASHVERS exit $EBASHVERS
} }
declare -r \
DOCDIR=%DOCDIR% \
LIBDIR=%LIBDIR% \
SHAREDIR=%SHAREDIR%
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
LC_ALL=C LC_ALL=C
shopt -s extglob shopt -s extglob
@ -149,11 +146,11 @@ cat >&3 <<-EOSelect
SELECT "AtOM:NoMoreFiles"; SELECT "AtOM:NoMoreFiles";
EOSelect EOSelect
read -u4 -r -d $'\0' line read -u4 line
until [[ $line == AtOM:NoMoreFiles ]] until [[ $line == AtOM:NoMoreFiles ]]
do do
lines+=( "$line" ) lines+=( "$line" )
read -u4 -r -d $'\0' line read -u4 line
done done
for line in "${lines[@]}" for line in "${lines[@]}"