Files
administrator 0f73a6b537 notebookVb
2026-06-04 22:56:27 +02:00

231 lines
8.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
preview_sample.py — zobrazí náhled fotek podle ID z databáze.
Použití:
python preview_sample.py 101 202 303 ...
python preview_sample.py --file ids.txt # jedno ID na řádek
python preview_sample.py --file ids.txt --thumb 400
"""
import argparse
import base64
import io
import sys
import tempfile
import webbrowser
from pathlib import Path
import psycopg2
from PIL import Image
# ── DB ────────────────────────────────────────────────────────────────────────
DB = dict(host="192.168.1.76", port=5432, user="vladimir.buzalka",
password="Vlado7309208104++", database="fotky_buzalkovi")
# ── Cesty ─────────────────────────────────────────────────────────────────────
NAS_LINUX_PREFIX = "/mnt/user/ZalohaVsechObrazku/"
NAS_WIN_UNC = r"\\Tower1\ZalohaVsechObrazku\\"
def linux_to_win(p: str) -> Path:
rel = p.removeprefix(NAS_LINUX_PREFIX).replace("/", "\\")
return Path(NAS_WIN_UNC + rel)
# ── Načtení dat z DB ──────────────────────────────────────────────────────────
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 fetch_rows(ids: list[int]) -> list:
conn = psycopg2.connect(**DB)
cur = conn.cursor()
cur.execute(QUERY, (ids,))
rows = cur.fetchall()
conn.close()
# zachovej původní pořadí ID
order = {rid: i for i, rid in enumerate(ids)}
return sorted(rows, key=lambda r: order.get(r[0], 9999))
# ── Thumbnail ─────────────────────────────────────────────────────────────────
def make_thumb_b64(win_path: Path, size: int) -> str | None:
try:
img = Image.open(win_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
win_path = linux_to_win(cesta)
b64 = make_thumb_b64(win_path, thumb_size)
if b64:
ok += 1
img_tag = f'<img src="data:image/jpeg;base64,{b64}" alt="foto">'
card_cls = "card"
else:
img_tag = '<div class="no-img">⚠ soubor nedostupný<br><small>zkontroluj přístup na Tower1</small></div>'
card_cls = "card broken"
camera = " ".join(filter(None, [make_, model])) or "<em>neznámá</em>"
date_str = taken_at.strftime("%Y-%m-%d %H:%M") if taken_at else "?"
res = f"{width}×{height}" if width and height else "?"
mp_str = f"{mp:.1f} Mpx" if mp else ""
gps_link = (
f'<a href="https://maps.google.com/?q={lat},{lon}" target="_blank">📍 {lat:.4f}, {lon:.4f}</a>'
if lat and lon else ""
)
chyby_html = f'<div class="err">{chyby}</div>' if chyby else ""
path_short = str(win_path).replace(NAS_WIN_UNC, "\\")
cards.append(f"""
<div class="{card_cls}">
<div class="thumb">{img_tag}</div>
<div class="meta">
<div class="idx">id={rid} &nbsp;#{i}</div>
<div><strong>{date_str}</strong> <span class="src">({taken_src})</span></div>
<div>📷 {camera}</div>
<div>{res} {mp_str} &nbsp; {format_size(velikost)}</div>
{"<div>" + gps_link + "</div>" if gps_link else ""}
{chyby_html}
<div class="path" title="{win_path}">{path_short}</div>
</div>
</div>""")
missing_ids = set(ids) - {r[0] for r in rows}
missing_note = ""
if missing_ids:
missing_note = f'<div class="warn">ID nenalezena v DB: {sorted(missing_ids)}</div>'
return f"""<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="utf-8">
<title>Preview fotek ({len(rows)})</title>
<style>
body {{ font-family: sans-serif; background: #1a1a2e; color: #eee; margin: 0; padding: 16px; }}
h1 {{ color: #e0c97f; margin-bottom: 4px; }}
.info {{ color: #aaa; margin-bottom: 16px; font-size: .9em; }}
.warn {{ color: #f99; margin-bottom: 12px; }}
.grid {{ display: flex; flex-wrap: wrap; gap: 16px; }}
.card {{ background: #16213e; border-radius: 8px; overflow: hidden;
width: {thumb_size + 24}px; box-shadow: 0 2px 8px #0006; }}
.card.broken {{ opacity: .55; }}
.thumb {{ display:flex; align-items:center; justify-content:center;
background:#0f3460; min-height: 120px; }}
.thumb img {{ max-width:100%; max-height:{thumb_size}px; display:block; }}
.no-img {{ color:#f88; padding:20px; text-align:center; font-size:.85em; }}
.meta {{ padding: 8px 12px; font-size: .78em; line-height: 1.75; }}
.meta strong {{ font-size: 1em; }}
.idx {{ float:right; color:#666; font-size:.75em; }}
.src {{ color:#888; }}
.err {{ color:#f99; }}
.path {{ color:#555; word-break:break-all; margin-top:4px; font-size:.7em; }}
a {{ color:#7ec8e3; }}
</style>
</head>
<body>
<h1>Preview fotek</h1>
<div class="info">
Celkem ID: <strong>{len(ids)}</strong> &nbsp;·&nbsp;
Načteno z DB: <strong>{len(rows)}</strong> &nbsp;·&nbsp;
Obrázky dostupné: <strong>{ok}/{len(rows)}</strong>
</div>
{missing_note}
<div class="grid">
{"".join(cards)}
</div>
</body>
</html>"""
# ── Main ──────────────────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(description="Preview fotek podle ID z DB")
parser.add_argument("ids", nargs="*", type=int, help="ID fotek (zaloha_obrazku.id)")
parser.add_argument("--file", "-f", help="Soubor s ID (jedno na řádek)")
parser.add_argument("--thumb", "-t", type=int, default=320, help="Velikost thumbnailů v px (default 320)")
args = parser.parse_args()
ids = list(args.ids)
if args.file:
with open(args.file) as fh:
for line in fh:
line = line.strip()
if line and line.isdigit():
ids.append(int(line))
if not ids:
print("Žádná ID. Zadej je jako argumenty nebo přes --file.", file=sys.stderr)
sys.exit(1)
print(f"Načítám {len(ids)} fotek z DB…")
rows = fetch_rows(ids)
print(f"Generuji thumbnaily (--thumb {args.thumb}px)…")
html = render_html(rows, ids, args.thumb)
tmp = tempfile.NamedTemporaryFile(
suffix=".html", delete=False, mode="w", encoding="utf-8"
)
tmp.write(html)
tmp.close()
print(f"Otevírám: {tmp.name}")
webbrowser.open(f"file:///{tmp.name}")
if __name__ == "__main__":
main()