Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e3bebfeb8 | |||
| 34eb7c7b19 | |||
| 972ee54b91 | |||
| d09a0b90e4 | |||
| c3cb7a4da9 | |||
| e3889fef29 |
+5
-3
@@ -1,7 +1,8 @@
|
|||||||
## v0.1.3 (2026-04-07)
|
## v0.1.5 (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))
|
- Make WebSocket token query parameter optional to prevent connection failures for clients that authenticate via other means ([34eb7c7](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/34eb7c7))
|
||||||
|
- Fetch playback status eagerly on new WebSocket connection instead of waiting for the next poll cycle ([d09a0b9](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/d09a0b9))
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
|
|
||||||
| Hash | Message | Author |
|
| Hash | Message | Author |
|
||||||
|------|---------|--------|
|
|------|---------|--------|
|
||||||
| [db777fa](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/db777fa) | fix: prevent dialog showModal from auto-focusing first input | alexei.dolgolyov |
|
| [34eb7c7](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/34eb7c7) | fix(ws): make WebSocket token parameter optional | alexei.dolgolyov |
|
||||||
|
| [d09a0b9](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/d09a0b9) | fix(ws): fetch status eagerly on new WebSocket connection | alexei.dolgolyov |
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
+9
-2
@@ -109,6 +109,13 @@ cleanup_site_packages() {
|
|||||||
# Strip debug symbols from native extensions
|
# Strip debug symbols from native extensions
|
||||||
find "$sp_dir" -name "*.$ext_suffix" -exec strip --strip-debug {} \; 2>/dev/null || true
|
find "$sp_dir" -name "*.$ext_suffix" -exec strip --strip-debug {} \; 2>/dev/null || true
|
||||||
|
|
||||||
# Remove .py source files (keep .pyc only) — saves ~30-40% on pure-Python packages
|
# NOTE: do NOT strip .py source files. A previous version of this function
|
||||||
find "$sp_dir" -name "*.py" ! -name "__init__.py" -delete 2>/dev/null || true
|
# ran `find ... -name "*.py" ! -name "__init__.py" -delete` with a comment
|
||||||
|
# claiming "keep .pyc only" — but no compileall step exists, so the dist
|
||||||
|
# shipped with __init__.py + .pyd only, missing every submodule (Image.py,
|
||||||
|
# ImageDraw.py, _version.py, ...). Fresh installs would fail with
|
||||||
|
# ModuleNotFoundError; in-place upgrades over an older install produced a
|
||||||
|
# half-old/half-new site-packages where PIL/__init__.py was new but
|
||||||
|
# PIL/_version.py was stale, yielding the runtime "_imaging extension was
|
||||||
|
# built for another version of Pillow" import error.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,19 @@ Section "!Core (required)" SecCore
|
|||||||
|
|
||||||
SetOutPath "$INSTDIR"
|
SetOutPath "$INSTDIR"
|
||||||
|
|
||||||
|
; Wipe previous payload before extracting so stale files from an older
|
||||||
|
; version cannot survive an upgrade. Without this, in-place upgrades
|
||||||
|
; produce a half-old/half-new site-packages — e.g. an old PIL/_version.py
|
||||||
|
; alongside a new PIL/_imaging.pyd, which raises "_imaging extension was
|
||||||
|
; built for another version of Pillow" at runtime. config.yaml lives at
|
||||||
|
; $INSTDIR root and is preserved.
|
||||||
|
RMDir /r "$INSTDIR\python"
|
||||||
|
RMDir /r "$INSTDIR\app"
|
||||||
|
RMDir /r "$INSTDIR\scripts"
|
||||||
|
Delete "$INSTDIR\${EXENAME}"
|
||||||
|
Delete "$INSTDIR\VERSION"
|
||||||
|
Delete "$INSTDIR\config.example.yaml"
|
||||||
|
|
||||||
; Copy entire distribution
|
; Copy entire distribution
|
||||||
File /r "dist\media-server\*.*"
|
File /r "dist\media-server\*.*"
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Generated
+2
-2
@@ -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
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "media-server-frontend",
|
"name": "media-server-frontend",
|
||||||
"version": "0.1.3",
|
"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": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "media-server"
|
name = "media-server"
|
||||||
version = "0.1.3"
|
version = "0.1.5"
|
||||||
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" }
|
||||||
|
|||||||
Reference in New Issue
Block a user