From 7464b41b1883d51d153963cffe4ce8b78c7ce114 Mon Sep 17 00:00:00 2001 From: masterdraco Date: Tue, 25 Feb 2025 12:03:10 +0100 Subject: [PATCH] unpacking failed - files where copied and unpacked --- etc/torrent/mover.conf | 6 +++ usr/local/bin/torrent-mover | 105 ++++++++++++++++++++++++++---------- 2 files changed, 82 insertions(+), 29 deletions(-) diff --git a/etc/torrent/mover.conf b/etc/torrent/mover.conf index 4cc44b1..7e54c57 100644 --- a/etc/torrent/mover.conf +++ b/etc/torrent/mover.conf @@ -37,6 +37,12 @@ LOG_FILE="/var/log/torrent_mover.log" # Logging level: set to "DEBUG" to enable debug messages; otherwise "INFO" LOG_LEVEL="INFO" +# Set CHECK_TRANSFER_INTEGRITY to "true" to verify copied files after transfer. +CHECK_TRANSFER_INTEGRITY="true" + +# Optionally, set USE_SYSLOG="true" to also log messages to syslog. +USE_SYSLOG="false" + # Auto-create directories mkdir -p "${DIR_GAMES_DST}" "${DIR_APPS_DST}" \ "${DIR_MOVIES_DST}" "${DIR_BOOKS_DST}" \ diff --git a/usr/local/bin/torrent-mover b/usr/local/bin/torrent-mover index 41dca97..588a31c 100755 --- a/usr/local/bin/torrent-mover +++ b/usr/local/bin/torrent-mover @@ -1,46 +1,68 @@ #!/bin/bash -# Torrent Mover v7.2 - Enhanced Version with Directory Deduplication +# Torrent Mover v7.2 - Enhanced & Robust Version with Directory Deduplication, +# Improved Archive Handling (keeping archives until ratio limits are reached) # # This script processes completed torrents reported by Transmission, # moving or copying files to designated destination directories. -# It includes improved logging (with debug support), error handling, -# configurable path mappings, and avoids re-processing the same source directory. - -############################# -# Global Variables & Config # -############################# +# It includes robust locking, advanced error handling & notifications, +# improved logging, optional post-transfer integrity checks, configurable path mapping, +# and improved archive extraction that preserves directory structure. +# +# Future improvements might include using Transmission’s RPC API. +############################## +# Robust Locking with flock # +############################## LOCK_FILE="/var/lock/torrent-mover.lock" -MAX_AGE=300 # 5 minutes in seconds +exec 200>"${LOCK_FILE}" || { echo "Cannot open lock file" >&2; exit 1; } +flock -n 200 || { echo "Another instance is running." >&2; exit 1; } -# Runtime flags (default values) +############################## +# Global Runtime Variables # +############################## DRY_RUN=0 INTERACTIVE=0 CACHE_WARMUP=0 -DEBUG=0 # Will be set to 1 if LOG_LEVEL is DEBUG or if --debug is passed +DEBUG=0 # Set to 1 if LOG_LEVEL is DEBUG or --debug is passed -# Associative array to track source directories that have been processed. +# To avoid reprocessing the same source directory (across different torrents) declare -A processed_source_dirs #################### # Logging Functions# #################### -# All log output goes to stderr so that command outputs remain clean. +# All log messages go to stderr. log_debug() { if [[ "${DEBUG}" -eq 1 ]]; then echo -e "[DEBUG] $(date '+%F %T') - $*" | tee -a "${LOG_FILE}" >&2 + [[ "${USE_SYSLOG}" == "true" ]] && logger -t torrent-mover "[DEBUG] $*" fi } log_info() { echo -e "[INFO] $(date '+%F %T') - $*" | tee -a "${LOG_FILE}" >&2 + [[ "${USE_SYSLOG}" == "true" ]] && logger -t torrent-mover "[INFO] $*" } log_warn() { echo -e "[WARN] $(date '+%F %T') - $*" | tee -a "${LOG_FILE}" >&2 + [[ "${USE_SYSLOG}" == "true" ]] && logger -t torrent-mover "[WARN] $*" } log_error() { echo -e "[ERROR] $(date '+%F %T') - $*" | tee -a "${LOG_FILE}" >&2 + [[ "${USE_SYSLOG}" == "true" ]] && logger -t torrent-mover "[ERROR] $*" } +################################# +# Error Handling & Notifications# +################################# +error_handler() { + local lineno="$1" + local msg="$2" + log_error "Error on line ${lineno}: ${msg}" + # Optionally send a notification (e.g., email) + exit 1 +} +trap 'error_handler ${LINENO} "$BASH_COMMAND"' ERR + ############################## # Configuration & Validation # ############################## @@ -51,13 +73,12 @@ if [[ ! -f "${CONFIG_FILE}" ]]; then fi source "${CONFIG_FILE}" -# Validate mandatory configuration values. +# Validate required configuration values. if [[ -z "${TRANSMISSION_PATH_PREFIX:-}" || -z "${LOCAL_PATH_PREFIX:-}" ]]; then echo "FATAL: TRANSMISSION_PATH_PREFIX and LOCAL_PATH_PREFIX must be set in ${CONFIG_FILE}" >&2 exit 1 fi -# Set DEBUG flag if LOG_LEVEL is DEBUG. if [[ "${LOG_LEVEL}" == "DEBUG" ]]; then DEBUG=1 fi @@ -68,14 +89,13 @@ if [[ -n "${STORAGE_DIRS}" ]]; then IFS=',' read -ra STORAGE_DIRS_ARRAY <<< "${STORAGE_DIRS}" fi -########################### +############################## # Helper & Utility Functions # -########################### +############################## # translate_source: Converts the Transmission‑reported path into the local path. translate_source() { local src="$1" - # Replace the Transmission reported prefix with the local prefix. echo "${src/#${TRANSMISSION_PATH_PREFIX}/${LOCAL_PATH_PREFIX}}" } @@ -100,10 +120,7 @@ parse_args() { check_dependencies() { local deps=("transmission-remote" "unrar" "unzip" "7z" "parallel" "bc") for dep in "${deps[@]}"; do - if ! command -v "${dep}" >/dev/null 2>&1; then - log_error "Missing dependency: ${dep}" - exit 1 - fi + command -v "${dep}" >/dev/null 2>&1 || { log_error "Missing dependency: ${dep}"; exit 1; } done } @@ -144,7 +161,7 @@ record_checksums() { mv "${CHECKSUM_DB}.tmp" "${CHECKSUM_DB}" } -# file_metadata: Returns an md5 hash for the file metadata. +# 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}' } @@ -265,16 +282,33 @@ get_destination() { echo "${destination}" } -# handle_archives: Extracts archives found in the source directory. +###################################### +# Improved Archive Extraction Handler # +###################################### +# For each archive found in the source directory, create a subdirectory in the destination +# named after the archive (without its extension) and extract into that subdirectory. +# IMPORTANT: The archive is now retained in the source, so it will remain until the ratio +# limits are reached and Transmission removes the torrent data. handle_archives() { local src="$1" dst="$2" find "${src}" -type f \( -iname "*.rar" -o -iname "*.zip" -o -iname "*.7z" \) | while read -r arch; do - log_info "Extracting ${arch##*/}" + log_info "Extracting archive: ${arch}" + local base + base=$(basename "${arch}") + local subdir="${dst}/${base%.*}" + mkdir -p "${subdir}" || { log_error "Failed to create subdirectory ${subdir}"; continue; } case "${arch##*.}" in - rar) unrar x -o- "${arch}" "${dst}" || log_error "unrar failed for ${arch}";; - zip) unzip -o "${arch}" -d "${dst}" || log_error "unzip failed for ${arch}";; - 7z) 7z x "${arch}" -o"${dst}" || log_error "7z extraction failed for ${arch}";; + rar) + unrar x -o- "${arch}" "${subdir}" || { log_error "unrar failed for ${arch}"; continue; } + ;; + zip) + unzip -o "${arch}" -d "${subdir}" || { log_error "unzip failed for ${arch}"; continue; } + ;; + 7z) + 7z x "${arch}" -o"${subdir}" || { log_error "7z extraction failed for ${arch}"; continue; } + ;; esac + log_info "Archive ${arch} retained in source until ratio limits are reached." done } @@ -297,6 +331,7 @@ copy_files() { } # 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 @@ -331,6 +366,18 @@ process_copy() { ;; 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 @@ -402,7 +449,7 @@ main() { percent_done=$(grep "Percent Done:" <<< "${info}" | awk '{gsub(/%/, ""); print $3 == "None" ? 0 : $3}') percent_done=${percent_done:-0} - # Extract Transmission reported directory and translate to local path. + # Extract Transmission-reported directory and translate to local path. local reported_dir reported_dir=$(grep -i "Location:" <<< "${info}" | awk -F": " '{print $2}' | xargs) local dir @@ -412,7 +459,7 @@ main() { dst=$(get_destination "${dir}") [[ -z "${warned_dirs["${dir}"]+x}" ]] && warned_dirs["${dir}"]=0 - # Check if this source directory has already been processed. + # Avoid processing the same directory more than once. if [[ -n "${processed_source_dirs["${dir}"]+x}" ]]; then log_info "Directory ${dir} has already been processed; skipping copy for torrent ${id}" elif (( $(bc <<< "${percent_done} >= 100") )) && ! is_processed "${hash}"; then