import os import sys import pymysql import pymysql.cursors # ================= KONFIGURACE ================= # --- BEZPEČNOSTNÍ POJISTKA --- # True = POUZE VÝPIS (nic se nesmaže, databáze se nezmění) # False = OSTRÝ REŽIM (maže soubory i záznamy v DB!) DRY_MODE = True # 1. Přístup k MySQL DB_CONFIG = { 'host': '192.168.1.76', 'port': 3307, 'user': 'root', 'password': 'Vlado9674+', 'db': 'torrents', 'charset': 'utf8mb4', 'autocommit': True } TABULKA = "file_md5_index" # 2. Mapování cest SERVER_PREFIX = "/mnt/user/Library" # Používáme 'r' pro raw string, aby se zpětná lomítka chápala správně LOCAL_PREFIX = r"\\tower1\#library" # =============================================== def get_connection(): return pymysql.connect( cursorclass=pymysql.cursors.DictCursor, **DB_CONFIG ) def convert_path(db_path): """Převede cestu z Linux serveru na lokální cestu Windows.""" if db_path.startswith(SERVER_PREFIX): relative_path = db_path[len(SERVER_PREFIX):] # Ořízneme počáteční lomítka z relativní cesty, aby fungoval join relative_path = relative_path.lstrip("/").lstrip("\\") # Spojí cesty a opraví lomítka local_path = os.path.join(LOCAL_PREFIX, relative_path) return os.path.normpath(local_path) return None def step_1_mark_duplicates(): print(f"\n--- KROK 1: Hledání duplicit v DB (DRY_MODE={DRY_MODE}) ---") try: conn = get_connection() with conn.cursor() as cursor: if DRY_MODE: # V DRY_MODE jen počítáme, co bychom označili (neprovádíme UPDATE) sql = f""" SELECT COUNT(*) as pocet FROM {TABULKA} t1 JOIN {TABULKA} t2 ON t1.blake3 = t2.blake3 WHERE t1.host_name = 'TOWER1' AND t2.host_name = 'SYNOLOGY' AND (t1.to_delete IS NULL OR t1.to_delete = 0); """ cursor.execute(sql) result = cursor.fetchone() affected = result['pocet'] print(f"[DRY-RUN] Našel jsem {affected} shodných záznamů (DB nebude změněna).") else: # V OSTRÉM režimu provádíme UPDATE sql = f""" UPDATE {TABULKA} t1 JOIN {TABULKA} t2 ON t1.blake3 = t2.blake3 SET t1.to_delete = 1 WHERE t1.host_name = 'TOWER' AND t2.host_name = 'SYNOLOGY' AND (t1.to_delete IS NULL OR t1.to_delete = 0); """ print("Provádím UPDATE záznamů v databázi...") cursor.execute(sql) affected = cursor.rowcount conn.commit() print(f"Hotovo. Označeno {affected} záznamů ke smazání.") conn.close() return affected except pymysql.MySQLError as e: print(f"Chyba MySQL při označování: {e}") sys.exit(1) def step_2_delete_files(): print(f"\n--- KROK 2: Mazání souborů (DRY_MODE={DRY_MODE}) ---") try: conn = get_connection() files_to_process = [] with conn.cursor() as cursor: print("Stahuji seznam souborů...") if DRY_MODE: # V DRY_MODE nemůžeme hledat podle 'to_delete=1' (protože jsme nic neoznačili), # takže musíme použít JOIN dotaz přímo pro simulaci výpisu. sql = f""" SELECT t1.id, t1.full_path FROM {TABULKA} t1 JOIN {TABULKA} t2 ON t1.blake3 = t2.blake3 WHERE t1.host_name = 'TOWER' AND t2.host_name = 'SYNOLOGY' AND (t1.to_delete IS NULL OR t1.to_delete = 0) """ else: # V OSTRÉM režimu bereme to, co jsme v kroku 1 označili sql = f"SELECT id, full_path FROM {TABULKA} WHERE host_name = 'TOWER' AND to_delete = 1" cursor.execute(sql) files_to_process = cursor.fetchall() count = len(files_to_process) print(f"Nalezeno {count} souborů.") if count == 0: print("Žádné soubory k zpracování. Konec.") return # V ostrém režimu se zeptáme na potvrzení if not DRY_MODE: confirm = input(f"-> [POZOR] Opravdu chcete SMAZAT {count} souborů? (napište 'ano'): ") if confirm.lower() != 'ano': print("Operace zrušena.") return else: print("-" * 40) print("VÝPIS SOUBORŮ, KTERÉ BY BYLY SMAZÁNY:") print("-" * 40) deleted_counter = 0 errors = 0 for row in files_to_process: db_id = row['id'] server_path = row['full_path'] local_path = convert_path(server_path) if not local_path: print(f"[SKIP PATH] Nesedí prefix: {server_path}") continue # --- LOGIKA DRY RUN vs REAL --- if DRY_MODE: # Pouze výpis print(f"[DRY-RUN] Bylo by smazáno: {local_path}") deleted_counter += 1 else: # Ostré mazání try: if os.path.exists(local_path): os.remove(local_path) print(f"[OK SMAZÁNO] {local_path}") # Smazání z DB with conn.cursor() as del_cursor: del_sql = f"DELETE FROM {TABULKA} WHERE id = %s" del_cursor.execute(del_sql, (db_id,)) conn.commit() deleted_counter += 1 else: print(f"[NENÍ NA DISKU] Mažu jen z DB: {local_path}") with conn.cursor() as del_cursor: del_sql = f"DELETE FROM {TABULKA} WHERE id = %s" del_cursor.execute(del_sql, (db_id,)) conn.commit() deleted_counter += 1 except OSError as e: print(f"[CHYBA OS] {local_path}: {e}") errors += 1 except pymysql.MySQLError as e: print(f"[CHYBA DB] ID {db_id}: {e}") conn.close() print("-" * 30) if DRY_MODE: print(f"DRY RUN DOKONČEN. Zobrazena simulace pro {deleted_counter} souborů.") else: print(f"HOTOVO. Úspěšně smazáno: {deleted_counter}, Chyby: {errors}") except pymysql.MySQLError as e: print(f"Kritická chyba DB: {e}") if __name__ == "__main__": step_1_mark_duplicates() step_2_delete_files()