Compare commits
3 Commits
f1a5967430
...
bf81c037a9
| Author | SHA1 | Date | |
|---|---|---|---|
| bf81c037a9 | |||
| 02da0247f1 | |||
| 1edbe8c1e7 |
@@ -1,12 +1,9 @@
|
||||
import pymysql
|
||||
import re
|
||||
import time
|
||||
import qbittorrentapi
|
||||
|
||||
# ============================================================
|
||||
# CONFIG
|
||||
# ============================================================
|
||||
MAX_SIZE_GB = 950
|
||||
DRY_RUN = False
|
||||
|
||||
QBT_URL = "https://vladob.zen.usbx.me/qbittorrent"
|
||||
@@ -23,70 +20,24 @@ DB_CONFIG = {
|
||||
"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("QB COMPLETED CLEANUP")
|
||||
print(f"DRY_RUN = {DRY_RUN}")
|
||||
print("=" * 60)
|
||||
|
||||
# --------------------------------------------------------
|
||||
# DB LOAD
|
||||
# DB CONNECT
|
||||
# --------------------------------------------------------
|
||||
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
|
||||
# CONNECT QB
|
||||
# --------------------------------------------------------
|
||||
print("🔌 Connecting qBittorrent...")
|
||||
|
||||
@@ -98,105 +49,67 @@ def main():
|
||||
)
|
||||
qbt.auth_log_in()
|
||||
|
||||
qb_hashes = {t.hash.lower() for t in qbt.torrents_info()}
|
||||
torrents = qbt.torrents_info()
|
||||
|
||||
print(f"qB active torrents: {len(qb_hashes)}")
|
||||
print(f"Loaded torrents from qB: {len(torrents)}")
|
||||
|
||||
processed = 0
|
||||
removed = 0
|
||||
|
||||
# --------------------------------------------------------
|
||||
# SELECTION
|
||||
# LOOP
|
||||
# --------------------------------------------------------
|
||||
selected = []
|
||||
total_size = 0.0
|
||||
for t in torrents:
|
||||
|
||||
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}")
|
||||
if getattr(t, "amount_left", 1) != 0:
|
||||
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
|
||||
print(f"✅ COMPLETED → {t.name}")
|
||||
|
||||
# ----------------------------------------------------
|
||||
# UPDATE DB
|
||||
# ----------------------------------------------------
|
||||
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_completed_datetime = COALESCE(qb_completed_datetime, NOW()),
|
||||
qb_state = %s,
|
||||
qb_last_update = NOW()
|
||||
WHERE torrent_hash = %s
|
||||
""", (item["hash"],))
|
||||
""", (t.state, t.hash.lower()))
|
||||
|
||||
print(f"[{i+1}/{len(selected)}] Uploaded → {item['title']}")
|
||||
success += 1
|
||||
else:
|
||||
print(f"[DRY] Would update DB → {t.name}")
|
||||
|
||||
time.sleep(0.2)
|
||||
processed += 1
|
||||
|
||||
# ----------------------------------------------------
|
||||
# REMOVE FROM QB (KEEP FILES)
|
||||
# ----------------------------------------------------
|
||||
try:
|
||||
|
||||
if DRY_RUN:
|
||||
print(f"[DRY] Would remove torrent from qB → {t.name}")
|
||||
else:
|
||||
qbt.torrents_delete(
|
||||
torrent_hashes=t.hash,
|
||||
delete_files=False
|
||||
)
|
||||
|
||||
removed += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error uploading {item['title']}: {e}")
|
||||
print(f"❌ Remove failed: {t.name} → {e}")
|
||||
|
||||
print("\n✔ DONE")
|
||||
print(f"Uploaded torrents: {success}")
|
||||
# --------------------------------------------------------
|
||||
# SUMMARY
|
||||
# --------------------------------------------------------
|
||||
print("\n" + "=" * 60)
|
||||
print(f"Completed processed: {processed}")
|
||||
print(f"Removed from qB: {removed}")
|
||||
print("=" * 60)
|
||||
|
||||
db.close()
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ QBT_USER = "vladob"
|
||||
QBT_PASS = "jCni3U6d#y4bfcm"
|
||||
|
||||
DB_CONFIG = {
|
||||
"host": "192.168.1.50",
|
||||
"host": "192.168.1.76",
|
||||
"port": 3306,
|
||||
"user": "root",
|
||||
"password": "Vlado9674+",
|
||||
|
||||
152
Seedbox/50 MrtveTorrenty.py
Normal file
152
Seedbox/50 MrtveTorrenty.py
Normal file
@@ -0,0 +1,152 @@
|
||||
import pymysql
|
||||
import qbittorrentapi
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# ============================================================
|
||||
# CONFIG
|
||||
# ============================================================
|
||||
|
||||
DRY_RUN = True # ← změň na False až si ověříš výstup
|
||||
|
||||
DEAD_AFTER_HOURS = 72 # torrent musí být v qB alespoň tolik hodin
|
||||
DEAD_PROGRESS_THRESHOLD = 95.0 # pokud je progress < tato % po uplynutí doby → dead
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# CONNECT
|
||||
# ============================================================
|
||||
|
||||
def connect_qbt():
|
||||
qbt = qbittorrentapi.Client(
|
||||
host=QBT_URL,
|
||||
username=QBT_USER,
|
||||
password=QBT_PASS,
|
||||
VERIFY_WEBUI_CERTIFICATE=False
|
||||
)
|
||||
qbt.auth_log_in()
|
||||
return qbt
|
||||
|
||||
|
||||
def connect_db():
|
||||
return pymysql.connect(**DB_CONFIG)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# MAIN
|
||||
# ============================================================
|
||||
|
||||
def main():
|
||||
|
||||
print("=" * 60)
|
||||
print("MRTVÉ TORRENTY — UltraCC Seedbox cleanup")
|
||||
print(f"DRY_RUN = {DRY_RUN}")
|
||||
print(f"Kritéria : v qB déle než {DEAD_AFTER_HOURS}h A progress < {DEAD_PROGRESS_THRESHOLD}%")
|
||||
print("=" * 60)
|
||||
|
||||
qbt = connect_qbt()
|
||||
db = connect_db()
|
||||
cursor = db.cursor()
|
||||
|
||||
torrents = qbt.torrents_info()
|
||||
|
||||
now = datetime.now()
|
||||
deadline = now - timedelta(hours=DEAD_AFTER_HOURS)
|
||||
|
||||
dead_count = 0
|
||||
skip_count = 0
|
||||
error_count = 0
|
||||
|
||||
for t in torrents:
|
||||
|
||||
# Přeskočit dokončené (ty řeší skript 40)
|
||||
if t.completion_on:
|
||||
skip_count += 1
|
||||
continue
|
||||
|
||||
added_on = t.added_on # unix timestamp
|
||||
if not added_on:
|
||||
skip_count += 1
|
||||
continue
|
||||
|
||||
added_dt = datetime.fromtimestamp(added_on)
|
||||
progress_pct = float(t.progress) * 100.0
|
||||
|
||||
# Torrent ještě není dost starý
|
||||
if added_dt > deadline:
|
||||
skip_count += 1
|
||||
continue
|
||||
|
||||
# Progress je OK (téměř hotov) → přeskočit
|
||||
if progress_pct >= DEAD_PROGRESS_THRESHOLD:
|
||||
skip_count += 1
|
||||
continue
|
||||
|
||||
# ── Torrent splňuje kritéria "mrtvý" ──────────────────
|
||||
thash = t.hash.lower()
|
||||
age_hours = (now - added_dt).total_seconds() / 3600
|
||||
|
||||
print(f"\n💀 MRTVÝ: {t.name}")
|
||||
print(f" Přidán : {added_dt} ({age_hours:.1f}h zpět)")
|
||||
print(f" Progress : {progress_pct:.1f}%")
|
||||
print(f" Stav : {t.state}")
|
||||
print(f" Seeds : {t.num_seeds} Peers: {t.num_leechs}")
|
||||
|
||||
if DRY_RUN:
|
||||
print(f" [DRY] Smazal bych z qBittorrentu a označil v DB jako incomplete")
|
||||
dead_count += 1
|
||||
continue
|
||||
|
||||
# Smazat z qBittorrentu (i soubory — nekompletní data jsou k ničemu)
|
||||
try:
|
||||
qbt.torrents_delete(torrent_hashes=thash, delete_files=True)
|
||||
print(f" ✔ Smazán z qBittorrentu")
|
||||
except Exception as e:
|
||||
print(f" ❌ Smazání selhalo: {e}")
|
||||
error_count += 1
|
||||
continue
|
||||
|
||||
# Označit v DB jako incomplete
|
||||
cursor.execute("""
|
||||
UPDATE torrents
|
||||
SET
|
||||
qb_state = 'incomplete',
|
||||
qb_progress = %s,
|
||||
qb_last_update = NOW()
|
||||
WHERE torrent_hash = %s
|
||||
""", (progress_pct, thash))
|
||||
|
||||
if cursor.rowcount > 0:
|
||||
print(f" ✔ DB → incomplete (progress={progress_pct:.1f}%)")
|
||||
else:
|
||||
print(f" ⚠ DB: žádný řádek pro hash {thash}")
|
||||
|
||||
dead_count += 1
|
||||
|
||||
# ============================================================
|
||||
# SUMMARY
|
||||
# ============================================================
|
||||
print("\n" + "=" * 60)
|
||||
print(f"Mrtvé torrenty zpracováno : {dead_count}")
|
||||
print(f"Přeskočeno : {skip_count}")
|
||||
print(f"Chyby : {error_count}")
|
||||
print(f"DRY_RUN : {DRY_RUN}")
|
||||
print("=" * 60)
|
||||
|
||||
db.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user