Compare commits

...

35 Commits

Author SHA1 Message Date
ScriptFanix
7a359ad2d6 Merge branch 'dev' into 'master'
v1.0.4 last minute fixes

See merge request atom/AtOM!32
2025-10-13 18:21:25 +02:00
Vincent Riquer
75159a4e2b Hotfix: createindex: fix ID3v1 genre index path 2025-10-13 18:20:54 +02:00
Vincent Riquer
325bdb3c96 Hotfix: missing quote in createindex 2025-10-13 18:20:54 +02:00
Vincent Riquer
2b5bf06e58 Hotfix: commit more often when reading tags 2025-10-13 18:20:54 +02:00
ScriptFanix
3a6c7bfc9c Merge branch 'dev' into 'master'
Bugfix release v1.0.4

Closes #26

See merge request atom/AtOM!30
2025-10-11 22:31:16 +02:00
ScriptFanix
f3e4aed71e Merge branch '26-ffmpeg-tags-are-always-null' into 'dev'
Resolve "ffmpeg tags are always NULL"

See merge request atom/AtOM!29
2025-10-11 22:23:17 +02:00
ScriptFanix
a0756b170c Resolve "ffmpeg tags are always NULL" 2025-10-11 22:23:16 +02:00
Vincent Riquer
1d7a09fcff quickfix: replace calls to date with builtin printf or $EPOCHSECONDS 2025-08-08 20:19:51 +02:00
ScriptFanix
1df976bcae Revert "getInfos::guess skeleton"
This reverts commit 0ea0dbe09d59703a2b468a9c96185a148eebc1aa
2025-04-30 02:45:37 +02:00
Vincent Riquer
0ea0dbe09d getInfos::guess skeleton 2025-04-30 02:41:29 +02:00
ScriptFanix
b61dddcd67 Edit README.md 2025-04-29 23:54:59 +02:00
Vincent Riquer
73652a329b Update changelog 2025-04-16 00:03:23 +02:00
Vincent Riquer
ad6d820496 doc: Update example.cfg
Fixes #23
2025-04-15 23:58:14 +02:00
ScriptFanix
af40b872a7 Merge branch '20-bug-setup-is-not-aware-of-copy_extension' into 'master'
Resolve "BUG: Setup is not aware of `copy_extension`"

Closes #20

See merge request atom/AtOM!26
2025-04-15 21:36:31 +00:00
ScriptFanix
95ce31a54a Resolve "BUG: Setup is not aware of copy_extension" 2025-04-15 21:36:31 +00:00
Vincent Riquer
29e8616ff9 README update URL 2025-04-05 06:28:11 +02:00
Vincent Riquer
9cdce7752a README update URL 2025-04-05 06:20:50 +02:00
Vincent Riquer
f7522e5f34 Merge branch '24-release-country-issues' into 'master'
Resolve "release country issues"

Closes #19, #21, #22, and #24

See merge request atom/AtOM!25
2025-04-03 22:51:20 +00:00
Vincent Riquer
6a888f02e7 Resolve "release country issues" 2025-04-03 22:51:19 +00:00
Vincent Riquer
73900210ad Edit CHANGELOG.md 2025-04-02 23:34:16 +00:00
Vincent Riquer
556ce4d142 Edit CHANGELOG.md 2025-04-02 23:28:46 +00:00
Vincent Riquer
c7bea0d3a6 Merge branch '17-read-and-copy-replaygain' into 'master'
Resolve "Read and copy replaygain"

Closes #17

See merge request atom/AtOM!24
2025-04-02 23:22:18 +00:00
Vincent Riquer
806a0f16a7 FEAT: Read and copy replaygain 2025-04-02 23:22:18 +00:00
Vincent Riquer
89100a2949 Move TODO to #18 2025-04-01 01:13:03 +00:00
Vincent Riquer
6cde6b4ada Merge branch 'fix-infinite-loop' into 'master'
fix infinite loop at end of task list

See merge request atom/AtOM!23
2025-03-15 02:10:54 +00:00
Vincent Riquer
0a9a5335bb fix infinite loop at end of task list 2025-03-15 03:01:27 +01:00
Vincent Riquer
2e3c47071f Merge branch '16-unable-to-saturate-cpus-on-sufficiently-powerfulm-hartdware' into 'master'
Resolve "Unable to saturate CPUs on sufficiently powerfulm hartdware"

Closes #16

See merge request atom/AtOM!21
2025-03-14 03:14:57 +00:00
Vincent Riquer
aaaad2ce9a Resolve "Unable to saturate CPUs on sufficiently powerfulm hartdware" 2025-03-14 03:14:57 +00:00
Vincent Riquer
0fd1fae6dc Merge branch '15-temp-directory-not-created-for-toys' into 'master'
Resolve "Temp directory not created for toys"

Closes #15

See merge request atom/AtOM!20
2025-03-13 23:24:59 +00:00
Vincent Riquer
dd72a6b0ba Resolve "Temp directory not created for toys" 2025-03-13 23:24:59 +00:00
Vincent Riquer
0dd962fb8c Enhancement: slight performance improve
atom: don't call `date` to get the timestamp multiple times in the main loop
2025-02-11 21:50:14 +01:00
Vincent Riquer
042cd72ecd Merge branch '12-update-changelog-for-v1-0-2' into 'master'
Resolve "Update CHANGELOG for v1.0.2"

Closes #12

See merge request atom/AtOM!17
2025-02-10 19:04:14 +00:00
Vincent Riquer
8effee243a Resolve "Update CHANGELOG for v1.0.2" 2025-02-10 19:04:13 +00:00
Vincent Riquer
45433fa214 Merge branch '10-bug-releasecountry-change-should-update-dest-files' into 'master'
Resolve "BUG: releasecountry change should update dest files"

Closes #10

See merge request atom/AtOM!16
2025-02-10 18:57:38 +00:00
Vincent Riquer
92a5ba0234 Resolve "BUG: releasecountry change should update dest files" 2025-02-10 18:57:38 +00:00
39 changed files with 999 additions and 627 deletions

View File

@ -1,11 +1,31 @@
# 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
### Bugs
* Tag reading hangs on vorbis files with embedded images.
* Recreate destination files on releasecountry tag change (bump database schema to 5).
# 1.0.1
## 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.
## `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.
## Bugs
Opus tags were not actually parsed due to an issue with the tag reader selector.
### Enhancements
* Copy releasecountry tag ("MusicBrainz Album Release Country" for MP3).
## Enhancements
### Bugs
* Opus tags were not actually parsed due to an issue with the tag reader selector.
### Enhancements
* Fetch releasecountry tag from files
* Add `%{releasecountry}` placeholder in `rename`
* Add `-r` (show release country) to toys/createindex

View File

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

5
TODO
View File

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

252
atom
View File

@ -183,163 +183,7 @@ set +H
(( 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
WebM metadata disabled" >&2
disableogginfo=1
(( sanitywarn++ ))
fi
if ! which soxi >/dev/null
then
echo "[WARNING] Tool soxi (from sox) is not" \
"installed or not in PATH
Vorbis metadata disabled" >&2
disablesoxi=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 mkvextract >/dev/null
then
echo "[WARNING] Tool mkvextract (from MKVToolNix) is not" \
"installed or not in PATH
WebM metadata disabled
WebM support disabled" >&2
disablemkvextract=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 (( textunidecodeneeded )) && ! perl -MText::Unidecode -e 'exit;' 2>/dev/null
then
echo "[WARNING] Perl module Text::Unidecode is not available
Renaming to ASCII-only disabled" >&2
unset destinationascii
destinationascii=0
textunidecodeneeded=0
(( 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
if ! (( cron ))
then
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
fi
sanityCheck
openDatabase
for destination in "${destinations[@]}"
@ -554,20 +398,23 @@ echo '
mime_type_actions.mime_text,
destinations.name,
destination_files.id,
tags.depth,
tags.rate,
tags.channels,
tags.bitrate,
tags.genre,
tags.albumartist,
tags.year,
tags.album,
tags.disc,
tags.albumartist,
tags.artist,
tags.track,
tags.title,
tags.bitrate,
tags.channels,
tags.composer,
tags.performer
tags.depth,
tags.disc,
tags.genre,
tags.performer,
tags.rate,
tags.releasecountry,
tags.replaygain_alb,
tags.replaygain_trk,
tags.title,
tags.track,
tags.year
FROM source_files
INNER JOIN destination_files
ON source_files.id
@ -610,33 +457,39 @@ do
rest=${rest#*::AtOM:SQL:Sep::}
destfileid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
bitdepth=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
rate=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
channels=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
bitrate=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
genre=${rest%%::AtOM:SQL:Sep::*}
album=${rest%%::AtOM:SQL:Sep::*}
rest=${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::}
track=${rest%%::AtOM:SQL:Sep::*}
bitrate=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
title=${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::}
rate=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
releasecountry=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
replaygain_alb=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
replaygain_trk=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
title=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
track=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
year=${rest%%::AtOM:SQL:Sep::*}
unset rest
case ${destinationformat["$destination"]} in
vorbis) (( disableoggenc )) && continue ;;
@ -648,7 +501,7 @@ do
getDestFile
for copy_ext in "${destinationcopyext[@]}"
do
if [[ $filename =~ '.*'"$copy_ext" ]]
if [[ $filename =~ '.*\.'"$copy_ext"'$' ]]
then
copied=1
break
@ -664,13 +517,13 @@ do
album \
albumartist \
artist \
bitdepth \
bitrate \
channels \
commandline \
composer \
copied \
decodetaskid \
depth \
destfileid \
destination \
disc \
@ -679,6 +532,9 @@ do
mimetype \
performer \
rate \
releasecountry \
replaygain_alb \
replaygain_trk \
rest \
sox_needed \
soxoptions_in \
@ -699,19 +555,20 @@ echo "Created ${count:-0} tasks for $filecount files ${togo:+($togo left) }(${co
concurrency=$(( maxload / 2 ))
(( concurrency )) || concurrency=1
active=0
concurrencychange=$(date +%s)
concurrencychange=$EPOCHSECONDS
starttime=$concurrencychange
taskcount=$count
remaining=$taskcount
failed=0
echo 'BEGIN TRANSACTION;' >&3
committime=$(date +%s)
committime=$EPOCHSECONDS
while (( (remaining || ${#workers[@]}) && ! quit ))
do
if (( $(date +%s) - committime >= 60 ))
timestamp=$EPOCHSECONDS
if (( $timestamp - committime >= 60 ))
then
echo $'COMMIT;\nBEGIN TRANSACTION;' >&3
committime=$(date +%s)
committime=$timestamp
fi
read humanload garbage < /proc/loadavg
load=${humanload%.*}
@ -721,16 +578,16 @@ do
else
if [ -z "$quit" ] \
&& (( ! pause )) \
&& (( $(date +%s)-concurrencychange >= loadinterval ))
&& (( timestamp - concurrencychange >= loadinterval ))
then
if (( concurrency > 1 )) \
&& (( load > maxload ))
then
concurrencychange=$(date +%s)
concurrencychange=$timestamp
(( --concurrency ))
elif (( load < maxload )) && (( active > concurrency - 1 ))
then
concurrencychange=$(date +%s)
concurrencychange=$timestamp
(( ++concurrency ))
fi
fi
@ -740,7 +597,7 @@ do
(( pause )) || master
if (( ran - failed ))
then
currenttime=$(date +%s)
currenttime=$timestamp
if (( pause ))
then
(( runtime = pausestart - starttime - pausedtime ))
@ -816,7 +673,7 @@ done
echo 'COMMIT;' >&3
unset count
endtime=$(date +%s)
endtime=$EPOCHSECONDS
(( elapsedseconds = endtime - starttime - pausedtime ))
(( days =
@ -952,6 +809,7 @@ do
tags.disc,
tags.genre,
tags.performer,
tags.releasecountry,
tags.title,
tags.track,
tags.year
@ -1020,6 +878,8 @@ do
rest=${rest#*::AtOM:SQL:Sep::}
performer=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
releasecountry=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
title=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
track=${rest%%::AtOM:SQL:Sep::*}

View File

@ -1,44 +1,174 @@
[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
max-load 6
load-interval 30
temporary-directory %HOME%/.atom/tmp
database %HOME%/.atom/atom.db
debug 0
# * temporary-directory <directory>: String. Name speaks for itself: this is
# where FIFOs (for communicating with sqlite) and temporary WAVE files will
# be created. Note that debug logs (if enabled) will go there too.
temporary-directory /tmp/AtOM-user/
# * 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
[source]
path /var/lib/mpd/music
# This section defines where are the files you want transcoded.
# * 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 /lastfm
skip /zzz-atrier
[Ogg]
[Vorbis]
# 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
# * format: copy, ogg, opus or mp3. Other formats may appear in the future -
# feel free to implement your preferred format.
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
# Optional parameters:
# * normalize <yes>/<no>: Normalize output files.
normalize yes
channels 2
frequency 44100
# * rename <string>: Destination files will be named according to <string>,
# 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
# 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 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]
enabled 1
path /mnt/Musique-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
normalize yes
frequency 48000
copy_mime-type image/*
copy_mime-type text/*
[MP3]
path /mnt/Musique-mp3.test
enabled 1
path /mnt/Musique-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
# * 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
normalize yes
higher-than 128
@ -50,6 +180,7 @@ skip_mime-type image/*
skip_mime-type text/*
[asterisk]
enabled 1
path /mnt/Musique-asterisk
format vorbis
quality 0

View File

@ -186,7 +186,7 @@ getConfigDestination() {
destinationcopymime[$destination]="${destinationcopymime[$destination]:+${destinationcopymime[$destination]}|}$value"
;;
'copy_extension')
destinationcopyext[$destination]="${destinationcopyext[$destination]:+${destinationcopyext[$destination]}|}\.$value$"
destinationcopyext[$destination]="${destinationcopyext[$destination]:+${destinationcopyext[$destination]}|}$value"
;;
'higher-than')
expr='^[0-9]*$'

View File

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

View File

@ -60,7 +60,7 @@ path $sourcepath
# Common parameters:
# Mandatory parameters:
# * enabled: Whether or not to treat this destination (1=tue/0=false)
# * enabled: Whether or not to treat this destination (1=true/0=false)
enabled 1
# * path: Where files will be written
@ -144,6 +144,7 @@ bitrate ${destinationquality["$destination"]}
# %{artist},
# %{disc},
# %{genre},
# %{releasecountry},
# %{title},
# %{track},
# %{year}.
@ -207,8 +208,7 @@ bitrate ${destinationquality["$destination"]}
done
cat <<-EOCfg
# * copy_extension <extension>: Same as skip_extension, except that files
# matching will be copied as-is to the destination.
# * copy_extension <extension>: Copy files whose name and with ".<extension>"
EOCfg
destinationcopyext["$destination"]="${destinationcopyext["$destination"]}|"
while [[ ${destinationcopyext["$destination"]} =~ \| ]]

View File

@ -1,5 +1,5 @@
#!/bin/bash
currentdbversion=4
#!/usr/bin/env bash
currentdbversion=6
checkDatabaseVersion() {
local dbversion
if dbversion=$(Select atom version <<<"\"1\" = 1")

View File

@ -1,5 +1,6 @@
#!/bin/bash
#!/usr/bin/env bash
openDatabase() {
[[ -f "$database" ]] || populate_db=1
rm -f "$tempdir"/sqlite.{in,out}
mkfifo "$tempdir"/sqlite.{in,out}
sqlite3 -bail "$database" \
@ -13,7 +14,7 @@ openDatabase() {
exec 5>&3
exec 3> >(tee -a "$tempdir/debug.log" >&5)
fi
cat $schema >&3
(( populate_db )) && cat $schema >&3
echo '.separator ::AtOM:SQL:Sep::' >&3
echo 'PRAGMA foreign_keys = ON;' >&3
echo 'PRAGMA recursive_triggers = ON;' >&3

View File

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

View File

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

View File

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

View File

@ -0,0 +1,33 @@
#!/usr/bin/env bash
upgradedatabase_4_5() {
echo "Upgrading database to version 5... (backup is $database.bak_v4)"
cp "$database" "$database.bak_v4"
echo 'DROP TRIGGER force_destination_update_on_tag_update;' >&3
echo '
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,
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 5 <<<"1 = 1"
}

View File

@ -0,0 +1,37 @@
#!/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

@ -26,7 +26,7 @@ decodeSox() {
commandline+=(-c ${destinationchannels["$destination"]})
soxoptions_out+=" -c ${destinationchannels["$destination"]}"
fi
if (( ${bitdepth:-0} > 16 ))
if (( ${depth:-0} > 16 ))
then
commandline+=(-b 16)
soxoptions_out+=" -b 16"

View File

@ -1,6 +1,6 @@
#!/bin/bash
#!/usr/bin/env bash
encodeFile::mp3() {
lameopts=(${ionice}lame --quiet)
lameopts=(${ionice}lame --quiet --noreplaygain)
lameopts+=(-v --abr ${destinationquality[$destination]})
[ -n "$album" ] && lameopts+=(--tl "$album" )
[ -n "$artist" ] && lameopts+=(--ta "$artist")
@ -13,6 +13,10 @@ encodeFile::mp3() {
[ -n "$performer" ] && lameopts+=(--tv TOPE="$performer")
[ -n "$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")
if (( ${destinationnoresample[$destination]:-0} == 1 ))
then

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
encodeFile::opus() {
opusencopts=(${ionice}opusenc --quiet)
opusencopts+=(--bitrate ${destinationquality[$destination]})
@ -13,6 +13,12 @@ encodeFile::opus() {
[ -n "$performer" ] && opusencopts+=(--comment "PERFORMER=$performer")
[ -n "$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 "$track" ] && opusencopts+=(--comment "TRACKNUMBER=${track%/*}")
[ -n "${track#*/}" ] && opusencopts+=(--comment "TRACKTOTAL=${track#*/}")

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
encodeFile::vorbis() {
oggencopts=(${ionice}oggenc -Q -q ${destinationquality[$destination]})
[ -n "$albumartist" ] && oggencopts+=(-c "ALBUMARTIST=$albumartist")
@ -9,7 +9,11 @@ encodeFile::vorbis() {
[ -n "$genre" ] && oggencopts+=(-G "$genre")
[ -n "$performer" ] && oggencopts+=(-c "PERFORMER=$performer")
[ -n "$releasecountry" ] \
&& oggencopts+=(--comment "RELEASECOUNTRY=$releasecountry")
&& oggencopts+=(-c "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 "$track" ] && oggencopts+=(-N "$track")
[ -n "$year" ] && oggencopts+=(-d "$year")

View File

@ -1,6 +1,6 @@
#!/bin/bash
getFiles() {
scantime=$(date +%s)
scantime=$EPOCHSECONDS
for prune_expression in "${skippeddirectories[@]}"
do
prunes+=( -path "$sourcepath$prune_expression" -prune -o )

View File

@ -251,6 +251,7 @@ setupDestination() {
%{artist},
%{disc},
%{genre},
%{releasecountry},
%{title},
%{track},
%{year}.
@ -409,6 +410,44 @@ setupDestination() {
fi
done
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
Channels (integer):

View File

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

View File

@ -1,65 +0,0 @@
#!/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
}

View File

@ -1,50 +0,0 @@
#!/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"
)
}

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

@ -0,0 +1,54 @@
#!/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,8 +1,11 @@
#!/bin/bash
getInfosOpus_version='Opus-3'
#!/usr/bin/env bash
getInfosOpus_version='Opus-4'
tagreaders+=( "$getInfosOpus_version" )
getInfos::Opus() {
tagreader="$getInfosOpus_version"
local \
infos \
infos=$(
opusinfo "$sourcepath/$filename" \
| sed 's/\t//'
@ -15,6 +18,8 @@ getInfos::Opus() {
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)

75
lib/tags/getInfos::ffmpeg Normal file
View File

@ -0,0 +1,75 @@
#!/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,8 +1,11 @@
#!/bin/bash
getInfosSoxi_version='soxi-2'
#!/usr/bin/env bash
getInfosSoxi_version='soxi-3'
tagreaders+=( "$getInfosSoxi_version" )
getInfos::soxi() {
tagreader="$getInfosSoxi_version"
local \
infos \
infos=$(
soxi "$sourcepath/$filename" \
| grep -v METADATA_BLOCK_PICTURE
@ -15,6 +18,8 @@ getInfos::soxi() {
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)
@ -29,6 +34,6 @@ getInfos::soxi() {
bitrate=$(gettag 'bit rate')
bitrate=${bitrate%k}
bitrate=${bitrate%%.*}
bitdepth=$(gettag precision)
bitdepth=${bitdepth%-bit}
depth=$(gettag precision)
depth=${depth%-bit}
}

View File

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

166
lib/tools/sanityChecks Normal file
View File

@ -0,0 +1,166 @@
#!/usr/bin/env bash
sanityCheck() {
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
WebM metadata disabled" >&2
disableogginfo=1
(( sanitywarn++ ))
fi
if ! which soxi >/dev/null
then
echo "[WARNING] Tool soxi (from sox) is not" \
"installed or not in PATH
Vorbis metadata disabled" >&2
disablesoxi=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 mkvextract >/dev/null
then
echo "[WARNING] Tool mkvextract (from MKVToolNix) is not"\
"installed or not in PATH
WebM metadata disabled
WebM support disabled" >&2
disablemkvextract=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 (( textunidecodeneeded )) && ! perl -MText::Unidecode -e 'exit;' 2>/dev/null
then
echo "[WARNING] Perl module Text::Unidecode is not available
Renaming to ASCII-only disabled" >&2
unset destinationascii
destinationascii=0
textunidecodeneeded=0
(( 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
if ! (( cron ))
then
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
fi
}
# vim:set ts=8 sw=8:

View File

@ -17,6 +17,8 @@ master() {
return 0
fi
until (( active == concurrency || remaining == 0 ))
do
echo '
SELECT COUNT(*)
FROM tasks
@ -110,7 +112,7 @@ master() {
if (( ready > 0 ))
then
createworker
return 0
continue
fi
echo '
@ -222,8 +224,17 @@ master() {
elif (( ready == 0 ))
then
sleep 0.1
break
else
createworker
fi
echo '
SELECT COUNT(*)
FROM tasks
WHERE status = 0;
'>&3
read -u4 remaining
done
fi
}

View File

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

View File

@ -62,6 +62,7 @@ done
getConfig
sanityCheck
openDatabase
(( update )) && getFiles

View File

@ -58,7 +58,7 @@ do
done
getConfig
sanityCheck
openDatabase
sourcepath=''

View File

@ -59,7 +59,7 @@ do
done
getConfig
sanityCheck
openDatabase
echo 'SELECT id,filename FROM destination_files WHERE filename IS NOT NULL;' >&3

View File

@ -59,7 +59,7 @@ do
done
getConfig
sanityCheck
openDatabase
checkwanted() {

View File

@ -38,7 +38,7 @@ LC_ALL=C
shopt -s extglob
source ./share/id3genres
source "$SHAREDIR"/id3genres
for function in "$LIBDIR"/*/*
do
@ -139,7 +139,7 @@ done
}
getConfig
sanityCheck
openDatabase
columns="${show[@]//*/-}"
@ -287,12 +287,12 @@ cat <<-EOBrag
#
# $0 $args
#
# Last database update: $(date -d @$lastupdate +'%x %X')
# Last database update: $(printf "%(%x %X)T" "$lastupdate")
EOBrag
printDate() {
date -d"@$1" +"${timeformat:-%x %X}"
printf "%("${timeformat:-%x %X}")T" "$1"
}
for index in ${!show[@]}

View File

@ -75,7 +75,7 @@ do
done
getConfig
sanityCheck
openDatabase
if (( update ))

View File

@ -94,7 +94,7 @@ done
}
getConfig
sanityCheck
openDatabase
if (( update ))