banyaro/backend/migrate_media.py
rene 5141ba9969 Session 2026-04-20: Medien-Konvertierung, Umami Analytics, Username/Privacy
- HEIC→JPEG, MOV/AVI→MP4 Konvertierung bei allen Upload-Endpoints (media_utils.py)
- ffmpeg im Docker-Image, Video-Thumbnails (extract_video_thumb, poster-Attribut)
- Google Analytics entfernt, Umami self-hosted eingebunden (index.html, datenschutz.js)
- Admin-Panel Analytics-Tab: Stat-Cards, Sparkline 7 Tage, Top-Seiten (Umami-API-Proxy)
- Admin-Panel Tab-Icons korrigiert (aus vorhandenem Phosphor-Sprite)
- users.real_name Spalte: Username öffentlich, echter Name privat und optional
- Registrierung: Label "Benutzername", Leerzeichen verboten, Profanity-Blockliste
- Datenschutzerklärung: GA-Abschnitt durch Umami-Text ersetzt
2026-04-20 18:36:58 +02:00

101 lines
3.3 KiB
Python

"""
Einmalige Migration: MOV/AVI/M4V → MP4, HEIC/HEIF → JPEG
Alle betroffenen Dateien auf Disk konvertieren + DB-URLs aktualisieren.
Ausführen im Container:
python3 /app/migrate_media.py
"""
import json
import os
import sys
from database import db
from media_utils import convert_media
MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
CONVERT_EXTS = {".mov", ".avi", ".m4v", ".heic", ".heif"}
def _new_ext(ext: str) -> str:
return ".mp4" if ext in {".mov", ".avi", ".m4v"} else ".jpg"
def convert_file(path: str) -> str | None:
"""Convert file in-place, return new path or None on failure."""
ext = os.path.splitext(path)[1].lower()
if ext not in CONVERT_EXTS:
return None
print(f" Konvertiere: {path}")
with open(path, "rb") as f:
data = f.read()
converted, new_ext = convert_media(data, os.path.basename(path))
if new_ext == ext:
print(f" ✗ Konvertierung fehlgeschlagen, übersprungen.")
return None
new_path = path[: -len(ext)] + new_ext
with open(new_path, "wb") as f:
f.write(converted)
os.unlink(path)
print(f" ✓ → {new_path}")
return new_path
def url_from_path(path: str) -> str:
rel = os.path.relpath(path, MEDIA_DIR)
return "/media/" + rel.replace(os.sep, "/")
def update_db(old_url: str, new_url: str) -> int:
updates = 0
with db() as conn:
# diary_media
r = conn.execute("UPDATE diary_media SET url=? WHERE url=?", (new_url, old_url))
updates += r.rowcount
# JSON-Arrays: forum_threads, forum_posts, routes
for table, col in [("forum_threads", "foto_urls"),
("forum_posts", "foto_urls"),
("routes", "foto_urls")]:
rows = conn.execute(f"SELECT id, {col} FROM {table} WHERE {col} LIKE ?",
(f"%{old_url}%",)).fetchall()
for row in rows:
urls = json.loads(row[col] or "[]")
new_urls = [new_url if u == old_url else u for u in urls]
conn.execute(f"UPDATE {table} SET {col}=? WHERE id=?",
(json.dumps(new_urls), row["id"]))
updates += 1
# Einzelne URL-Felder
for table, col in [("lost_dogs", "foto_url"), ("poison", "foto_url")]:
r = conn.execute(f"UPDATE {table} SET {col}=? WHERE {col}=?",
(new_url, old_url))
updates += r.rowcount
return updates
def main():
total_files = 0
total_db = 0
for root, _, files in os.walk(MEDIA_DIR):
for fname in files:
ext = os.path.splitext(fname)[1].lower()
if ext not in CONVERT_EXTS:
continue
old_path = os.path.join(root, fname)
old_url = url_from_path(old_path)
new_path = convert_file(old_path)
if new_path:
new_url = url_from_path(new_path)
n = update_db(old_url, new_url)
print(f" DB: {n} Zeile(n) aktualisiert ({old_url}{new_url})")
total_files += 1
total_db += n
print(f"\n✓ Fertig: {total_files} Datei(en) konvertiert, {total_db} DB-Einträge aktualisiert.")
if __name__ == "__main__":
main()