Files
ledgrab/server/src/wled_controller/storage/asset.py
T
alexei.dolgolyov e2e1107df7
Lint & Test / test (push) Has been cancelled
feat: asset-based image/video sources, notification sounds, UI improvements
- 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
2026-03-26 20:40:25 +03:00

81 lines
2.7 KiB
Python

"""Asset data model.
An Asset represents an uploaded file (sound, image, video, or other)
stored on the server. Assets are referenced by ID from other entities
(e.g. NotificationColorStripSource uses sound assets for alert sounds).
"""
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Optional
# Map MIME type prefixes to asset_type categories
_MIME_TO_ASSET_TYPE = {
"audio/": "sound",
"image/": "image",
"video/": "video",
}
def asset_type_from_mime(mime_type: str) -> str:
"""Derive asset_type from a MIME type string."""
for prefix, asset_type in _MIME_TO_ASSET_TYPE.items():
if mime_type.startswith(prefix):
return asset_type
return "other"
@dataclass
class Asset:
"""Persistent metadata for an uploaded file asset."""
id: str
name: str
filename: str # original upload filename
stored_filename: str # on-disk filename (uuid-based)
mime_type: str # e.g. "audio/wav", "image/png"
asset_type: str # "sound" | "image" | "video" | "other"
size_bytes: int
created_at: datetime
updated_at: datetime
description: Optional[str] = None
tags: List[str] = field(default_factory=list)
prebuilt: bool = False # True for shipped assets
deleted: bool = False # soft-delete for prebuilt assets
def to_dict(self) -> dict:
return {
"id": self.id,
"name": self.name,
"filename": self.filename,
"stored_filename": self.stored_filename,
"mime_type": self.mime_type,
"asset_type": self.asset_type,
"size_bytes": self.size_bytes,
"description": self.description,
"tags": self.tags,
"prebuilt": self.prebuilt,
"deleted": self.deleted,
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat(),
}
@staticmethod
def from_dict(data: dict) -> "Asset":
return Asset(
id=data["id"],
name=data["name"],
filename=data.get("filename", ""),
stored_filename=data.get("stored_filename", ""),
mime_type=data.get("mime_type", "application/octet-stream"),
asset_type=data.get("asset_type", "other"),
size_bytes=int(data.get("size_bytes", 0)),
description=data.get("description"),
tags=data.get("tags", []),
prebuilt=bool(data.get("prebuilt", False)),
deleted=bool(data.get("deleted", False)),
created_at=datetime.fromisoformat(data["created_at"]),
updated_at=datetime.fromisoformat(data["updated_at"]),
)