diff --git a/lib/tags/update b/lib/tags/update index 90681a7..4b5c844 100644 --- a/lib/tags/update +++ b/lib/tags/update @@ -1,11 +1,33 @@ #!/usr/bin/env 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. + updateTags() { local reader \ + # Build a WHERE clause fragment that excludes files already read by any + # known reader version; files not matching any known version need a + # re-read for reader in "${tagreaders[@]}" do tagreaderclause+="${tagreaderclause:+ AND }NOT tags.tagreader = \"$reader\"" done + # Query source files that need tag updates: either the file's + # last_change differs from the stored tag row's last_change, or the + # tagreader version has changed. + # Only files linked to a destination with action=1 (transcode) are + # included. echo ' SELECT DISTINCT source_files.id, @@ -48,11 +70,13 @@ updateTags() { ) AND mime_type_actions.action = 1 ORDER BY source_files.id' >&3 + # Optionally cap the number of files processed in one run (( maxbatch )) && echo "LIMIT $maxbatch" >&3 echo ' ; SELECT "AtOM:NoMoreFiles";' >&3 + # Collect all result rows; sentinel "AtOM:NoMoreFiles" ends the loop read -u4 -r -d $'\0' line while ! [[ $line = AtOM:NoMoreFiles ]] do @@ -60,9 +84,10 @@ echo ' (( filecount++ )) read -u4 -r -d $'\0' line done - echo 'BEGIN TRANSACTION;' >&3 + # Wrap all updates in a transaction echo 'BEGIN TRANSACTION;' >&3 for line in "${tagfiles[@]}" do + # Split each row on the ::AtOM:SQL:Sep:: column separator sourcefileid=${line%%::AtOM:SQL:Sep::*} rest=${line#*::AtOM:SQL:Sep::} lastchange=${rest%%::AtOM:SQL:Sep::*} @@ -107,7 +132,9 @@ echo ' rest=${rest#*::AtOM:SQL:Sep::} oldbitrate=${rest%%::AtOM:SQL:Sep::*} ((++count)) + # Show percentage progress in interactive mode (( cron )) || echo -en "\rTags: $((count*100/filecount))%" + # Commit every 100 files: limit reprocessing on interrupt/crash if (( count % 100 == 0 )) then echo 'COMMIT;BEGIN TRANSACTION;' >&3 @@ -116,6 +143,10 @@ echo ' fi if getTags then + # Set a dirty flag for each field that changed since + # the last read; + # only changed fields will be included in the UPDATE + # statement below [[ $oldalbum != "$album" ]]&& ual=1 [[ $oldalbumartist != "$albumartist" ]]&&uaa=1 [[ $oldartist != "$artist" ]]&& uar=1 @@ -133,6 +164,13 @@ echo ' [[ $oldrate != "$rate" ]]&& ura=1 [[ $oldchannels != "$channels" ]]&& uch=1 [[ $oldbitrate != "$bitrate" ]]&& ubi=1 + # Emit an Update with only dirty columns plus + # last_change/tagreader. + # ::AtOM:FT:: prefix forces text quoting even for + # numeric-looking values. + # Absent values become SQL NULL (no quotes). + # Each ${flag:+col val} expands to nothing when the + # flag is unset. Update tags \ ${ual:+album "${album:+::AtOM:FT::}${album:-NULL}"}\ ${uaa:+albumartist "${albumartist:+::AtOM:FT::}${albumartist:-NULL}"}\ @@ -156,6 +194,8 @@ echo ' ${ubi:+bitrate "${bitrate:-NULL}"} \ tagreader "$tagreader" \ >/dev/null <<<"source_file = $sourcefileid" + # Clear all tag variables and dirty flags for the next + # iteration unset genre \ albumartist \ year \ @@ -212,6 +252,7 @@ echo ' ubi fi done + # Commit the final partial batch echo 'COMMIT;' >&3 (( cron )) || echo -n $'\r' (( count )) && echo -n "Read tags from $count files."