#!/usr/bin/env python3 import subprocess import argparse from datetime import datetime torrents = [] def get_torrent_list(args): try: process = subprocess.run(['transmission-remote', f'{args.host}:{args.port}', '-l'], capture_output=True, text=True, check=True) return process.stdout.splitlines() except subprocess.CalledProcessError as e: print(f"Command failed with return code {e.returncode}") print(f"Error output: {e.stderr}") exit() def parse_torrent_list(list): id = 0 for line in list: if id == 0: id += 1 continue data = [x.strip() for x in line.split(" ") if x] print(line) if "Sum" in data[0]: continue torrent = { "id": id, "size": data[2], "ratio": data[6], "name": data[8] } print(torrent) torrents.append(torrent) id += 1 def add_torrent_info(args): try: process = subprocess.run(['transmission-remote', f'{args.host}:{args.port}', '-t', 'all', '-i'], capture_output=True, text=True, check=True) torrent_info = process.stdout.splitlines() except subprocess.CalledProcessError as e: print(f"Command failed with return code {e.returncode}") print(f"Error output: {e.stderr}") exit() id = 1 # Will be read from output. current_torrent = None for info in torrent_info: try: if "Id: " in info: print(info) id = int(info.split(": ")[1].strip()) print(id) if 0 <= id-1 < len(torrents): current_torrent = torrents[id-1] else: current_torrent = None continue if current_torrent is None: continue if "Date added" in info: date_str = info.split(": ")[1].strip() current_torrent["date_added"] = datetime.strptime(date_str, "%a %b %d %H:%M:%S %Y") elif "Latest activity" in info: date_str = info.split(": ")[1].strip() current_torrent["latest_activity"] = datetime.strptime(date_str, "%a %b %d %H:%M:%S %Y") elif "Source" in info: current_torrent["source"] = info.split(": ")[1].strip() except (IndexError, ValueError, KeyError) as e: print(f"Error processing torrent info: {e}") continue def calculate_deletion_score(torrent): """ Calculate a deletion score where higher scores indicate better deletion candidates. Factors considered: - Size (larger = higher score) - Age (older = higher score) - Inactivity period (longer inactive = higher score) - Ratio (lower = higher score, but with diminishing returns after 1.0) """ now = datetime.now() #print(torrent) # Convert size to GB for scoring (assuming size is in format like "1.5 GB" or "700 MB") try: size_str = torrent.get('size', '0 GB') size_num = float(size_str.split()[0]) if 'MB' in size_str: size_gb = size_num / 1024 else: size_gb = size_num except (ValueError, IndexError): size_gb = 0 # Calculate age in days try: age_days = (now - torrent.get('date_added', now)).days except (TypeError, AttributeError): age_days = 0 # Calculate days since last activity try: inactive_days = (now - torrent.get('latest_activity', now)).days except (TypeError, AttributeError): inactive_days = 0 try: ratio = float(torrent.get('ratio', '0')) except ValueError: ratio = 0 # Calculate individual scores size_score = size_gb * 10 # points per GB age_score = min(age_days / 7, 52) * 1 # 1 points per week, max 1 year inactive_score = min(inactive_days / 7, 12) * 8 # 8 points per week inactive, max 12 weeks ratio_score = 1000 * (1 - min(ratio, 1.0)) # 100 points at ratio 0, 0 points at ratio >= 1 # Combine scores total_score = size_score + age_score + inactive_score + ratio_score return total_score def get_deletion_candidates(torrents, limit=10): """ Sort torrents by deletion score and return the top candidates. """ scored_torrents = [(t, calculate_deletion_score(t)) for t in torrents] sorted_torrents = sorted(scored_torrents, key=lambda x: x[1], reverse=True) # Print results print("\nTop deletion candidates:") print("-" * 120) print(f"{'Score':>8} {'Size':>10} {'Ratio':>6} {'Age(d)':>7} {'Inactive(d)':>11} {'Size_pts':>9} {'Age_pts':>8} {'Inact_pts':>9} {'Ratio_pts':>9} {'Name':<30}") print("-" * 120) for torrent, score in sorted_torrents[:limit]: now = datetime.now() age_days = (now - torrent['date_added']).days inactive_days = (now - torrent['latest_activity']).days size_str = torrent['size'] size_num = float(size_str.split()[0]) if 'MB' in size_str: size_gb = size_num / 1024 else: size_gb = size_num size_score = size_gb * 10 age_score = min(age_days / 7, 52) * 1 inactive_score = min(inactive_days / 7, 12) * 8 ratio = float(torrent['ratio']) ratio_score = 100 * (1 - min(ratio, 1.0)) print(f"{score:8.1f} {torrent['size']:>10} {torrent['ratio']:>6} {age_days:>7d} {inactive_days:>11d} {size_score:>9.1f} {age_score:>8.1f} {inactive_score:>9.1f} {ratio_score:>9.1f} {torrent['name'][:30]}") return [t[0] for t in sorted_torrents] def main(): parser = argparse.ArgumentParser() parser.add_argument('--host', default='localhost', help='Transmission host') parser.add_argument('--port', default='9091', help='Transmission port') parser.add_argument('--num', default='10', help='Amount of candidates to show') args = parser.parse_args() torrent_list = get_torrent_list(args) parse_torrent_list(torrent_list) add_torrent_info(args) #print(torrents) candidates = get_deletion_candidates(torrents, int(args.num)) if __name__ == "__main__": main()