#!/usr/bin/env python3
"""
preview_server.py — HTTP server pro náhled fotek z DB.
Spustit v Docker kontejneru python-runner na Tower.
Spuštění:
docker exec -d python-runner python /scripts/preview_server.py
API:
GET /preview?ids=101,202,303
GET /preview?ids=101,202,303&thumb=400
GET /health
"""
import base64
import io
import socketserver
import urllib.parse
from http.server import BaseHTTPRequestHandler
import psycopg
from PIL import Image
# ── Konfigurace ───────────────────────────────────────────────────────────────
PORT = 8765
DB = dict(
host="192.168.1.76", port=5432,
user="vladimir.buzalka",
password="Vlado7309208104++",
dbname="fotky_buzalkovi"
)
# Prefix v DB (jak to zapsal collect_pictures.py na Tower1)
NAS_LINUX_PREFIX = "/mnt/user/ZalohaVsechObrazku/"
# Cesta uvnitř kontejneru (NFS mount přidaný v Unraid Docker UI)
NAS_CONTAINER_PATH = "/mnt/ZalohaVsechObrazku/"
# ── DB dotaz ──────────────────────────────────────────────────────────────────
QUERY = """
SELECT
z.id,
z.cesta_zalohy,
z.velikost,
p.taken_at,
p.taken_at_source,
p.camera_make,
p.camera_model,
p.width,
p.height,
p.megapixels,
p.gps_lat,
p.gps_lon,
COALESCE(
(SELECT string_agg(e.error_code, ', ')
FROM photo_errors e WHERE e.photo_id = p.id),
''
) AS chyby
FROM zaloha_obrazku z
JOIN photos p ON p.zaloha_id = z.id
WHERE z.id = ANY(%s)
"""
def linux_to_container(p: str) -> str:
rel = p.removeprefix(NAS_LINUX_PREFIX)
return NAS_CONTAINER_PATH + rel
def fetch_rows(ids: list[int]) -> list:
conn = psycopg.connect(**DB)
cur = conn.cursor()
cur.execute(QUERY, (ids,))
rows = cur.fetchall()
conn.close()
order = {rid: i for i, rid in enumerate(ids)}
return sorted(rows, key=lambda r: order.get(r[0], 9999))
# ── Thumbnaily ────────────────────────────────────────────────────────────────
def make_thumb_b64(path: str, size: int) -> str | None:
try:
img = Image.open(path)
img.thumbnail((size, size), Image.LANCZOS)
if img.mode not in ("RGB", "L"):
img = img.convert("RGB")
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=82)
return base64.b64encode(buf.getvalue()).decode()
except Exception:
return None
# ── HTML ──────────────────────────────────────────────────────────────────────
def format_size(n) -> str:
if n is None:
return "?"
for unit in ("B", "kB", "MB", "GB"):
if n < 1024:
return f"{n:.0f} {unit}"
n /= 1024
return f"{n:.1f} GB"
def render_html(rows: list, ids: list[int], thumb_size: int) -> str:
cards = []
ok = 0
for i, row in enumerate(rows, 1):
(rid, cesta, velikost, taken_at, taken_src,
make_, model, width, height, mp, lat, lon, chyby) = row
container_path = linux_to_container(cesta)
b64 = make_thumb_b64(container_path, thumb_size)
if b64:
ok += 1
img_tag = f''
card_cls = "card"
else:
img_tag = (
'