Last active
December 27, 2025 09:31
-
-
Save NecRaul/c2d4121fe862252efdc24748169e6080 to your computer and use it in GitHub Desktop.
QBT_cleanup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| import os | |
| import logging | |
| import shutil | |
| import qbittorrentapi | |
| QBT_USERNAME = os.environ.get("USERNAME") | |
| QBT_PASSWORD = os.environ.get("PASSWORD") | |
| if not QBT_USERNAME or not QBT_PASSWORD: | |
| raise ValueError("USERNAME and PASSWORD environment variables must be set") | |
| log_dir = os.path.expanduser("~/.local/state/qbt-cleanup") | |
| os.makedirs(log_dir, exist_ok=True) | |
| log_file = os.path.join(log_dir, "qbt-cleanup.log") | |
| logging.basicConfig( | |
| filename=log_file, | |
| level=logging.INFO, | |
| format="%(asctime)s - %(levelname)s - %(message)s", | |
| ) | |
| logging.info("Starting qBittorrent auto-remove script") | |
| try: | |
| client = qbittorrentapi.Client( | |
| host="http://localhost:8080", | |
| username=QBT_USERNAME, | |
| password=QBT_PASSWORD, | |
| ) | |
| client.auth_log_in() | |
| logging.info("Logged in to qBittorrent Web UI") | |
| except qbittorrentapi.LoginFailed as e: | |
| logging.error(f"Login failed: {e}") | |
| exit(1) | |
| try: | |
| removable_states = { | |
| "uploading", | |
| "stalledUP", | |
| "pausedUP", | |
| "queuedUP", | |
| "checkingUP", | |
| "error", | |
| } | |
| delete_categories = {"prowlarr", "radarr", "tv-sonarr"} | |
| torrents = client.torrents_info() | |
| hashes_to_remove = [] | |
| hashes_to_delete_files = [] | |
| for torrent in torrents: | |
| if torrent.state in removable_states: | |
| hashes_to_remove.append(torrent.hash) | |
| delete_files = False | |
| if torrent.category == "seasonal": | |
| src = "/media" + torrent.info.content_path | |
| dst = os.path.join( | |
| "/home/necraul/Videos/Seasonals", os.path.basename(src) | |
| ) | |
| try: | |
| if os.path.exists(src): | |
| if os.path.isdir(src): | |
| shutil.copytree(src, dst, dirs_exist_ok=True) | |
| else: | |
| shutil.copy2(src, dst) | |
| logging.info( | |
| f"Successfully merged/copied: {torrent.name}" | |
| ) | |
| else: | |
| logging.warning( | |
| f"Destination already exists (likely merged by a previous torrent): {dst}" | |
| ) | |
| delete_files = True | |
| hashes_to_delete_files.append(torrent.hash) | |
| except Exception as e: | |
| logging.error(f"Failed to copy {torrent.name}: {e}") | |
| hashes_to_remove.remove(torrent.hash) | |
| continue | |
| elif torrent.category in delete_categories: | |
| delete_files = True | |
| if delete_files: | |
| hashes_to_delete_files.append(torrent.hash) | |
| logging.info( | |
| "Removing torrent: %s (%s) - State: %s - Delete files: %s", | |
| torrent.name, | |
| torrent.hash, | |
| torrent.state, | |
| delete_files, | |
| ) | |
| if hashes_to_delete_files: | |
| client.torrents_delete( | |
| delete_files=True, torrent_hashes=hashes_to_delete_files | |
| ) | |
| remaining_to_remove = [ | |
| h for h in hashes_to_remove if h not in hashes_to_delete_files | |
| ] | |
| if remaining_to_remove: | |
| client.torrents_delete( | |
| delete_files=False, torrent_hashes=remaining_to_remove | |
| ) | |
| except Exception as e: | |
| logging.error(f"Error while processing torrents: {e}") | |
| logging.info("Script completed") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment