feat: asset-based image/video sources, notification sounds, UI improvements
Some checks failed
Lint & Test / test (push) Has been cancelled
Some checks failed
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
This commit is contained in:
@@ -130,17 +130,19 @@ async def get_version():
|
||||
async def list_all_tags(_: AuthRequired):
|
||||
"""Get all tags used across all entities."""
|
||||
all_tags: set[str] = set()
|
||||
from wled_controller.api.dependencies import get_asset_store
|
||||
store_getters = [
|
||||
get_device_store, get_output_target_store, get_color_strip_store,
|
||||
get_picture_source_store, get_audio_source_store, get_value_source_store,
|
||||
get_sync_clock_store, get_automation_store, get_scene_preset_store,
|
||||
get_template_store, get_audio_template_store, get_pp_template_store,
|
||||
get_pattern_template_store,
|
||||
get_pattern_template_store, get_asset_store,
|
||||
]
|
||||
for getter in store_getters:
|
||||
try:
|
||||
store = getter()
|
||||
except RuntimeError:
|
||||
except RuntimeError as e:
|
||||
logger.debug("Store not available during entity count: %s", e)
|
||||
continue
|
||||
# BaseJsonStore subclasses provide get_all(); DeviceStore provides get_all_devices()
|
||||
fn = getattr(store, "get_all", None) or getattr(store, "get_all_devices", None)
|
||||
@@ -211,10 +213,10 @@ async def get_displays(
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get displays: {e}")
|
||||
logger.error("Failed to get displays: %s", e, exc_info=True)
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to retrieve display information: {str(e)}"
|
||||
detail="Internal server error"
|
||||
)
|
||||
|
||||
|
||||
@@ -232,10 +234,10 @@ async def get_running_processes(_: AuthRequired):
|
||||
sorted_procs = sorted(processes)
|
||||
return ProcessListResponse(processes=sorted_procs, count=len(sorted_procs))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get processes: {e}")
|
||||
logger.error("Failed to get processes: %s", e, exc_info=True)
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to retrieve process list: {str(e)}"
|
||||
detail="Internal server error"
|
||||
)
|
||||
|
||||
|
||||
@@ -300,7 +302,7 @@ def list_api_keys(_: AuthRequired):
|
||||
"""List API key labels (read-only; keys are defined in the YAML config file)."""
|
||||
config = get_config()
|
||||
keys = [
|
||||
{"label": label, "masked": key[:4] + "****" + key[-4:] if len(key) >= 8 else "****"}
|
||||
{"label": label, "masked": key[:4] + "****" if len(key) >= 8 else "****"}
|
||||
for label, key in config.auth.api_keys.items()
|
||||
]
|
||||
return {"keys": keys, "count": len(keys)}
|
||||
|
||||
Reference in New Issue
Block a user