153 lines
4.5 KiB
Python
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)
|