refactor: standalone dashboard folders with index.html, zip-based import/export (.tsd/.tss/.tsp)
- Each dashboard is now a self-contained folder: manifest.json + index.html - Auto-scan dashboards/ and data/dashboards/ on startup - Export: .tsd (dashboards), .tss (scenes), .tsp (game plugins) - all zip format - Dashboard index.html is complete standalone page (WS + data binding) - Aspect ratio constraints handled in each dashboard's own JS - Removed template-based dashboard rendering in favor of static serve - Import via file upload endpoints, export via direct file download
This commit is contained in:
+22
-15
@@ -1,7 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import json
|
||||
import shutil
|
||||
import uuid
|
||||
import zipfile
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
@@ -124,24 +127,28 @@ class SceneManager:
|
||||
return True
|
||||
return False
|
||||
|
||||
def export_scene(self, scene_id: str) -> dict[str, Any] | None:
|
||||
scene = self.get(scene_id)
|
||||
if not scene:
|
||||
def export_scene_zip(self, scene_id: str) -> bytes | None:
|
||||
filepath = SCENES_DIR / f"{scene_id}.json"
|
||||
if not filepath.exists():
|
||||
return None
|
||||
return {
|
||||
"type": "scene",
|
||||
"version": "1.0",
|
||||
"data": scene,
|
||||
}
|
||||
buf = io.BytesIO()
|
||||
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||
zf.write(filepath, "scene.json")
|
||||
logger.info("Scene exported as zip: %s", scene_id)
|
||||
return buf.getvalue()
|
||||
|
||||
def import_scene(self, data: dict[str, Any]) -> bool:
|
||||
if data.get("type") != "scene":
|
||||
def import_scene_zip(self, zip_data: bytes) -> bool:
|
||||
try:
|
||||
with zipfile.ZipFile(io.BytesIO(zip_data), 'r') as zf:
|
||||
if "scene.json" not in zf.namelist():
|
||||
logger.error("No scene.json found in zip")
|
||||
return False
|
||||
data = json.loads(zf.read("scene.json").decode('utf-8'))
|
||||
scene = Scene.from_dict(data)
|
||||
return self.save(scene)
|
||||
except Exception as e:
|
||||
logger.error("Failed to import scene zip: %s", e)
|
||||
return False
|
||||
scene_data = data.get("data", {})
|
||||
if "id" not in scene_data:
|
||||
scene_data["id"] = str(uuid.uuid4())
|
||||
scene = Scene.from_dict(scene_data)
|
||||
return self.save(scene)
|
||||
|
||||
|
||||
scene_manager = SceneManager()
|
||||
|
||||
Reference in New Issue
Block a user