# backend/src/media_library_viewer_api/widgets
dir: backend/src/media_library_viewer_api/widgets

index: backend/src/media_library_viewer_api/widgets/.pi-map.index.md

## role
Widget subsystem providing configurable dashboard widget definitions, source adapters, and data transformation helpers for the media library viewer API.
## files
- __init__.py | Marks the directory as a Python package for the widget subsystem.
- builtin.py | Defines built-in widget kinds that don't require external services, providing their configurations, schemas, and validation. | exp: class:StaticConfig, func:get_builtin_widget_kind(kind: str) → WidgetKind | None, call:BUILTIN_WIDGET_KINDS.get, func:is_builtin_kind(kind: str) → bool, func:builtin_widget_kind_models() → dict[str, type], call:Field, func:validate_builtin_config(kind: str, config: dict[str, Any]) → dict[str, Any], call:builtin_widget_kind_models, call:models.get, call:dict, call:model_cls.model_validate(config or {}).model_dump | dep: typing, media_library_viewer_api.integrations.base, pydantic
- prometheus_range.py | Provides shared helper functions for Prometheus range queries, including step-size derivation and matrix/series normalization for frontend chart rendering. | exp: func:step_for_window(window_seconds: int, target_points) → int, call:max, call:round, func:normalize_prometheus_matrix(result: list[dict[str, Any]]) → list[dict[str, Any]], call:entry.get, call:sorted, call:metric.items, call:str(k).startswith, call:" ".join, call:_safe_int, call:points.append, call:_safe_float, call:series.append, func:_safe_float(raw: Any) → float | None, call:float, func:_safe_int(ts: Any) → int | None, call:int, call:float | dep: typing
- sources.py | Provides widget source adapters that translate widget instances into dashboard data by fetching and transforming data from various services (Prometheus, Alertmanager, Jellyfin, qBittorrent, SSH tasks) or built-in sources (backups, static). | exp: class:ServiceRecord, class:WidgetSource, method:fetch(self, service: ServiceRecord | None, widget_kind: str, config: dict[str, Any]) → dict[str, Any], class:BackupsWidgetSource, method:fetch(self, service: ServiceRecord | None, widget_kind: str, config: dict[str, Any]) → dict[str, Any], call:get_settings_store, call:build_backup_dashboard_summary, call:summary.model_dump, call:logger.exception, class:StaticWidgetSource, method:fetch(self, service: ServiceRecord | None, widget_kind: str, config: dict[str, Any]) → dict[str, Any], call:config.get, class:PrometheusWidgetSource, method:fetch(self, service: ServiceRecord | None, widget_kind: str, config: dict[str, Any]) → dict[str, Any], call:str(service.config.get("base_url") or "").rstrip, call:service.config.get, call:int, call:self._fetch_chart, call:self._fetch_gauge, call:self._fetch_mean, call:self._instant_query, call:config.get, call:logger.exception, method:_range_query(self, base_url: str, timeout: int, promql: str, window: int) → dict[str, Any], call:step_for_window, call:int, call:time.time, call:asyncio.wait_for, call:asyncio.to_thread, call:response.raise_for_status, call:response.json, call:logger.exception, call:payload.get("data", {}).get, method:_instant_query(self, base_url: str, timeout: int, promql: str) → dict[str, Any], call:asyncio.wait_for, call:asyncio.to_thread, call:response.raise_for_status, call:response.json, call:logger.exception, call:payload.get, method:_fetch_chart(self, base_url: str, timeout: int, config: dict[str, Any]) → dict[str, Any], call:config.get, call:WINDOW_PRESETS.get, call:self._range_query, call:normalize_prometheus_matrix, method:_fetch_gauge(self, base_url: str, timeout: int, config: dict[str, Any]) → dict[str, Any], call:self._instant_query, call:config.get, call:raw["result"].get, call:len, call:float, method:_fetch_mean(self, base_url: str, timeout: int, config: dict[str, Any]) → dict[str, Any], call:config.get, call:WINDOW_PRESETS.get, call:self._range_query, call:len, call:result[0].get, call:nums.append, call:float, call:sum, class:AlertmanagerWidgetSource, method:fetch(self, service: ServiceRecord | None, widget_kind: str, config: dict[str, Any]) → dict[str, Any], call:str(service.config.get("base_url") or "").rstrip, call:service.config.get, call:int, call:config.get, call:service.secrets.get, call:asyncio.wait_for, call:asyncio.to_thread, call:response.raise_for_status, call:response.json, call:payload.get, call:isinstance, call:summarize_alerts, call:logger.exception, class:JellyfinWidgetSource, method:fetch(self, service: ServiceRecord | None, widget_kind: str, config: dict[str, Any]) → dict[str, Any], call:str, call:service.config.get, call:service.secrets.get, call:int, call:asyncio.wait_for, call:asyncio.to_thread, call:s.get("PlayState", {}).get, call:_map_sessions_to_activity_rows, call:logger.exception, class:SshTaskWidgetSource, method:fetch(self, service: ServiceRecord | None, widget_kind: str, config: dict[str, Any]) → dict[str, Any], call:get_settings_store, call:config.get, call:store.get_task, call:task.get, call:int, call:service.config.get, call:asyncio.wait_for, call:asyncio.to_thread, call:_record_timeout, call:logger.exception, class:QbittorrentWidgetSource, method:fetch(self, service: ServiceRecord | None, widget_kind: str, config: dict[str, Any]) → dict[str, Any], call:str, call:service.config.get, call:service.secrets.get, call:int, call:QbittorrentClient, call:asyncio.wait_for, call:asyncio.to_thread, call:data.get, call:torrents.values, call:t.get, call:by_state.get, call:len, call:server_state.get, call:time.time, call:QbittorrentSampleStore, call:store.append, call:store.window, call:logger.exception, func:build_service_record(store: SettingsStore, service_row: dict[str, Any]) → ServiceRecord, call:ServiceRecord, call:service_row.get, call:decrypt_secrets, call:bool, func:_record_timeout(service: ServiceRecord | None, config: dict[str, Any], timeout: int) → None, call:get_settings_store, call:store.record_service_task_run, call:str, call:config.get, call:logger.exception, func:get_service_adapter(service_type: str) → WidgetSource | None, call:SERVICE_ADAPTERS.get, func:get_builtin_adapter(kind: str) → WidgetSource | None, call:BUILTIN_ADAPTERS.get | dep: asyncio, logging, time, dataclasses, typing, requests, media_library_viewer_api.clients.jellyfin, media_library_viewer_api.clients.qbittorrent, media_library_viewer_api.domain.dashboard, media_library_viewer_api.integrations.alertmanager, media_library_viewer_api.services.qbittorrent_store, media_library_viewer_api.services.settings_store, media_library_viewer_api.services.task_runner, media_library_viewer_api.widgets.prometheus_range, media_library_viewer_api.services.secrets, JellyfinClient, QbittorrentClient, SettingsStore, summarize_alerts, run_saved_task, decrypt_secrets, normalize_prometheus_matrix
## arch
Adapter pattern with modular source connectors per service, built-in widget registry/schema definitions, and shared utility functions for data normalization and frontend rendering.
## tags
widget, fetch, call:logger.exception, source, call:config.get, call:int, call:self., builtin
## symbols
- StaticConfig
- ServiceRecord
- WidgetSource
- BackupsWidgetSource
- StaticWidgetSource
- PrometheusWidgetSource
- AlertmanagerWidgetSource
- JellyfinWidgetSource
## workflows
- change widgets behavior
  read: __init__.py, builtin.py, prometheus_range.py
## dirty
-
