AtOM/lib/copy/action
2026-03-13 03:54:31 +01:00

170 lines
5.1 KiB
Bash

#!/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.
copyFiles_action() {
(( cron )) || echo -n $'Copying files...\033[K'
# Query all destination_files whose last_change doesn't match the
# source, restricted to mime_type_actions with action=2 (direct copy,
# not transcode).
# The join on mime_type_actions ensures we only copy files destined for
# a destination that has explicitly mapped this MIME type to action=2.
echo '
SELECT
source_files.filename,
source_files.last_change,
destinations.id,
destinations.name,
destination_files.id
FROM source_files
INNER JOIN destination_files
ON source_files.id
= destination_files.source_file_id
INNER JOIN destinations
ON destination_files.destination_id=destinations.id
INNER JOIN mime_type_actions
ON mime_type_actions.id = source_files.mime_type
WHERE CAST(destination_files.last_change AS TEXT)
<> CAST(source_files.last_change AS TEXT)
AND mime_type_actions.destination_id = destinations.id
AND mime_type_actions.action = 2;
SELECT "AtOM:NoMoreFiles";' >&3
# Results are NUL-delimited rows; sentinel value signals end of result
# set.
read -u4 -r -d $'\0' line
while ! [[ $line = AtOM:NoMoreFiles ]]
do
copyfiles+=("$line")
read -u4 -r -d $'\0' line
done
# Wrap all DB updates in a single transaction for performance.
echo 'BEGIN TRANSACTION;' >&3
for copyfile in "${copyfiles[@]}"
do
# Each row is a single string with columns joined by
# ::AtOM:SQL:Sep::.
# Strip prefix to extract each field in order, advancing $rest
# each time.
sourcefilename=${copyfile%%::AtOM:SQL:Sep::*}
sourcedir=${sourcefilename%/*}
rest="${copyfile#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::"
lastchange=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
destinationid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
destination=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
destfileid=${rest%%::AtOM:SQL:Sep::*}
rest=${rest#*::AtOM:SQL:Sep::}
(( count++ ))
(( cron )) || printf '\b\b\b\b%3i%%' $(( (count * 100) / ${#copyfiles[@]} ))
# If this destination uses a rename/path pattern, delegate path
# resolution to guessPath(), which looks up the
# already-transcoded sibling to find the correct output
# directory.
if [ -n "${destinationrenamepath["$destination"]}" ]
then
destdir="$(guessPath)"
guessstatus=$?
case $guessstatus in
1)
# guessPath found no transcoded
# sibling; skip this file entirely.
continue
;;
2)
# Transcoded siblings exist but are not
# yet up to date; defer this copy until
# the next run so the directory is
# stable.
(( postponed++ ))
continue
;;
esac
else
# No rename pattern: mirror the source directory
# structure under the destination root, sanitizing each
# path component for the target FS.
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
# Try the cheapest copy methods first: reflink (CoW, same
# filesystem), then hardlink (csame filesystem), falling back
# to a full data copy.
if cp -a --reflink=always \
"$sourcepath/$sourcefilename" \
"$destdir" \
2>/dev/null \
|| cp -al \
"$sourcepath/$sourcefilename" \
"$destdir" \
2>/dev/null \
|| cp -a \
"$sourcepath/$sourcefilename" \
"$destdir"
then
# Newlines in filenames are stored as a safe inline
# placeholder so the value can be embedded in SQL
# without breaking row parsing.
destfilename=${sourcefilename//$'\n'/::AtOM:NewLine:SQL:Inline::}
Update destination_files \
filename \
"$destdir/${destfilename##*/}"\
rename_pattern \
"${destinationrenamepath[$destination]}/${destinationrename[$destination]}"\
fat32compat \
${destinationfat32compat["$destination"]}\
ascii \
${destinationascii["$destination"]}\
last_change \
$lastchange \
<<-EOWhere
id = $destfileid
EOWhere
(( done++ ))
fi
done
echo 'COMMIT;' >&3
if (( count ))
then
(( cron )) || echo -n $'\r'
echo -n "Copied ${done:-0} of $count" \
"files${postponed+ ($postponed postponed)}."
(( cron )) || echo -en "\033[K"
echo
else
(( cron )) || echo -n $'\r\033[K'
fi
unset count done
}