206 lines
5.1 KiB
Python
206 lines
5.1 KiB
Python
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()
|