Skip to content

Instantly share code, notes, and snippets.

@brandonb927
Last active August 14, 2025 03:41
Show Gist options
  • Save brandonb927/652ecf1bb23dc7a94e140c95a9e7b5e1 to your computer and use it in GitHub Desktop.
Save brandonb927/652ecf1bb23dc7a94e140c95a9e7b5e1 to your computer and use it in GitHub Desktop.
Transmission mover.py for unRAID — Based on qbit_manage mover.py https://github.com/StuffAnThings/qbit_manage/blob/master/scripts/mover.py
#!/usr/bin/env python3
# This standalone script is used to pause torrents older than last x days,
# run mover (in unRAID) and start torrents again once mover has completed.
import argparse
import logging
import os
import sys
import time
from datetime import datetime
from datetime import timedelta
from urllib import parse as urllib_parse
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
handlers=[logging.StreamHandler(sys.stdout)],
)
parser = argparse.ArgumentParser(prog="Transmission mover", description="Stop torrents and kick off Unraid mover process")
parser.add_argument("--host", help="Transmission host including port", required=True)
parser.add_argument("-u", "--user", help="Transmission user", default="admin")
parser.add_argument("-p", "--password", help="Transmission password", default="adminadmin")
parser.add_argument(
"--cache-mount",
help="Cache mount point in Unraid. This is used to additionally filter for only torrents that exists on the cache mount"
"Use this option ONLY if you follow TRaSH Guides folder structure. (For default cache drive set this to /mnt/cache)",
default=None,
)
parser.add_argument(
"--days-from", "--days_from", help="Set Number of Days to stop torrents between two offsets", type=int, default=0
)
parser.add_argument("--days-to", "--days_to", help="Set Number of Days to stop torrents between two offsets", type=int, default=2)
parser.add_argument(
"--mover-old",
help="Use mover.old instead of mover. Useful if you're using the Mover Tuning Plugin",
action="store_true",
default=False,
)
parser.add_argument(
"--status-filter",
help="Define a status to limit which torrents to pause. Useful if you want to leave certain torrents unpaused",
choices=[
"check pending",
"checking",
"downloading",
"download pending",
"seeding",
"seed pending",
"stopped",
],
default="stopped",
)
# --DEFINE VARIABLES--#
# --START SCRIPT--#
try:
from transmission_rpc import TransmissionConnectError, Client, TransmissionAuthError
except ModuleNotFoundError:
logging.error(
'Requirements Error: transmission-rpc not installed. Please install using the command "pip install transmission-rpc"'
)
sys.exit(1)
def filter_torrents(torrent_list, timeoffset_from, timeoffset_to, cache_mount):
result = []
for torrent in torrent_list:
torrent_added_date = torrent.added_date.timestamp()
if torrent_added_date >= timeoffset_to and torrent_added_date <= timeoffset_from:
if not cache_mount or exists_in_cache(cache_mount, os.path.join(torrent.download_dir, torrent.name)):
result.append(torrent)
elif torrent_added_date < timeoffset_to:
break
return result
def exists_in_cache(cache_mount, content_path):
cache_path = os.path.join(cache_mount, content_path.lstrip("/"))
return os.path.exists(cache_path)
def stop_start_torrents(client, torrent_list, pause=True):
ids = [t.id for t in torrent_list]
if pause:
for torrent in torrent_list:
logging.info(f"Pausing: {torrent.name} [{torrent.added_date}]")
client.stop_torrent(ids)
else:
for torrent in torrent_list:
logging.info(f"Resuming: {torrent.name} [{torrent.added_date}]")
client.start_torrent(ids)
if __name__ == "__main__":
current = datetime.now()
args = parser.parse_args()
if args.days_from > args.days_to:
raise ("Config Error: days_from must be set lower than days_to")
timeoffset_from = current - timedelta(days=args.days_from)
timeoffset_to = current - timedelta(days=args.days_to)
try:
host = urllib_parse.urlparse(args.host)
port = 9091
if host.scheme == 'http':
port = 80
if host.scheme == 'https':
port = 443
if host.scheme and host.netloc:
client = Client(protocol=host.scheme, host=host.netloc, port=port, username=args.user, password=args.password)
else:
client = Client(host=args.host, username=args.user, password=args.password)
except TransmissionAuthError:
raise Exception("Transmission Error: Failed to login, invalid username/password.")
except TransmissionConnectError:
raise Exception("Transmission Error: Unable to connect to the client, connection error.")
except Exception:
raise Exception("Transmission Error: Unable to connect to the client, unknown error")
torrent_list = [t for t in client.get_torrents() if t.status == args.status_filter]
torrent_list.sort(key=lambda t: t.added_date)
torrents = filter_torrents(torrent_list, timeoffset_from.timestamp(), timeoffset_to.timestamp(), args.cache_mount)
if len(torrents) == 0:
logging.info(f"No torrents found within range, exiting")
exit(0)
# Pause Torrents
logging.info(f"Pausing [{len(torrents)}] torrents from {args.days_from} - {args.days_to} days ago")
stop_start_torrents(client, torrents, True)
time.sleep(10)
# Or using mover tunning
if args.mover_old:
# Start mover
logging.info("Starting mover.old to move files in to array disks")
os.system("/usr/local/sbin/mover.old start")
else:
# Start mover
logging.info("Starting mover to move files in to array disks based on mover tuning preferences")
os.system("/usr/local/sbin/mover start")
# Start Torrents
logging.info(f"Resuming [{len(torrents)}] paused torrents from {args.days_from} - {args.days_to} days ago")
stop_start_torrents(client, torrents, False)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment