import qbittorrentapi from pprint import pprint from datetime import datetime import time import qbittorrentapi import logging import sys import time from datetime import datetime, timedelta # Creating and Configuring Logger Log_Format = "%(levelname)s %(asctime)s - %(message)s" logging.basicConfig( stream=sys.stdout, format=Log_Format, level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S' ) logger = logging.getLogger() def delete_torrents_by_tag(qbt_client, tag, days_threshold=60): """ Delete torrents with a specific tag that are older than the specified days :param qbt_client: qBittorrent client instance :param tag: Tag to filter torrents :param days_threshold: Number of days after which to delete torrents """ current_time = int(time.time()) threshold_time = current_time - (days_threshold * 24 * 60 * 60) deleted_count = 0 total_size_deleted = 0 for torrent in qbt_client.torrents_info(): # Check if the torrent has the specified tag if tag in torrent.tags.split(','): # Check if torrent's added time is older than threshold if torrent.added_on < threshold_time: # Use time.localtime() to convert timestamp added_date = time.localtime(torrent.added_on) formatted_date = time.strftime("%Y-%m-%d %H:%M:%S", added_date) logger.info(f'Deleting old tagged torrent: {torrent.name}') logger.info(f' - Added on: {formatted_date}') logger.info(f' - Size: {torrent.size / (1024*1024*1024):.2f} GB') logger.info(f' - Hash: {torrent.hash}') try: # Delete torrent and its files qbt_client.torrents_delete( delete_files=True, # Important: this will remove the actual files torrent_hashes=torrent.hash ) deleted_count += 1 total_size_deleted += torrent.size except Exception as e: logger.error(f'Error deleting torrent {torrent.name}: {e}') # Summary logging logger.info(f'Deletion Summary for tag "{tag}":') logger.info(f' - Total torrents deleted: {deleted_count}') logger.info(f' - Total size deleted: {total_size_deleted / (1024*1024*1024):.2f} GB') def delete_old_torrents(qbt_client, category="", days_threshold=60): """ Delete torrents older than specified days in a given category If no category is specified, works on uncategorized torrents :param qbt_client: qBittorrent client instance :param category: Category name to filter torrents (default is uncategorized) :param days_threshold: Number of days after which to delete torrents """ current_time = int(time.time()) threshold_time = current_time - (days_threshold * 24 * 60 * 60) for torrent in qbt_client.torrents_info(category=category): # Check if torrent's added time is older than threshold if torrent.added_on < threshold_time: # Use time.localtime() to convert timestamp added_date = time.localtime(torrent.added_on) formatted_date = time.strftime("%Y-%m-%d %H:%M:%S", added_date) logger.info(f'Deleting old torrent: {torrent.name} (Added on: {formatted_date})') try: # Delete torrent and its files qbt_client.torrents_delete( delete_files=True, # Important: this will remove the actual files torrent_hashes=torrent.hash ) except Exception as e: logger.error(f'Error deleting torrent {torrent.name}: {e}') def main(): # instantiate a Client using the appropriate WebUI configuration qbt_client = qbittorrentapi.Client( host='qbittorrent.service.dc1.consul', port=8080, username='admin', password='adminadmin', ) # the Client will automatically acquire/maintain a logged-in state # in line with any request. therefore, this is not strictly necessary; # however, you may want to test the provided login credentials. try: qbt_client.auth_log_in() except qbittorrentapi.LoginFailed as e: print(e) # display qBittorrent info logger.info(f'qBittorrent: {qbt_client.app.version}') logger.info(f'qBittorrent Web API: {qbt_client.app.web_api_version}') for k,v in qbt_client.app.build_info.items(): print(f'{k}: {v}') # Creating an empty dictionary trackermap = { "Docspedia": ["http://science.docspedia.world:2710/f200baf50d45595c269b7b2d8c475a56/announce"], "MMA": ["http://a.mma-tracker.org:2710/ed6d78535267e979de36ec2401999d3a/announce"], "IPT": [ "http://127.0.0.1.stackoverflow.tech/cc7288bf91565af486c8e4bad2b63a37/announce", "http://routing.bgp.technology/cc7288bf91565af486c8e4bad2b63a37/announce", "http://async.empirehost.me/cc7288bf91565af486c8e4bad2b63a37/announce" ], "Anthelion": ["https://tracker.anthelion.me:34001/LmD45Qf7p0MVgYkPm1Uogc8wNqDtvsjF/announce"], "Cathode": ["https://signal.cathode-ray.tube/yebawgmvnvojwjnfw2a1qr5wg3pqwe4o/announce"], "RedSeeding": ["https://flacsfor.me/f08a15129e4276f609c8b99abb746195/announce"], "cinemaz": ["https://tracker.cinemaz.to/50500ba3815e18c837cd753ceb0080e3/announce"], "iMetal": ["http://metal.iplay.ro/announce.php?passkey=2b4d98fe0f4b7325a15e5654961498ea"], "rawk": [ "http://rawkbawx.rocks:2710/announce", "http://rawkbawx.rocks:2710/f7903677d2c030b89b69799f4bd9edbd/announce" ], "torrentleech": [ "https://tracker.tleechreload.org/a/3d6cde5fd3bf1a375f3466d40f9ee9bb/announce", "https://tracker.torrentleech.org/a/3d6cde5fd3bf1a375f3466d40f9ee9bb/announce" ], "MyAnonamouse": ["https://t.myanonamouse.net/tracker.php/VPRYYAL-WpTwnr9G9aIN6044YVZ7x8Ao/announce"], "Nebulance": ["https://tracker.nebulance.io/edcd6847fb3c31fd9958dd7144f0ea15/announce"], "Orpheus_seeding": ["https://home.opsfet.ch/EAvBpDtmBtbziuydzwzhasgqAxrCqFwo/announce"], "tvchaos": ["https://tvchaosuk.com/announce/cbaade5ac5612edf854b295153a60e6b"], "filelist": [ "http://reactor.filelist.io/98ece6e971fe7e89a0c86a00c20c1037/announce", "http://reactor.flro.org/98ece6e971fe7e89a0c86a00c20c1037/announce" ] } categories_to_tidy = ["radarr","tv-sonarr","lidar","readarr","readarrAudio","","tv-sonarrsmall"] #ensutre cats exist torrents_cats = qbt_client.torrents_categories() for tracker in trackermap: if tracker not in torrents_cats: savepath = "/downloads/seeding/" + tracker qbt_client.torrents_create_category(name=tracker, save_path=savepath) if tracker in torrents_cats: savepath = "/downloads/seeding/" + tracker if torrents_cats[tracker]["savePath"] != savepath: # print(tracker) # print(torrents_cats[tracker]["savePath"]) qbt_client.torrents_edit_category(name=tracker, save_path=savepath) # retrieve and show all torrents for torrent in qbt_client.torrents_info(): # pprint(torrent.category) for messycat in categories_to_tidy: if messycat == torrent.category: # pprint(torrent["name"]) # pprint(torrent.trackers) for tracker in torrent.trackers: for knowntracker in trackermap: if tracker["url"] in trackermap[knowntracker]: name = torrent["name"] pprint(f"{knowntracker} detected: {name}") #seeding_time in seconds if int(torrent.seeding_time) > 86400: pprint(f"Moving {name} to {knowntracker}") qbt_client.torrents_set_category(category=knowntracker,torrent_hashes=torrent.hash) else: pprint(f"seedtime {name} to {knowntracker}") delete_old_torrents(qbt_client, category="IPT") delete_old_torrents(qbt_client, category="Nebulance") delete_torrents_by_tag(qbt_client, tag='PublicTracker') if __name__ == "__main__": main()