Files
administrator 97c1796753 notebookVb
2026-05-29 06:21:55 +02:00

5.7 KiB

verify_photos.py

Verifikace fotek v databázi fotky_buzalkovi — pro každý záznam v tabulce photos zkusí soubor otevřít a dekódovat, zapíše nalezené chyby do tabulky photo_errors a označí photos.verified_at = NOW().

Závislosti

  • Migrace migrations/002_add_photo_errors.sql (tabulka photo_errors + sloupec photos.verified_at + partial index).
  • .env s DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD (hledá se vedle skriptu i v rodičovském adresáři).
  • Python balíčky: psycopg2-binary, Pillow, python-dotenv.

Cesty napříč prostředími

Stejný princip jako collect_pictures_*.py a generate_thumbnails.py:

Prostředí Lokální mount
Windows \\Tower1\ZalohaVsechObrazku\...
Tower1 (Unraid) /mnt/user/ZalohaVsechObrazku/...
tower / ostatní Linux /mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku/...

V DB jsou cesty vždy v nativním Tower1 formátu (/mnt/user/...) — skript si je za běhu přemapuje.

Konfigurace (na začátku skriptu)

MAX_PHOTOS     = 0       # 0 = všechny, jinak limit
RESET          = False   # True = TRUNCATE photo_errors + verified_at = NULL
REVERIFY_ALL   = False   # True = ignoruj verified_at, projdi všechny znovu
BATCH_SIZE     = 500
SIZE_MIN_BYTES = 10 * 1024            # < 10 KB → size_suspicious
SIZE_MAX_BYTES = 50 * 1024 * 1024     # > 50 MB → size_suspicious

Spuštění

python verify_photos.py                  # produkce
python verify_photos.py --dry-run        # nic neukládá, jen loguje
python verify_photos.py --batch-size 200

Co skript dělá s každou fotkou

  1. Existence souborulocal_path.is_file()
  2. Velikoststat().st_size
  3. Pillow verify() — kontrola JPEG/PNG hlavičky
  4. Pillow load() (strict) — plný dekód pixelů. Pokud spadne na truncation, zkusí znovu s ImageFile.LOAD_TRUNCATED_IMAGES = True.
  5. dimension_mismatch — porovná photos.width/height s hlavičkou dekódovaného obrázku.
  6. Info checksmissing_exif, missing_datetime_original, missing_gps.
  7. Pro každou fotku v jedné transakci:
    • DELETE FROM photo_errors WHERE photo_id = ?
    • INSERT INTO photo_errors (...) — všechny nalezené chyby
    • UPDATE photos SET verified_at = NOW() WHERE id = ?

Kategorie chyb

🔴 critical — soubor je nepoužitelný

error_code Význam
file_missing Cesta v DB existuje, soubor na disku ne
read_error IOError / Permission / SMB timeout / stat() selhal
invalid_format Pillow neumí soubor rozpoznat ani otevřít
decode_failed Hlavička OK, ale load() praskne (vážně poškozená data)
zero_size file_size == 0

🟡 warning — soubor lze použít, ale má vadu

error_code Význam
truncated load() strict praskne, ale s LOAD_TRUNCATED_IMAGES=True dojede
exif_parse_error EXIF blok má vadu (zatím rezervováno)
dimension_mismatch DB width/height ≠ skutečné rozměry souboru
size_suspicious < 10 KB nebo > 50 MB

🔵 info — jen poznámka, nic není rozbité

error_code Význam
missing_exif Žádný exif_raw
missing_datetime_original Není EXIF DateTimeOriginal
missing_gps Žádné gps_lat / gps_lon

Výběr fotek

  • Default: WHERE verified_at IS NULL (díky partial indexu rychlé)
  • REVERIFY_ALL=True → projede všechny
  • RESET=TrueTRUNCATE photo_errors + verified_at = NULL, pak plná verifikace

Idempotence

Před zápisem se vždy smažou všechny photo_errors pro danou photo_id, takže každý běh nahradí předchozí výsledek. Historie chyb se neudržuje.

Užitečné dotazy

Přehled chyb podle typu

SELECT severity, error_code, COUNT(*) AS cnt
FROM photo_errors
GROUP BY 1, 2
ORDER BY 1, 3 DESC;

Počet unikátních postižených fotek

SELECT severity, COUNT(DISTINCT photo_id) AS photos
FROM photo_errors
GROUP BY severity;

Truncated fotky

SELECT p.id, p.file_path, p.file_size
FROM photo_errors e
JOIN photos p ON p.id = e.photo_id
WHERE e.error_code = 'truncated'
ORDER BY p.file_size DESC;

Kritické chyby

SELECT p.id, p.file_path, e.error_code, e.error_message
FROM photo_errors e
JOIN photos p ON p.id = e.photo_id
WHERE e.severity = 'critical'
ORDER BY e.error_code, p.id;

Pokrok verifikace

SELECT
  COUNT(*) FILTER (WHERE verified_at IS NOT NULL) AS verified,
  COUNT(*) FILTER (WHERE verified_at IS NULL)     AS remaining,
  COUNT(*)                                        AS total
FROM photos;

Fotky bez nalezených chyb (čisté)

SELECT COUNT(*) FROM photos p
WHERE p.verified_at IS NOT NULL
  AND NOT EXISTS (SELECT 1 FROM photo_errors e WHERE e.photo_id = p.id);

Doporučený workflow při prvním spuštění

  1. Spustit migraci 002_add_photo_errors.sql v Navicatu.
  2. MAX_PHOTOS=20, --dry-run — vidíš v logu, co by se zapsalo.
  3. MAX_PHOTOS=100, ostrý běh — ověříš zápis do DB.
  4. MAX_PHOTOS=0, ostrý běh — plná verifikace.

Schéma photo_errors

CREATE TABLE photo_errors (
    id            BIGSERIAL PRIMARY KEY,
    photo_id      BIGINT      NOT NULL REFERENCES photos(id) ON DELETE CASCADE,
    severity      VARCHAR(20) NOT NULL CHECK (severity IN ('critical', 'warning', 'info')),
    error_code    VARCHAR(50) NOT NULL,
    error_message TEXT,
    detected_at   TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_photo_errors_photo_id ON photo_errors (photo_id);
CREATE INDEX idx_photo_errors_severity ON photo_errors (severity);
CREATE INDEX idx_photo_errors_code     ON photo_errors (error_code);