Merge branch 'master' into video

This commit is contained in:
Vincent Riquer 2013-05-31 22:21:27 +02:00
commit 95e7bbbbf6
37 changed files with 1374 additions and 526 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.ex
*.EX
trace.log

47
README
View File

@ -19,21 +19,26 @@ Required:
Optional: Optional:
* vorbis-tools * vorbis-tools
http://www.vorbis.com/ http://www.vorbis.com/
* ogginfo (Ogg Vorbis tags support) * ogginfo (Ogg Vorbis metadata)
* oggenc (Ogg Vorbis destination) * oggenc (Ogg Vorbis encoding)
* opus-tools * opus-tools
http://opus-codec.org/ http://opus-codec.org/
* opusinfo (Opus tags support) * opusinfo (Opus metadata)
* opusenc (Opus destination) * opusenc (Opus encoding)
* opusdec (Opus decoding)
* LAME MP3 Encoder * LAME MP3 Encoder
http://lame.sourceforge.net/ http://lame.sourceforge.net/
* lame (MP3 destination) * lame (MP3 encoding)
* FLAC * FLAC
http://flac.sourceforge.net/ http://flac.sourceforge.net/
* metaflac (FLAC tags support) * metaflac (FLAC metadata)
* Musepack * Musepack
http://www.musepack.net/ http://www.musepack.net/
* mpcdec (Musepack decoding) * mpcdec (Musepack decoding)
* FFmpeg
http://ffmpeg.org/
* ffprobe (ID3v2, Musepack, Windows Media and video metadata)
* ffmpeg (Windows Media and video decoding)
================== ==================
Using the software Using the software
@ -41,3 +46,33 @@ Using the software
Configuration: Configuration:
Please read doc/config before anything else. Please read doc/config before anything else.
====
Toys
----
AtOM requires a database to function. Now that we have a database containing
various information about our media files, why not use it?
AtOM comes with a small set of tools in the toys/ directory. These are
documented in toys/README.
========================
Shameless Self Promotion
------------------------
I am the author of free (Creative Commons CC-By-SA) music which you can stream
for free, or buy to get high quality and bonuses from Bandcamp
(http://djblackred.bandcamp.com). If you like electronic music taking its
inspiration from Trance, Drum & Bass, Ambient and (rarely) Free Jazz, please
check it out!
Downloads are available in FLAC, Ogg, MP3, and more, and includes the "source
code" (sequencer files and the likes) for most tracks.
I am receiving 85% of the money you'll spend, so you won't be feeding some
greedy BigCorp producer or distributor.
And if you don't like it, you can still spread the word to friends who may like.
You can see this as a way to thank me for this piece of code.
=====
Legal
-----
Some of the format and/or tool names cited above are trademarks belonging to
their rightful owners. AtOM and its authors are not linked in any way to
those companies or individuals. Said companies do not endorse nor support
AtOM in any way.

396
atom
View File

@ -96,8 +96,6 @@ do
esac esac
done done
#FIXME: check sanity
if [ ! -f "$cffile" ] if [ ! -f "$cffile" ]
then then
if [ ! -d ~/.atom ] if [ ! -d ~/.atom ]
@ -117,9 +115,143 @@ fi
getConfig getConfig
set +H set +H
# Apply CLI overrides
[ -n "$cliload" ] && maxload=$cliload
[ -n "$cliltimer" ] && loadinterval=$cliltimer
(( debug || cfgdump )) && printConfig (( debug || cfgdump )) && printConfig
(( cfgdump )) && exit (( cfgdump )) && exit
# check sanity
if [ ! -d "$tempdir" ] && ! mkdir -p "$tempdir"
then
echo "[FATAL] Could not create temp directory $tempdir" >&2
(( sanityfail++ ))
fi
if [ ! -f "$database" ] && [ ! -d "${database%/*}" ] && ! mkdir -p "${database%/*}"
then
echo "[FATAL] Directory holding database file does not exist and could" \
"not be created" >&2
(( sanityfail++ ))
fi
if [ ! -d "$sourcepath" ]
then
echo "[FATAL] Source path $sourcepath does not exist or is not a directory" >&2
(( sanityfail++ ))
fi
if ! which sed >/dev/null
then
echo "[FATAL] Required tool sed is not installed or not in PATH
I never thought this would actually hit someone..." >&2
(( sanityfail++ ))
fi
if ! which sox >/dev/null
then
echo "[FATAL] Required tool sox is not installed or not in PATH" >&2
(( sanityfail++ ))
fi
if ! which ogginfo >/dev/null
then
echo "[WARNING] Tool ogginfo (from vorbis-tools) is not" \
"installed or not in PATH
Vorbis metadata disabled" >&2
disableogginfo=1
(( sanitywarn++ ))
fi
if (( oggencneeded )) && ! which oggenc >/dev/null
then
echo "[WARNING] Tool oggenc (from vorbis-tools) is not" \
"installed or not in PATH
Vorbis targets disabled" >&2
disableoggenc=1
(( sanitywarn++ ))
fi
if ! which opusinfo >/dev/null
then
echo "[WARNING] Tool opusinfo (from opus-tools) is not" \
"installed or not in PATH
Opus metadata disabled" >&2
disableopusinfo=1
(( sanitywarn++ ))
fi
if (( opusencneeded )) && ! which opusenc >/dev/null
then
echo "[WARNING] Tool opusenc (from opus-tools) is not" \
"installed or not in PATH
Opus targets disabled" >&2
disableopusenc=1
(( sanitywarn++ ))
fi
if ! which opusdec >/dev/null
then
echo "[WARNING] Tool opusdec (from opus-tools) is not" \
"installed or not in PATH
Opus support disabled" >&2
disableopusdec=1
(( sanitywarn++ ))
fi
if (( lameneeded )) && ! which lame >/dev/null
then
echo "[WARNING] Tool lame is not installed or not in PATH
MP3 targets disabled" >&2
disablelame=1
(( sanitywarn++ ))
fi
if ! which metaflac >/dev/null
then
echo "[WARNING] Tool metaflac (from FLAC) is not installed" \
"or not in PATH
FLAC metadata disabled" >&2
disableflac=1
(( sanitywarn++ ))
fi
if ! which mpcdec >/dev/null
then
echo "[WARNING] Tool mpcdec (from Musepack) is not" \
"installed or not in PATH
Musepack support disabled" >&2
disablempcdec=1
(( sanitywarn++ ))
fi
if ! which ffprobe >/dev/null
then
echo "[WARNING] Tool ffprobe (from FFmpeg) is not installed or not in PATH
Video metadata disabled
MPEG metadata disabled
MusePack metadata disabled
Unknown format metadata disabled" >&2
disableffprobe=1
(( sanitywarn++ ))
fi
if ! which ffmpeg >/dev/null
then
echo "[WARNING] Tool ffmpeg is not installed or not in PATH
Video support disabled" >&2
disablevideo=1
(( sanitywarn++ ))
fi
if (( sanityfail ))
then
echo "
Sanity checks raised ${sanitywarn:-0} warnings, $sanityfail failures. Dying now." >&2
exit $ESANITY
elif (( sanitywarn ))
then
echo "
Sanity checks raised $sanitywarn warnings... Hit Control-C to abort." >&2
timeout=$(( sanitywarn * 10 ))
echo -n "Starting in $(printf %3i $timeout)" \
$'seconds...\b\b\b\b\b\b\b\b\b\b\b' >&2
while (( timeout ))
do
echo -n $'\b\b\b'"$(printf %3i $timeout)" >&2
sleep 1
(( timeout-- ))
done
echo -en "\r\033[K"
fi
openDatabase openDatabase
createDestinations createDestinations
@ -144,8 +276,8 @@ removed=0
read -u4 line read -u4 line
until [[ $line == AtOM:NoMoreFiles ]] until [[ $line == AtOM:NoMoreFiles ]]
do do
id=${line%|*} id=${line%::AtOM:SQL:Sep::*}
filename=${line#*|} filename=${line#*::AtOM:SQL:Sep::}
if [ -n "$filename" ] if [ -n "$filename" ]
then then
if rm -f "$filename" if rm -f "$filename"
@ -161,115 +293,20 @@ do
done done
echo "Suppressed $deleted files, $removed removed from database" echo "Suppressed $deleted files, $removed removed from database"
# get files updateTags
for reader in "${tagreaders[@]}"
do
tagreaderclause+="${tagreaderclause:+ AND }NOT tags.tagreader = \"$reader\""
done
echo '
SELECT COUNT(DISTINCT source_files.filename)
FROM source_files
INNER JOIN destination_files
ON destination_files.source_file_id=source_files.id
INNER JOIN destinations
ON destination_files.destination_id=destinations.id
INNER JOIN mime_type_actions
ON destinations.id=mime_type_actions.destination_id
INNER JOIN tags
ON source_files.id=tags.source_file
WHERE mime_type_actions.id = source_files.mime_type
AND (
CAST(tags.last_change AS TEXT)
<>
CAST(source_files.last_change AS TEXT)
OR ('"$tagreaderclause"')
)
AND mime_type_actions.action = 1;' >&3
read -u4 filecount
echo '
SELECT DISTINCT
source_files.id,
source_files.last_change,
mime_type_actions.mime_text,
source_files.filename
FROM source_files
INNER JOIN destination_files
ON destination_files.source_file_id=source_files.id
INNER JOIN destinations
ON destination_files.destination_id=destinations.id
INNER JOIN mime_type_actions
ON destinations.id=mime_type_actions.destination_id
INNER JOIN tags
ON source_files.id=tags.source_file
WHERE mime_type_actions.id = source_files.mime_type
AND (
CAST(tags.last_change AS TEXT)
<>
CAST(source_files.last_change AS TEXT)
OR ('"$tagreaderclause"')
)
AND mime_type_actions.action = 1;
SELECT "AtOM:NoMoreFiles";' >&3 for forcedest in "${forceall[@]}"
read -u4 line
while ! [[ $line = AtOM:NoMoreFiles ]]
do do
tagfiles+=("$line") if forcedestid=$(Select destinations id <<<"name = $forcedest")
read -u4 line
done
echo 'BEGIN TRANSACTION;' >&3
for line in "${tagfiles[@]}"
do
sourcefileid=${line%%|*}
rest=${line#*|}
lastchange=${rest%%|*}
rest=${rest#*|}
mimetype=${rest%%|*}
filename=${rest#*|}
echo -en "\rTags: $((++count*100/filecount))%"
if (( count % 1000 == 0 ))
then then
echo 'COMMIT;BEGIN TRANSACTION;' >&3 echo "Resetting destination files timestamps on" \
(( debug )) \ "$forcedest ($forcedestid)..."
&& echo -n " $count files read, committing..." Update destination_files last_change 1 \
fi <<<"destination_id = $forcedestid"
if getTags else
then echo "Destination $forcedest does not exist!" >&2
Update tags \
album "${album:-NULL}" \
albumartist "${albumartist:-NULL}" \
artist "${artist:-NULL}" \
composer "${composer:-NULL}" \
disc "${disc:-NULL}" \
genre "${genre:-NULL}" \
performer "${performer:-NULL}" \
title "${title:-NULL}" \
track "${tracknum:-NULL}" \
year "${year:-NULL}" \
last_change "$lastchange" \
rate "${rate:-NULL}" \
channels "${channels:-NULL}" \
bitrate "${bitrate:-NULL}" \
tagreader "$tagreader" \
>/dev/null <<<"source_file = $sourcefileid"
unset genre \
albumartist \
year \
album \
disc \
artist \
tracknum \
title \
composer \
performer \
rate \
bitrate \
channels
fi fi
done done
echo 'COMMIT;' >&3
echo -e "\rRead tags from ${count:-0} files."
unset count tagfiles
echo ' echo '
CREATE TEMPORARY TABLE tasks( CREATE TEMPORARY TABLE tasks(
@ -376,7 +413,7 @@ echo '
read -u4 line read -u4 line
while ! [[ $line = AtOM:NoMoreFiles ]] while ! [[ $line = AtOM:NoMoreFiles ]]
do do
decodefiles+=("$line|") decodefiles+=("$line::AtOM:SQL:Sep::")
read -u4 line read -u4 line
done done
echo -n 'Creating tasks... ' echo -n 'Creating tasks... '
@ -384,42 +421,47 @@ echo -n 'Creating tasks... '
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
for line in "${decodefiles[@]}" for line in "${decodefiles[@]}"
do do
fileid=${line%%|*} fileid=${line%%::AtOM:SQL:Sep::*}
rest=${line#*|} rest=${line#*::AtOM:SQL:Sep::}
filename=${rest%%|*} filename=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
mimetype=${rest%%|*} mimetype=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
destination=${rest%%|*} destination=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
destfileid=${rest%%|*} destfileid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
rate=${rest%%|*} rate=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
channels=${rest%%|*} channels=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
bitrate=${rest%%|*} bitrate=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
genre=${rest%%|*} genre=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
albumartist=${rest%%|*} albumartist=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
year=${rest%%|*} year=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
album=${rest%%|*} album=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
disc=${rest%%|*} disc=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
artist=${rest%%|*} artist=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
track=${rest%%|*} track=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
title=${rest%%|*} title=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
composer=${rest%%|*} composer=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
performer=${rest%%|*} performer=${rest%%::AtOM:SQL:Sep::*}
unset rest unset rest
case ${destinationformat["$destination"]} in
vorbis) (( disableoggenc )) && continue ;;
opus) (( disableopusenc )) && continue ;;
mp3) (( disablelame )) && continue ;;
esac
decodeFile decodeFile
getDestDir getDestDir
getDestFile getDestFile
@ -502,13 +544,13 @@ do
checkworkers checkworkers
cleaner cleaner
master master
if (( ran )) if (( ran - failed ))
then then
currenttime=$(date +%s) currenttime=$(date +%s)
avgduration=$(( avgduration=$((
((currenttime - starttime) * 1000) ((currenttime - starttime) * 1000)
/ /
ran ( ran - failed )
)) ))
secsremaining=$(( remaining * avgduration / 1000 )) secsremaining=$(( remaining * avgduration / 1000 ))
(( days = (( days =
@ -647,32 +689,32 @@ do
echo -n "$destination: rename pattern changed, renaming files... " echo -n "$destination: rename pattern changed, renaming files... "
while [[ $line != AtOM:NoMoreFiles ]] while [[ $line != AtOM:NoMoreFiles ]]
do do
oldfilename=${line%%|*} oldfilename=${line%%::AtOM:SQL:Sep::*}
rest=${line#*|}'|' rest=${line#*::AtOM:SQL:Sep::}'::AtOM:SQL:Sep::'
destfileid=${rest%%|*} destfileid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
filename=${rest%%|*} filename=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
album=${rest%%|*} album=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
albumartist=${rest%%|*} albumartist=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
artist=${rest%%|*} artist=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
composer=${rest%%|*} composer=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
disc=${rest%%|*} disc=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
genre=${rest%%|*} genre=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
performer=${rest%%|*} performer=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
title=${rest%%|*} title=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
track=${rest%%|*} track=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
year=${rest%%|*} year=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
if [ -n "$oldfilename" -a -f "$oldfilename" ] if [ -n "$oldfilename" -a -f "$oldfilename" ]
then then
getDestDir getDestDir
@ -720,11 +762,11 @@ done
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
for line in "${lines[@]}" for line in "${lines[@]}"
do do
id=${line%%|*} id=${line%%::AtOM:SQL:Sep::*}
rest=${line#*|} rest=${line#*::AtOM:SQL:Sep::}
filename=${rest%%|*} filename=${rest%%::AtOM:SQL:Sep::*}
oldfilename=${rest#*|} oldfilename=${rest#*::AtOM:SQL:Sep::}
if [[ $oldfilename != $filename ]] && [ -f "$oldfilename" ] if [[ $oldfilename != "$filename" ]] && [ -f "$oldfilename" ]
then then
rm -f "$oldfilename" rm -f "$oldfilename"
fi fi

View File

@ -8,12 +8,15 @@ getConfigDestination() {
case "$value" in case "$value" in
'mp3') 'mp3')
destinationformat["$destination"]=mp3 destinationformat["$destination"]=mp3
lameneeded=1
;; ;;
'opus') 'opus')
destinationformat["$destination"]=opus destinationformat["$destination"]=opus
opusencneeded=1
;; ;;
'vorbis') 'vorbis')
destinationformat["$destination"]=vorbis destinationformat["$destination"]=vorbis
oggencneeded=1
;; ;;
*) *)
echo "Unsupported destination format: $value" >2& echo "Unsupported destination format: $value" >2&

View File

@ -6,6 +6,7 @@ copyFiles_action() {
source_files.filename, source_files.filename,
source_files.last_change, source_files.last_change,
destinations.id, destinations.id,
destinations.name,
destination_files.id destination_files.id
FROM source_files FROM source_files
INNER JOIN destination_files INNER JOIN destination_files
@ -31,36 +32,43 @@ copyFiles_action() {
echo 'BEGIN TRANSACTION;' >&3 echo 'BEGIN TRANSACTION;' >&3
for copyfile in "${copyfiles[@]}" for copyfile in "${copyfiles[@]}"
do do
sourcefilename=${copyfile%%|*} sourcefilename=${copyfile%%::AtOM:SQL:Sep::*}
sourcedir=${sourcefilename%/*} sourcedir=${sourcefilename%/*}
rest="${copyfile#*|}|" rest="${copyfile#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::"
lastchange=${rest%%|*} lastchange=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
destinationid=${rest%%|*} destinationid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
destfileid=${rest%%|*} destination=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
echo 'SELECT IFNULL( ( destfileid=${rest%%::AtOM:SQL:Sep::*}
SELECT destination_files.filename rest=${rest#*::AtOM:SQL:Sep::}
FROM destination_files (( count++ ))
INNER JOIN source_files printf '\b\b\b\b%3i%%' $(( (count * 100) / ${#copyfiles[@]} ))
ON destination_files.source_file_id=source_files.id if [ -n "${renamepath["$destination"]}" ]
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
"'"${sourcedir//\"/\"\"}"'/%"
AND mime_type_actions.action = 1
LIMIT 1
),"AtOM:NotFound");
'>&3
read -u4 filename
if [[ $filename != AtOM:NotFound ]]
then then
destdir=${filename%/*} destdir="$(guessPath)" || continue
else
destdir="${destinationpath["$destination"]}/"
if [[ $sourcefilename =~ / ]]
then
destdir+=$(
sanitizeFile "${sourcefilename%%/*}" dir
)
part=${sourcefilename#*/}
while [[ $part =~ / ]]
do
destdir+="/$(
sanitizeFile "${part%%/*}" dir
)"
part=${part#*/}
done
if ! [ -d "$destdir" ]
then
mkdir -p "$destdir"
fi
fi
fi
if cp -al "$sourcepath/$sourcefilename" "$destdir" 2>/dev/null\ if cp -al "$sourcepath/$sourcefilename" "$destdir" 2>/dev/null\
|| cp -a "$sourcepath/$sourcefilename" "$destdir" || cp -a "$sourcepath/$sourcefilename" "$destdir"
then then
@ -73,11 +81,13 @@ copyFiles_action() {
EOWhere EOWhere
(( done++ )) (( done++ ))
fi fi
fi
(( count++ ))
printf '\b\b\b\b%3i%%' $(( (count * 100) / ${#copyfiles[@]} ))
done done
echo 'COMMIT;' >&3 echo 'COMMIT;' >&3
if (( count ))
then
echo -e "\rCopied ${done:-0} of $count files.\033[K" echo -e "\rCopied ${done:-0} of $count files.\033[K"
else
echo -e "\rNothing to copy.\033[K"
fi
unset count done unset count done
} }

28
lib/copy/guessPath Normal file
View File

@ -0,0 +1,28 @@
#!/bin/bash
guessPath() {
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
"'"${sourcedir//\"/\"\"}"'/%"
AND mime_type_actions.action = 1
LIMIT 1
),"AtOM:NotFound");
'>&3
read -u4 filename
if [[ $filename != AtOM:NotFound ]]
then
echo "${filename%/*}"
else
return 1
fi
}

View File

@ -15,6 +15,10 @@ Insert() {
insert_keys+='`'"$key"'`' insert_keys+='`'"$key"'`'
(( ${#insert_values} )) && insert_values+="," (( ${#insert_values} )) && insert_values+=","
case $value in case $value in
'::AtOM:FT::'*)
value="${value//::AtOM:FT::/}"
insert_values+='"'"${value//\"/\"\"}"'"'
;;
'NULL') 'NULL')
insert_values+="NULL" insert_values+="NULL"
;; ;;

View File

@ -25,7 +25,8 @@ InsertIfUnset() {
Select "$table" "$column" < <( Select "$table" "$column" < <(
for key in ${!keys[@]} for key in ${!keys[@]}
do do
echo "${keys[$key]}" = "${values[$key]}" echo "${keys[$key]}" = \
"${values[$key]//::AtOM:FT::}"
done done
) )
) )

View File

@ -26,6 +26,10 @@ Update() {
;; ;;
value) value)
case $argument in case $argument in
'::AtOM:FT::'*)
argument="${argument//::AtOM:FT::/}"
set_statement+=" = "'"'"${argument//\"/\"\"}"'"'
;;
'NULL') 'NULL')
set_statement+=" = NULL" set_statement+=" = NULL"
;; ;;

View File

@ -1,19 +1,7 @@
#!/bin/bash #!/bin/bash
openDatabase() { openDatabase() {
if [ ! -d "$tempdir" ]
then
mkdir -p "$tempdir"
fi
rm -f "$tempdir"/sqlite.{in,out} rm -f "$tempdir"/sqlite.{in,out}
mkfifo "$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" \ sqlite3 -bail "$database" \
< "$tempdir/sqlite.in" \ < "$tempdir/sqlite.in" \
> "$tempdir/sqlite.out" & > "$tempdir/sqlite.out" &
@ -24,5 +12,7 @@ openDatabase() {
exec 5>&3 exec 5>&3
exec 3> >(tee -a $tempdir/debug.log >&5) exec 3> >(tee -a $tempdir/debug.log >&5)
fi fi
cat $schema >&3
echo '.separator ::AtOM:SQL:Sep::' >&3
echo 'PRAGMA foreign_keys = ON;' >&3 echo 'PRAGMA foreign_keys = ON;' >&3
} }

View File

@ -2,6 +2,7 @@
decodeFile() { decodeFile() {
case "$mimetype" in case "$mimetype" in
'video/'*) 'video/'*)
(( disablevideo )) && continue
extractAudio extractAudio
if (( ${destinationnormalize["$destination"]}))\ if (( ${destinationnormalize["$destination"]}))\
|| ( || (
@ -30,6 +31,7 @@ decodeFile() {
then then
copied=1 copied=1
else else
(( disableopusdec )) && continue
decodeOpusdec decodeOpusdec
if (( ${destinationnormalize["$destination"]}))\ if (( ${destinationnormalize["$destination"]}))\
|| ( || (
@ -60,6 +62,7 @@ decodeFile() {
extendedtype=$(file -b "$sourcepath/$filename") extendedtype=$(file -b "$sourcepath/$filename")
case "$extendedtype" in case "$extendedtype" in
*'Musepack '*) *'Musepack '*)
(( disablempcdec )) && continue
decodeMpcdec decodeMpcdec
if (( ${destinationnormalize["$destination"]}))\ if (( ${destinationnormalize["$destination"]}))\
|| ( || (

View File

@ -51,7 +51,12 @@ encodeFile::mp3() {
$( $(
for key in ${!lameopts[@]} for key in ${!lameopts[@]}
do do
echo "cmd_arg$key ${lameopts[key]}" cleanedopts="${lameopts[key]//\&/\\\&}"
cleanedopts="${cleanedopts//\[/\\[}"
cleanedopts="${cleanedopts//\]/\\]}"
cleanedopts="${cleanedopts//\{/\\{}"
cleanedopts="${cleanedopts//\}/\\\}}"
echo "cmd_arg$key $cleanedopts"
done done
) )
cleanup $tempdir/$tmpfile.wav cleanup $tempdir/$tmpfile.wav

View File

@ -26,7 +26,12 @@ encodeFile::opus() {
$( $(
for key in ${!opusencopts[@]} for key in ${!opusencopts[@]}
do do
echo "cmd_arg$key ${opusencopts[key]}" cleanedopts="${opusencopts[key]//\&/\\\&}"
cleanedopts="${cleanedopts//\[/\\[}"
cleanedopts="${cleanedopts//\]/\\]}"
cleanedopts="${cleanedopts//\{/\\{}"
cleanedopts="${cleanedopts//\}/\\\}}"
echo "cmd_arg$key $cleanedopts"
done done
) )
cleanup $tempdir/$tmpfile.wav cleanup $tempdir/$tmpfile.wav

View File

@ -22,6 +22,11 @@ encodeFile::vorbis() {
$( $(
for key in ${!oggencopts[@]} for key in ${!oggencopts[@]}
do do
cleanedopts="${oggencopts[key]//\&/\\\&}"
cleanedopts="${cleanedopts//\[/\\[}"
cleanedopts="${cleanedopts//\]/\\]}"
cleanedopts="${cleanedopts//\{/\\{}"
cleanedopts="${cleanedopts//\}/\\\}}"
echo "cmd_arg$key ${oggencopts[key]}" echo "cmd_arg$key ${oggencopts[key]}"
done done
) )

View File

@ -1,7 +1,34 @@
#!/bin/bash #!/bin/bash
getDestDir() { getDestDir() {
destdir="${destinationpath[$destination]}/" destdir="${destinationpath[$destination]}/"
if [ -n "${destinationrenamepath[$destination]}" ] if [ -n "${destinationrenamepath[$destination]}" ] \
&& (
(
! [[ ${destinationrenamepath[$destination]} =~ %\{album\} ]] \
|| [ -n "$album" ]
) && (
! [[ ${destinationrenamepath[$destination]} =~ %\{albumartist\} ]] \
|| [ -n "$albumartist" ]
) && (
! [[ ${destinationrenamepath[$destination]} =~ %\{artist\} ]] \
|| [ -n "$artist" ]
) && (
! [[ ${destinationrenamepath[$destination]} =~ %\{genre\} ]] \
|| [ -n "$genre" ]
) && (
! [[ ${destinationrenamepath[$destination]} =~ %\{title\} ]] \
|| [ -n "$title" ]
) && (
! [[ ${destinationrenamepath[$destination]} =~ %\{track\} ]] \
|| [ -n "$track" ]
) && (
! [[ ${destinationrenamepath[$destination]} =~ %\{year\} ]] \
|| [ -n "$year" ]
) && (
! [[ ${destinationrenamepath[$destination]} =~ %\{disc\} ]] \
|| [ -n "$disc" ]
)
)
then then
replace=$(sanitizeFile "$album" dir) replace=$(sanitizeFile "$album" dir)
destdir+="${destinationrenamepath[$destination]//%\{album\}/$replace}" destdir+="${destinationrenamepath[$destination]//%\{album\}/$replace}"

View File

@ -1,6 +1,33 @@
#!/bin/bash #!/bin/bash
getDestFile() { getDestFile() {
if [ -n "${destinationrename[$destination]}" ] if [ -n "${destinationrename[$destination]}" ] \
&& (
(
! [[ ${destinationrename[$destination]} =~ %\{album\} ]] \
|| [ -n "$album" ]
) && (
! [[ ${destinationrename[$destination]} =~ %\{albumartist\} ]] \
|| [ -n "$albumartist" ]
) && (
! [[ ${destinationrename[$destination]} =~ %\{artist\} ]] \
|| [ -n "$artist" ]
) && (
! [[ ${destinationrename[$destination]} =~ %\{genre\} ]] \
|| [ -n "$genre" ]
) && (
! [[ ${destinationrename[$destination]} =~ %\{title\} ]] \
|| [ -n "$title" ]
) && (
! [[ ${destinationrename[$destination]} =~ %\{track\} ]] \
|| [ -n "$track" ]
) && (
! [[ ${destinationrename[$destination]} =~ %\{year\} ]] \
|| [ -n "$year" ]
) && (
! [[ ${destinationrename[$destination]} =~ %\{disc\} ]] \
|| [ -n "$disc" ]
)
)
then then
destfile="${destinationrename[$destination]//%\{album\}/$album}" destfile="${destinationrename[$destination]//%\{album\}/$album}"
destfile="${destfile//%\{albumartist\}/$albumartist}" destfile="${destfile//%\{albumartist\}/$albumartist}"

View File

@ -19,7 +19,7 @@ getFiles() {
mimetype=$(file -b --mime-type "$sourcepath/$filename") mimetype=$(file -b --mime-type "$sourcepath/$filename")
if [[ $mimetype == application/ogg ]] if [[ $mimetype == application/ogg ]]
then then
case "$(head -n1 "$sourcepath/$filename")" in case "$(head -n5 "$sourcepath/$filename")" in
*'vorbis'*) *'vorbis'*)
mimetype+=' vorbis' mimetype+=' vorbis'
;; ;;

View File

@ -1,55 +0,0 @@
#!/bin/bash
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"
}

View File

@ -1,27 +0,0 @@
#!/bin/bash
getInfosMP3_version='ID3-3'
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="${id3genres[$genre]}"
fi
infos="${infos/: /=}"
channels=$(gettag channels)
rate=$(gettag 'sample rate')
bitrate=$(gettag 'bit rate')
bitrate=${bitrate%%.*}
bitrate=${bitrate%k}
}

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
getInfosOgg_version='Ogg-1' getInfosOgg_version='Ogg-2'
tagreaders+=( "$getInfosOgg_version" ) tagreaders+=( "$getInfosOgg_version" )
getInfos::Ogg() { getInfos::Ogg() {
tagreader="$getInfosOgg_version" tagreader="$getInfosOgg_version"
@ -22,9 +22,9 @@ getInfos::Ogg() {
tracknum="$tracknum/$tracktotal" tracknum="$tracknum/$tracktotal"
fi fi
year=$(gettag date) year=$(gettag date)
infos="${infos/: /=}" infos="${infos//: /=}"
rate=$(gettag rate|head -n1) rate=$(gettag rate|head -n1)
channels=$(gettag channels|head -n1) channels=$(gettag channels|head -n1)
bitrate=$(gettag 'nominal bitrate') bitrate=$(gettag 'average bitrate')
bitrate=${bitrate%%,*} bitrate=${bitrate%%,*}
} }

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
getInfosOpus_version='Opus-1' getInfosOpus_version='Opus-2'
tagreaders+=( "$getInfosOpus_version" ) tagreaders+=( "$getInfosOpus_version" )
getInfos::Opus() { getInfos::Opus() {
tagreader="$getInfosOpus_version" tagreader="$getInfosOpus_version"
@ -22,7 +22,7 @@ getInfos::Opus() {
tracknum="$tracknum/$tracktotal" tracknum="$tracknum/$tracktotal"
fi fi
year=$(gettag date) year=$(gettag date)
infos="${infos/: /=}" infos="${infos//: /=}"
rate=$(gettag 'original sample rate'|head -n1) rate=$(gettag 'original sample rate'|head -n1)
channels=$(gettag channels|head -n1) channels=$(gettag channels|head -n1)
bitrate=$(gettag 'average bitrate') bitrate=$(gettag 'average bitrate')

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
getInfosffmpeg_version='ffmpeg-1' getInfosffmpeg_version='ffmpeg-2'
tagreaders+=( "$getInfosffmpeg_version" ) tagreaders+=( "$getInfosffmpeg_version" )
getInfos::ffmpeg() { getInfos::ffmpeg() {
tagreader="$getInfosffmpeg_version" tagreader="$getInfosffmpeg_version"
@ -31,8 +31,8 @@ getInfos::ffmpeg() {
genre=$(gettag genre) genre=$(gettag genre)
performer=$(gettag TOPE) performer=$(gettag TOPE)
title=$(gettag title) title=$(gettag title)
tracknum=$(gettag tracknumber) tracknum=$(gettag track)
year=$(gettag year) year=$(gettag date)
expr='^[0-9]*$' expr='^[0-9]*$'
if [ -n "$genre" ] && [[ $genre =~ $expr ]] if [ -n "$genre" ] && [[ $genre =~ $expr ]]
then then

View File

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

View File

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

View File

@ -1,39 +1,31 @@
#!/bin/bash #!/bin/bash
getTags_version='unknown-3' getTags_version='unknown-4'
tagreaders+=( "$getTags_version" )
getTags() { getTags() {
unset type unset type
case "$mimetype" in case "$mimetype" in
audio/mpeg) audio/mpeg)
type=MP3 type=ffmpeg
(( disableffprobe )) && unset type
;; ;;
'application/ogg opus') 'application/ogg opus')
type=Opus type=Opus
(( disableopusinfo )) && unset type
;; ;;
application/ogg*) application/ogg*)
type=Ogg type=Ogg
(( disableogginfo )) && unset type
;; ;;
audio/x-flac) audio/x-flac)
type=FLAC type=FLAC
(( disableflac )) && unset type
;; ;;
video/*) video/*)
type=ffmpeg type=ffmpeg
(( disableffprobe )) && unset type
;; ;;
*) *)
extendedtype=$(file -b "$sourcepath/$filename") type=ffmpeg
case "$extendedtype" in (( disableffprobe )) && unset type
*' ID3 '*)
type=MP3
;;
*'Musepack '*)
getRateChannelMPC
tryAPE
;;
*)
getRateChannelSoxi
tryAPE
;;
esac
;; ;;
esac esac
if [ -n "$type" ] if [ -n "$type" ]

158
lib/tags/updateTags Normal file
View File

@ -0,0 +1,158 @@
#!/bin/bash
updateTags() {
for reader in "${tagreaders[@]}"
do
tagreaderclause+="${tagreaderclause:+ AND }NOT tags.tagreader = \"$reader\""
done
echo '
SELECT DISTINCT
source_files.id,
source_files.last_change,
mime_type_actions.mime_text,
source_files.filename,
tags.album,
tags.albumartist,
tags.artist,
tags.composer,
tags.disc,
tags.genre,
tags.performer,
tags.title,
tags.track,
tags.year,
tags.rate,
tags.channels,
tags.bitrate
FROM source_files
INNER JOIN destination_files
ON destination_files.source_file_id=source_files.id
INNER JOIN destinations
ON destination_files.destination_id=destinations.id
INNER JOIN mime_type_actions
ON destinations.id=mime_type_actions.destination_id
INNER JOIN tags
ON source_files.id=tags.source_file
WHERE mime_type_actions.id = source_files.mime_type
AND (
CAST(tags.last_change AS TEXT)
<>
CAST(source_files.last_change AS TEXT)
OR ('"$tagreaderclause"')
)
AND mime_type_actions.action = 1;
SELECT "AtOM:NoMoreFiles";' >&3
read -u4 line
while ! [[ $line = AtOM:NoMoreFiles ]]
do
tagfiles+=("$line")
(( filecount++ ))
read -u4 line
done
echo 'BEGIN TRANSACTION;' >&3
for line in "${tagfiles[@]}"
do
sourcefileid=${line%%::AtOM:SQL:Sep::*}
rest=${line#*::AtOM:SQL:Sep::}
lastchange=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
mimetype=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
filename=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldalbum=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldalbumartist=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldartist=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldcomposer=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
olddisc=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldgenre=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldperformer=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldtitle=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldtrack=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldyear=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldrate=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldchannels=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
oldbitrate=${rest%%::AtOM:SQL:Sep::*}
echo -en "\rTags: $((++count*100/filecount))%"
if (( count % 1000 == 0 ))
then
echo 'COMMIT;BEGIN TRANSACTION;' >&3
(( debug )) \
&& echo -n " $count files read, committing..."
fi
if getTags
then
[[ $oldalbum != "$album" ]]&& ual=1
[[ $oldalbumartist != "$albumartist" ]]&&uaa=1
[[ $oldartist != "$artist" ]]&& uar=1
[[ $oldcomposer != "$composer" ]]&& uco=1
[[ $olddisc != "$disc" ]]&& udi=1
[[ $oldgenre != "$genre" ]]&& uge=1
[[ $oldperformer != "$performer" ]]&& upe=1
[[ $oldtitle != "$title" ]]&& uti=1
[[ $oldtrack != "$tracknum" ]]&& utr=1
[[ $oldyear != "$year" ]]&& uye=1
[[ $oldrate != "$rate" ]]&& ura=1
[[ $oldchannels != "$channels" ]]&& uch=1
[[ $oldbitrate != "$bitrate" ]]&& ubi=1
Update tags \
${ual:+album "::AtOM:FT::${album:-NULL}"}\
${uaa:+albumartist "::AtOM:FT::${albumartist:-NULL}"}\
${uar:+artist "::AtOM:FT::${artist:-NULL}"}\
${uco:+composer "::AtOM:FT::${composer:-NULL}"}\
${udi:+disc "${disc:-NULL}"} \
${uge:+genre "${genre:-NULL}"} \
${upe:+performer "::AtOM:FT::${performer:-NULL}"}\
${uti:+title "::AtOM:FT::${title:-NULL}"}\
${utr:+track "::AtOM:FT::${tracknum:-NULL}"}\
${uye:+year "${year:-NULL}"} \
last_change "$lastchange" \
${ura:+rate "${rate:-NULL}"} \
${uch:+channels "${channels:-NULL}"} \
${ubi:+bitrate "${bitrate:-NULL}"} \
tagreader "$tagreader" \
>/dev/null <<<"source_file = $sourcefileid"
unset genre \
albumartist \
year \
album \
disc \
artist \
tracknum \
title \
composer \
performer \
rate \
bitrate \
channels \
ual \
uaa \
uar \
uco \
udi \
uge \
upe \
uti \
utr \
uye \
ura \
uch \
ubi
fi
done
echo 'COMMIT;' >&3
echo -e "\rRead tags from ${count:-0} files.\033[K"
unset count tagfiles
}

View File

@ -12,16 +12,16 @@ gettaskinfos() {
WHERE id='$1'; WHERE id='$1';
' >&3 ' >&3
read -u4 line read -u4 line
taskid=${line%%|*} taskid=${line%%::AtOM:SQL:Sep::*}
rest="${line#*|}|" rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::"
sourcefileid=${rest%%|*} sourcefileid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
required=${rest%%|*} required=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cleanup=${rest%%|*} cleanup=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
destfileid=${rest%%|*} destfileid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
destfilename=${rest%%|*} destfilename=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
} }

View File

@ -10,6 +10,7 @@ cleaner() {
EOWhere EOWhere
) )
(( failed+=faildepends )) (( failed+=faildepends ))
(( ran+=faildepends ))
Update tasks status 2 <<<"id = $taskid" Update tasks status 2 <<<"id = $taskid"
Update tasks status 2 <<<"requires = $taskid" Update tasks status 2 <<<"requires = $taskid"
echo "SELECT COUNT(*) echo "SELECT COUNT(*)

View File

@ -69,78 +69,78 @@ master() {
else else
(( ++active )) (( ++active ))
read -u4 line read -u4 line
taskid=${line%%|*} taskid=${line%%::AtOM:SQL:Sep::*}
rest="${line#*|}|" rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::"
sourcefileid=${rest%%|*} sourcefileid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
required=${rest%%|*} required=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg=("${rest%%|*}") cmd_arg=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cmd_arg+=("${rest%%|*}") cmd_arg+=("${rest%%::AtOM:SQL:Sep::*}")
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
cleanup=${rest%%|*} cleanup=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
destfileid=${rest%%|*} destfileid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
destfilename=${rest%%|*} destfilename=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
for key in ${!cmd_arg[@]} for key in ${!cmd_arg[@]}
do do
[ -z "${cmd_arg[key]}" ] && unset cmd_arg[key] [ -z "${cmd_arg[key]}" ] && unset cmd_arg[key]

View File

@ -4,12 +4,3 @@ worker() {
(( debug >= 2 )) && echo "${cmd_arg[@]}" >&2 (( debug >= 2 )) && echo "${cmd_arg[@]}" >&2
"${cmd_arg[@]}" >/dev/null "${cmd_arg[@]}" >/dev/null
} }
createworker() {
worker $1 &
workers[$1]=$!
}
destroyworker() {
dyingworker=${workers[$1]}
unset workers[$1]
wait $dyingworker
}

View File

@ -1,5 +1,5 @@
BEGIN TRANSACTION; BEGIN TRANSACTION;
CREATE TABLE source_files ( CREATE TABLE IF NOT EXISTS source_files (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
filename TEXT UNIQUE NOT NULL, filename TEXT UNIQUE NOT NULL,
size INTEGER NOT NULL, size INTEGER NOT NULL,
@ -10,11 +10,11 @@ CREATE TABLE source_files (
FOREIGN KEY (mime_type) REFERENCES mime_types(id) FOREIGN KEY (mime_type) REFERENCES mime_types(id)
ON DELETE SET NULL ON DELETE SET NULL
); );
CREATE TABLE destinations ( CREATE TABLE IF NOT EXISTS destinations (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
name TEXT UNIQUE NOT NULL name TEXT UNIQUE NOT NULL
); );
CREATE TABLE destination_files ( CREATE TABLE IF NOT EXISTS destination_files (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
filename TEXT, filename TEXT,
old_filename TEXT, old_filename TEXT,
@ -27,11 +27,11 @@ CREATE TABLE destination_files (
FOREIGN KEY (destination_id) REFERENCES destinations(id) FOREIGN KEY (destination_id) REFERENCES destinations(id)
ON DELETE CASCADE ON DELETE CASCADE
); );
CREATE TABLE mime_types ( CREATE TABLE IF NOT EXISTS mime_types (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
mime_text TEXT UNIQUE NOT NULL mime_text TEXT UNIQUE NOT NULL
); );
CREATE TABLE mime_actions ( CREATE TABLE IF NOT EXISTS mime_actions (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
mime_type INTEGER, mime_type INTEGER,
destination_id INTEGER, destination_id INTEGER,
@ -40,7 +40,7 @@ CREATE TABLE mime_actions (
FOREIGN KEY (destination_id) REFERENCES destinations(id) FOREIGN KEY (destination_id) REFERENCES destinations(id)
ON DELETE CASCADE ON DELETE CASCADE
); );
CREATE TABLE tags ( CREATE TABLE IF NOT EXISTS tags (
source_file INTEGER PRIMARY KEY, source_file INTEGER PRIMARY KEY,
genre TEXT, genre TEXT,
albumartist TEXT, albumartist TEXT,
@ -61,13 +61,14 @@ CREATE TABLE tags (
ON DELETE CASCADE ON DELETE CASCADE
); );
CREATE VIEW mime_type_actions AS CREATE VIEW IF NOT EXISTS mime_type_actions AS
SELECT SELECT
mime_types.id,mime_types.mime_text, mime_types.id,mime_types.mime_text,
mime_actions.destination_id,mime_actions.action mime_actions.destination_id,mime_actions.action
FROM mime_types INNER JOIN mime_actions FROM mime_types INNER JOIN mime_actions
ON mime_actions.mime_type = mime_types.id; ON mime_actions.mime_type = mime_types.id;
CREATE TRIGGER update_mime_actions INSTEAD OF UPDATE OF action ON mime_type_actions CREATE TRIGGER IF NOT EXISTS update_mime_actions
INSTEAD OF UPDATE OF action ON mime_type_actions
BEGIN BEGIN
UPDATE mime_actions UPDATE mime_actions
SET action=new.action SET action=new.action
@ -75,7 +76,8 @@ BEGIN
AND destination_id=old.destination_id; AND destination_id=old.destination_id;
END; END;
CREATE TRIGGER create_dest_files_and_mime_actions AFTER INSERT ON destinations CREATE TRIGGER IF NOT EXISTS create_dest_files_and_mime_actions
AFTER INSERT ON destinations
BEGIN BEGIN
INSERT INTO mime_actions INSERT INTO mime_actions
(mime_type,destination_id) (mime_type,destination_id)
@ -87,7 +89,7 @@ BEGIN
FROM source_files; FROM source_files;
END; END;
CREATE TRIGGER create_mime_actions AFTER INSERT ON mime_types CREATE TRIGGER IF NOT EXISTS create_mime_actions AFTER INSERT ON mime_types
BEGIN BEGIN
INSERT INTO mime_actions (mime_type,destination_id) INSERT INTO mime_actions (mime_type,destination_id)
SELECT mime_types.id,destinations.id SELECT mime_types.id,destinations.id
@ -95,18 +97,39 @@ BEGIN
WHERE mime_types.id=new.id; WHERE mime_types.id=new.id;
END; END;
CREATE INDEX sourcefiles_by_name ON source_files (filename,id); CREATE INDEX IF NOT EXISTS sourcefiles_by_name ON source_files (filename,id);
CREATE TRIGGER create_destinations AFTER INSERT ON source_files CREATE TRIGGER IF NOT EXISTS create_destinations AFTER INSERT ON source_files
BEGIN BEGIN
INSERT INTO destination_files (source_file_id,destination_id) INSERT INTO destination_files (source_file_id,destination_id)
SELECT source_files.id,destinations.id FROM source_files SELECT source_files.id,destinations.id FROM source_files
INNER JOIN destinations INNER JOIN destinations
WHERE source_files.id=new.id; WHERE source_files.id=new.id;
END; END;
CREATE TRIGGER create_tags AFTER INSERT ON source_files CREATE TRIGGER IF NOT EXISTS create_tags AFTER INSERT ON source_files
BEGIN BEGIN
INSERT INTO tags (source_file,last_change) VALUES (new.id,0); INSERT INTO tags (source_file,last_change) VALUES (new.id,0);
END; END;
DROP TRIGGER IF EXISTS force_destination_update_on_tag_update;
CREATE TRIGGER force_destination_update_on_tag_update
AFTER UPDATE OF
genre,
albumartist,
year,
album,
disc,
artist,
track,
title,
composer,
performer,
rate,
channels,
bitrate
ON tags
BEGIN
UPDATE destination_files SET last_change=0
WHERE source_file_id=old.source_file;
END;
COMMIT; COMMIT;

View File

@ -1,3 +1,37 @@
createindex
===========
Creates a nice index of your collection, similar to what oidua
(http://oidua.suxbad.com/) does, but much faster, as it is using AtOM's DB
instead of rescanning your whole collection.
Options define what will be shown and may optionnally be followed by the column
width (default: 50).
Options:
-f: Path
-b: Average bitrate
-C: Channels
-s: Sample rate
-m: Mofification time
-M: Format
-A: Album artist
-l: Album
-a: Artist
-c: Composer
-d: Disc
-g: Genre
-p: Performer
-t: Title
-y: Year
-T <format>: date-time format (see 'man date' for possible values)
-o -|<file>: output file (path relative to Source) - mandatory - must
appear last.
-u: update database first
-D: debug
checkextensions checkextensions
=============== ===============
Reports files whose extension does not match the (detected) mime-type. Reports files whose extension does not match the (detected) mime-type.

View File

@ -112,8 +112,8 @@ getdstfiles() {
done done
for line in "${lines[@]}" for line in "${lines[@]}"
do do
fileid=${line%|*} fileid=${line%::AtOM:SQL:Sep::*}
filename=${line#*|} filename=${line#*::AtOM:SQL:Sep::}
echo $'\t'"$filename" echo $'\t'"$filename"
(( rename )) && echo -n $'\t' (( rename )) && echo -n $'\t'
(( rename )) && renameFile (( rename )) && renameFile
@ -149,12 +149,12 @@ do
done done
for line in "${lines[@]}" for line in "${lines[@]}"
do do
fileid=${line%%|*} fileid=${line%%::AtOM:SQL:Sep::*}
rest="${line#*|}|" rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::"
filename=${rest%%|*} filename=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
mimetype=${rest%%|*} mimetype=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
dest=$sourcepath dest=$sourcepath
case "$mimetype" in case "$mimetype" in
'audio/mpeg') 'audio/mpeg')

View File

@ -61,7 +61,7 @@ checkwanted() {
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 filename while read -r filename
do do
if ! Select destination_files id \ if ! Select destination_files id \
>/dev/null \ >/dev/null \

435
toys/createindex Executable file
View File

@ -0,0 +1,435 @@
#!/bin/bash
#!/bin/bash
# config structures
declare -A \
destinationchannels \
destinationfat32compat \
destinationcopymime \
destinationformat \
destinationfrequency \
destinationid \
destinationloss \
destinationmaxbps \
destinationnormalize \
destinationpath \
destinationquality \
destinationrename \
destinationnoresample \
destinationrenamepath \
destinationskipmime \
|| {
echo "Check your Bash version. You need >= 4.0" >&2
exit $EBASHVERS
}
declare -r \
DOCDIR=./doc \
LIBDIR=./lib \
SHAREDIR=./share
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
cffile="$HOME/.atom/atom.cfg"
shopt -s extglob
for function in "$LIBDIR"/*/*
do
source "$function"
done
while [ -n "$1" ]
do
opt="$1"
shift
case $opt in
'-A') show+=(albumartists) ;;
'-l') show+=(albums) ;;
'-a') show+=(artists) ;;
'-b') show+=(bitrates) ;;
'-c') show+=(composers) ;;
'-C') show+=(channelss) ;;
'-d') show+=(discs) ;;
'-f') show+=(path) ;;
'-g') show+=(genres) ;;
'-m') show+=(oldtimestamp) ;;
'-M') show+=(types) ;;
'-p') show+=(performers) ;;
'-s') show+=(rates) ;;
'-t') show+=(titles) ;;
'-y') show+=(years) ;;
'-T') timeformat="$OPTARG" ;;
'-o') output="$1"
continue ;;
'-u') update=1 ;;
'-D') (( debug++ )) ;;
[0-9]*) length[count-1]=$opt
continue ;;
esac
(( count++ ))
done
[ -z "$output" ] && {
cat <<-EOHelp
No output specified!
-f Path
-b Average bitrate
-C Channels
-s Sample rate
-m Mofification time
-M Format
-A Album artist
-l Album
-a Artist
-c Composer
-d Disc
-g Genre
-p Performer
-t Title
-y Year
-T <format>: date-time format (see 'man date' for possible values)
-o <file> : output file (path relative to Source)
-u : update database first
-D : debug
EOHelp
exit 1
}
getConfig
openDatabase
columns="${show[@]//*/-}"
if ! [[ "$output" == - ]]
then
exec > "$output"
fi
printPath() {
for key in ${!pathparts[@]}
do
if [[ ${pathparts[key]} == ${oldpathparts[key]} ]]
then
echo -n " ${pathparts[key]//?/ }"
else
echo -n "${pathparts[key]}/"
fi
done
}
printline() {
local print
for index in ${!show[@]}
do
info="${show[index]}"
locallength="${length[index]:=50}"
path="$olddir"
case $info in
'bitrates')
info=$((bitrates/count))
(( info )) || unset info
;;
'oldtimestamp')
info=$(printDate ${!info})
;;
'path')
while [[ $path =~ / ]]
do
pathparts+=("${path%%/*}")
path=${path#*/}
done
pathparts+=("$path")
info=$(printPath)
unset oldpathparts
for key in ${!pathparts[@]}
do
oldpathparts[key]=${pathparts[key]}
done
unset pathparts
;;
*)
info="${!info}"
;;
esac
printtmp="${info:0:$locallength}"
if [ -z "$printtmp" ]
then
until (( ${#printtmp} == locallength/2))
do
printtmp+=' '
done
printtmp+='-'
fi
until (( ${#printtmp} == locallength ))
do
printtmp+=' '
done
print+=(${print+|} "$printtmp")
done
echo "${print[@]}"
}
if (( update ))
then
getFiles
updateMimes
updateTags
fi
printDate() {
date -d"@$1" +"${timeformat:-%x %X}"
}
for index in ${!show[@]}
do
info="${show[index]}"
locallength="${length[index]:=50}"
case $info in
albumartists) info="Album artist" ;;
albums) info="Album" ;;
artists) info="Artist" ;;
bitrates) info="Bitrate" ;;
composers) info="Composer" ;;
channelss) info="Channels" ;;
discs) info="Disc" ;;
path) info="Directory name" ;;
genres) info="Genre" ;;
oldtimestamp) info="Last modified" ;;
types) info="Format" ;;
performers) info="Performer" ;;
rates) info="Sample rate" ;;
titles) info="Title" ;;
years) info="Date" ;;
esac
printtmp="${info:0:$locallength}"
until (( ${#printtmp} == locallength ))
do
printtmp+=' '
done
print+=(${print+|} "$printtmp")
done
echo "${print[@]}"
echo "${print[@]//[^|]/=}"
unset print
echo '
SELECT
source_files.filename,
tags.bitrate,
tags.channels,
tags.rate,
source_files.last_change,
mime_types.mime_text,
tags.albumartist,
tags.album,
tags.artist,
tags.composer,
tags.disc,
tags.genre,
tags.performer,
tags.title,
tags.year
FROM source_files
INNER JOIN mime_types
ON source_files.mime_type=mime_types.id
INNER JOIN tags
ON source_files.id=tags.source_file
WHERE
NOT mime_types.mime_text LIKE "text/%"
AND NOT mime_types.mime_text LIKE "image/%"
ORDER BY source_files.filename
COLLATE NOCASE;
SELECT "AtOM:NoMoreFiles";' >&3
read -u4 line
until [[ $line == AtOM:NoMoreFiles ]]
do
files+=("$line")
read -u4 line
done
for line in "${files[@]}"
do
filename="${line%%::AtOM:SQL:Sep::*}"
dir="${filename%/*}"
rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::"
bitrate="${rest%%::AtOM:SQL:Sep::*}"
rest="${rest#*::AtOM:SQL:Sep::}"
channels="${rest%%::AtOM:SQL:Sep::*}"
rest="${rest#*::AtOM:SQL:Sep::}"
rate="${rest%%::AtOM:SQL:Sep::*}"
rest="${rest#*::AtOM:SQL:Sep::}"
timestamp="${rest%%::AtOM:SQL:Sep::*}"
timestamp="${timestamp%%.*}"
rest="${rest#*::AtOM:SQL:Sep::}"
mimetype="${rest%%::AtOM:SQL:Sep::*}"
rest="${rest#*::AtOM:SQL:Sep::}"
albumartist="${rest%%::AtOM:SQL:Sep::*}"
rest="${rest#*::AtOM:SQL:Sep::}"
album="${rest%%::AtOM:SQL:Sep::*}"
rest="${rest#*::AtOM:SQL:Sep::}"
artist="${rest%%::AtOM:SQL:Sep::*}"
rest="${rest#*::AtOM:SQL:Sep::}"
composer="${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::}"
title="${rest%%::AtOM:SQL:Sep::*}"
rest="${rest#*::AtOM:SQL:Sep::}"
year="${rest%%::AtOM:SQL:Sep::*}"
rest="${rest#*::AtOM:SQL:Sep::}"
case $mimetype in
application/ogg\ opus) type=Opus ;;
application/ogg\ vorbis) type=Vorbis ;;
audio/mp4) type=MPEG4\ Audio;;
audio/mpeg) type=MPEG\ Audio;;
audio/x-flac) type=FLAC ;;
video/mpeg) type=MPEG\ Video;;
video/webm) type=WebM ;;
audio/*) type=Other\ Audio;;
video/*) type=Other\ Video;;
*) type=Other ;;
esac
if [[ $dir == $olddir ]]
then
(( $bitrate )) && (( count++ , bitrates+=bitrate ))
((
oldtimestamp = (
timestamp > oldtimestamp
? timestamp
: oldtimestamp
)
))
expr1='(^|,)'
expr2='(,|$)'
if ! [[ $channelss =~ $expr1"$channels"$expr2 ]]
then
if [ -n "$channelss" ] \
&& (( channels < ${channelss%%,*} ))
then
channelss="$channels,$channelss"
else
channelss+="${channelss+,}$channels"
fi
fi
if ! [[ $rates =~ $expr1"$rate"$expr2 ]]
then
if [ -n "$rates" ] \
&& (( rate < ${rates%%,*} ))
then
rates="$rate,$rates"
else
rates+="${rates+,}$rate"
fi
fi
if ! [[ $types =~ $expr1"$type"$expr2 ]]
then
[ -z "$types" ] \
&& unset types
[ -n "$type" ] \
&& types+="${types+,}$type"
fi
if ! [[ $albumartists =~ $expr1"$albumartist"$expr2 ]]
then
[ -z "$albumartists" ] \
&& unset albumartists
[ -n "$albumartist" ] \
&& albumartists+="${albumartists+,}$albumartist"
fi
if ! [[ $albums =~ $expr1"$album"$expr2 ]]
then
[ -z "$albums" ] \
&& unset albums
[ -n "$album" ] \
&& albums+="${albums+,}$album"
fi
if ! [[ $artists =~ $expr1"$artist"$expr2 ]]
then
[ -z "$artists" ] \
&& unset artists
[ -n "$artist" ] \
&& artists+="${artists+,}$artist"
fi
if ! [[ $composers =~ $expr1"$composer"$expr2 ]]
then
[ -z "$composers" ] \
&& unset composers
[ -n "$composer" ] \
&& composers+="${composers+,}$composer"
fi
if ! [[ $discs =~ $expr1"$disc"$expr2 ]]
then
[ -z "$discs" ] \
&& unset discs
[ -n "$disc" ] \
&& discs+="${discs+,}$disc"
fi
if ! [[ $genres =~ $expr1"$genre"$expr2 ]]
then
[ -z "$genres" ] \
&& unset genres
[ -n "$genre" ] \
&& genres+="${genres+,}$genre"
fi
if ! [[ $performers =~ $expr1"$performer"$expr2 ]]
then
[ -z "$performers" ] \
&& unset performers
[ -n "$performer" ] \
&& performers+="${performers+,}$performer"
fi
if ! [[ $titles =~ $expr1"$title"$expr2 ]]
then
[ -z "$titles" ] \
&& unset titles
[ -n "$title" ] \
&& titles+="${titles+,}$title"
fi
if ! [[ $years =~ $expr1"$year"$expr2 ]]
then
[ -z "$years" ] \
&& unset years
[ -n "$year" ] \
&& years+="${years+,}$year"
fi
else
if [ -n "$olddir" ]
then
printline
fi
unset bitrates
channelss="$channels"
rates="$rate"
types="$type"
albumartists="$albumartist"
albums="$album"
artists="$artist"
composers="$composer"
discs="$disc"
genres="$genre"
performers="$performer"
titles="$title"
years="$year"
(( bitrate )) && (( count=1 , bitrates=bitrate ))
oldmimetype=$mimetype
oldrate=$rate
oldtimestamp=$timestamp
fi
olddir="$dir"
done
printline

130
toys/lowquality Executable file
View File

@ -0,0 +1,130 @@
#!/bin/bash
# config structures
declare -A \
destinationchannels \
destinationfat32compat \
destinationcopymime \
destinationformat \
destinationfrequency \
destinationid \
destinationloss \
destinationmaxbps \
destinationnormalize \
destinationpath \
destinationquality \
destinationrename \
destinationnoresample \
destinationrenamepath \
destinationskipmime \
|| {
echo "Check your Bash version. You need >= 4.0" >&2
exit $EBASHVERS
}
declare -r \
DOCDIR=./doc \
LIBDIR=./lib \
SHAREDIR=./share
declare -r \
exampleconf=$DOCDIR/example.cfg \
schema=$SHAREDIR/schema.sql \
\
oldIFS="$IFS"
cffile="$HOME/.atom/atom.cfg"
LC_ALL=C
shopt -s extglob
for function in "$LIBDIR"/*/*
do
source "$function"
done
while getopts 'fm:o:p:*:uD' opt
do
case $opt in
f) mimetypes+=("audio/x-flac")
bitrates+=("")
;;
m) mimetypes+=("audio/mpeg")
bitrates+=("$OPTARG")
;;
o) mimetypes+=("application/ogg vorbis")
bitrates+=("$OPTARG")
;;
p) mimetypes+=("application/ogg opus")
bitrates+=("$OPTARG")
;;
\*) mimetypes+=("*")
bitrates+=("$OPTARG")
;;
u) update=1 ;;
D) (( debug++ )) ;;
esac
done
getConfig
openDatabase
if (( update ))
then
getFiles
updateMimes
updateTags
fi
echo '
SELECT DISTINCT
mime_type_actions.mime_text,
tags.bitrate,
source_files.filename
FROM source_files
INNER JOIN tags
ON source_files.id=tags.source_file
INNER JOIN mime_type_actions
ON mime_type_actions.id=source_files.mime_type
WHERE mime_type_actions.action=1
AND (
' >&3
for indice in ${!mimetypes[@]}
do
(( notfirst )) && echo OR >&3
case ${mimetypes[indice]} in
'audio/x-flac')
echo 'mime_type_actions.mime_text="audio/x-flac"'>&3
;;
'*')
echo '( (
NOT mime_type_actions.mime_text="audio/x-flac"
AND NOT mime_type_actions.mime_text="audio/mpeg"
AND NOT
mime_type_actions.mime_text="application/ogg vorbis"
AND NOT
mime_type_actions.mime_text="application/ogg opus"
) AND tags.bitrate < '${bitrates[indice]}' )' >&3
;;
*)
echo '(
mime_type_actions.mime_text="'"${mimetypes[indice]}"'"
AND tags.bitrate < '${bitrates[indice]}' )' >&3
;;
esac
notfirst=1
done
echo ') ORDER BY bitrate;' >&3
echo 'SELECT "AtOM:NoMoreFiles";' >&3
read -u4 line
until [[ $line == AtOM:NoMoreFiles ]]
do
echo "${line//::AtOM:SQL:Sep::/$'\t'}"
read -u4 line
done
closeDatabase

View File

@ -93,29 +93,17 @@ if (( update ))
then then
getFiles getFiles
updateMimes updateMimes
updateTags
fi fi
for check in ${!checks[@]} for check in ${!checks[@]}
do do
case $check in case $check in
albumartist)
cat >&2 <<-EONotice
Album artist is not supported for ID3. Files in this format will be ignored by
this check (other checks still apply).
EONotice
mimemp3=$(
Select mime_types id <<<"mime_text = audio/mpeg"
)
whereclause+="${whereclause+ OR }("
whereclause+='tags.albumartist IS NULL '
whereclause+="AND NOT tags.tagreader LIKE \"ID3-%\""
whereclause+=')'
;;
tracktotal) tracktotal)
whereclause+="${whereclause+ OR }NOT tags.track LIKE \"%/%\"" whereclause+="${whereclause+OR }NOT tags.track LIKE \"%/%\""
;; ;;
*) *)
whereclause+="${whereclause+ OR }tags.$check IS NULL" whereclause+="${whereclause+OR }tags.$check IS NULL"
;; ;;
esac esac
whereclause+=' whereclause+='
@ -137,8 +125,15 @@ cat >&3 <<-EOSelect
FROM tags FROM tags
INNER JOIN source_files INNER JOIN source_files
ON tags.source_file=source_files.id ON tags.source_file=source_files.id
WHERE $whereclause INNER JOIN mime_types
AND NOT tags.tagreader LIKE "unknown-%"; ON source_files.mime_type=mime_types.id
WHERE (
$whereclause
)
AND NOT tags.tagreader LIKE "unknown-%"
AND NOT mime_types.mime_text LIKE "text/%"
AND NOT mime_types.mime_text LIKE "image/%"
;
SELECT "AtOM:NoMoreFiles"; SELECT "AtOM:NoMoreFiles";
EOSelect EOSelect
@ -153,33 +148,33 @@ done
for line in "${lines[@]}" for line in "${lines[@]}"
do do
missing=() missing=()
filename=${line%%|*} filename=${line%%::AtOM:SQL:Sep::*}
rest="${line#*|}|" rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::"
(( ${checks[genre]} )) && [ -z "${rest%%|*}" ] && missing+=( "Genre" ) (( ${checks[genre]} )) && [ -z "${rest%%::AtOM:SQL:Sep::*}" ] && missing+=( "Genre" )
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
(( ${checks[albumartist]} )) && [ -z "${rest%%|*}" ] && missing+=( "AlbumArtist" ) (( ${checks[albumartist]} )) && [ -z "${rest%%::AtOM:SQL:Sep::*}" ] && missing+=( "AlbumArtist" )
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
(( ${checks[year]} )) && [ -z "${rest%%|*}" ] && missing+=( "Year" ) (( ${checks[year]} )) && [ -z "${rest%%::AtOM:SQL:Sep::*}" ] && missing+=( "Year" )
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
(( ${checks[album]} )) && [ -z "${rest%%|*}" ] && missing+=( "Album" ) (( ${checks[album]} )) && [ -z "${rest%%::AtOM:SQL:Sep::*}" ] && missing+=( "Album" )
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
(( ${checks[disc]} )) && [ -z "${rest%%|*}" ] && missing+=( "Disc" ) (( ${checks[disc]} )) && [ -z "${rest%%::AtOM:SQL:Sep::*}" ] && missing+=( "Disc" )
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
(( ${checks[artist]} )) && [ -z "${rest%%|*}" ] && missing+=( "Artist" ) (( ${checks[artist]} )) && [ -z "${rest%%::AtOM:SQL:Sep::*}" ] && missing+=( "Artist" )
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
track=${rest%%|*} track=${rest%%::AtOM:SQL:Sep::*}
(( ${checks[track]} )) && [ -z "$track" ] && missing+=( "Track" ) (( ${checks[track]} )) && [ -z "$track" ] && missing+=( "Track" )
if (( ${checks[tracktotal]} )) && ! [[ $track =~ / ]] if (( ${checks[tracktotal]} )) && ! [[ $track =~ / ]]
then then
missing+=( TrackTotal ) missing+=( TrackTotal )
fi fi
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
(( ${checks[title]} )) && [ -z "${rest%%|*}" ] && missing+=( "Title" ) (( ${checks[title]} )) && [ -z "${rest%%::AtOM:SQL:Sep::*}" ] && missing+=( "Title" )
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
(( ${checks[composer]} )) && [ -z "${rest%%|*}" ] && missing+=( "Composer" ) (( ${checks[composer]} )) && [ -z "${rest%%::AtOM:SQL:Sep::*}" ] && missing+=( "Composer" )
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
(( ${checks[performer]} )) && [ -z "${rest%%|*}" ] && missing+=( "Performer" ) (( ${checks[performer]} )) && [ -z "${rest%%::AtOM:SQL:Sep::*}" ] && missing+=( "Performer" )
rest=${rest#*|} rest=${rest#*::AtOM:SQL:Sep::}
echo "$filename: ${missing[@]}" echo "$filename: ${missing[@]}"
done done