refactor: improve torrent transfer workflow - replace unsafe word splitting with Bash arrays - improve command-line argument parsing - centralize remote and local configuration - replace remote ls parsing with find - simplify confirmation handling - improve SSH and SCP error handling - safely quote local paths and filenames - separate copy, rename, and delete operations - add comprehensive usage documentation
377 lines
8.2 KiB
Bash
Executable File
377 lines
8.2 KiB
Bash
Executable File
```bash
|
|
#!/bin/bash
|
|
|
|
# Torrent Copy / Rename / Delete Utility
|
|
#
|
|
# Description:
|
|
# Selects completed torrents from a remote Deluge server, copies them
|
|
# locally, optionally renames the downloaded files, and finally removes
|
|
# the remote torrents.
|
|
#
|
|
# Workflow:
|
|
# 1. List files from the configured remote torrent directory.
|
|
# 2. Optionally select individual files.
|
|
# 3. Confirm and copy selected files locally.
|
|
# 4. Confirm and rename copied files.
|
|
# 5. Confirm and remove the remote files.
|
|
#
|
|
# Usage:
|
|
# ./torrent_copy.sh [options]
|
|
#
|
|
# Options:
|
|
# -y Automatically answer "yes" to all confirmations.
|
|
# -m Use the remote Mega torrent directory.
|
|
# -yt Use the remote YouTube torrent directory.
|
|
# -l Select individual files interactively.
|
|
# -f Copy files to the configured torrent destination.
|
|
# -d Copy files to the current directory.
|
|
# -help Display this help.
|
|
#
|
|
# Examples:
|
|
# ./torrent_copy.sh
|
|
# ./torrent_copy.sh -y
|
|
# ./torrent_copy.sh -l
|
|
# ./torrent_copy.sh -m -l
|
|
# ./torrent_copy.sh -yt -f
|
|
# ./torrent_copy.sh -d
|
|
#
|
|
# Requirements:
|
|
# - bash
|
|
# - ssh
|
|
# - scp
|
|
# - Remote SSH access to "serverhost"
|
|
# - /home/lucas/scripts/rename_episodes.sh
|
|
#
|
|
|
|
set -o pipefail
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Configuration
|
|
# ---------------------------------------------------------------------------
|
|
|
|
readonly REMOTE_HOST="serverhost"
|
|
readonly REMOTE_USER="deluge-user"
|
|
|
|
readonly DEFAULT_DEST="/data/Nextcloud/Filme/New/"
|
|
readonly TORRENT_DEST="/data/Torrents"
|
|
|
|
readonly RENAME_SCRIPT="/home/lucas/scripts/rename_episodes.sh"
|
|
|
|
REMOTE_PATH="/torrent/complete/"
|
|
DESTINATION="$DEFAULT_DEST"
|
|
|
|
ASSUME_YES=false
|
|
LIST_MODE=false
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Help
|
|
# ---------------------------------------------------------------------------
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage:
|
|
$(basename "$0") [options]
|
|
|
|
Options:
|
|
-y Automatically answer yes to all confirmations
|
|
-m Use Mega torrent directory
|
|
-yt Use YouTube torrent directory
|
|
-l Select files individually
|
|
-f Copy to $TORRENT_DEST
|
|
-d Copy to the current directory
|
|
-help Display this help
|
|
|
|
Examples:
|
|
$(basename "$0") -y
|
|
$(basename "$0") -l
|
|
$(basename "$0") -m -l
|
|
$(basename "$0") -yt -f
|
|
$(basename "$0") -d
|
|
EOF
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Argument parsing
|
|
# ---------------------------------------------------------------------------
|
|
|
|
parse_args() {
|
|
while (($#)); do
|
|
case "$1" in
|
|
-help|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
|
|
-y)
|
|
ASSUME_YES=true
|
|
;;
|
|
|
|
-yt)
|
|
REMOTE_PATH="/torrent/youtube/"
|
|
;;
|
|
|
|
-m)
|
|
REMOTE_PATH="/torrent/mega/"
|
|
;;
|
|
|
|
-l)
|
|
LIST_MODE=true
|
|
;;
|
|
|
|
-d)
|
|
DESTINATION="$(pwd)"
|
|
;;
|
|
|
|
-f)
|
|
DESTINATION="$TORRENT_DEST"
|
|
;;
|
|
|
|
*)
|
|
echo "Error: unknown option '$1'" >&2
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
shift
|
|
done
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
confirm() {
|
|
local message=$1
|
|
|
|
if [[ "$ASSUME_YES" == true ]]; then
|
|
return 0
|
|
fi
|
|
|
|
printf '%s (y/*) ' "$message"
|
|
read -r -n 1 answer
|
|
printf '\n'
|
|
|
|
[[ "$answer" == "y" ]]
|
|
}
|
|
|
|
|
|
die() {
|
|
echo "Error: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Get remote file list
|
|
# ---------------------------------------------------------------------------
|
|
|
|
get_file_list() {
|
|
local remote_listing
|
|
|
|
echo "Remote directory: $REMOTE_PATH"
|
|
echo "Destination: $DESTINATION"
|
|
echo
|
|
|
|
if ! remote_listing=$(
|
|
ssh "$REMOTE_USER@$REMOTE_HOST" \
|
|
"find '$REMOTE_PATH' -mindepth 1 -maxdepth 1 -printf '%f\n'"
|
|
); then
|
|
die "Unable to retrieve remote file list."
|
|
fi
|
|
|
|
if [[ -z "$remote_listing" ]]; then
|
|
echo "Nothing to copy/delete."
|
|
exit 0
|
|
fi
|
|
|
|
mapfile -t FILES <<< "$remote_listing"
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Select individual files
|
|
# ---------------------------------------------------------------------------
|
|
|
|
select_files() {
|
|
local file
|
|
local selected=()
|
|
|
|
echo "Select files:"
|
|
echo
|
|
|
|
for file in "${FILES[@]}"; do
|
|
if confirm "Copy '$file'?"; then
|
|
selected+=("$file")
|
|
else
|
|
echo "Skipping '$file'"
|
|
fi
|
|
done
|
|
|
|
FILES=("${selected[@]}")
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Copy files
|
|
# ---------------------------------------------------------------------------
|
|
|
|
copy_files() {
|
|
local file
|
|
local remote_file
|
|
|
|
[[ ${#FILES[@]} -gt 0 ]] || {
|
|
echo "Nothing to copy."
|
|
return
|
|
}
|
|
|
|
echo
|
|
printf 'Files to copy:\n'
|
|
printf ' %s\n' "${FILES[@]}"
|
|
echo
|
|
|
|
if ! confirm "Copy files?"; then
|
|
echo "Nothing copied."
|
|
return 1
|
|
fi
|
|
|
|
for file in "${FILES[@]}"; do
|
|
echo "Copying: $file"
|
|
|
|
# Pass the remote path as a single SSH/SCP argument.
|
|
remote_file="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}${file}"
|
|
|
|
if ! scp -r "$remote_file" "$DESTINATION/"; then
|
|
die "Copy failed: $file"
|
|
fi
|
|
done
|
|
|
|
echo "Torrents copied."
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Rename files
|
|
# ---------------------------------------------------------------------------
|
|
|
|
rename_files() {
|
|
local file
|
|
local filepath
|
|
|
|
[[ ${#FILES[@]} -gt 0 ]] || {
|
|
echo "Nothing to rename."
|
|
return
|
|
}
|
|
|
|
echo
|
|
printf 'Files to rename:\n'
|
|
printf ' %s\n' "${FILES[@]}"
|
|
echo
|
|
|
|
if ! confirm "Rename files?"; then
|
|
echo "Nothing renamed."
|
|
return
|
|
fi
|
|
|
|
if [[ ! -x "$RENAME_SCRIPT" && ! -f "$RENAME_SCRIPT" ]]; then
|
|
die "Rename script not found: $RENAME_SCRIPT"
|
|
fi
|
|
|
|
for file in "${FILES[@]}"; do
|
|
filepath="${DESTINATION%/}/$file"
|
|
|
|
echo "Renaming: $filepath"
|
|
|
|
if [[ -d "$filepath" ]]; then
|
|
echo "$filepath is a directory."
|
|
|
|
bash "$RENAME_SCRIPT" \
|
|
-del \
|
|
-files \
|
|
-dir \
|
|
-repair \
|
|
-path "$filepath"
|
|
|
|
elif [[ -f "$filepath" ]]; then
|
|
echo "$filepath is a file."
|
|
|
|
bash "$RENAME_SCRIPT" \
|
|
-files \
|
|
-file "$filepath"
|
|
|
|
else
|
|
echo "Warning: $filepath is neither a directory nor a regular file."
|
|
fi
|
|
done
|
|
|
|
echo "Rename complete."
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Delete remote files
|
|
# ---------------------------------------------------------------------------
|
|
|
|
delete_files() {
|
|
local file
|
|
local remote_file
|
|
|
|
[[ ${#FILES[@]} -gt 0 ]] || {
|
|
echo "Nothing to remove."
|
|
return
|
|
}
|
|
|
|
echo
|
|
printf 'Remote files to remove:\n'
|
|
printf ' %s\n' "${FILES[@]}"
|
|
echo
|
|
|
|
if ! confirm "Delete remote files?"; then
|
|
echo "Nothing removed."
|
|
return
|
|
fi
|
|
|
|
for file in "${FILES[@]}"; do
|
|
echo "Removing: $file"
|
|
|
|
remote_file="${REMOTE_PATH}${file}"
|
|
|
|
if ! ssh "$REMOTE_USER@$REMOTE_HOST" \
|
|
"rm -rf -- '$remote_file'"; then
|
|
echo "Warning: failed to remove '$file'." >&2
|
|
fi
|
|
done
|
|
|
|
echo "Torrents removed."
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Main
|
|
# ---------------------------------------------------------------------------
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
|
|
get_file_list
|
|
|
|
if [[ "$LIST_MODE" == true ]]; then
|
|
select_files
|
|
fi
|
|
|
|
[[ ${#FILES[@]} -gt 0 ]] || {
|
|
echo "No files selected."
|
|
exit 0
|
|
}
|
|
|
|
copy_files || exit 1
|
|
rename_files
|
|
delete_files
|
|
}
|
|
|
|
main "$@"
|
|
```
|