e25240c2f3
The Jellyseerr API key was stored as plaintext in the Jellyfin service config. It is now a SecretField on the Jellyfin service, so it is encrypted at rest and rendered as a masked secret input (the generic config editor stops exposing it, and the secret editor picks it up automatically). Migration (idempotent, runs in ensure_defaults): - _migrate_jellyseerr_api_key_to_secret: for every Jellyfin service with a plaintext jellyseerr_api_key still in config, encrypt it ONCE into the secrets blob (direct UPDATE so existing encrypted secrets are preserved, not re-encrypted) and remove it from config. - _migrate_jellyseerr_into_jellyfin: standalone-jellyseerr absorption now stores the key as a secret, and decrypts the Jellyfin api_key before handing it to upsert_service (fixes a pre-existing double-encrypt on that rare path). The stats provider already reads jellyseerr_api_key from secrets-or-config, so it works before, during, and after the migration. Tests: absorbed-key lands in secrets (and existing api_key isn't corrupted); new plaintext-config -> secret migration + idempotency. 401/401 backend pass.
40 KiB
40 KiB
backend/src/media_library_viewer_api/services
dir: backend/src/media_library_viewer_api/services
index: backend/src/media_library_viewer_api/services/.pi-map.index.md
role
Backend service layer providing business logic for backup monitoring, media indexing, email delivery, task execution, secrets management, and persistent data storage.
files
- __init__.py | Swaps the position of two tmux panes within a window or between windows | dep: tmux, sh
- backup_alert_engine.py | Generates alerts for backup job runs based on failure status, size/duration anomalies compared to historical medians, and missed schedules. | exp: func:generate_alerts_for_run(run: dict[str, Any], previous_runs: list[dict[str, Any]], job: dict[str, Any] | None) → list[dict[str, Any]], call:alerts.append, call:run.get, call:r.get, call:len, call:statistics.median, func:check_missed_schedules(jobs: list[dict[str, Any]], get_latest_run: callable, existing_alerts: list[dict[str, Any]]) → list[dict[str, Any]], call:int, call:time.time, call:job.get, call:get_latest_run, call:alerts.append, call:any | dep: statistics, typing, time
- backup_poller.py | Runs a background daemon thread that periodically checks backup jobs for missed schedules, creates alerts, and prunes old resolved alerts. | exp: class:BackupAlertPoller, method:init(self) → None, call:threading.Event, call:threading.Lock, method:start(self) → None, call:self._thread.is_alive, call:self._stop_event.clear, call:threading.Thread, call:self._thread.start, method:stop(self, timeout) → None, call:self._stop_event.set, call:thread.join, method:_run(self) → None, call:self._stop_event.wait, call:get_settings_store, call:self._stop_event.is_set, call:self._run_cycle, call:logger.exception, method:_run_cycle(self, store: SettingsStore) → None, call:time.perf_counter, call:store.list_backup_jobs, call:store.list_backup_alerts, call:check_missed_schedules, call:store.get_latest_backup_run, call:store.create_backup_alert, call:int, call:time.time, call:store.prune_backup_alerts, func:get_backup_poller() → BackupAlertPoller | dep: logging, threading, time, typing, .backup_alert_engine, .settings_store, backup_alert_engine, settings_store
- db_maintenance.py | Provides safe deletion helpers for SQLite databases including their WAL and SHM sidecar files. | exp: func:sqlite_sidecar_paths(db_path: Path) → list[Path], call:Path, call:db_path.with_name, func:remove_sqlite_database(db_path: Path) → list[str], call:sqlite_sidecar_paths, call:path.unlink, call:removed.append, call:str | dep: pathlib
- known_hosts.py | Synthesizes and manages SSH known_hosts entries by fetching server keys dynamically rather than mounting host configuration, enabling strict host-key checking in containerized environments. | exp: func:_host_alias(host: str, port: int) → str, call:int, func:_fetch_server_key(host: str, port: int, timeout) → paramiko.PKey, call:socket.create_connection, call:int, call:paramiko.Transport, call:transport.start_client, call:transport.get_remote_server_key, call:transport.close, call:sock.close, raise:RuntimeError, func:has_known_host(host: str, port: int, known_hosts_path: Path) → bool, call:known_hosts_path.exists, call:_host_alias, call:paramiko.HostKeys, call:host_keys.load, call:str, call:host_keys.lookup, func:ensure_known_host(host: str, port: int, known_hosts_path: Path, strict) → bool, call:known_hosts_path.parent.mkdir, call:_fetch_server_key, call:_host_alias, call:paramiko.HostKeys, call:known_hosts_path.exists, call:host_keys.load, call:str, call:host_keys.lookup, call:host_key.get_name, call:existing[key_type].get_base64, call:host_key.get_base64, call:host_keys.add, call:host_keys.save, call:logger.info, raise:RuntimeError, func:ensure_known_hosts_for_machines(machines: list[dict[str, Any]], known_hosts_path: Path, strict) → int, call:str(machine.get("mode") or "").lower, call:machine.get, call:str(machine.get("host") or "").strip, call:int, call:ensure_known_host | dep: logging, socket, pathlib, typing, paramiko
- mail_queue.py | Implements an in-process background queue with a single worker thread for asynchronous SMTP email delivery to keep API requests responsive. | exp: class:QueuedEmailMessage, class:MailQueue, method:init(self) → None, call:queue.Queue, call:threading.Event, call:threading.Lock, method:start(self) → None, call:self._thread.is_alive, call:self._stop_event.clear, call:threading.Thread, call:self._thread.start, call:logger.info, method:stop(self, timeout) → None, call:self._stop_event.set, call:self._queue.put, call:thread.join, call:thread.is_alive, call:logger.warning, call:logger.info, method:enqueue(self, settings: Any, recipients: list[str], subject: str, html_body: str, text_body, attachments) → str, call:uuid.uuid4, call:QueuedEmailMessage, call:list, call:time.time, call:self._queue.put, call:logger.info, call:len, method:status(self) → dict[str, Any], call:bool, call:self._thread.is_alive, call:self._stop_event.is_set, method:_run(self) → None, call:self._stop_event.is_set, call:self._queue.get, call:max, call:time.time, call:logger.info, call:len, call:send_email_message, call:(result.get("selected_mode") or {}).get, call:result.get, call:record_mail_queue, call:describe_smtp_error, call:logger.exception, call:getattr, call:self._queue.task_done, func:get_mail_queue() → MailQueue | dep: logging, queue, threading, time, uuid, dataclasses, typing, media_library_viewer_api.observability, media_library_viewer_api.services.mailer
- mailer.py | Re-exports all symbols from mailer_impl module to provide a public interface for mail functionality | dep: .mailer_impl, mailer_impl
- mailer_impl.py | SMTP email sending implementation with HTML-to-text conversion, attachment handling, multi-mode connection attempts, and sender fallback retry logic. | exp: class:EmailAttachment, class:_HTMLToTextParser, method:init(self) → None, call:super().init, method:handle_starttag(self, tag: str, attrs), call:tag.lower, call:self.parts[-1].endswith, call:self.parts.append, method:handle_endtag(self, tag: str) → None, call:tag.lower, call:self.parts[-1].endswith, call:self.parts.append, method:handle_data(self, data: str) → None, call:self.parts.append, method:text(self) → str, call:"".join, func:html_to_text(html: str) → str, call:_HTMLToTextParser, call:parser.feed, call:parser.text, call:line.rstrip, call:text.splitlines, call:"\n".join(line for line in lines if line).strip, func:_from_address(settings: object) → str, call:str(getattr(settings, "smtp_from_address", "") or "").strip, call:getattr, call:str(getattr(settings, "smtp_username", "") or "").strip, raise:ValueError, func:validate_smtp_settings(settings: object) → None, call:str(getattr(settings, "smtp_host", "") or "").strip, call:getattr, call:_from_address, raise:ValueError, func:_smtp_settings(settings: object) → dict[str, object], call:str(getattr(settings, "smtp_host", "") or "").strip, call:getattr, call:int, call:str(getattr(settings, "smtp_username", "") or "").strip, call:bool, raise:ValueError, func:_smtp_mode_candidates(settings: object) → list[dict[str, Any]], call:_smtp_settings, call:dict, call:str(base["smtp_host"]).lower, call:any, call:candidates.append, func:_probe_smtp_connection(mode: dict[str, Any]) → None, call:ssl.create_default_context, call:str, call:int, call:bool, call:smtplib.SMTP_SSL, call:smtplib.SMTP, call:smtp.ehlo, call:smtp.starttls, call:smtp.login, call:smtp.noop, func:_smtp_sender_not_authorized(error: Exception) → bool, call:getattr, call:isinstance, call:raw_error.decode, call:str, call:f"{code} {raw_error_text} {error}".lower, func:_smtp_attempt_metadata(mode: dict[str, Any]) → dict[str, Any], call:str, call:mode.get, call:int, call:bool, func:describe_smtp_error(error: Exception) → str, call:chain.append, call:isinstance, call:str(item).strip, call:text.lower, call:getattr, func:build_email_message(settings: object, recipients: list[str], subject: str, html_body: str, text_body: str, attachments, sender_address, reply_to_address) → tuple[EmailMessage, str], call:_from_address, call:str(getattr(settings, "smtp_from_name", "") or "").strip, call:getattr, call:EmailMessage, call:formataddr, call:text_body.strip, call:html_to_text, call:html_body.strip, call:msg.set_content, call:msg.add_alternative, call:mimetypes.guess_type, call:content_type.split, call:msg.add_attachment, func:_send_email_via_mode(mode: dict[str, Any], message: EmailMessage, recipients: list[str], from_address: str) → None, call:ssl.create_default_context, call:str, call:int, call:bool, call:smtplib.SMTP_SSL, call:smtplib.SMTP, call:smtp.ehlo, call:smtp.starttls, call:smtp.login, call:smtp.send_message, func:send_email_message(settings: object, recipients: list[str], subject: str, html_body: str, text_body, attachments) → dict[str, object], call:list, call:_from_address, call:str(getattr(settings, "smtp_username", "") or "").strip, call:getattr, call:build_email_message, call:logger.info, call:len, call:_smtp_mode_candidates, call:_smtp_attempt_metadata, call:_send_email_via_mode, call:describe_smtp_error, call:attempts.append, call:_smtp_sender_not_authorized, call:logger.warning, raise:ValueError, raise:RuntimeError | dep: logging, mimetypes, smtplib, socket, ssl, dataclasses, email.message, email.utils, html.parser, typing
- media_index.py | Re-exports all symbols from the media_index_impl module to provide a public API interface for media indexing services | dep: media_library_viewer_api.services.media_index_impl
- media_index_impl.py | Provides a SQLite-backed media inventory service that builds, stores, and queries indexed media items from Jellyfin with filtering, sorting, pagination, and multi-instance support. | exp: class:MediaIndexBuildCancelled, class:MediaIndexStatus, class:MediaIndex, method:init(self, db_path), call:Path, call:self.db_path.parent.mkdir, method:connect(self) → sqlite3.Connection, call:sqlite3.connect, call:conn.execute, method:init_schema(self) → None, call:self.connect, call:conn.executescript, method:set_metadata(self, key: str, value: str | int | float) → None, call:self.init_schema, call:self.connect, call:conn.execute, call:str, method:replace_items(self, rows: Iterable[dict[str, Any]], service_id) → int, call:self.init_schema, call:list, call:",".join, call:len, call:self.connect, call:conn.execute, call:conn.executemany, call:','.join, call:row.get, call:str, call:int, call:time.time, method:status(self) → MediaIndexStatus, call:self.db_path.exists, call:MediaIndexStatus, call:self.connect, call:int, call:conn.execute("SELECT COUNT() FROM media_items").fetchone, call:conn.execute("SELECT key, value FROM index_metadata").fetchall, call:meta.get, call:str(updated_at_raw).isdigit, call:time.strftime, call:time.localtime, call:float, call:str(meta.get(key, str(default))).strip().lower, call:_bool, call:_float, call:_int, method:query(self, library_id, library_ids, media_types, search, hdr_filter, sort_key, sort_order, limit, offset, service_id) → tuple[list[dict[str, Any]], int], call:self.init_schema, call:where.append, call:",".join, call:len, call:params.extend, call:params.append, call:search.lower, call:" AND ".join, call:SORT_COLUMNS.get, call:self.connect, call:int, call:conn.execute("SELECT COUNT() FROM media_items" + where_sql, params).fetchone, call:conn.execute( "SELECT * FROM media_items" + where_sql + order_sql + " LIMIT ? OFFSET ?", [*params, int(limit), int(offset)], ).fetchall, call:display_media_row, call:dict, func:_estimate_remaining_seconds(elapsed_seconds: float, progress: float | None) → float | None, call:max, call:min, func:build_media_index(client: JellyfinClient, user_id: str, libraries: list[dict[str, Any]], index, page_size, media_root, fallback_prefix, progress_callback, should_cancel, service_id) → int, call:MediaIndex, call:time.perf_counter, call:should_cancel, call:progress_callback, call:_estimate_remaining_seconds, call:len, call:ensure_not_cancelled, call:logger.info, call:emit, call:enumerate, call:library.get, call:client.items, call:response.get, call:int, call:max, call:normalized_rows.extend, call:resolve_remote_media_path, call:row.get, call:normalize_media_item, call:logger.debug, call:index.replace_items, call:index.set_metadata, raise:MediaIndexBuildCancelled, func:ensure_not_cancelled() → None, call:should_cancel, raise:MediaIndexBuildCancelled, func:emit(stage: str, message: str) → None, call:time.perf_counter, call:progress_callback, call:_estimate_remaining_seconds, call:len | dep: logging, sqlite3, time, dataclasses, pathlib, typing, media_library_viewer_api.clients.jellyfin, media_library_viewer_api.domain.media, media_library_viewer_api.path_utils, media_library_viewer_api.services.service_data, JellyfinClient
- qbittorrent_store.py | Provides a SQLite-backed time-series store for qBittorrent download/upload speed samples with automatic pruning of old entries. | exp: class:QbittorrentSampleStore, method:init(self, harness) → None, call:get_service_data_harness, method:append(self, service_id: str, ts: int, dl_speed: int, up_speed: int) → None, call:self._harness.connect, call:conn.execute, call:conn.commit, method:window(self, service_id: str, since_ts) → list[dict[str, Any]], call:self._harness.connect, call:conn.execute( "SELECT ts, dl_speed, up_speed FROM qbittorrent_speed_samples " "WHERE service_id = ? AND ts >= ? ORDER BY ts ASC", (service_id, since_ts), ).fetchall, call:conn.execute( "SELECT ts, dl_speed, up_speed FROM qbittorrent_speed_samples WHERE service_id = ? ORDER BY ts ASC", (service_id,), ).fetchall | dep: logging, typing, media_library_viewer_api.services.service_data
- secrets.py | Provides symmetric authenticated encryption for service secrets using Fernet with a mandatory environment variable master key. | exp: class:EncryptionKeyError, func:get_encryption_key() → bytes, call:os.environ.get, call:raw.strip().encode, call:Fernet, raise:EncryptionKeyError, func:reset_encryption_key_cache() → None, call:get_encryption_key.cache_clear, func:_fernet() → Fernet, call:Fernet, call:get_encryption_key, func:encrypt_value(plaintext: str) → str, call:_fernet().encrypt(plaintext.encode()).decode, call:plaintext.encode, func:decrypt_value(ciphertext: str) → str, call:_fernet().decrypt(ciphertext.encode()).decode, call:ciphertext.encode, raise:EncryptionKeyError, func:encrypt_secrets(values: dict[str, str]) → dict[str, str], call:_fernet, call:fernet.encrypt(value.encode()).decode, call:value.encode, call:values.items, func:decrypt_secrets(blob: dict[str, str]) → dict[str, str], call:_fernet, call:blob.items, call:fernet.decrypt(ciphertext.encode()).decode, call:ciphertext.encode, raise:EncryptionKeyError, func:generate_development_key() → str, call:Fernet.generate_key().decode, func:validate_encryption_key() → None, call:get_encryption_key | dep: os, functools, cryptography.fernet, functools.lru_cache
- service_data.py | Manages the lifecycle (provisioning, migration, cascade-delete) of per-concern SQLite databases for service-owned persistent data via a singleton harness. | exp: class:StorageConcern, class:ServiceDataHarness, method:init(self, base_dir: Path | str) → None, call:Path, method:register(self, concern: StorageConcern) → None, method:db_path(self, concern_key: str) → Path, method:connect(self, concern_key: str) → sqlite3.Connection, call:self.db_path, call:path.parent.mkdir, call:sqlite3.connect, call:conn.execute, method:run_migrations(self) → None, call:self._concerns.values, call:self.db_path, call:path.parent.mkdir, call:sqlite3.connect, call:conn.execute, call:s.strip, call:migration_sql.split, call:str(exc).lower, call:logger.debug, call:conn.commit, call:conn.close, method:cascade_delete(self, service_id: str) → None, call:self._concerns.values, call:self.db_path, call:path.exists, call:sqlite3.connect, call:conn.execute(f"PRAGMA table_info({table})").fetchall, func:get_service_data_harness() → ServiceDataHarness, call:Path, call:os.environ.get, call:ServiceDataHarness, call:_HARNESS.register, call:_HARNESS.run_migrations, func:reset_service_data_harness() → None | dep: logging, os, sqlite3, dataclasses, pathlib, media_library_viewer_api.services.qbittorrent_store, media_library_viewer_api.services.media_index_impl, qbittorrent_store, media_index_impl
- service_resolution.py | Provides a shared helper function to resolve a service instance by ID or fall back to the first enabled instance of a given service type. | exp: func:resolve_service_record(store: SettingsStore, service_type: str, service_id) → ServiceRecord | None, call:store.get_service, call:row.get, call:build_service_record, call:store.list_services | dep: media_library_viewer_api.services.settings_store, media_library_viewer_api.widgets.sources, media_library_viewer_api.services.settings_store.SettingsStore, media_library_viewer_api.widgets.sources.ServiceRecord, media_library_viewer_api.widgets.sources.build_service_record
- settings_store.py | SQLite-backed persistent storage manager for machine definitions, SSH keys, saved tasks, dashboard widgets, backup jobs, services, and task run history. | exp: class:SettingsStore, method:init(self, db_path), call:Path, call:self.db_path.parent.mkdir, method:connect(self) → sqlite3.Connection, call:sqlite3.connect, call:conn.execute, method:init_schema(self) → None, call:self.connect, call:conn.execute("PRAGMA table_info(ssh_keys)").fetchall, call:conn.execute("PRAGMA table_info(saved_tasks)").fetchall, call:conn.execute("PRAGMA table_info(dashboard_widgets)").fetchall, call:conn.execute("PRAGMA table_info(backup_jobs)").fetchall, method:_row_to_machine(self, row: sqlite3.Row) → dict[str, Any], call:json.loads, call:self._normalize_services, call:data.get, call:bool, call:int, method:_normalize_machine_payload(self, payload: dict[str, Any], machine_id) → dict[str, Any], call:self.get_machine, call:str(payload.get("id") or machine_id or uuid.uuid4().hex[:12]).strip, call:payload.get, call:uuid.uuid4, call:str(payload.get("mode") or (current or {}).get("mode") or "local").strip().lower, call:(current or {}).get, call:bool, call:str(payload.get("name") or (current or {}).get("name") or "").strip, call:self._normalize_services, call:str( payload.get(field) if payload.get(field) is not None else (current or {}).get(field, default) or default ).strip, call:_current_str, call:int, method:_seed_local_machine(self) → None, call:_default_local_machine, call:int, call:time.time, call:machine.get, call:self.connect, call:conn.execute, call:json.dumps, method:_seed_dashboard_widgets(self) → None, method:ensure_defaults(self) → None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT COUNT(*) FROM monitoring_machines").fetchone, call:int, call:self._seed_local_machine, call:self._migrate_jellyseerr_into_jellyfin, call:self._migrate_jellyseerr_api_key_to_secret, method:_migrate_jellyseerr_api_key_to_secret(self) → None, call:self.init_schema, call:self.list_services, call:dict, call:row.get, call:str(config.get("jellyseerr_api_key") or "").strip, call:config.get, call:encrypt_value, call:config.pop, call:self.connect, call:conn.execute, call:json.dumps, call:int, call:time.time, call:conn.commit, call:logger.info, method:_migrate_jellyseerr_into_jellyfin(self) → None, call:self.init_schema, call:self.connect, call:conn.execute( "SELECT * FROM services WHERE service_type = 'jellyseerr' ORDER BY name ASC" ).fetchall, call:self.list_services, call:json.loads, call:str(js_config.get("base_url", "")).strip, call:js_config.get, call:str(js_secrets.get("api_key", "")).strip, call:js_secrets.get, call:decrypt_value, call:logger.warning, call:len, call:str(jf["config"].get("jellyseerr_url", "")).strip, call:jf["config"].get, call:target["secrets"].get, call:dict, call:self.upsert_service, call:logger.info, call:conn.commit, method:list_machines(self) → list[dict[str, Any]], call:self.init_schema, call:self.connect, call:conn.execute( "SELECT * FROM monitoring_machines ORDER BY CASE WHEN id = ? THEN 0 ELSE 1 END, name COLLATE NOCASE", (LOCAL_MACHINE_ID,), ).fetchall, call:self._row_to_machine, method:get_machine(self, machine_id: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM monitoring_machines WHERE id = ?", (machine_id,)).fetchone, call:self._row_to_machine, method:get_machine_config(self, machine_id: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM monitoring_machines WHERE id = ?", (machine_id,)).fetchone, call:json.loads, call:bool, call:self._normalize_services, call:data.get, call:int, method:list_machines_for_service(self, service: str) → list[dict[str, Any]], call:self.list_machines, call:machine.get, method:get_machine_for_service(self, service: str, machine_id) → dict[str, Any] | None, call:self.get_machine, call:machine.get, call:self.list_machines_for_service, method:upsert_machine(self, payload: dict[str, Any], machine_id) → dict[str, Any], call:self.init_schema, call:self._normalize_machine_payload, call:int, call:time.time, call:machine.get, call:self.connect, call:conn.execute( "SELECT created_at FROM monitoring_machines WHERE id = ?", (machine["id"],), ).fetchone, call:json.dumps, call:self.get_machine, method:delete_machine(self, machine_id: str) → None, call:self.init_schema, call:self.connect, call:conn.execute, method:_row_to_ssh_key(self, row: sqlite3.Row, usage_count) → dict[str, Any], call:self._private_key_summary, call:str, call:bool, method:_normalize_ssh_key_payload(self, payload: dict[str, Any], key_id) → dict[str, Any], call:self.get_ssh_key, call:str(payload.get("id") or key_id or uuid.uuid4().hex[:12]).strip, call:payload.get, call:uuid.uuid4, call:str(payload.get("name") or (current or {}).get("name") or key_id).strip, call:(current or {}).get, call:str( payload.get("notes") if payload.get("notes") is not None else (current or {}).get("notes", "") or "" ).strip, call:self._private_key_summary, call:str( payload.get("public_key") if payload.get("public_key") is not None else (current or {}).get("public_key", "") or summary["public_key"] or "" ).strip, call:str( payload.get("fingerprint") if payload.get("fingerprint") is not None else (current or {}).get("fingerprint", "") or summary["fingerprint"] or "" ).strip, method:list_ssh_keys(self) → list[dict[str, Any]], call:self.init_schema, call:self.list_machines, call:str(machine.get("ssh_key_id") or "").strip, call:machine.get, call:usage_counts.get, call:self.connect, call:conn.execute("SELECT * FROM ssh_keys ORDER BY name COLLATE NOCASE").fetchall, call:self._row_to_ssh_key, method:get_ssh_key(self, key_id: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM ssh_keys WHERE id = ?", (key_id,)).fetchone, call:self._private_key_summary, call:str, method:upsert_ssh_key(self, payload: dict[str, Any], key_id) → dict[str, Any], call:self.init_schema, call:self._normalize_ssh_key_payload, call:int, call:time.time, call:self.connect, call:conn.execute("SELECT created_at FROM ssh_keys WHERE id = ?", (key["id"],)).fetchone, call:self.get_ssh_key, method:delete_ssh_key(self, key_id: str) → None, call:self.init_schema, call:self.connect, call:conn.execute, method:_row_to_task(self, row: sqlite3.Row) → dict[str, Any], call:bool, method:_normalize_task_payload(self, payload: dict[str, Any], task_id) → dict[str, Any], call:self.get_task, call:str(payload.get("id") or task_id or uuid.uuid4().hex[:12]).strip, call:payload.get, call:uuid.uuid4, call:str(payload.get("name") or (current or {}).get("name") or task_id).strip, call:(current or {}).get, call:str(payload.get("task_type") or (current or {}).get("task_type") or "shell").strip().lower, call:bool, call:str( payload.get("default_service_id") if payload.get("default_service_id") is not None else (current or {}).get("default_service_id", "") or "" ).strip, call:str( payload.get("notes") if payload.get("notes") is not None else (current or {}).get("notes", "") or "" ).strip, method:list_tasks(self) → list[dict[str, Any]], call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM saved_tasks ORDER BY name COLLATE NOCASE").fetchall, call:self._row_to_task, method:get_task(self, task_id: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM saved_tasks WHERE id = ?", (task_id,)).fetchone, call:self._row_to_task, method:upsert_task(self, payload: dict[str, Any], task_id) → dict[str, Any], call:self.init_schema, call:self._normalize_task_payload, call:int, call:time.time, call:self.connect, call:conn.execute("SELECT created_at FROM saved_tasks WHERE id = ?", (task["id"],)).fetchone, call:self.get_task, method:delete_task(self, task_id: str) → None, call:self.init_schema, call:self.connect, call:conn.execute, method:_row_to_shortcut(self, row: sqlite3.Row) → dict[str, Any], call:json.loads, call:bool, call:target.get, method:_normalize_shortcut_payload(self, payload: dict[str, Any], shortcut_id) → dict[str, Any], call:self.get_shortcut, call:str(payload.get("id") or shortcut_id or uuid.uuid4().hex[:12]).strip, call:payload.get, call:uuid.uuid4, call:str(payload.get("shortcut_type") or (current or {}).get("shortcut_type") or "website").strip().lower, call:(current or {}).get, call:str(payload.get("label") or (current or {}).get("label") or "").strip, call:bool, call:str( payload.get(field) if payload.get(field) is not None else (current or {}).get(field, default) or default ).strip, call:_field, method:list_shortcuts(self) → list[dict[str, Any]], call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM dashboard_shortcuts ORDER BY label COLLATE NOCASE").fetchall, call:self._row_to_shortcut, method:get_shortcut(self, shortcut_id: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM dashboard_shortcuts WHERE id = ?", (shortcut_id,)).fetchone, call:self._row_to_shortcut, method:upsert_shortcut(self, payload: dict[str, Any], shortcut_id) → dict[str, Any], call:self.init_schema, call:self._normalize_shortcut_payload, call:int, call:time.time, call:self.connect, call:conn.execute( "SELECT created_at FROM dashboard_shortcuts WHERE id = ?", (shortcut["id"],), ).fetchone, call:json.dumps, call:self.get_shortcut, method:delete_shortcut(self, shortcut_id: str) → None, call:self.init_schema, call:self.connect, call:conn.execute, method:_row_to_job(self, row: sqlite3.Row) → dict[str, Any], method:_normalize_backup_job_payload(self, payload: dict[str, Any], job_id) → dict[str, Any], call:self.get_backup_job, call:str(payload.get("id") or job_id or uuid.uuid4().hex[:12]).strip, call:payload.get, call:uuid.uuid4, call:str(payload.get("name") or (current or {}).get("name") or job_id).strip, call:(current or {}).get, call:str( payload.get("source") if payload.get("source") is not None else (current or {}).get("source", "") or "" ).strip, call:str( payload.get("target") if payload.get("target") is not None else (current or {}).get("target", "") or "" ).strip, call:int, call:str( payload.get("service_id") if payload.get("service_id") is not None else (current or {}).get("service_id", "") or "" ).strip, method:get_backup_job_by_name(self, name: str) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM backup_jobs WHERE name = ?", (name,)).fetchone, call:self._row_to_job, method:upsert_backup_job(self, payload: dict[str, Any]) → dict[str, Any], call:self.init_schema, call:self._normalize_backup_job_payload, call:int, call:time.time, call:self.connect, call:conn.execute("SELECT created_at FROM backup_jobs WHERE id = ?", (job["id"],)).fetchone, call:self.get_backup_job, method:get_backup_job(self, job_id: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM backup_jobs WHERE id = ?", (job_id,)).fetchone, call:self._row_to_job, method:list_backup_jobs(self, service_id) → list[dict[str, Any]], call:self.init_schema, call:params.append, call:self.connect, call:conn.execute(sql, params).fetchall, call:self._row_to_job, method:_row_to_run(self, row: sqlite3.Row) → dict[str, Any], call:json.loads, method:create_backup_run(self, payload: dict[str, Any]) → dict[str, Any], call:self.init_schema, call:str(payload.get("id") or uuid.uuid4().hex[:12]).strip, call:payload.get, call:uuid.uuid4, call:int, call:time.time, call:json.dumps, call:self.connect, call:conn.execute, call:self.get_backup_run, method:get_backup_run(self, run_id: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM backup_runs WHERE id = ?", (run_id,)).fetchone, call:self._row_to_run, method:list_backup_runs(self, job_id, status, limit, service_id) → list[dict[str, Any]], call:self.init_schema, call:clauses.append, call:params.append, call:' AND '.join, call:max, call:min, call:int, call:self.connect, call:conn.execute(sql, params).fetchall, call:self._row_to_run, method:get_latest_backup_run(self, job_id: str) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute( "SELECT * FROM backup_runs WHERE job_id = ? ORDER BY created_at DESC LIMIT 1", (job_id,), ).fetchone, call:self._row_to_run, method:_row_to_alert(self, row: sqlite3.Row) → dict[str, Any], call:bool, method:create_backup_alert(self, payload: dict[str, Any]) → dict[str, Any], call:self.init_schema, call:str(payload.get("id") or uuid.uuid4().hex[:12]).strip, call:payload.get, call:uuid.uuid4, call:int, call:time.time, call:self.connect, call:conn.execute, call:self.get_backup_alert, method:get_backup_alert(self, alert_id: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM backup_alerts WHERE id = ?", (alert_id,)).fetchone, call:self._row_to_alert, method:list_backup_alerts(self, job_id, acknowledged, severity, service_id) → list[dict[str, Any]], call:self.init_schema, call:clauses.append, call:params.append, call:' AND '.join, call:self.connect, call:conn.execute(sql, params).fetchall, call:self._row_to_alert, method:acknowledge_backup_alert(self, alert_id: str) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute, call:self.get_backup_alert, method:resolve_backup_alerts_for_job(self, job_id: str, alert_type: str) → int, call:self.init_schema, call:int, call:time.time, call:self.connect, call:conn.execute, method:prune_backup_alerts(self, cutoff_ts: int) → int, call:self.init_schema, call:self.connect, call:conn.execute, call:int, method:get_settings(self) → dict[str, Any], call:self.init_schema, call:self.connect, call:conn.execute("SELECT key, value FROM app_settings").fetchall, method:get_setting(self, key: str, default) → Any, call:self.init_schema, call:self.connect, call:conn.execute("SELECT value FROM app_settings WHERE key = ?", (key,)).fetchone, method:update_setting(self, key: str, value: str) → None, call:self.init_schema, call:int, call:time.time, call:self.connect, call:conn.execute, method:_row_to_widget(self, row: sqlite3.Row) → dict[str, Any], call:row.keys, call:json.loads, call:bool, call:int, method:_normalize_widget_payload(self, payload: dict[str, Any], widget_id) → dict[str, Any], call:self.get_widget, call:str(payload.get("id") or widget_id or uuid.uuid4().hex[:12]).strip, call:payload.get, call:uuid.uuid4, call:str(payload.get("service_id") or (current or {}).get("service_id") or "").strip, call:(current or {}).get, call:str(payload.get("widget_kind") or (current or {}).get("widget_kind", "")).strip, call:str(payload.get("title") or (current or {}).get("title", "") or "").strip, call:isinstance, call:_validate_config_keys, call:bool, call:int, method:list_widgets(self, service_id, scope, all_widgets) → list[dict[str, Any]], call:self.init_schema, call:clauses.append, call:params.append, call:' AND '.join, call:self.connect, call:conn.execute( f"SELECT * FROM dashboard_widgets{where} ORDER BY sort_order ASC, created_at ASC", params, ).fetchall, call:self._row_to_widget, method:get_widget(self, widget_id: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM dashboard_widgets WHERE id = ?", (widget_id,)).fetchone, call:self._row_to_widget, method:upsert_widget(self, payload: dict[str, Any], widget_id) → dict[str, Any], call:self.init_schema, call:self._normalize_widget_payload, call:int, call:time.time, call:self.connect, call:conn.execute( "SELECT created_at FROM dashboard_widgets WHERE id = ?", (widget["id"],), ).fetchone, call:json.dumps, call:self.get_widget, method:delete_widget(self, widget_id: str) → None, call:self.init_schema, call:self.connect, call:conn.execute, method:list_widget_references(self, dashboard_scope: str) → list[dict[str, Any]], call:self.init_schema, call:self.connect, call:conn.execute( """ SELECT wr.id AS ref_id, wr.dashboard_scope, wr.widget_id, wr.sort_order, wr.created_at AS ref_created_at FROM widget_references wr WHERE wr.dashboard_scope = ? ORDER BY wr.sort_order ASC, wr.created_at ASC """, (dashboard_scope,), ).fetchall, call:self.get_widget, call:result.append, call:int, method:create_widget_reference(self, dashboard_scope: str, widget_id: str, sort_order) → dict[str, Any], call:self.init_schema, call:self.get_widget, call:uuid.uuid4, call:int, call:time.time, call:self.connect, call:conn.execute, raise:ValueError, method:delete_widget_reference(self, reference_id: str) → None, call:self.init_schema, call:self.connect, call:conn.execute, method:update_widget_reference(self, reference_id: str, sort_order: int) → dict[str, Any], call:self.init_schema, call:self.connect, call:conn.execute( "SELECT * FROM widget_references WHERE id = ?", (reference_id,), ).fetchone, call:self.get_widget, call:int, raise:ValueError, method:detach_widget_reference(self, reference_id: str, dashboard_scope: str) → dict[str, Any], call:self.init_schema, call:self.connect, call:conn.execute( "SELECT widget_id FROM widget_references WHERE id = ?", (reference_id,), ).fetchone, call:self.get_widget, call:self.upsert_widget, call:source.get, call:self.delete_widget_reference, raise:ValueError, method:_row_to_service(self, row: sqlite3.Row) → dict[str, Any], call:json.loads, call:bool, method:list_services(self, service_type) → list[dict[str, Any]], call:self.init_schema, call:self.connect, call:conn.execute( "SELECT * FROM services WHERE service_type = ? ORDER BY name ASC", (service_type,), ).fetchall, call:conn.execute("SELECT * FROM services ORDER BY name ASC").fetchall, call:self._row_to_service, method:get_service(self, service_id: str) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM services WHERE id = ?", (service_id,)).fetchone, call:self._row_to_service, method:_normalize_service_payload(self, payload: dict[str, Any], service_id) → dict[str, Any], call:self.get_service, call:str(payload.get("id") or service_id or uuid.uuid4().hex[:12]).strip, call:payload.get, call:uuid.uuid4, call:str(payload.get("service_type") or (current or {}).get("service_type", "")).strip, call:(current or {}).get, call:str(payload.get("name") or (current or {}).get("name", "") or "").strip, call:isinstance, call:bool, method:upsert_service(self, payload: dict[str, Any], secret_values, service_id) → dict[str, Any], call:self.init_schema, call:self._normalize_service_payload, call:int, call:time.time, call:self.get_service, call:dict, call:secret_values.items, call:secrets_blob.pop, call:encrypt_value, call:self.connect, call:conn.execute, call:json.dumps, method:delete_service(self, service_id: str) → None, call:self.init_schema, call:self.connect, call:conn.execute("PRAGMA table_info(dashboard_widgets)").fetchall, call:get_service_data_harness().cascade_delete, call:logger.exception, method:record_service_task_run(self, payload: dict[str, Any]) → dict[str, Any], call:self.init_schema, call:str, call:payload.get, call:uuid.uuid4, call:int, call:time.time, call:self.connect, call:conn.execute, method:list_service_task_runs(self, service_id, task_id, limit) → list[dict[str, Any]], call:self.init_schema, call:clauses.append, call:params.append, call:" AND ".join, call:int, call:self.connect, call:conn.execute( f"SELECT * FROM service_task_runs {where} ORDER BY created_at DESC LIMIT ?", params, ).fetchall, method:_unique_slug(self, slug: str, exclude_id) → str, call:self.init_schema, call:self.connect, call:conn.execute( "SELECT id FROM named_dashboards WHERE slug = ? AND id != ?", (slug, exclude_id or ""), ).fetchone, method:_row_to_dashboard(self, row: sqlite3.Row) → dict[str, Any], call:json.loads, method:list_dashboards(self) → list[dict[str, Any]], call:self.init_schema, call:self.connect, call:conn.execute( "SELECT * FROM named_dashboards ORDER BY sort_order ASC, label COLLATE NOCASE" ).fetchall, call:self._row_to_dashboard, method:get_dashboard(self, dashboard_id: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM named_dashboards WHERE id = ?", (dashboard_id,)).fetchone, call:self._row_to_dashboard, method:get_dashboard_by_slug(self, slug: str | None) → dict[str, Any] | None, call:self.init_schema, call:self.connect, call:conn.execute("SELECT * FROM named_dashboards WHERE slug = ?", (slug,)).fetchone, call:self._row_to_dashboard, method:upsert_dashboard(self, payload: dict[str, Any], dashboard_id) → dict[str, Any], call:self.init_schema, call:self.get_dashboard, call:str(payload.get("id") or dashboard_id or uuid.uuid4().hex[:12]).strip, call:payload.get, call:uuid.uuid4, call:str(payload.get("label") or (current or {}).get("label") or "Dashboard").strip, call:(current or {}).get, call:str(payload.get("slug") or "").strip, call:self._slugify, call:self._unique_slug, call:int, call:time.time, call:self.connect, call:conn.execute("SELECT created_at FROM named_dashboards WHERE id = ?", (dash_id,)).fetchone, call:json.dumps, method:delete_dashboard(self, dashboard_id: str) → None, call:self.init_schema, call:self.connect, call:conn.execute, func:_default_local_machine() → dict[str, Any], call:list, func:get_settings_store() → SettingsStore, call:SettingsStore | dep: json, logging, sqlite3, time, uuid, io, pathlib, typing, paramiko, media_library_viewer_api.models.widgets
- targets.py | Builds a Prometheus HTTP service discovery target list of remote Node Exporter endpoints from configured SSH machines. | exp: func:_scrape_address(machine: dict[str, Any]) → str | None, call:machine.get, call:str(machine.get("node_exporter_scrape_host") or "").strip, call:str(machine.get("host") or "").strip, call:int, func:build_node_exporter_targets(store: SettingsStore) → list[dict[str, Any]], call:store.list_machines, call:machine.get, call:str(machine.get("mode") or "local").strip().lower, call:_scrape_address, call:targets.append | dep: logging, typing, media_library_viewer_api.services.settings_store
- task_runner.py | Provides a unified execution path for running saved tasks over SSH against ssh_tasks service instances, including client building, command rendering, execution, and run logging. | exp: class:TaskRunResult, func:build_ssh_client(store: SettingsStore, service: "ServiceRecord") → RemoteSSHClient, call:str(config.get("host") or "").strip, call:config.get, call:str(config.get("username") or "").strip, call:get_settings, call:str(config.get("ssh_key_id") or "").strip, call:store.get_ssh_key, call:ssh_key.get, call:service.secrets.get, call:RemoteSSHClient, call:int, raise:ValueError, func:_render_command(task: dict[str, Any]) → str, call:str(task.get("task_type") or "shell").lower, call:task.get, call:shlex.quote, raise:ValueError, func:run_saved_task(store: SettingsStore, task: dict[str, Any], service: "ServiceRecord", timeout) → TaskRunResult, call:int, call:service.config.get, call:build_ssh_client, call:_render_command, call:time.perf_counter, call:client.run, call:_record, call:str, call:logger.exception, call:task.get, call:TaskRunResult, func:_record(store: SettingsStore, task: dict[str, Any], service: "ServiceRecord", status: str, exit_status, duration_ms, stdout_tail, stderr_tail, error) → None, call:store.record_service_task_run, call:str, call:task.get | dep: logging, shlex, time, dataclasses, typing, media_library_viewer_api.clients.ssh, media_library_viewer_api.config, media_library_viewer_api.services.settings_store, media_library_viewer_api.widgets.sources, RemoteSSHClient, get_settings, SettingsStore, ServiceRecord
arch
Mix of singleton service harnesses, background daemon threads, SQLite-backed stores with facade re-export modules, and synchronous utility helpers.
tags
call:conn.execute, call:str, call:self.connect, schema, call:self., call:self.init, call:int, call:
symbols
- BackupAlertPoller
- QueuedEmailMessage
- MailQueue
- EmailAttachment
- _HTMLToTextParser
- MediaIndexBuildCancelled
- MediaIndexStatus
- MediaIndex
workflows
- change services behavior read: __init__.py, backup_alert_engine.py, backup_poller.py