Files
media-player-server/RELEASE_NOTES.md
T
alexei.dolgolyov b023d72165
Lint & Test / test (push) Has been skipped
Release / create-release (push) Successful in 4s
Release / build-linux (push) Successful in 1m29s
Release / build-windows (push) Successful in 1m42s
chore: release v0.3.0
2026-05-22 22:41:11 +03:00

8.5 KiB

v0.3.0 (2026-05-22)

Production-readiness hardening release: security, performance, accessibility, and observability. Substantial new functionality (HTTPS, audit log, OS mediaSession integration, rate limiter, X-Request-ID, ETag-cached artwork) alongside the security defaults flip described below.

Behavioral Changes (worth reading before upgrade)

  • Admin scope is now required for management endpoints, and scripts_management, callbacks_management, links_management, media_folders_management default to False. Legacy bare-string api_tokens entries are auto-promoted to admin scope, so existing single-token deployments keep working. If you ran with a non-admin token and used CRUD on /api/scripts, /api/callbacks, /api/links, or /api/media-folders, you'll need an admin-scope token (see new TokenSpec format in config.example.yaml). (d131ba4)
  • cors_origins: ["*"] is now refused at startup — set explicit origins instead. (d131ba4)
  • Thumbnail cache directory moved from project-root .cache to %LOCALAPPDATA%/media-server/cache on Windows and $XDG_CACHE_HOME/media-server/thumbnails on POSIX. The old .cache directory can be deleted. (d131ba4)
  • WebSocket auth prefers the Sec-WebSocket-Protocol: media-server.token.<T> subprotocol so the token no longer ends up in URL/history/Referer. The ?token= query fallback is retained for HA integration back-compat. (d131ba4)

Features

  • HTTPS support via ssl_certfile + ssl_keyfile (+ optional ssl_keyfile_password); startup refuses to launch with only one of the pair set. (d131ba4)
  • Reverse-proxy support: proxy_headers + forwarded_allow_ips plumbed through Settings to uvicorn.Config. (d131ba4)
  • OS media session integration: headset / lockscreen / Bluetooth media-key buttons now drive play/pause/next/prev/seek and the browser-level mediaSession shows track metadata + artwork. (d131ba4)
  • Token scope hierarchy (read | control | admin) with structured TokenSpec entries; legacy bare-string tokens promote to admin. (d131ba4)
  • In-process token-bucket rate limiter: 5/min for failed auths, 10/min for /api/scripts/execute and /api/callbacks/execute. (d131ba4)
  • WebSocket Origin allow-list check (CSWSH defence). (d131ba4)
  • Script parameter validation: per-parameter pattern regex in ScriptParameterConfig plus shell=False (shlex.split) execution path to harden against parameter injection on cmd.exe. (d131ba4)
  • CSP tightened with form-action, worker-src, manifest-src directives. (d131ba4)
  • noopener noreferrer + no-referrer referrerpolicy applied to every outbound link in the WebUI. (d131ba4)
  • Windows config.yaml ACL hardening via icacls (current user + SYSTEM + Administrators only); 0600 continues to be enforced on POSIX. (d131ba4)
  • PWA installability: manifest.json gets id, scope, and theme_color / background_color matching the Studio Reference base (#0E0D0B). (d131ba4)
  • Accessibility: ARIA labels on mini-player icon buttons; inner SVGs marked aria-hidden. (d131ba4)

Performance

  • Album-art read in windows_media gated by track key — was decoding the WinRT thumbnail twice per second regardless of track changes. (d131ba4)
  • /api/media/artwork returns content-derived ETag + Cache-Control so the browser sends If-None-Match and gets 304 on track repeats. (d131ba4)
  • Foreground-service ctypes argtypes hoisted to one-time module init — was re-declaring ~14 prototypes per probe. (d131ba4)
  • display_service._static_cache keyed by (edid_hash, ...) tuple with eviction of disappeared monitors — fixes stale capabilities on hot-plug swaps where the new topology has the same monitor count. (d131ba4)
  • Visualizer requestAnimationFrame loop paused on document.hidden, resumed on visible. (d131ba4)

Bug Fixes

  • Lifespan rewritten as try / yield / finally so a partial-startup failure cannot orphan background tasks or executors. (d131ba4)
  • _run_callback in routes/media.py keeps a strong task reference (GC-safe) and uses the dedicated callback executor instead of the default pool. (d131ba4)
  • macos_media.set_volume() no longer always returns True regardless of the underlying AppleScript result. (d131ba4)
  • TrayManager._restart_requested initialised in __init__ and set before signalling exit so the main thread observes it correctly. (d131ba4)
  • Missing static_dir now logs a WARNING instead of silently disabling the UI. (d131ba4)
  • WebSocket volume handler clamps input and never drops the socket on bad messages. (d131ba4)
  • Gitea release tag validated against a strict SemVer regex before being used in a release URL. (d131ba4)

Observability

  • X-Request-ID middleware — accepts an upstream id if it matches a safe regex, otherwise generates a UUID4. request_id_var added to ContextVars and included in every log line alongside the token label. (d131ba4)
  • Append-only JSONL audit log for every script + callback execution (including on_play / on_pause / etc. event callbacks). Background-thread writer; queue capped; flushed in lifespan teardown. (d131ba4)
  • token=... stripped from uvicorn access logs. (d131ba4)

Development / Internal

Tests

  • 35 new tests across auth scopes, the rate limiter, browser path traversal (../, NUL, UNC, absolute paths), script-parameter validation including the regex, the Gitea tag whitelist, and atomic config writes + POSIX permissions. Suite: 47 passed / 4 skipped. (d131ba4)

All Commits
Hash Message Author
d131ba4 fix: production-readiness hardening — security, perf, a11y, observability alexei.dolgolyov