Files
administrator ee14efbd48 notebookVb
2026-06-06 06:26:42 +02:00

126 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""
immich_upload.py — nahrava wanted fotky z DB fotky_buzalkovi do Immiche.
Cte primo ze zalohy (NFS), nahrava KOPII pres Immich API. Zalohu nesaha.
Resumable: tabulka immich_upload (zaloha_id -> asset id, status).
Spousti se v kontejneru python-runner:
docker exec python-runner python3 /scripts/immich_upload.py --like '...%' --limit 200
"""
import os
import sys
import argparse
from datetime import datetime, timezone
import requests
import psycopg
sys.stdout.reconfigure(encoding="utf-8")
DB = dict(host="192.168.1.76", port=5432, user="vladimir.buzalka",
password="Vlado7309208104++", dbname="fotky_buzalkovi")
IMMICH = "http://192.168.1.76:8888"
APIKEY = "UQV5PS1Td50hKOZTItnXEcXVkfQSUxcUH0XHZYxc"
SRC_PREFIX = "/mnt/user/ZalohaVsechObrazku" # jak je cesta v DB
DST_PREFIX = "/mnt/ZalohaVsechObrazku" # jak ji vidi kontejner
ap = argparse.ArgumentParser()
ap.add_argument("--like", default=None, help="filtr na z.cesta_zalohy LIKE")
ap.add_argument("--category", default=None, help="filtr na p.category")
ap.add_argument("--limit", type=int, default=0)
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args()
conn = psycopg.connect(**DB, autocommit=True)
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS immich_upload(
zaloha_id bigint PRIMARY KEY,
immich_id uuid,
status text,
uploaded_at timestamptz DEFAULT now()
)
""")
sql = """
SELECT z.id, z.cesta_zalohy, p.taken_at
FROM photos p
JOIN zaloha_obrazku z ON p.zaloha_id = z.id
LEFT JOIN immich_upload iu ON iu.zaloha_id = z.id
WHERE p.wanted = TRUE AND iu.zaloha_id IS NULL
"""
params = []
if args.category:
sql += " AND p.category = %s"; params.append(args.category)
if args.like:
sql += " AND z.cesta_zalohy LIKE %s"; params.append(args.like)
sql += " ORDER BY z.cesta_zalohy"
if args.limit:
sql += " LIMIT %s"; params.append(args.limit)
cur.execute(sql, params)
rows = cur.fetchall()
total = len(rows)
print(f"K nahrani (jeste nenahrano): {total:,}")
if args.dry_run:
miss = sum(1 for _, c, _ in rows if not os.path.isfile(c.replace(SRC_PREFIX, DST_PREFIX, 1)))
print(f"DRY-RUN: chybejicich souboru: {miss}")
sys.exit(0)
sess = requests.Session()
sess.headers.update({"x-api-key": APIKEY, "Accept": "application/json"})
created = dup = err = 0
for i, (zid, cesta, taken) in enumerate(rows, 1):
path = cesta.replace(SRC_PREFIX, DST_PREFIX, 1)
if not os.path.isfile(path):
err += 1
print("MISSING:", path)
continue
st = os.stat(path)
fmod = datetime.fromtimestamp(st.st_mtime, tz=timezone.utc)
fcreated = taken if taken else fmod
if fcreated.tzinfo is None:
fcreated = fcreated.replace(tzinfo=timezone.utc)
try:
with open(path, "rb") as fh:
r = sess.post(
IMMICH + "/api/assets",
files={"assetData": (os.path.basename(path), fh, "application/octet-stream")},
data={
"deviceAssetId": f"fb-{zid}",
"deviceId": "fotky-buzalkovi",
"fileCreatedAt": fcreated.isoformat(),
"fileModifiedAt": fmod.isoformat(),
"isFavorite": "false",
},
timeout=300,
)
j = r.json()
status = j.get("status")
aid = j.get("id")
cur.execute(
"""INSERT INTO immich_upload(zaloha_id, immich_id, status)
VALUES (%s,%s,%s)
ON CONFLICT (zaloha_id) DO UPDATE
SET immich_id=EXCLUDED.immich_id, status=EXCLUDED.status, uploaded_at=now()""",
(zid, aid, status),
)
if status == "created":
created += 1
elif status == "duplicate":
dup += 1
else:
err += 1
print("? neznamy status:", status, j)
except Exception as e:
err += 1
print("ERR", zid, repr(e))
if i % 50 == 0 or i == total:
print(f"... {i}/{total} created={created} dup={dup} err={err}", flush=True)
print(f"\nHOTOVO. created={created} dup={dup} err={err}")
conn.close()