Files
fotkyBuzalkovi/40 Dedup/dedup.py
T
2026-06-01 21:35:12 +02:00

153 lines
4.5 KiB
Python

"""
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)