torrent/usr/local/lib/torrent-mover/file_operations.sh
2025-02-28 10:07:04 +01:00

191 lines
6.7 KiB
Bash

#!/bin/bash
# File operation functions for torrent-mover
# init_checksum_db: Initializes the checksum database.
init_checksum_db() {
mkdir -p "$(dirname "${CHECKSUM_DB}")"
touch "${CHECKSUM_DB}" || { log_error "Could not create ${CHECKSUM_DB}"; exit 1; }
chmod 600 "${CHECKSUM_DB}"
}
# record_checksums: Generates checksums for files in given directories.
record_checksums() {
log_info "Generating checksums with ${PARALLEL_THREADS:-$(nproc)} threads"
find "$@" -type f ! \( -iname "*.nfo" -o -iname "*.sfv" \) -print0 | \
parallel -0 -j ${PARALLEL_THREADS:-$(nproc)} md5sum | sort > "${CHECKSUM_DB}.tmp"
mv "${CHECKSUM_DB}.tmp" "${CHECKSUM_DB}"
}
# file_metadata: Returns an md5 hash for file metadata.
file_metadata() {
find "$1" -type f ! \( -iname "*.nfo" -o -iname "*.sfv" \) -exec md5sum {} \; | sort | awk '{print $1}'
}
# files_need_processing: Checks if the source files need processing.
files_need_processing() {
local src="$1"
shift
local targets=("$@")
if [[ ! -d "${src}" ]]; then
log_warn "Source directory missing: ${src}"
return 1
fi
log_info "=== FILE VERIFICATION DEBUG START ==="
log_info "Source directory: ${src}"
log_info "Verification targets: ${targets[*]}"
local empty_target_found=0
for target in "${targets[@]}"; do
if [[ ! -d "${target}" ]]; then
log_info "Target missing: ${target}"
empty_target_found=1
continue
fi
local file_count
file_count=$(find "${target}" -mindepth 1 -maxdepth 1 -print | wc -l)
log_debug "File count for target ${target}: ${file_count}"
if [[ "${file_count}" -eq 0 ]]; then
log_info "Empty target directory: ${target}"
empty_target_found=1
else
log_info "Target contains ${file_count} items: ${target}"
log_info "First 5 items:"
find "${target}" -mindepth 1 -maxdepth 1 | head -n 5 | while read -r item; do
log_info " - ${item##*/}"
done
fi
done
if [[ "${empty_target_found}" -eq 1 ]]; then
log_info "Empty target detected - processing needed"
log_info "=== FILE VERIFICATION DEBUG END ==="
return 0
fi
log_info "Generating source checksums..."
local src_checksums
src_checksums=$(find "${src}" -type f ! \( -iname "*.nfo" -o -iname "*.sfv" \) -exec md5sum {} \; | sort)
log_info "First 5 source checksums:"
echo "${src_checksums}" | head -n 5 | while read -r line; do
log_info " ${line}"
done
local match_found=0
for target in "${targets[@]}"; do
log_info "Checking against target: ${target}"
log_info "Generating target checksums..."
local target_checksums
target_checksums=$(find "${target}" -type f ! \( -iname "*.nfo" -o -iname "*.sfv" \) -exec md5sum {} \; | sort)
log_info "First 5 target checksums:"
echo "${target_checksums}" | head -n 5 | while read -r line; do
log_info " ${line}"
done
if diff <(echo "${src_checksums}") <(echo "${target_checksums}") >/dev/null; then
log_info "Exact checksum match found in: ${target}"
match_found=1
break
else
log_info "No match in: ${target}"
fi
done
log_info "=== FILE VERIFICATION DEBUG END ==="
[[ "${match_found}" -eq 1 ]] && return 1 || return 0
}
# warm_cache: Pre-calculates checksums for storage directories.
warm_cache() {
log_info "Starting cache warmup for Movies..."
local targets=("${DIR_MOVIES_DST}" "${STORAGE_DIRS_ARRAY[@]}")
record_checksums "${targets[@]}"
log_info "Cache warmup completed. Checksums stored in ${CHECKSUM_DB}"
}
# is_processed: Checks if the torrent (by hash) has already been processed.
is_processed() {
grep -q "^${1}$" "${PROCESSED_LOG}" 2>/dev/null
}
# mark_processed: Records a processed torrent.
mark_processed() {
echo "${1}" >> "${PROCESSED_LOG}"
}
# move_files: Moves files using parallel processing if enabled.
move_files() {
if (( PARALLEL_PROCESSING )); then
retry_command "parallel -j ${PARALLEL_THREADS:-$(nproc)} mv {} \"${1}\" ::: \"${2}\"/*" 3 15
else
retry_command "mv \"${2}\"/* \"${1}\"" 3 15
fi
}
# copy_files: Copies files using parallel processing if enabled.
copy_files() {
if (( PARALLEL_PROCESSING )); then
retry_command "parallel -j ${PARALLEL_THREADS:-$(nproc)} cp -r {} \"${1}\" ::: \"${2}\"/*" 3 15
else
retry_command "cp -r \"${2}\"/* \"${1}\"" 3 15
fi
}
# process_copy: Validates directories, then copies/moves files from source to destination.
# Optionally verifies integrity after transfer if CHECK_TRANSFER_INTEGRITY is "true".
process_copy() {
local id="$1" hash="$2" src="$3" dst="$4"
if [[ ! -d "${src}" ]]; then
log_error "Source directory missing: ${src}"
return 1
fi
if [[ ! -d "${dst}" ]]; then
log_info "Creating destination directory: ${dst}"
mkdir -p "${dst}" || { log_error "Failed to create directory: ${dst}"; return 1; }
chmod 775 "${dst}"
chown ${TORRENT_USER:-debian-transmission}:${TORRENT_GROUP:-debian-transmission} "${dst}"
fi
if [[ ! -w "${dst}" ]]; then
log_error "No write permissions for: ${dst}"
return 1
fi
if (( DRY_RUN )); then
log_info "[DRY RUN] Would process torrent ${id}:"
log_info " - Copy files from ${src} to ${dst}"
log_info " - File count: $(find "${src}" -maxdepth 1 -type f | wc -l)"
return
fi
handle_archives "${src}" "${dst}"
case "${COPY_MODE}" in
move)
log_info "Moving files from ${src} to ${dst}"
move_files "${dst}" "${src}"
;;
copy)
log_info "Copying files from ${src} to ${dst}"
copy_files "${dst}" "${src}"
;;
esac
if [ $? -eq 0 ]; then
if [[ "${CHECK_TRANSFER_INTEGRITY}" == "true" ]]; then
log_info "Verifying integrity of transferred files..."
local src_checksum target_checksum
src_checksum=$(find "${src}" -type f ! \( -iname "*.nfo" -o -iname "*.sfv" \) -exec md5sum {} \; | sort)
target_checksum=$(find "${dst}" -type f ! \( -iname "*.nfo" -o -iname "*.sfv" \) -exec md5sum {} \; | sort)
if diff <(echo "${src_checksum}") <(echo "${target_checksum}") >/dev/null; then
log_info "Integrity check passed."
else
log_error "Integrity check FAILED for ${src}"
return 1
fi
fi
log_info "Transfer completed successfully"
mark_processed "${hash}"
else
log_error "Transfer failed for ${src}"
return 1
fi
return 0
}