"""Jellyfin service definition.""" from __future__ import annotations from typing import TYPE_CHECKING, Any from media_library_viewer_api.clients.jellyfin import JellyfinClient from media_library_viewer_api.integrations.base import ( SecretField, ServiceBaseUrl, ServiceConfigBase, ServiceDefinition, TestResult, WidgetConfigBase, translate_connection_error, widget_kind, ) if TYPE_CHECKING: from media_library_viewer_api.services.settings_store import SettingsStore def test_connection( config: dict[str, Any], secrets: dict[str, str], store: SettingsStore, ) -> TestResult: """Call JellyfinClient.users() — the lightest authenticated probe.""" try: base_url = str(config.get("base_url") or "") api_key = str(secrets.get("api_key") or "") timeout = int(config.get("timeout_seconds") or 10) client = JellyfinClient(base_url, api_key, timeout=timeout) users = client.users() return TestResult(ok=True, detail="Connected to Jellyfin.", evidence=f"{len(users)} users") except Exception as exc: return translate_connection_error(exc, context="Jellyfin") class JellyfinConfig(ServiceConfigBase): """Non-secret Jellyfin connection config. The optional ``jellyseerr_url`` / ``jellyseerr_api_key`` fields carry the paired Jellyseerr companion config, absorbed from the former standalone ``jellyseerr`` service type (see OpenSpec change ``services-as-hub-ia``). When both are set, the Jellyfin service page renders a Requests tab backed by Jellyseerr. """ base_url: ServiceBaseUrl user_id: str = "" timeout_seconds: int = 10 jellyseerr_url: str = "" jellyseerr_api_key: str = "" class JellyfinActivityWidgetConfig(WidgetConfigBase): """Live Jellyfin session activity.""" # No user-overridable fields; the service record carries user_id. pass class JellyfinNowPlayingWidgetConfig(WidgetConfigBase): """Only show sessions with active playback (not idle/paused).""" pass DEFINITION = ServiceDefinition( service_type="jellyfin", name="Jellyfin", description="Media server with live session activity.", config_model=JellyfinConfig, secret_fields=[ SecretField(key="api_key", label="API key", required=True), ], widget_kinds=[ widget_kind( kind="activity", name="Activity", description="Live sessions and idle users.", model_cls=JellyfinActivityWidgetConfig, default_config={}, refresh_interval_ms=30_000, ), widget_kind( kind="now_playing", name="Now Playing", description="Only sessions actively playing media.", model_cls=JellyfinNowPlayingWidgetConfig, default_config={}, refresh_interval_ms=30_000, ), ], test_callable=test_connection, )