e2e1107df7
Lint & Test / test (push) Has been cancelled
- Replace URL-based image_source/url fields with image_asset_id/video_asset_id on StaticImagePictureSource and VideoCaptureSource (clean break, no migration) - Resolve asset IDs to file paths at runtime via AssetStore.get_file_path() - Add EntitySelect asset pickers for image/video in stream editor modal - Add notification sound configuration (global sound + per-app overrides) - Unify per-app color and sound overrides into single "Per-App Overrides" section - Persist notification history between server restarts - Add asset management system (upload, edit, delete, soft-delete) - Replace emoji buttons with SVG icons throughout UI - Various backend improvements: SQLite stores, auth, backup, MQTT, webhooks
66 lines
2.1 KiB
Python
66 lines
2.1 KiB
Python
"""Module-level holder for the uvicorn Server and TrayManager references.
|
|
|
|
Allows the shutdown API endpoint to trigger graceful shutdown via
|
|
``server.should_exit = True`` + ``tray.stop()``, which is the same
|
|
mechanism the system tray "Shutdown" menu item uses.
|
|
"""
|
|
|
|
from typing import Any, Optional
|
|
|
|
from wled_controller.utils import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
_server: Optional[Any] = None # uvicorn.Server
|
|
_tray: Optional[Any] = None # TrayManager
|
|
|
|
|
|
def set_server(server: Any) -> None:
|
|
"""Store the uvicorn Server instance (called from __main__)."""
|
|
global _server
|
|
_server = server
|
|
|
|
|
|
def set_tray(tray: Any) -> None:
|
|
"""Store the TrayManager instance (called from __main__)."""
|
|
global _tray
|
|
_tray = tray
|
|
|
|
|
|
def request_shutdown() -> None:
|
|
"""Signal uvicorn + tray to perform a graceful shutdown.
|
|
|
|
Broadcasts a ``server_restarting`` event so the frontend can show
|
|
a restart indicator, persists all stores to disk, then sets
|
|
``should_exit = True`` on the uvicorn Server and stops the tray.
|
|
"""
|
|
# Notify connected clients that a restart is in progress
|
|
_broadcast_restarting()
|
|
|
|
# Persist stores before signaling shutdown.
|
|
# The lifespan shutdown handler also saves, but it may not run
|
|
# reliably when uvicorn is in a daemon thread.
|
|
try:
|
|
from wled_controller.main import _save_all_stores
|
|
_save_all_stores()
|
|
except Exception as e:
|
|
logger.debug("Best-effort store save on shutdown failed: %s", e)
|
|
pass # best-effort; lifespan handler is the backup
|
|
|
|
if _server is not None:
|
|
_server.should_exit = True
|
|
if _tray is not None:
|
|
_tray.stop()
|
|
|
|
|
|
def _broadcast_restarting() -> None:
|
|
"""Push a server_restarting event to all connected WebSocket clients."""
|
|
try:
|
|
from wled_controller.api.dependencies import _deps
|
|
pm = _deps.get("processor_manager")
|
|
if pm is not None:
|
|
pm.fire_event({"type": "server_restarting"})
|
|
except Exception as e:
|
|
logger.debug("Failed to broadcast server_restarting event: %s", e)
|
|
pass
|