Add audio sources as first-class entities, add mapped CSS type, simplify target editor for mapped sources

- Audio sources moved to separate tab with dedicated CRUD API, store, and editor modal
- New "mapped" color strip source type: assigns different CSS sources to distinct LED sub-ranges (zones)
- Mapped stream runtime with per-zone sub-streams, auto-sizing, hot-update support
- Target editor auto-collapses segments UI when mapped CSS is selected
- Delete protection for CSS sources referenced by mapped zones
- Compact header/footer layout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 23:35:58 +03:00
parent 199039326b
commit 9efb08acb6
28 changed files with 1729 additions and 153 deletions

View File

@@ -8,6 +8,7 @@ from wled_controller.storage.pattern_template_store import PatternTemplateStore
from wled_controller.storage.picture_source_store import PictureSourceStore
from wled_controller.storage.picture_target_store import PictureTargetStore
from wled_controller.storage.color_strip_store import ColorStripStore
from wled_controller.storage.audio_source_store import AudioSourceStore
from wled_controller.storage.profile_store import ProfileStore
from wled_controller.core.profiles.profile_engine import ProfileEngine
@@ -19,6 +20,7 @@ _pattern_template_store: PatternTemplateStore | None = None
_picture_source_store: PictureSourceStore | None = None
_picture_target_store: PictureTargetStore | None = None
_color_strip_store: ColorStripStore | None = None
_audio_source_store: AudioSourceStore | None = None
_processor_manager: ProcessorManager | None = None
_profile_store: ProfileStore | None = None
_profile_engine: ProfileEngine | None = None
@@ -73,6 +75,13 @@ def get_color_strip_store() -> ColorStripStore:
return _color_strip_store
def get_audio_source_store() -> AudioSourceStore:
"""Get audio source store dependency."""
if _audio_source_store is None:
raise RuntimeError("Audio source store not initialized")
return _audio_source_store
def get_processor_manager() -> ProcessorManager:
"""Get processor manager dependency."""
if _processor_manager is None:
@@ -103,13 +112,14 @@ def init_dependencies(
picture_source_store: PictureSourceStore | None = None,
picture_target_store: PictureTargetStore | None = None,
color_strip_store: ColorStripStore | None = None,
audio_source_store: AudioSourceStore | None = None,
profile_store: ProfileStore | None = None,
profile_engine: ProfileEngine | None = None,
):
"""Initialize global dependencies."""
global _device_store, _template_store, _processor_manager
global _pp_template_store, _pattern_template_store, _picture_source_store, _picture_target_store
global _color_strip_store, _profile_store, _profile_engine
global _color_strip_store, _audio_source_store, _profile_store, _profile_engine
_device_store = device_store
_template_store = template_store
_processor_manager = processor_manager
@@ -118,5 +128,6 @@ def init_dependencies(
_picture_source_store = picture_source_store
_picture_target_store = picture_target_store
_color_strip_store = color_strip_store
_audio_source_store = audio_source_store
_profile_store = profile_store
_profile_engine = profile_engine