"""Server-gerenderte Seiten mit Jinja2 + HTMX""" import logging from aiohttp import web import aiohttp_jinja2 from app.config import Config from app.services.queue import QueueService from app.services.encoder import EncoderService from app.models.media import MediaFile def setup_page_routes(app: web.Application, config: Config, queue_service: QueueService) -> None: """Registriert Seiten-Routes""" def _build_ws_url(request) -> str: """Baut WebSocket-URL fuer den Client""" srv = config.server_config ext_url = srv.get("external_url", "") use_https = srv.get("use_https", False) ws_path = srv.get("websocket_path", "/ws") protocol = "wss" if use_https else "ws" if ext_url: return f"{protocol}://{ext_url}{ws_path}" return f"{protocol}://{request.host}{ws_path}" @aiohttp_jinja2.template("dashboard.html") async def dashboard(request: web.Request) -> dict: """GET / - Dashboard""" return { "ws_url": _build_ws_url(request), "active_jobs": queue_service.get_active_jobs().get("data_convert", {}), "queue": queue_service.get_queue_state().get("data_queue", {}), } @aiohttp_jinja2.template("admin.html") async def admin(request: web.Request) -> dict: """GET /admin - Einstellungsseite""" gpu_available = EncoderService.detect_gpu_available() gpu_devices = EncoderService.get_available_render_devices() return { "settings": config.settings, "presets": config.presets, "gpu_available": gpu_available, "gpu_devices": gpu_devices, } @aiohttp_jinja2.template("library.html") async def library(request: web.Request) -> dict: """GET /library - Bibliothek""" return {} @aiohttp_jinja2.template("statistics.html") async def statistics(request: web.Request) -> dict: """GET /statistics - Statistik-Seite""" entries = await queue_service.get_statistics(limit=50) summary = await queue_service.get_statistics_summary() return { "entries": entries, "summary": summary, "format_size": MediaFile.format_size, "format_time": MediaFile.format_time, } # --- HTMX Partials --- async def htmx_save_settings(request: web.Request) -> web.Response: """POST /htmx/settings - Settings via Formular speichern""" data = await request.post() # Formular-Daten in Settings-Struktur konvertieren settings = config.settings # Encoding settings["encoding"]["mode"] = data.get("encoding_mode", "cpu") settings["encoding"]["gpu_device"] = data.get("gpu_device", "/dev/dri/renderD128") settings["encoding"]["default_preset"] = data.get("default_preset", "cpu_av1") settings["encoding"]["max_parallel_jobs"] = int( data.get("max_parallel_jobs", 1) ) # Files settings["files"]["target_container"] = data.get("target_container", "webm") settings["files"]["target_folder"] = data.get("target_folder", "same") settings["files"]["delete_source"] = data.get("delete_source") == "on" settings["files"]["recursive_scan"] = data.get("recursive_scan") == "on" # Cleanup settings["cleanup"]["enabled"] = data.get("cleanup_enabled") == "on" cleanup_ext = data.get("cleanup_extensions", "") if cleanup_ext: settings["cleanup"]["delete_extensions"] = [ e.strip() for e in cleanup_ext.split(",") if e.strip() ] exclude_pat = data.get("cleanup_exclude", "") if exclude_pat: settings["cleanup"]["exclude_patterns"] = [ p.strip() for p in exclude_pat.split(",") if p.strip() ] # Audio audio_langs = data.get("audio_languages", "ger,eng,und") settings["audio"]["languages"] = [ l.strip() for l in audio_langs.split(",") if l.strip() ] settings["audio"]["default_codec"] = data.get("audio_codec", "libopus") settings["audio"]["keep_channels"] = data.get("keep_channels") == "on" # Subtitle sub_langs = data.get("subtitle_languages", "ger,eng") settings["subtitle"]["languages"] = [ l.strip() for l in sub_langs.split(",") if l.strip() ] # Logging settings["logging"]["level"] = data.get("log_level", "INFO") # Bibliothek / TVDB settings.setdefault("library", {}) settings["library"]["tvdb_api_key"] = data.get("tvdb_api_key", "") settings["library"]["tvdb_pin"] = data.get("tvdb_pin", "") settings["library"]["tvdb_language"] = data.get("tvdb_language", "deu") config.save_settings() logging.info("Settings via Admin-UI gespeichert") # Erfolgs-HTML zurueckgeben (HTMX swap) return web.Response( text='
Settings gespeichert!
', content_type="text/html", ) @aiohttp_jinja2.template("partials/stats_table.html") async def htmx_stats_table(request: web.Request) -> dict: """GET /htmx/stats?page=1 - Paginierte Statistik""" page = int(request.query.get("page", 1)) limit = 25 offset = (page - 1) * limit entries = await queue_service.get_statistics(limit, offset) return { "entries": entries, "page": page, "format_size": MediaFile.format_size, "format_time": MediaFile.format_time, } async def redirect_to_library(request: web.Request): """GET / -> Weiterleitung zur Bibliothek""" raise web.HTTPFound("/library") # Routes registrieren app.router.add_get("/", redirect_to_library) app.router.add_get("/dashboard", dashboard) app.router.add_get("/library", library) app.router.add_get("/admin", admin) app.router.add_get("/statistics", statistics) app.router.add_post("/htmx/settings", htmx_save_settings) app.router.add_get("/htmx/stats", htmx_stats_table)