Files
media-player-server/RELEASE_NOTES.md
T
alexei.dolgolyov 527f3d0aa4
Lint & Test / test (push) Has been skipped
Release / create-release (push) Successful in 4s
Release / build-linux (push) Successful in 44s
Release / build-windows (push) Successful in 1m9s
chore: release v0.2.5
2026-05-16 20:16:45 +03:00

66 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## v0.2.5 (2026-05-16)
### Security
- **Loopback-by-default + auto-generated token:** Server now binds `127.0.0.1` by default; first-run bootstrap generates a random `api_token` and refuses to bind a non-loopback interface without auth unless explicitly opted in. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Browser path-traversal hardening:** `BrowserService.validate_path` now rejects absolute paths, drive letters, UNC paths, and NUL bytes. `/api/browser/{play,metadata,thumbnail}` require a `folder_id` plus a folder-relative path — arbitrary filesystem reads via the browser API are no longer possible. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Strict input validation on links/scripts:** Pydantic validators reject non-http(s) URLs and any icon outside the `mdi:<slug>` namespace. Create/update/delete on scripts, callbacks, and links is gated by the corresponding `*_management` flags. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Hardened response headers + CORS:** Strict `Content-Security-Policy`, `X-Frame-Options: DENY`, `Referrer-Policy: no-referrer`, `X-Content-Type-Options: nosniff`. CORS locked to `localhost:<port>` + `127.0.0.1:<port>` by default; configurable for trusted origins. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Atomic config writes with restrictive permissions:** `config.yaml` writes go through a temp file + `os.replace` and land with `0o600` on POSIX, so a crash mid-write can never leave a half-written token on disk readable to other users. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Subprocess process-group isolation:** Spawned scripts/callbacks now get their own process group (`CREATE_NEW_PROCESS_GROUP` on Windows, `start_new_session=True` on POSIX), so a timeout actually kills the whole tree instead of orphaning child processes. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Frontend XSS hardening:** Monitor name + details are `escapeHtml`'d, the power button moved to a delegated `data-action` handler, and remote MDI SVGs are parsed and sanitized (strip `<script>`, `<foreignObject>`, `on*` handlers, `javascript:` hrefs) before they touch `innerHTML`. All dynamic URL segments now go through `encodeURIComponent`. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **CSP-compliant event wiring:** Strict `script-src 'self'` was blocking every inline `onclick`/`onchange`/`oninput`/`onsubmit` in the UI, leaving buttons and forms silently dead. All 53 inline handler attributes in `index.html` were renamed to `data-on*` and a new `wireInlineHandlers()` in `app.js` parses each expression on `DOMContentLoaded` and attaches a real `addEventListener` — supports no-arg calls, string/number/bool/null literals, and the `event` token. No `unsafe-inline` or `unsafe-hashes` needed. ([eaeebb6](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/eaeebb6))
### Bug Fixes
- **WebSocket reconnect robustness:** Close the previous socket before opening a new one, clear the ping interval per-socket, clear `reconnectTimeout` up-front, retry on `online`/`visibilitychange`, and wrap `JSON.parse` in try/catch — eliminates the stale-socket leaks and "stuck offline after sleep" cases. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Artwork fetch race:** `AbortController` + generation guard so a rapid track change can no longer paint the previous track's artwork over the current one. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Audio analyzer no longer spins infinitely without a loopback device:** A sticky `_unavailable` flag short-circuits start/stop; cleared by `set_device()` so the user can recover once a device appears. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Volume short-circuit cache invalidation:** Cache is now busted when the server reports a remote volume change, so the UI no longer ignores volume updates that happened outside the app. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Browser thumbnail race:** Per-folder generation counter + `isConnected` checks; in-flight fetches are aborted on navigation, so thumbnails from a folder you already left can't paint into the current view. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Track-skip uses cached title** instead of a full WinRT status round-trip — skip feedback is now instant. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Browser list column alignment:** `.browser-list` switched to CSS grid + subgrid so header and rows share column tracks, eliminating the misaligned columns when content widths differed between rows. Matching responsive column overrides applied at the parent. Root-folder SVG sizing (hardcoded 24×24 in `browser.js`) now fills the 56px icon box instead of rendering at ~43%. Compact-grid icon fills its thumb wrapper so the emoji centers instead of being stranded top-left. Premature `isConnected` bail removed from `loadThumbnail` — the img element is intentionally detached when called from `renderBrowserGrid/List`, and the post-await checks already handle navigation-away correctly. ([982dda4](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/982dda4))
### Performance
- **Blocking IO off the event loop:** Linux MPRIS/`pactl` calls, `/api/display` DDC/CI handlers, and `browse_directory` are all wrapped in `asyncio.to_thread` — slow SMB shares or laggy monitors can no longer stall the entire async runtime. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Windows status poll loop reuse:** The 0.5s status poll now caches one asyncio loop per worker thread via `threading.local` instead of `new_event_loop`/`close` on every tick. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **WebSocket broadcast: serialize once:** `broadcast()` serializes JSON a single time and uses `send_text` to fan out to all clients. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Thumbnail cache cleanup actually runs:** The hourly cleanup task was defined but never scheduled — it is now wired into the lifespan handler so the cache no longer grows unbounded. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Progress drag listeners attached only while dragging** — no more global `mousemove` handler firing on every cursor twitch. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
### UI/UX
- **Copper accent consistency:** Green leftover focus rings (`rgba(29,185,84,…)`) replaced with copper (`rgba(var(--copper-rgb),…)`) across the UI. Dialogs now have square corners and a copper top hairline so they read as part of the editorial chrome. `.browser-item` is transparent with a copper hover border (was a filled card). ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Audio device select** uses `var(--sans)` instead of the generic system font so it matches surrounding controls. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Mobile padding tuned for ≤480px screens.** ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **Accessible breadcrumb home:** Now a real `<button>` with `aria-label`, and `aria-current` is set on the root. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- **i18n gaps filled:** `display.msg.power_*`, `execution.*`, `scripts.params.execute`, `callbacks.empty` now have proper en + ru strings. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
---
### Development / Internal
#### Quality
- All `asyncio.get_event_loop()` in coroutines migrated to `get_running_loop()` (the former is deprecated in Python 3.12+). ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- `ThreadPoolExecutor`s now shut down cleanly during lifespan teardown. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- `config_manager` dedup: 12 near-identical CRUD methods collapsed onto generic `_upsert`/`_delete` helpers — about **290 lines removed** with no behavior change. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- Service worker no longer pass-throughs every fetch. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- M3U playlist written via `NamedTemporaryFile` so a fixed-path symlink can no longer clobber it. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- `__version__` prefers live `pyproject.toml` in dev checkouts so `pip install -e .` users see the source-of-truth version, not the stale metadata baked in at install time. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
- `_broadcast_after_open` hardening: initialize status, swallow per-poll errors, and track background tasks in a strong-ref set with done-callback cleanup so they aren't garbage-collected mid-flight. ([bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40))
---
<details>
<summary>All Commits</summary>
| Hash | Message | Author |
|------|---------|--------|
| [982dda4](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/982dda4) | fix(browser): align list columns via subgrid and fix icon sizing | alexei.dolgolyov |
| [eaeebb6](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/eaeebb6) | fix(csp): replace inline on* handlers with data-on* + JS wiring | alexei.dolgolyov |
| [bcc6d40](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/bcc6d40) | fix: comprehensive security, bug, performance, and UI/UX audit | alexei.dolgolyov |
</details>