b2277e54f6
For cleaning torrent files from transmission. Needs some more work.
179 lines
6.1 KiB
Python
Executable File
179 lines
6.1 KiB
Python
Executable File
#!/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()
|