5.7 KiB
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(tabulkaphoto_errors+ sloupecphotos.verified_at+ partial index). .envsDB_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
- Existence souboru —
local_path.is_file() - Velikost —
stat().st_size - Pillow
verify()— kontrola JPEG/PNG hlavičky - Pillow
load()(strict) — plný dekód pixelů. Pokud spadne na truncation, zkusí znovu sImageFile.LOAD_TRUNCATED_IMAGES = True. dimension_mismatch— porovnáphotos.width/heights hlavičkou dekódovaného obrázku.- Info checks —
missing_exif,missing_datetime_original,missing_gps. - Pro každou fotku v jedné transakci:
DELETE FROM photo_errors WHERE photo_id = ?INSERT INTO photo_errors (...)— všechny nalezené chybyUPDATE 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šechnyRESET=True→TRUNCATE 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í
- Spustit migraci
002_add_photo_errors.sqlv Navicatu. MAX_PHOTOS=20,--dry-run— vidíš v logu, co by se zapsalo.MAX_PHOTOS=100, ostrý běh — ověříš zápis do DB.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);