refactor: scenes now folder-based with preview image support

This commit is contained in:
2026-07-27 11:36:25 +08:00
parent 30697e4a64
commit 7a03fd3ebd
2 changed files with 50 additions and 18 deletions
+47 -17
View File
@@ -84,34 +84,51 @@ class SceneManager:
def list_all(self, game_id: str | None = None) -> list[dict[str, Any]]:
scenes = []
for f in SCENES_DIR.glob("*.json"):
for item in SCENES_DIR.iterdir():
if not item.is_dir():
continue
f = item / "scene.json"
if not f.exists():
continue
try:
with open(f, "r", encoding="utf-8") as fp:
data = json.load(fp)
scene = Scene.from_dict(data)
if game_id is None or scene.game_id == game_id:
scenes.append(scene.to_dict())
d = scene.to_dict()
for ext in ["jpg","jpeg","png","webp","gif"]:
if (item / f"preview.{ext}").exists():
d["preview_ext"] = ext
break
scenes.append(d)
except Exception as e:
logger.error("Failed to load scene %s: %s", f.name, e)
logger.error("Failed to load scene %s: %s", item.name, e)
return scenes
def get(self, scene_id: str) -> dict[str, Any] | None:
filepath = SCENES_DIR / f"{scene_id}.json"
if not filepath.exists():
item = SCENES_DIR / scene_id
f = item / "scene.json"
if not f.exists():
return None
try:
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
return Scene.from_dict(data).to_dict()
with open(f, "r", encoding="utf-8") as fp:
data = json.load(fp)
d = Scene.from_dict(data).to_dict()
for ext in ["jpg","jpeg","png","webp","gif"]:
if (item / f"preview.{ext}").exists():
d["preview_ext"] = ext
break
return d
except Exception as e:
logger.error("Failed to read scene %s: %s", scene_id, e)
return None
def save(self, scene: Scene) -> bool:
scene.updated_at = datetime.now().isoformat()
filepath = SCENES_DIR / f"{scene.id}.json"
item = SCENES_DIR / scene.id
item.mkdir(parents=True, exist_ok=True)
try:
with open(filepath, "w", encoding="utf-8") as f:
with open(item / "scene.json", "w", encoding="utf-8") as f:
json.dump(scene.to_dict(), f, indent=2, ensure_ascii=False)
logger.info("Scene saved: %s", scene.id)
return True
@@ -120,20 +137,23 @@ class SceneManager:
return False
def delete(self, scene_id: str) -> bool:
filepath = SCENES_DIR / f"{scene_id}.json"
if filepath.exists():
filepath.unlink()
import shutil
item = SCENES_DIR / scene_id
if item.exists():
shutil.rmtree(item)
logger.info("Scene deleted: %s", scene_id)
return True
return False
def export_scene_zip(self, scene_id: str) -> bytes | None:
filepath = SCENES_DIR / f"{scene_id}.json"
if not filepath.exists():
item = SCENES_DIR / scene_id
if not item.exists():
return None
buf = io.BytesIO()
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf:
zf.write(filepath, "scene.json")
for f in sorted(item.rglob("*")):
if f.is_file():
zf.write(f, f.relative_to(item))
logger.info("Scene exported as zip: %s", scene_id)
return buf.getvalue()
@@ -145,7 +165,17 @@ class SceneManager:
return False
data = json.loads(zf.read("scene.json").decode('utf-8'))
scene = Scene.from_dict(data)
return self.save(scene)
item = SCENES_DIR / scene.id
if item.exists():
import shutil; shutil.rmtree(item)
item.mkdir(parents=True)
for name in zf.namelist():
if name.endswith('/'): continue
dest = item / name
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_bytes(zf.read(name))
logger.info("Scene imported from zip: %s", scene.id)
return True
except Exception as e:
logger.error("Failed to import scene zip: %s", e)
return False