""" Deduplikace: zkontroluje obrázky ve vybrané složce oproti tabulce zaloha_obrazku. Pokud je obrázek bezpečně zazálohován (blake3 sedí + záložní soubor existuje), smaže ho. """ import sys import os import tkinter as tk from tkinter import filedialog, messagebox from pathlib import Path import psycopg2 import blake3 from dotenv import load_dotenv load_dotenv() # ── DB připojení ──────────────────────────────────────────────────────────── DB_DSN = ( f"host={os.getenv('DB_HOST', 'localhost')} " f"port={os.getenv('DB_PORT', '5432')} " f"dbname={os.getenv('DB_NAME')} " f"user={os.getenv('DB_USER')} " f"password={os.getenv('DB_PASSWORD')}" ) IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".tif", ".webp", ".heic", ".heif", ".raw", ".cr2", ".nef", ".arw"} # Překlad Linux cest na Windows UNC LINUX_TO_WINDOWS = { "/mnt/user/ZalohaVsechObrazku": r"\\Tower1\ZalohaVsechObrazku", "/mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku": r"\\Tower1\ZalohaVsechObrazku", } def linux_to_windows_path(linux_path: str) -> Path: for prefix, unc in LINUX_TO_WINDOWS.items(): if linux_path.startswith(prefix): rest = linux_path[len(prefix):].replace("/", "\\") return Path(unc + rest) return Path(linux_path) def compute_blake3(path: Path) -> str: h = blake3.blake3() with open(path, "rb") as f: for chunk in iter(lambda: f.read(1024 * 1024), b""): h.update(chunk) return h.hexdigest() def pick_folder() -> Path | None: root = tk.Tk() root.withdraw() folder = filedialog.askdirectory(title="Vyber složku k deduplikaci") root.destroy() return Path(folder) if folder else None def log(msg: str): print(msg, flush=True) def dedup(folder: Path): images = [p for p in folder.rglob("*") if p.suffix.lower() in IMAGE_EXTENSIONS] log(f"Nalezeno obrázků: {len(images)}") if not images: log("Žádné obrázky k zpracování.") return conn = psycopg2.connect(DB_DSN) cur = conn.cursor() deleted = 0 skipped = 0 errors = 0 for img in images: try: size = img.stat().st_size name = img.name # 1. Hledej podle velikosti cur.execute( "SELECT blake3_hash, cesta_zalohy, nazev_souboru FROM zaloha_obrazku " "WHERE velikost = %s", (size,) ) rows = cur.fetchall() if not rows: log(f" PŘESKOČEN (žádná záloha se stejnou velikostí): {img.name}") skipped += 1 continue # 2. Spočítej blake3 lokálního souboru (jednou pro všechny kandidáty) local_hash = compute_blake3(img) matched = False for db_hash, cesta_zalohy, db_name in rows: if local_hash != db_hash: continue # 3. Zkontroluj, že záložní soubor fyzicky existuje backup_path = linux_to_windows_path(cesta_zalohy) if not backup_path.exists(): log(f" PŘESKOČEN (záložní soubor neexistuje): {cesta_zalohy}") continue # 4. Zkontroluj blake3 záložního souboru backup_hash = compute_blake3(backup_path) if backup_hash != db_hash: log(f" PŘESKOČEN (blake3 zálohy nesedí s DB): {cesta_zalohy}") continue matched = True break if matched: img.unlink() if img.name != db_name: log(f" SMAZÁN [JINÉ JMÉNO]: {img.name} != záloha: {db_name} (binary identické)") else: log(f" SMAZÁN: {img}") deleted += 1 else: log(f" PŘESKOČEN (žádná platná záloha neprošla): {img.name}") skipped += 1 except Exception as e: log(f" CHYBA ({img.name}): {e}") errors += 1 cur.close() conn.close() log(f"\n--- Hotovo ---") log(f"Smazáno: {deleted}") log(f"Přeskočeno: {skipped}") log(f"Chyby: {errors}") if __name__ == "__main__": folder = pick_folder() if not folder: print("Žádná složka nevybrána. Končím.") sys.exit(0) print(f"Zpracovávám složku: {folder}") dedup(folder)