import pymysql import re import time import qbittorrentapi # ============================================================ # CONFIG # ============================================================ MAX_SIZE_GB = 950 DRY_RUN = False QBT_URL = "https://vladob.zen.usbx.me/qbittorrent" QBT_USER = "vladob" QBT_PASS = "jCni3U6d#y4bfcm" DB_CONFIG = { "host": "192.168.1.50", "port": 3306, "user": "root", "password": "Vlado9674+", "database": "torrents", "charset": "utf8mb4", "autocommit": True, } # ============================================================ # SIZE PARSER # ============================================================ def parse_size_to_gb(size_str): if not size_str: return 0.0 s = str(size_str).upper().replace(",", ".").strip() match = re.search(r"([\d\.]+)", s) if not match: return 0.0 val = float(match.group(1)) if "TB" in s: return val * 1024 if "GB" in s: return val if "MB" in s: return val / 1024 if "KB" in s: return val / 1024 / 1024 return 0.0 # ============================================================ # MAIN # ============================================================ def main(): print("=" * 60) print("TORRENT DOWNLOAD SCHEDULER") print(f"DRY_RUN = {DRY_RUN}") print("=" * 60) # -------------------------------------------------------- # DB LOAD # -------------------------------------------------------- db = pymysql.connect(**DB_CONFIG) cursor = db.cursor() print("šŸ“„ Loading DB candidates...") cursor.execute(""" SELECT torrent_hash, title_visible, size_pretty, seeders, torrent_content FROM torrents WHERE qb_completed_datetime IS NULL AND torrent_content IS NOT NULL ORDER BY seeders DESC """) rows = cursor.fetchall() print(f"Candidates from DB: {len(rows)}") # -------------------------------------------------------- # CONNECT QBITTORRENT # -------------------------------------------------------- print("šŸ”Œ Connecting qBittorrent...") qbt = qbittorrentapi.Client( host=QBT_URL, username=QBT_USER, password=QBT_PASS, VERIFY_WEBUI_CERTIFICATE=False ) qbt.auth_log_in() qb_hashes = {t.hash.lower() for t in qbt.torrents_info()} print(f"qB active torrents: {len(qb_hashes)}") # -------------------------------------------------------- # SELECTION # -------------------------------------------------------- selected = [] total_size = 0.0 for row in rows: t_hash, title, size_str, seeders, content = row t_hash = t_hash.lower() # Guard 3 – already in qB if t_hash in qb_hashes: print(f"ā­ Already in qB → {title}") continue size_gb = parse_size_to_gb(size_str) if total_size + size_gb > MAX_SIZE_GB: print(f"šŸ›‘ Capacity reached → {title}") break selected.append({ "hash": t_hash, "title": title, "size": size_gb, "seeders": seeders, "content": content }) total_size += size_gb print(f"āœ… Selected → {title} | {size_gb:.2f} GB | seeders={seeders}") # -------------------------------------------------------- # REPORT # -------------------------------------------------------- print("-" * 50) print(f"Selected torrents: {len(selected)}") print(f"Total size: {total_size:.2f} GB / {MAX_SIZE_GB} GB") print("-" * 50) if not selected: print("Nothing to schedule.") return if not DRY_RUN: confirm = input("Upload to qBittorrent? (ano/ne): ") if confirm.lower() not in ["ano", "yes", "y"]: print("Cancelled.") return # -------------------------------------------------------- # UPLOAD LOOP # -------------------------------------------------------- print("šŸš€ Uploading torrents...") success = 0 for i, item in enumerate(selected): try: if DRY_RUN: print(f"[DRY] Would upload: {item['title']}") continue file_dict = { f"{item['hash']}.torrent": item["content"] } qbt.torrents_add( torrent_files=file_dict, is_paused=False ) # Mark scheduled in DB cursor.execute(""" UPDATE torrents SET qb_state = 'scheduled', qb_last_update = NOW() WHERE torrent_hash = %s """, (item["hash"],)) print(f"[{i+1}/{len(selected)}] Uploaded → {item['title']}") success += 1 time.sleep(0.2) except Exception as e: print(f"āŒ Error uploading {item['title']}: {e}") print("\nāœ” DONE") print(f"Uploaded torrents: {success}") db.close() if __name__ == "__main__": main()