Compare commits

...

5 Commits

Author SHA1 Message Date
alexei.dolgolyov 26b4672a99 chore: release v0.1.6
Release / create-release (push) Successful in 3s
Lint & Test / test (push) Successful in 10s
Release / build-linux (push) Successful in 31s
Release / build-windows (push) Successful in 52s
2026-04-11 03:36:55 +03:00
alexei.dolgolyov 2e3bebfeb8 chore: release v0.1.5
Release / create-release (push) Successful in 10s
Lint & Test / test (push) Successful in 15s
Release / build-linux (push) Successful in 48s
Release / build-windows (push) Successful in 1m10s
2026-04-11 02:09:29 +03:00
alexei.dolgolyov 34eb7c7b19 fix(ws): make WebSocket token parameter optional
Required token query param caused connection failures for clients
that authenticate via other means.
2026-04-11 02:04:36 +03:00
alexei.dolgolyov 972ee54b91 chore: release v0.1.5
Release / create-release (push) Successful in 3s
Lint & Test / test (push) Successful in 10s
Release / build-linux (push) Successful in 53s
Release / build-windows (push) Successful in 1m17s
2026-04-11 01:43:43 +03:00
alexei.dolgolyov d09a0b90e4 fix(ws): fetch status eagerly on new WebSocket connection
Instead of waiting for the next poll cycle, new clients now get the
current playback status immediately on connect by calling get_status_func
if no cached status is available yet.
2026-04-11 01:40:40 +03:00
7 changed files with 28 additions and 37 deletions
+4 -30
View File
@@ -1,35 +1,9 @@
## v0.1.4 (2026-04-07) ## v0.1.6 (2026-04-11)
### Bug Fixes ### Bug Fixes
- Prevent dialog `showModal` from auto-focusing first input ([db777fa](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/db777fa)) - Bundle `wmi` explicitly in the Windows distribution. It's a transitive dependency of `screen-brightness-control` gated on `platform_system == "Windows"`, but pip evaluates environment markers against the host (Linux in CI), so it was silently skipped during cross-build. Now listed explicitly in both `pyproject.toml` and `build-dist-windows.sh` so the wheel lands in the Windows bundle.
--- ---
### Development / Internal ### Contributors
- @alexei.dolgolyov — 1 change
#### CI/Build
- Replace `uvicorn[standard]` with explicit Windows-safe extras; avoids uvloop cross-build deadlock ([28293c6](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/28293c6))
- Move `pystray` into the unified cross-deps resolve so its Pillow matches the core one ([8450040](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/8450040))
- Hybrid pip-download: single call for cross-platform deps (consistent `pydantic-core`), per-dep loop with `--pre` for Windows-only ([39b3aed](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/39b3aed))
- Normalize non-PEP440 versions before stamping `pyproject.toml` ([69df9b6](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/69df9b6))
- Pass `--pre` to `pip download` for winsdk beta wheels ([760c3df](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/760c3df))
- Revert broken action caching (Gitea cache backend not configured) ([60f287b](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/60f287b))
---
<details>
<summary>All Commits</summary>
| Hash | Message | Author |
|------|---------|--------|
| [8450040](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/8450040) | fix(ci): move pystray to VIS_DEPS so its Pillow resolves with core | alexei.dolgolyov |
| [28293c6](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/28293c6) | fix(ci): replace uvicorn[standard] with explicit extras for cross-build | alexei.dolgolyov |
| [39b3aed](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/39b3aed) | fix(ci): hybrid pip download - single call for cross-platform deps | alexei.dolgolyov |
| [ba90dff](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/ba90dff) | fix(ci): revert to per-dep pip download loop with --pre | alexei.dolgolyov |
| [69df9b6](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/69df9b6) | fix(ci): normalize non-PEP440 versions before stamping pyproject.toml | alexei.dolgolyov |
| [760c3df](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/760c3df) | fix(ci): pass --pre to pip download for winsdk beta wheels | alexei.dolgolyov |
| [60f287b](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/60f287b) | ci: revert action caching, gitea cache backend not configured | alexei.dolgolyov |
| [f52af51](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/f52af51) | ci: cache pip wheels, npm deps, and embedded Python in release workflow | alexei.dolgolyov |
| [db777fa](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/db777fa) | fix: prevent dialog showModal from auto-focusing first input | alexei.dolgolyov |
</details>
+6
View File
@@ -60,12 +60,18 @@ CORE_DEPS=(
) )
# Windows-specific dependencies # Windows-specific dependencies
# NOTE: wmi is a transitive dep of screen-brightness-control gated on
# `platform_system == "Windows"`. pip evaluates env markers against the HOST
# (Linux in CI), so it gets skipped during cross-build. Listed explicitly here
# so the wheel actually lands in the Windows bundle. Same gotcha as the
# uvicorn[standard]/uvloop case documented above.
WIN_DEPS=( WIN_DEPS=(
"winsdk>=1.0.0b10" "winsdk>=1.0.0b10"
"pywin32>=306" "pywin32>=306"
"comtypes>=1.2.0" "comtypes>=1.2.0"
"pycaw>=20230407" "pycaw>=20230407"
"screen-brightness-control>=0.20.0" "screen-brightness-control>=0.20.0"
"wmi>=1.5.1"
"monitorcontrol>=3.0.0" "monitorcontrol>=3.0.0"
) )
+1 -1
View File
@@ -323,7 +323,7 @@ async def set_visualizer_device(
@router.websocket("/ws") @router.websocket("/ws")
async def websocket_endpoint( async def websocket_endpoint(
websocket: WebSocket, websocket: WebSocket,
token: str = Query(..., description="API authentication token"), token: str | None = Query(None, description="API authentication token"),
) -> None: ) -> None:
"""WebSocket endpoint for real-time media status updates. """WebSocket endpoint for real-time media status updates.
+12 -2
View File
@@ -19,6 +19,7 @@ class ConnectionManager:
self._active_connections: set[WebSocket] = set() self._active_connections: set[WebSocket] = set()
self._lock = asyncio.Lock() self._lock = asyncio.Lock()
self._last_status: dict[str, Any] | None = None self._last_status: dict[str, Any] | None = None
self._get_status_func: Callable[[], Coroutine[Any, Any, Any]] | None = None
self._broadcast_task: asyncio.Task | None = None self._broadcast_task: asyncio.Task | None = None
self._poll_interval: float = 0.5 # Internal poll interval for change detection self._poll_interval: float = 0.5 # Internal poll interval for change detection
self._position_broadcast_interval: float = 5.0 # Send position updates every 5s during playback self._position_broadcast_interval: float = 5.0 # Send position updates every 5s during playback
@@ -39,9 +40,17 @@ class ConnectionManager:
) )
# Send current status immediately upon connection # Send current status immediately upon connection
if self._last_status: status = self._last_status
if not status and self._get_status_func:
try: try:
await websocket.send_json({"type": "status", "data": self._last_status}) result = await self._get_status_func()
status = result.model_dump()
self._last_status = status
except Exception as e:
logger.debug("Failed to fetch initial status: %s", e)
if status:
try:
await websocket.send_json({"type": "status", "data": status})
except Exception as e: except Exception as e:
logger.debug("Failed to send initial status: %s", e) logger.debug("Failed to send initial status: %s", e)
@@ -251,6 +260,7 @@ class ConnectionManager:
if self._running: if self._running:
return return
self._get_status_func = get_status_func
self._running = True self._running = True
self._broadcast_task = asyncio.create_task( self._broadcast_task = asyncio.create_task(
self._status_monitor_loop(get_status_func) self._status_monitor_loop(get_status_func)
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "media-server-frontend", "name": "media-server-frontend",
"version": "0.1.2", "version": "0.1.5",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "media-server-frontend", "name": "media-server-frontend",
"version": "0.1.2", "version": "0.1.5",
"devDependencies": { "devDependencies": {
"esbuild": "^0.27.4" "esbuild": "^0.27.4"
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "media-server-frontend", "name": "media-server-frontend",
"version": "0.1.4", "version": "0.1.5",
"private": true, "private": true,
"description": "Frontend build tooling for media server WebUI", "description": "Frontend build tooling for media server WebUI",
"scripts": { "scripts": {
+2 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "media-server" name = "media-server"
version = "0.1.4" version = "0.1.6"
description = "REST API server for controlling system-wide media playback" description = "REST API server for controlling system-wide media playback"
readme = "README.md" readme = "README.md"
license = { text = "MIT" } license = { text = "MIT" }
@@ -41,6 +41,7 @@ windows = [
"comtypes>=1.2.0", "comtypes>=1.2.0",
"pycaw>=20230407", "pycaw>=20230407",
"screen-brightness-control>=0.20.0", "screen-brightness-control>=0.20.0",
"wmi>=1.5.1",
"monitorcontrol>=3.0.0", "monitorcontrol>=3.0.0",
"pystray>=0.19.0", "pystray>=0.19.0",
] ]