diff --git a/docs/Downloaders/qBittorrent/Tips/How-to-run-the-unRaid-mover-for-qBittorrent.md b/docs/Downloaders/qBittorrent/Tips/How-to-run-the-unRaid-mover-for-qBittorrent.md
index 5e1daa7b0f..3749aaeaa6 100644
--- a/docs/Downloaders/qBittorrent/Tips/How-to-run-the-unRaid-mover-for-qBittorrent.md
+++ b/docs/Downloaders/qBittorrent/Tips/How-to-run-the-unRaid-mover-for-qBittorrent.md
@@ -125,10 +125,10 @@ chmod +x /mnt/user/appdata/qbt-mover/mover-tuning-end.sh
After editing the scripts, make sure they use LF line endings (Unix format). If you edited the files on Windows, they may have CRLF line endings, which can cause errors.
-Run the following command to convert all `.sh` files to the correct format:
+Run the following command to convert all `.sh` and `.cfg` files to the correct format:
```bash
-for file in *.sh; do [ -f "$file" ] && sed -i 's/\r$//' "$file" && echo "Converted $file"; done
+for file in *.sh *.cfg; do [ -f "$file" ] && sed -i 's/\r$//' "$file" && echo "Converted $file"; done
```
**How to use this command:**
@@ -143,7 +143,7 @@ for file in *.sh; do [ -f "$file" ] && sed -i 's/\r$//' "$file" && echo "Convert
3. Paste the command above
4. Press ++enter++
-This converts all `.sh` files from CRLF (Windows line endings) to LF (Unix line endings) in the current directory.
+This converts all `.sh` and `.cfg` files from CRLF (Windows line endings) to LF (Unix line endings) in the current directory.
---
diff --git a/includes/downloaders/mover-tuning-end.sh b/includes/downloaders/mover-tuning-end.sh
index 096ef62533..310bf5d7fa 100644
--- a/includes/downloaders/mover-tuning-end.sh
+++ b/includes/downloaders/mover-tuning-end.sh
@@ -3,12 +3,12 @@ set -euo pipefail # Exit on error, undefined variables, and pipe failures
# =====================================
# Script: qBittorrent Cache Mover - End
-# Version: 1.0.0
-# Updated: 20251121
+# Version: 1.1.0
+# Updated: 20251201
# =====================================
# Script version and update check URLs
-readonly SCRIPT_VERSION="1.0.0"
+readonly SCRIPT_VERSION="1.1.0"
readonly SCRIPT_RAW_URL="https://raw.githubusercontent.com/TRaSH-Guides/Guides/refs/heads/master/includes/downloaders/mover-tuning-end.sh"
# Get the directory where the script is located
@@ -44,6 +44,63 @@ notify() {
fi
}
+# ================================
+# CONFIG FORMAT DETECTION
+# ================================
+detect_config_format() {
+ # Check if array-based config is used
+ if [[ -v HOSTS[@] ]] && [[ ${#HOSTS[@]} -gt 0 ]]; then
+ echo "array"
+ else
+ echo "legacy"
+ fi
+}
+
+get_instance_count() {
+ local format
+ format=$(detect_config_format)
+
+ if [[ "$format" == "array" ]]; then
+ echo "${#HOSTS[@]}"
+ else
+ # Legacy format: count based on ENABLE_QBIT_2
+ if [[ "${ENABLE_QBIT_2:-false}" == true ]]; then
+ echo "2"
+ else
+ echo "1"
+ fi
+ fi
+}
+
+get_instance_details() {
+ local index="$1"
+ local format
+ format=$(detect_config_format)
+
+ if [[ "$format" == "array" ]]; then
+ # Array format: set global variables
+ INSTANCE_NAME="${NAMES[$index]:-qBit-Instance-$((index + 1))}"
+ INSTANCE_HOST="${HOSTS[$index]}"
+ INSTANCE_USER="${USERS[$index]}"
+ INSTANCE_PASSWORD="${PASSWORDS[$index]}"
+ else
+ # Legacy format: map index to old variables
+ if [[ $index -eq 0 ]]; then
+ INSTANCE_NAME="${QBIT_NAME_1}"
+ INSTANCE_HOST="${QBIT_HOST_1}"
+ INSTANCE_USER="${QBIT_USER_1}"
+ INSTANCE_PASSWORD="${QBIT_PASS_1}"
+ elif [[ $index -eq 1 ]]; then
+ INSTANCE_NAME="${QBIT_NAME_2}"
+ INSTANCE_HOST="${QBIT_HOST_2}"
+ INSTANCE_USER="${QBIT_USER_2}"
+ INSTANCE_PASSWORD="${QBIT_PASS_2}"
+ else
+ error "Invalid instance index: $index"
+ fi
+ fi
+}
+
# ================================
# VERSION CHECK FUNCTION
# ================================
@@ -91,9 +148,17 @@ check_script_version() {
# Compare versions
if [[ "$SCRIPT_VERSION" != "$latest_version" ]]; then
# Simple version comparison (works for semantic versioning)
- if printf '%s\n' "$latest_version" "$SCRIPT_VERSION" | sort -V | head -n1 | grep -q "^$SCRIPT_VERSION$" 2>/dev/null || true; then
+ # Sort both versions and check if SCRIPT_VERSION comes first (is older)
+ local oldest_version
+ oldest_version=$(printf '%s\n' "$latest_version" "$SCRIPT_VERSION" | sort -V | head -n1)
+
+ if [[ "$oldest_version" == "$SCRIPT_VERSION" ]]; then
+ # SCRIPT_VERSION is older, so there's a newer version available
log "⚠ New version available: $latest_version"
notify "mover-tuning-end.sh Update" "Version $latest_version available (current: $SCRIPT_VERSION)
📖 Visit the TRaSH-Guides for the latest version"
+ else
+ # latest_version is older, local version is newer
+ log "✓ Local version ($SCRIPT_VERSION) is newer than remote ($latest_version)"
fi
else
log "✓ Script is up to date"
@@ -297,6 +362,31 @@ validate_config() {
[[ -d "$CACHE_MOUNT" ]] || error "Cache mount not found: $CACHE_MOUNT"
[[ -f "${QBIT_MOVER_PATH}mover.py" ]] || error "mover.py not found: ${QBIT_MOVER_PATH}mover.py"
+ # Validate instance configuration
+ local format
+ format=$(detect_config_format)
+
+ if [[ "$format" == "array" ]]; then
+ # Validate array-based config
+ [[ ${#HOSTS[@]} -gt 0 ]] || error "HOSTS array is empty"
+ [[ ${#USERS[@]} -eq ${#HOSTS[@]} ]] || error "USERS array length doesn't match HOSTS"
+ [[ ${#PASSWORDS[@]} -eq ${#HOSTS[@]} ]] || error "PASSWORDS array length doesn't match HOSTS"
+
+ # NAMES array is optional, but if present should match
+ if [[ -v NAMES[@] ]] && [[ ${#NAMES[@]} -gt 0 ]]; then
+ [[ ${#NAMES[@]} -eq ${#HOSTS[@]} ]] || error "NAMES array length doesn't match HOSTS"
+ fi
+
+ log "✓ Using array-based configuration (${#HOSTS[@]} instance(s))"
+ else
+ # Validate legacy config
+ [[ -n "${QBIT_HOST_1:-}" ]] || error "QBIT_HOST_1 is not set"
+ [[ -n "${QBIT_USER_1:-}" ]] || error "QBIT_USER_1 is not set"
+ [[ -n "${QBIT_PASS_1:-}" ]] || error "QBIT_PASS_1 is not set"
+
+ log "✓ Using legacy configuration"
+ fi
+
# Validate duplicate finder if enabled
if [[ "$ENABLE_DUPLICATE_FINDER" == true ]]; then
if [[ "$ENABLE_AUTO_INSTALLER" == true ]]; then
@@ -372,18 +462,15 @@ main() {
# Validate configuration
validate_config || exit 1
- # Process primary instance
- process_qbit_instance "$QBIT_NAME_1" "$QBIT_HOST_1" "$QBIT_USER_1" "$QBIT_PASS_1" || \
- ((failed_instances++))
+ # Process all instances
+ local instance_count
+ instance_count=$(get_instance_count)
- # Process secondary instance if enabled
- if [[ "$ENABLE_QBIT_2" == true ]]; then
- log "Processing secondary instance..."
- process_qbit_instance "$QBIT_NAME_2" "$QBIT_HOST_2" "$QBIT_USER_2" "$QBIT_PASS_2" || \
- ((failed_instances++))
- else
- log "Secondary instance disabled"
- fi
+ for ((i=0; i/dev/null || log "⚠ Warning: Could not set ownership for $1"
}
+# ================================
+# CONFIG FORMAT DETECTION
+# ================================
+detect_config_format() {
+ # Check if array-based config is used
+ if [[ -v HOSTS[@] ]] && [[ ${#HOSTS[@]} -gt 0 ]]; then
+ echo "array"
+ else
+ echo "legacy"
+ fi
+}
+
+get_instance_count() {
+ local format
+ format=$(detect_config_format)
+
+ if [[ "$format" == "array" ]]; then
+ echo "${#HOSTS[@]}"
+ else
+ # Legacy format: count based on ENABLE_QBIT_2
+ if [[ "${ENABLE_QBIT_2:-false}" == true ]]; then
+ echo "2"
+ else
+ echo "1"
+ fi
+ fi
+}
+
+get_instance_details() {
+ local index="$1"
+ local format
+ format=$(detect_config_format)
+
+ if [[ "$format" == "array" ]]; then
+ # Array format: set global variables
+ INSTANCE_NAME="${NAMES[$index]:-qBit-Instance-$((index + 1))}"
+ INSTANCE_HOST="${HOSTS[$index]}"
+ INSTANCE_USER="${USERS[$index]}"
+ INSTANCE_PASSWORD="${PASSWORDS[$index]}"
+ else
+ # Legacy format: map index to old variables
+ if [[ $index -eq 0 ]]; then
+ INSTANCE_NAME="${QBIT_NAME_1}"
+ INSTANCE_HOST="${QBIT_HOST_1}"
+ INSTANCE_USER="${QBIT_USER_1}"
+ INSTANCE_PASSWORD="${QBIT_PASS_1}"
+ elif [[ $index -eq 1 ]]; then
+ INSTANCE_NAME="${QBIT_NAME_2}"
+ INSTANCE_HOST="${QBIT_HOST_2}"
+ INSTANCE_USER="${QBIT_USER_2}"
+ INSTANCE_PASSWORD="${QBIT_PASS_2}"
+ else
+ error "Invalid instance index: $index"
+ fi
+ fi
+}
+
# ================================
# VERSION CHECK FUNCTION
# ================================
@@ -104,9 +161,17 @@ check_script_version() {
# Compare versions
if [[ "$SCRIPT_VERSION" != "$latest_version" ]]; then
# Simple version comparison (works for semantic versioning)
- if printf '%s\n' "$latest_version" "$SCRIPT_VERSION" | sort -V | head -n1 | grep -q "^$SCRIPT_VERSION$" 2>/dev/null || true; then
+ # Sort both versions and check if SCRIPT_VERSION comes first (is older)
+ local oldest_version
+ oldest_version=$(printf '%s\n' "$latest_version" "$SCRIPT_VERSION" | sort -V | head -n1)
+
+ if [[ "$oldest_version" == "$SCRIPT_VERSION" ]]; then
+ # SCRIPT_VERSION is older, so there's a newer version available
log "⚠ New version available: $latest_version"
notify "mover-tuning-start.sh Update" "Version $latest_version available (current: $SCRIPT_VERSION)
📖 Visit the TRaSH-Guides for the latest version"
+ else
+ # latest_version is older, local version is newer
+ log "✓ Local version ($SCRIPT_VERSION) is newer than remote ($latest_version)"
fi
else
log "✓ Script is up to date"
@@ -162,9 +227,17 @@ check_config_version() {
# Compare versions
if [[ "$current_config_version" != "$remote_config_version" ]]; then
# Simple version comparison (works for semantic versioning)
- if printf '%s\n' "$remote_config_version" "$current_config_version" | sort -V | head -n1 | grep -q "^$current_config_version$" 2>/dev/null || true; then
+ # Sort both versions and check if current_config_version comes first (is older)
+ local oldest_version
+ oldest_version=$(printf '%s\n' "$remote_config_version" "$current_config_version" | sort -V | head -n1)
+
+ if [[ "$oldest_version" == "$current_config_version" ]]; then
+ # current_config_version is older, so there's a newer version available
log "⚠ New config version available: $remote_config_version"
notify "mover-tuning.cfg Update" "Config version $remote_config_version available
Current version: $current_config_version
📖 Visit the TRaSH-Guides for the latest version"
+ else
+ # remote_config_version is older, local version is newer
+ log "✓ Local config version ($current_config_version) is newer than remote ($remote_config_version)"
fi
else
log "✓ Config is up to date"
@@ -272,6 +345,45 @@ validate_config() {
[[ "$DAYS_FROM" -ge 2 ]] || error "DAYS_FROM must be at least 2"
[[ "$DAYS_TO" -ge "$DAYS_FROM" ]] || error "DAYS_TO must be >= DAYS_FROM"
[[ -d "$CACHE_MOUNT" ]] || error "Cache mount does not exist: $CACHE_MOUNT"
+
+ # Validate instance configuration
+ local format
+ format=$(detect_config_format)
+
+ if [[ "$format" == "array" ]]; then
+ # Validate array-based config
+ if [[ ${#HOSTS[@]} -eq 0 ]]; then
+ notify "Configuration Error" "HOSTS array is empty"
+ error "HOSTS array is empty"
+ fi
+
+ if [[ ${#USERS[@]} -ne ${#HOSTS[@]} ]]; then
+ notify "Configuration Error" "USERS array length (${#USERS[@]}) doesn't match HOSTS (${#HOSTS[@]})"
+ error "USERS array length doesn't match HOSTS"
+ fi
+
+ if [[ ${#PASSWORDS[@]} -ne ${#HOSTS[@]} ]]; then
+ notify "Configuration Error" "PASSWORDS array length (${#PASSWORDS[@]}) doesn't match HOSTS (${#HOSTS[@]})"
+ error "PASSWORDS array length doesn't match HOSTS"
+ fi
+
+ # NAMES array is optional, but if present should match
+ if [[ -v NAMES[@] ]] && [[ ${#NAMES[@]} -gt 0 ]]; then
+ if [[ ${#NAMES[@]} -ne ${#HOSTS[@]} ]]; then
+ notify "Configuration Error" "NAMES array length (${#NAMES[@]}) doesn't match HOSTS (${#HOSTS[@]})"
+ error "NAMES array length doesn't match HOSTS"
+ fi
+ fi
+
+ log "✓ Using array-based configuration (${#HOSTS[@]} instance(s))"
+ else
+ # Validate legacy config
+ [[ -n "${QBIT_HOST_1:-}" ]] || error "QBIT_HOST_1 is not set"
+ [[ -n "${QBIT_USER_1:-}" ]] || error "QBIT_USER_1 is not set"
+ [[ -n "${QBIT_PASS_1:-}" ]] || error "QBIT_PASS_1 is not set"
+
+ log "✓ Using legacy configuration"
+ fi
}
# ================================
@@ -350,12 +462,15 @@ main() {
fi
fi
- # Process instances
- process_qbit_instance "$QBIT_NAME_1" "$QBIT_HOST_1" "$QBIT_USER_1" "$QBIT_PASS_1" || ((failed_instances++))
+ # Process all instances
+ local instance_count
+ instance_count=$(get_instance_count)
- if [[ "$ENABLE_QBIT_2" == true ]]; then
- process_qbit_instance "$QBIT_NAME_2" "$QBIT_HOST_2" "$QBIT_USER_2" "$QBIT_PASS_2" || ((failed_instances++))
- fi
+ for ((i=0; i
# =============================================
-readonly CONFIG_VERSION="1.0.0" # ONLY UPDATE THE VERSION NUMBER WHEN A CONFIGURATION FILE CHANGE HAS BEEN MADE
+readonly CONFIG_VERSION="1.1.0" # ONLY UPDATE THE VERSION NUMBER WHEN A CONFIGURATION FILE CHANGE HAS BEEN MADE
readonly ENABLE_VERSION_CHECK=true # Set to false to disable all update checks and notifications (not recommended)
# =============================================
@@ -15,22 +15,18 @@ readonly ENABLE_AUTO_INSTALLER=true # Set to false to disable qBit-Api and qBit
# qBit-Mover Settings
# >>> NOTE: Setting "DAYS_FROM" below 2 days may not work properly <<<
readonly DAYS_FROM=25 # How old torrents must be (in days) before they're paused and moved (Must be at least 2 days)
-readonly DAYS_TO=99 # Maximum age (days) for torrent selection
+readonly DAYS_TO=99 # Maximum age (days) for torrent selection (must be >= DAYS_FROM)
readonly CACHE_MOUNT="/mnt/cache/" # Cache mount point in Unraid
readonly QBIT_MOVER_PATH="/mnt/user/appdata/qbt-mover/" # Path to mover.py
-# Primary qBittorrent instance (REQUIRED)
-readonly QBIT_NAME_1="qBit-Movies" # qBittorrent instance name
-readonly QBIT_HOST_1="192.168.2.200:8800" # qBittorrent host:port
-readonly QBIT_USER_1="admin" # qBittorrent username
-readonly QBIT_PASS_1="qbt1-password" # qBittorrent password
-
-# Secondary qBittorrent instance (OPTIONAL)
-readonly ENABLE_QBIT_2=false # Set to true to enable secondary instance
-readonly QBIT_NAME_2="qBit-TV" # qBittorrent instance name
-readonly QBIT_HOST_2="192.168.2.200:8811" # qBittorrent host:port
-readonly QBIT_USER_2="admin" # qBittorrent username
-readonly QBIT_PASS_2="qbt2-password" # qBittorrent password
+# qBittorrent instances
+# Supports unlimited qBittorrent instances
+# Arrays NAMES, HOSTS, USERS and PASSWORDS must all have the same length
+# NAMES array is optional - if omitted, instances will be named "qBit-Instance-1", "qBit-Instance-2", etc.
+readonly NAMES=("qBit-Movies" "qBit-TV" "qBit-Music") # qBittorrent instance names
+readonly HOSTS=("192.168.2.200:8088" "192.168.2.200:8811" "192.168.2.200:8822") # qBittorrent host:port
+readonly USERS=("admin" "admin" "admin") # qBittorrent usernames
+readonly PASSWORDS=("qbt1-password" "qbt2-password" "qbt3-password") # qBittorrent passwords
# qBit-Manage integration (OPTIONAL)
readonly ENABLE_QBIT_MANAGE=true # Set to false to disable qBit-Manage