#!/bin/bash # # Torrent Mover Configuration Utility # A helper tool to safely update and manage your torrent-mover configuration set -e CONFIG_PATH="/etc/torrent/mover.conf" BACKUP_DIR="/etc/torrent/backups" DEFAULT_EDITOR="${EDITOR:-nano}" # Colors for terminal output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color print_header() { echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${BLUE}║ Torrent Mover Config Utility ║${NC}" echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}" echo "" } usage() { print_header echo -e "Usage: ${GREEN}$(basename "$0")${NC} [OPTION]" echo "" echo "Options:" echo -e " ${YELLOW}edit${NC} Edit the configuration file with your default editor" echo -e " ${YELLOW}backup${NC} Create a backup of the current configuration" echo -e " ${YELLOW}restore${NC} [file] Restore a previous backup (lists available backups if no file specified)" echo -e " ${YELLOW}validate${NC} Check the configuration for errors" echo -e " ${YELLOW}default${NC} Show the default configuration values" echo -e " ${YELLOW}show${NC} Display the current configuration" echo -e " ${YELLOW}set${NC} key value Update a specific configuration value" echo -e " ${YELLOW}get${NC} key Get the value of a specific configuration key" echo -e " ${YELLOW}help${NC} Display this help message" echo "" echo "Examples:" echo " $(basename "$0") edit # Edit the configuration file" echo " $(basename "$0") backup # Create a timestamped backup" echo " $(basename "$0") set COPY_MODE move # Change the copy mode to 'move'" echo " $(basename "$0") get TRANSMISSION_IP # Show the Transmission server IP" echo "" } # Check if user is root or using sudo check_permissions() { if [ "$EUID" -ne 0 ]; then echo -e "${RED}Error: This command requires root privileges.${NC}" echo "Please run with sudo:" echo -e " ${YELLOW}sudo $(basename "$0") $*${NC}" exit 1 fi } # Create a backup of the current configuration backup_config() { check_permissions "$@" if [ ! -f "$CONFIG_PATH" ]; then echo -e "${RED}Error: Configuration file not found at $CONFIG_PATH${NC}" exit 1 fi mkdir -p "$BACKUP_DIR" local timestamp=$(date +"%Y%m%d_%H%M%S") local backup_file="$BACKUP_DIR/mover.conf.$timestamp" cp "$CONFIG_PATH" "$backup_file" echo -e "${GREEN}Configuration backed up to:${NC} $backup_file" } # Restore a configuration from backup restore_config() { check_permissions "$@" if [ ! -d "$BACKUP_DIR" ]; then echo -e "${RED}Error: Backup directory not found at $BACKUP_DIR${NC}" exit 1 fi if [ -z "$1" ]; then # List available backups echo -e "${BLUE}Available backups:${NC}" local count=0 for file in "$BACKUP_DIR"/mover.conf.*; do if [ -f "$file" ]; then count=$((count+1)) local date_part=$(basename "$file" | cut -d. -f3) echo -e "${YELLOW}$count)${NC} $(basename "$file") ($(date -d "${date_part:0:8} ${date_part:9:2}:${date_part:11:2}:${date_part:13:2}" "+%Y-%m-%d %H:%M:%S"))" fi done if [ "$count" -eq 0 ]; then echo -e "${YELLOW}No backups found.${NC}" exit 0 fi echo "" read -p "Enter the number of the backup to restore: " selection if ! [[ "$selection" =~ ^[0-9]+$ ]] || [ "$selection" -lt 1 ] || [ "$selection" -gt "$count" ]; then echo -e "${RED}Error: Invalid selection.${NC}" exit 1 fi # Get the filename of the selected backup local selected_file=$(ls -1 "$BACKUP_DIR"/mover.conf.* | sed -n "${selection}p") else # Use the specified backup file local selected_file="$BACKUP_DIR/$1" if [ ! -f "$selected_file" ]; then echo -e "${RED}Error: Backup file not found at $selected_file${NC}" exit 1 fi fi # Create a backup of the current config before restoring backup_config # Restore the selected backup cp "$selected_file" "$CONFIG_PATH" echo -e "${GREEN}Configuration restored from:${NC} $selected_file" } # Edit the configuration file edit_config() { check_permissions "$@" if [ ! -f "$CONFIG_PATH" ]; then echo -e "${RED}Error: Configuration file not found at $CONFIG_PATH${NC}" exit 1 fi # Create a backup before editing backup_config # Open in the user's preferred editor $DEFAULT_EDITOR "$CONFIG_PATH" # Validate after editing validate_config } # Validate the configuration for errors validate_config() { check_permissions "$@" if [ ! -f "$CONFIG_PATH" ]; then echo -e "${RED}Error: Configuration file not found at $CONFIG_PATH${NC}" exit 1 fi echo -e "${BLUE}Validating configuration file...${NC}" # Source the config file in a subshell to check for syntax errors if ! (bash -n "$CONFIG_PATH"); then echo -e "${RED}Error: The configuration file contains syntax errors.${NC}" exit 1 fi # Load the configuration source "$CONFIG_PATH" # Check mandatory settings local required_vars=( "TRANSMISSION_IP" "TRANSMISSION_PORT" "TRANSMISSION_PATH_PREFIX" "LOCAL_PATH_PREFIX" "DIR_MOVIES_DST" "DIR_APPS_DST" "DIR_GAMES_DST" "DIR_BOOKS_DST" "DEFAULT_DST" "COPY_MODE" ) local error_count=0 for var in "${required_vars[@]}"; do if [ -z "${!var}" ]; then echo -e "${RED}Error: Required setting '$var' is not defined.${NC}" error_count=$((error_count+1)) fi done # Validate COPY_MODE if [ -n "$COPY_MODE" ] && [ "$COPY_MODE" != "copy" ] && [ "$COPY_MODE" != "move" ]; then echo -e "${RED}Error: COPY_MODE must be 'copy' or 'move', not '$COPY_MODE'.${NC}" error_count=$((error_count+1)) fi # Validate directory paths local dir_vars=( "DIR_GAMES_DST" "DIR_APPS_DST" "DIR_MOVIES_DST" "DIR_BOOKS_DST" "DIR_TV_DST" "DIR_MUSIC_DST" "DEFAULT_DST" ) for var in "${dir_vars[@]}"; do if [ -n "${!var}" ]; then if [[ ! "${!var}" == /* ]]; then echo -e "${RED}Error: Directory path for '$var' must be absolute (start with /).${NC}" error_count=$((error_count+1)) fi fi done # Check if any pattern in CUSTOM_PATTERNS references undefined variables if [ -n "$CUSTOM_PATTERNS" ]; then IFS=';' read -ra PATTERN_ARRAY <<< "$CUSTOM_PATTERNS" for pattern in "${PATTERN_ARRAY[@]}"; do IFS='=' read -ra PARTS <<< "$pattern" if [ "${#PARTS[@]}" -eq 2 ]; then local dest="${PARTS[1]}" if [[ "$dest" == *'${'*'}'* ]]; then local var_name=$(echo "$dest" | sed -n 's/.*\${//;s/}.*//p') if [ -z "${!var_name}" ]; then echo -e "${RED}Error: Custom pattern uses undefined variable: \${$var_name}${NC}" error_count=$((error_count+1)) fi fi fi done fi if [ "$error_count" -eq 0 ]; then echo -e "${GREEN}Configuration validation passed. No errors found.${NC}" else echo -e "${RED}Configuration validation failed with $error_count error(s).${NC}" exit 1 fi } # Show the current configuration show_config() { if [ ! -f "$CONFIG_PATH" ]; then echo -e "${RED}Error: Configuration file not found at $CONFIG_PATH${NC}" exit 1 fi print_header echo -e "${BLUE}Current Configuration:${NC}" echo "" # Load config and display it categorized source "$CONFIG_PATH" echo -e "${YELLOW}=== Connection Settings ===${NC}" echo -e "TRANSMISSION_IP=${GREEN}${TRANSMISSION_IP:-}${NC}" echo -e "TRANSMISSION_PORT=${GREEN}${TRANSMISSION_PORT:-}${NC}" if [ -n "$TRANSMISSION_USER" ]; then echo -e "TRANSMISSION_USER=${GREEN}${TRANSMISSION_USER}${NC}" echo -e "TRANSMISSION_PASSWORD=${GREEN}********${NC}" else echo -e "TRANSMISSION_USER=${YELLOW}${NC}" echo -e "TRANSMISSION_PASSWORD=${YELLOW}${NC}" fi echo -e "TRANSMISSION_PATH_PREFIX=${GREEN}${TRANSMISSION_PATH_PREFIX:-}${NC}" echo -e "LOCAL_PATH_PREFIX=${GREEN}${LOCAL_PATH_PREFIX:-}${NC}" echo "" echo -e "${YELLOW}=== Destination Directories ===${NC}" echo -e "DIR_GAMES_DST=${GREEN}${DIR_GAMES_DST:-}${NC}" echo -e "DIR_APPS_DST=${GREEN}${DIR_APPS_DST:-}${NC}" echo -e "DIR_MOVIES_DST=${GREEN}${DIR_MOVIES_DST:-}${NC}" echo -e "DIR_BOOKS_DST=${GREEN}${DIR_BOOKS_DST:-}${NC}" echo -e "DIR_TV_DST=${GREEN}${DIR_TV_DST:-}${NC}" echo -e "DIR_MUSIC_DST=${GREEN}${DIR_MUSIC_DST:-}${NC}" echo -e "DEFAULT_DST=${GREEN}${DEFAULT_DST:-}${NC}" echo "" echo -e "${YELLOW}=== Additional Storage Libraries ===${NC}" echo -e "STORAGE_DIRS=${GREEN}${STORAGE_DIRS:-}${NC}" echo -e "STORAGE_TV_DIRS=${GREEN}${STORAGE_TV_DIRS:-}${NC}" echo "" echo -e "${YELLOW}=== Security Settings ===${NC}" echo -e "TORRENT_USER=${GREEN}${TORRENT_USER:-debian-transmission}${NC}" echo -e "TORRENT_GROUP=${GREEN}${TORRENT_GROUP:-debian-transmission}${NC}" echo "" echo -e "${YELLOW}=== Performance Settings ===${NC}" echo -e "PARALLEL_THREADS=${GREEN}${PARALLEL_THREADS:-$(nproc)}${NC}" echo -e "PARALLEL_PROCESSING=${GREEN}${PARALLEL_PROCESSING:-1}${NC}" echo -e "COPY_MODE=${GREEN}${COPY_MODE:-}${NC}" echo "" echo -e "${YELLOW}=== Error Recovery ===${NC}" echo -e "MAX_RETRY_ATTEMPTS=${GREEN}${MAX_RETRY_ATTEMPTS:-3}${NC}" echo -e "RETRY_WAIT_TIME=${GREEN}${RETRY_WAIT_TIME:-15}${NC}" echo "" echo -e "${YELLOW}=== Logging & Integrity ===${NC}" echo -e "LOG_FILE=${GREEN}${LOG_FILE:-/var/log/torrent_mover.log}${NC}" echo -e "LOG_LEVEL=${GREEN}${LOG_LEVEL:-INFO}${NC}" echo -e "USE_SYSLOG=${GREEN}${USE_SYSLOG:-false}${NC}" echo -e "PROCESSED_LOG=${GREEN}${PROCESSED_LOG:-/var/log/torrent_processed.log}${NC}" echo -e "CHECKSUM_DB=${GREEN}${CHECKSUM_DB:-/var/lib/torrent/checksums.db}${NC}" echo -e "CHECK_TRANSFER_INTEGRITY=${GREEN}${CHECK_TRANSFER_INTEGRITY:-true}${NC}" echo "" if [ -n "$CUSTOM_PATTERNS" ]; then echo -e "${YELLOW}=== Custom Content Patterns ===${NC}" IFS=';' read -ra PATTERN_ARRAY <<< "$CUSTOM_PATTERNS" for pattern in "${PATTERN_ARRAY[@]}"; do if [ -n "$pattern" ]; then IFS='=' read -ra PARTS <<< "$pattern" if [ "${#PARTS[@]}" -eq 2 ]; then local regex="${PARTS[0]}" local dest="${PARTS[1]}" echo -e "Pattern: ${GREEN}${regex}${NC} → ${BLUE}${dest}${NC}" fi fi done echo "" fi } # Update a specific configuration value set_config_value() { check_permissions "$@" if [ -z "$1" ] || [ -z "$2" ]; then echo -e "${RED}Error: Both key and value must be provided.${NC}" echo "Usage: $(basename "$0") set KEY VALUE" exit 1 fi local key="$1" local value="$2" if [ ! -f "$CONFIG_PATH" ]; then echo -e "${RED}Error: Configuration file not found at $CONFIG_PATH${NC}" exit 1 fi # Create a backup before modifying backup_config # Check if the key already exists in the config if grep -q "^$key=" "$CONFIG_PATH"; then # Update the existing key sed -i "s|^$key=.*|$key=\"$value\"|" "$CONFIG_PATH" echo -e "${GREEN}Updated configuration:${NC} $key = \"$value\"" else # Add the new key echo "$key=\"$value\"" >> "$CONFIG_PATH" echo -e "${GREEN}Added new configuration:${NC} $key = \"$value\"" fi # Validate after updating validate_config } # Get a specific configuration value get_config_value() { if [ -z "$1" ]; then echo -e "${RED}Error: Key must be provided.${NC}" echo "Usage: $(basename "$0") get KEY" exit 1 fi local key="$1" if [ ! -f "$CONFIG_PATH" ]; then echo -e "${RED}Error: Configuration file not found at $CONFIG_PATH${NC}" exit 1 fi # Source the config file to get the value source "$CONFIG_PATH" if [ -n "${!key+x}" ]; then echo -e "${key}=${GREEN}${!key}${NC}" else echo -e "${RED}Error: Configuration key '$key' is not defined.${NC}" exit 1 fi } # Show default configuration options show_default_config() { print_header echo -e "${BLUE}Default Configuration Values:${NC}" echo "" cat << EOF # Transmission Settings TRANSMISSION_IP="192.168.1.100" TRANSMISSION_PORT="9091" TRANSMISSION_USER="" TRANSMISSION_PASSWORD="" # Path Mapping TRANSMISSION_PATH_PREFIX="/downloads" LOCAL_PATH_PREFIX="/mnt/data" # Destination Directories DIR_GAMES_DST="/mnt/media/Games" DIR_APPS_DST="/mnt/media/Apps" DIR_MOVIES_DST="/mnt/media/Movies" DIR_BOOKS_DST="/mnt/media/Books" DIR_TV_DST="/mnt/media/TV" DIR_MUSIC_DST="/mnt/media/Music" DEFAULT_DST="/mnt/media/Other" # Additional Storage STORAGE_DIRS="" STORAGE_TV_DIRS="" # Security TORRENT_USER="torrent-mover" TORRENT_GROUP="torrent-mover" # Error Recovery MAX_RETRY_ATTEMPTS="3" RETRY_WAIT_TIME="15" # Performance PARALLEL_THREADS="$(nproc)" PARALLEL_PROCESSING="1" COPY_MODE="copy" # Logging & Integrity LOG_FILE="/var/log/torrent_mover.log" LOG_LEVEL="INFO" USE_SYSLOG="false" PROCESSED_LOG="/var/log/torrent_processed.log" CHECKSUM_DB="/var/lib/torrent/checksums.db" CHECK_TRANSFER_INTEGRITY="true" # Custom Content Patterns CUSTOM_PATTERNS=".*documentary.*=\${DIR_MOVIES_DST}/Documentary;.*anime.*=\${DIR_TV_DST}/Anime" EOF echo "" } # Main command processing case "$1" in edit) edit_config "${@:2}" ;; backup) backup_config "${@:2}" ;; restore) restore_config "${@:2}" ;; validate) validate_config "${@:2}" ;; show) show_config ;; set) set_config_value "${@:2}" ;; get) get_config_value "${@:2}" ;; default) show_default_config ;; help|--help|-h) usage ;; *) usage exit 1 ;; esac exit 0