From c99825912f117a0b0400a110b216076b7419858e Mon Sep 17 00:00:00 2001 From: Vincent Riquer Date: Fri, 13 Mar 2026 02:26:32 +0100 Subject: [PATCH] Comment config handling (#LLM-assisted - Claude Code) --- configure | 7 ++- lib/config/get | 26 +++++++++-- lib/config/getDestination | 92 ++++++++++++++++++++++++++++++--------- lib/config/getGeneral | 29 +++++++++++- lib/config/getSource | 17 ++++++++ lib/config/print | 28 ++++++++++-- lib/config/write | 23 ++++++++++ 7 files changed, 192 insertions(+), 30 deletions(-) diff --git a/configure b/configure index 7e3d395..f84ed5f 100755 --- a/configure +++ b/configure @@ -1,25 +1,28 @@ #!/usr/bin/env bash -# defaults +# Default install prefix if --prefix= is not provided default_prefix=/usr/local #default_bindir=$default_prefix/bin #default_libdir=$default_prefix/lib #default_sharedir=$default_prefix/share +# Parse command-line arguments (only --prefix=VALUE is supported) while (( $# )) do case "$1" in - --prefix=*) prefix="${1#*=}" + --prefix=*) prefix="${1#*=}" # Strip the "--prefix=" portion to get the value ;; esac shift done +# Derive install directories from prefix (or default_prefix if unset) bindir="${prefix:-$default_prefix}"/bin libdir="${prefix:-$default_prefix}"/lib/AtOM sharedir="${prefix:-$default_prefix}"/share/AtOM docdir="${prefix:-$default_prefix}"/share/doc/AtOM +# Write Makefile.in so the Makefile can substitute real paths at build time cat > Makefile.in <<-EOMakefile.in bindir = "$bindir" libdir = "$libdir" diff --git a/lib/config/get b/lib/config/get index 63e0e50..6048ea5 100644 --- a/lib/config/get +++ b/lib/config/get @@ -1,27 +1,47 @@ #!/bin/bash + +# Copyright © 2012-2026 ScriptFanix +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# A copy of the GNU General Public License v3 is includded in the LICENSE file +# at the root of the project. + getConfig() { + # Read the config file line by line; 'key' gets the first word, 'value' + # the rest while read key value do case $key in '#'*) - #comment + #comment - skip comment lines ;; '') - #empty line + #empty line - skip blank lines ;; '[general]') + # Switch parsing context to the [general] section context=General ;; '[source]') + # Switch parsing context to the [source] section context=Source ;; \[*\]) + # Any other [section] header is a destination name context=Destination destination="${key#[}" destination="${destination%]}" - destinations+=("${destination%]}") + destinations+=("${destination%]}") # Append to list of destinations ;; *) + # Dispatch key=value to the handler for the current section context getConfig$context ;; esac diff --git a/lib/config/getDestination b/lib/config/getDestination index 041917f..fe9818f 100644 --- a/lib/config/getDestination +++ b/lib/config/getDestination @@ -1,30 +1,50 @@ #!/bin/bash + +# Copyright © 2012-2026 ScriptFanix +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# A copy of the GNU General Public License v3 is includded in the LICENSE file +# at the root of the project. + getConfigDestination() { case "$key" in 'enabled') + # 1 = process this destination, 0 = skip it destinationenabled["$destination"]="$value" ;; 'path') + # Strip trailing slash for consistency destinationpath["$destination"]="${value%/}" ;; 'format') case "$value" in 'mp3') destinationformat["$destination"]=mp3 + # Flag that lame must be present lameneeded=1 - # MP3 can't handfle more than 2 channels + # MP3 can't handle more than 2 channels [[ -z ${destinationchannels["$destination"]} ]] \ && destinationchannels["$destination"]=2 ;; 'opus') destinationformat["$destination"]=opus + # Flag that opusenc must be present opusencneeded=1 ;; 'vorbis') destinationformat["$destination"]=vorbis + # Flag that oggenc must be present oggencneeded=1 ;; 'copy') + # Files are copied/hardlinked as-is destinationformat["$destination"]=copy ;; *) @@ -34,6 +54,7 @@ getConfigDestination() { esac ;; 'quality') + # Vorbis-only: oggenc quality scale (integer, typically 0-10) expr='^[0-9]*$' if ! [[ $value =~ $expr ]] then @@ -46,27 +67,32 @@ getConfigDestination() { destinationquality["$destination"]="$value" ;; *) - echo "Invalid parameter \"$key\" for format \"${destinationformat["$destination"]}\"" >&2 + echo "Invalid parameter '$key' for" \ + "format" \ + "'${destinationformat["$destination"]}'" >&2 exit $EFMTINVPARM ;; esac ;; 'normalize') + # Whether to normalize audio volume (via sox --norm) case $value in - 'true'|'on'|'yes') + 'true'|'on'|'yes'|'1') destinationnormalize["$destination"]=1 ;; - 'false'|'off'|'no') + 'false'|'off'|'no'|'0') destinationnormalize["$destination"]=0 ;; *) - echo "normalize takes values:" \ - "'yes' ,'true' ,'on', 'no', 'false',"\ - "'off'" + echo "normalize takes values:" \ + "'yes' ,'true' ,'on', '1', 'no'," \ + "'false','off', '0'" ;; esac ;; 'bitrate') + # Opus/MP3: target bitrate in kbps + # integer only; Bash doesn't support floats expr='^[0-9]*$' if ! [[ $value =~ $expr ]] then @@ -88,6 +114,7 @@ getConfigDestination() { esac ;; 'loss') + # Opus Forward Error Correction: expected packet loss percentage (0-100) expr='^[0-9]*$' if ! [[ $value =~ $expr ]] then @@ -100,12 +127,15 @@ getConfigDestination() { destinationloss["$destination"]="$value" ;; *) - echo "$Invalid parameter \"$key\" for format \"${destinationformat["$destination"]}\"" >&2 + echo "Invalid parameter '$key' for" \ + "format" \ + "'${destinationformat["$destination"]}'" >&2 exit $EFMTINVPARM ;; esac ;; 'channels') + # Up/downmix to this many channels if needed expr='^[0-9]*$' if ! [[ $value =~ $expr ]] then @@ -116,6 +146,7 @@ getConfigDestination() { destinationchannels["$destination"]=$value ;; 'frequency') + # Resample to this sample rate in Hz (e.g. 44100) expr='^[0-9]*$' if ! [[ $value =~ $expr ]] then @@ -126,21 +157,24 @@ getConfigDestination() { destinationfrequency["$destination"]=$value ;; 'noresample') + # MP3-only: prevent lame from auto-downsampling at low + # bitrates case $value in - 'true'|'on'|'yes') + 'true'|'on'|'yes'|'1') destinationnoresample["$destination"]=1 ;; - 'false'|'off'|'no') + 'false'|'off'|'no'|'0') destinationnoresample["$destination"]=0 ;; *) echo "noresample takes values:" \ - "'yes' ,'true' ,'on', 'no', 'false',"\ - "'off'" + "'yes' ,'true' ,'on', '1', 'no',"\ + "'false','off', '0'" ;; esac ;; 'rename') + # File rename pattern using %{tag} tokens case "$value" in */*) destinationrenamepath["$destination"]="${value%/*}" @@ -149,46 +183,62 @@ getConfigDestination() { destinationrename["$destination"]="${value##*/}" ;; 'fat32compat') + # Strip FAT32-illegal characters (? \ < > : * | ") + # trim spaces/dots case $value in - 'true'|'on'|'yes') + 'true'|'on'|'yes'|'1') destinationfat32compat["$destination"]=1 ;; - 'false'|'off'|'no') + 'false'|'off'|'no'|'0') destinationfat32compat["$destination"]=0 ;; *) - echo "fat32compat takes values:" \ - "'yes' ,'true' ,'on', 'no', 'false',"\ - "'off'" + echo "fat32compat takes values:" \ + "'yes' ,'true' ,'on', '1', 'no',"\ + "'false','off', '0'" ;; esac ;; 'ascii-only') + # Transliterate Unicode filenames to ASCII using + #Requires Perl Text::Unidecode case $value in - 'true'|'on'|'yes') + 'true'|'on'|'yes'|'1') destinationascii["$destination"]=1 + # Signal that the perl coprocess will + # be needed textunidecodeneeded=1 ;; - 'false'|'off'|'no') + 'false'|'off'|'no'|'0') destinationascii["$destination"]=0 ;; *) echo "ascii-only takes values:" \ - "'yes' ,'true' ,'on', 'no', 'false',"\ - "'off'" + "'yes' ,'true' ,'on', '1', 'no',"\ + "'false','off', '0'" ;; esac ;; 'skip_mime-type') + # Accumulate pipe-separated list of mime patterns to + # exclude entirely destinationskipmime[$destination]="${destinationskipmime[$destination]:+${destinationskipmime[$destination]}|}$value" ;; 'copy_mime-type') + # Accumulate pipe-separated list of mime patterns to + # copy verbatim (action=2) destinationcopymime[$destination]="${destinationcopymime[$destination]:+${destinationcopymime[$destination]}|}$value" ;; 'copy_extension') + # Accumulate pipe-separated list of file extensions to + # copy verbatim destinationcopyext[$destination]="${destinationcopyext[$destination]:+${destinationcopyext[$destination]}|}$value" ;; 'higher-than') + # Only re-encode source files with bitrate ABOVE this + # threshold (kbps) + # Files at or below this bitrate will be + # hardlinked/copied instead expr='^[0-9]*$' if ! [[ $value =~ $expr ]] then diff --git a/lib/config/getGeneral b/lib/config/getGeneral index c5d5f2e..922835c 100644 --- a/lib/config/getGeneral +++ b/lib/config/getGeneral @@ -1,7 +1,23 @@ #!/bin/bash + +# Copyright © 2012-2026 ScriptFanix +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# A copy of the GNU General Public License v3 is includded in the LICENSE file +# at the root of the project. + getConfigGeneral() { case $key in 'max-load') + # Target system 1-minute load average + # concurrency is adjusted to stay near this expr='^[0-9]*$' if [[ $value =~ $expr ]] then @@ -13,6 +29,7 @@ getConfigGeneral() { unset expr ;; 'load-interval') + # How often (seconds) to adjust worker count expr='^[0-9]*$' if [[ $value =~ $expr ]] then @@ -48,6 +65,7 @@ getConfigGeneral() { fi ;; 2) + # Best-effort class; niceness 0 (highest) to 7 (lowest) if [ -n "$niceness" ] \ && (( niceness >= 0 && niceness <= 7 )) then @@ -59,10 +77,11 @@ getConfigGeneral() { fi ;; 3) + # Idle class: only gets I/O when no other process needs it ionice="ionice -c3 " ;; *) - echo "Invalid ionice parameters $value"\ + echo "Invalid ionice class $value"\ >&2 exit $EIONICE ;; @@ -70,15 +89,23 @@ getConfigGeneral() { fi ;; 'temporary-directory') + # Directory for + # * SQLite FIFOs + # * intermediate WAV files + # * debug logs tempdir="$value" ;; 'database') + # Path to the SQLite database file database="$value" ;; 'skip-timestamp-microsec') + # If non-zero, ignore sub-second precision in file timestamps + # Useful on filesystems that don't preserve microseconds skip_us_timestamp="$value" ;; debug) + # Allow config file to raise debug level (but not lower it) (( value > debug )) && debug=$value ;; esac diff --git a/lib/config/getSource b/lib/config/getSource index 1042969..99e2fbe 100644 --- a/lib/config/getSource +++ b/lib/config/getSource @@ -1,10 +1,27 @@ #!/bin/bash + +# Copyright © 2012-2026 ScriptFanix +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# A copy of the GNU General Public License v3 is includded in the LICENSE file +# at the root of the project. + getConfigSource() { case "$key" in 'path') + # Root directory of music collection to transcode from sourcepath="$value" ;; 'skip') + # Directory pattern to exclude from scanning + # Multiple 'skip' entries accumulate into this array skippeddirectories+=( "$value" ) ;; esac diff --git a/lib/config/print b/lib/config/print index d684dcc..9ae6a09 100644 --- a/lib/config/print +++ b/lib/config/print @@ -1,6 +1,22 @@ #!/bin/bash + +# Copyright © 2012-2026 ScriptFanix +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# A copy of the GNU General Public License v3 is includded in the LICENSE file +# at the root of the project. + printConfig() { { + # Build a pipe-delimited table + # 'column -t -s|' will transform into columns echo "General|Config file|$cffile" [ -n "$ionice" ] && echo "|IO Nice|$ionice" cat <<-EOF @@ -12,24 +28,29 @@ printConfig() { Source|Path|$sourcepath EOF + # Print skipped directories + # use a continuation prefix after the first for prune_expression in "${skippeddirectories[@]}" do - (( printed )) \ + (( printed )) \ && echo -n ' | |' \ || echo -n ' |Skipped directories|' echo "$prune_expression" printed=1 done unset printed + # Loop over destinations and print their settings + # use the destination name as a key into the associative arrays for destination in ${destinations[@]} do cat <<-EOF - + $destination|Path|${destinationpath["$destination"]} |Enabled|${destinationenabled["$destination"]} |Format|${destinationformat["$destination"]} |Quality|${destinationquality["$destination"]} EOF + # Show format-specific fields if [[ ${destinationformat["$destination"]} == opus ]] then echo " |Expected loss|${destinationloss["$destination"]}" @@ -47,6 +68,7 @@ printConfig() { |Path Change|${destinationrenamepath["$destination"]} |File Rename|${destinationrename["$destination"]} EOF + # Display pipe-separated mime lists: one entry per row [ -n "${destinationskipmime["$destination"]}" ] \ && echo " |Skipped mime-types|${destinationskipmime["$destination"]//\|/ | |}" @@ -57,5 +79,5 @@ printConfig() { && echo " |Copied extensions|${destinationcopyext["$destination"]//\|/ | |}" done - }|column -t -s'|' + }|column -t -s'|' # Format as aligned columns using '|' as delimiter } diff --git a/lib/config/write b/lib/config/write index e436231..88dd487 100644 --- a/lib/config/write +++ b/lib/config/write @@ -1,5 +1,22 @@ #!/bin/bash +# Copyright © 2012-2026 ScriptFanix +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# A copy of the GNU General Public License v3 is includded in the LICENSE file +# at the root of the project. + + +# writeConfig() generates a new atom.cfg from the current in-memory settings. +# Output is sent to stdout so callers can redirect it to any config file path. +# Each setting is annotated with a description. writeConfig() { cat <<-EOCfg [general] @@ -47,6 +64,7 @@ path $sourcepath # * skip : String. Files in will be ignored. Note that # can be any expression accepted by find. EOCfg + # Emit one skip line per skipped directory for dir in "${skippeddirectories[@]}" do echo $'skip\t\t\t'"$dir" @@ -55,6 +73,7 @@ path $sourcepath EOCfg + # Emit one section per configured destination for destination in "${destinations[@]}" do cat <<-EOCfg @@ -190,6 +209,8 @@ bitrate ${destinationquality["$destination"]} # be included in that destination. For more than one mime-type, use multiple # times, as needed. The '*' character is a wildcard. EOCfg + # Emit one skip_mime-type line per MIME pattern (pipe-separated + # in the array) destinationskipmime["$destination"]="${destinationskipmime["$destination"]}|" while [[ ${destinationskipmime["$destination"]} =~ \| ]] do @@ -203,6 +224,7 @@ bitrate ${destinationquality["$destination"]} # covers and other images to the destination. In fact, AtOM will try to use # hard links instead of copies. EOCfg + # Emit one copy_mime-type line per MIME pattern destinationcopymime["$destination"]="${destinationcopymime["$destination"]}|" while [[ ${destinationcopymime["$destination"]} =~ \| ]] do @@ -213,6 +235,7 @@ bitrate ${destinationquality["$destination"]} # * copy_extension : Copy files whose name and with "." EOCfg + # Emit one copy_extension line per extension pattern destinationcopyext["$destination"]="${destinationcopyext["$destination"]}|" while [[ ${destinationcopyext["$destination"]} =~ \| ]] do