Files
olivv-cs-ComfyUI-FunPack/movie_editor/backend/projects.py
T
digital-garbageandClaude Opus 4.8 a534f8bb6f opencut_ui: hard-remove the character feature (no UI surface remained)
The rebuilt editor never exposed character creation/assignment, so the feature
was vestigial: dead frontend plumbing plus generation branches that only fired
for legacy/imported projects. Removed it wholesale per decision.

Backend:
- Deleted movie_editor/backend/characters.py and the /library/characters routes.
- timeline.py: dropped normalize_character_bible / build_characters_anchor /
  _load_character_map / _scene_character_prefix / character_media_refs_for_project
  and friends. effective_anchor/effective_negative_prompt now return the project's
  manual text; resolve_scene_identity_pin(cs) returns the project-level identity
  pin only. Removed Scene.character_ids and Project.character_bible fields and the
  character_ids fingerprint key.
- Continuity auto-guides keep working off continuity_settings.identity_pin_ref.

Frontend:
- store.js: removed state.characters, the character preview-key plumbing,
  load/save/deleteCharacter, sceneCharacterIds/toggleSceneCharacter, character_ids
  in patch/archive paths and exports.
- timeline.js: dropped clip-char badge rendering; api.js: dropped character routes;
  view_bus.js: dropped chars fingerprint; tour.js: dropped demo characters + copy;
  styles.css: dropped .clip-chars/.clip-char.

Tests: removed test_characters.py and the character-bible timeline tests; added
manual-anchor / manual-negative / project-identity-pin tests. 133 passed, 4 skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 11:08:48 +03:00

69 lines
1.8 KiB
Python

"""On-disk JSON project store. One file per project, keyed by id.
Deliberately simple and synchronous — projects are small. This is the persistence
seam that Phase 2+ extends with cached per-scene latents/assets.
"""
from __future__ import annotations
import json
import time
from typing import Optional
from . import config
from .timeline import Project, Scene
def _path(project_id: str):
return config.PROJECTS_DIR / f"{project_id}.json"
def list_projects() -> list[dict]:
config.ensure_dirs()
out: list[dict] = []
for p in sorted(config.PROJECTS_DIR.glob("*.json")):
try:
d = json.loads(p.read_text())
except (json.JSONDecodeError, OSError):
continue
out.append({
"id": d.get("id", p.stem),
"name": d.get("name", p.stem),
"scene_count": len(d.get("scenes", [])),
"updated_at": d.get("updated_at", 0),
})
out.sort(key=lambda x: x.get("updated_at", 0), reverse=True)
return out
def get(project_id: str) -> Optional[Project]:
p = _path(project_id)
if not p.exists():
return None
return Project.from_dict(json.loads(p.read_text()))
def save(project: Project) -> Project:
config.ensure_dirs()
project.updated_at = time.time()
path = _path(project.id)
payload = json.dumps(project.to_dict(), indent=2)
tmp = path.with_suffix(".json.tmp")
tmp.write_text(payload)
tmp.replace(path)
return project
def create(name: str = "Untitled") -> Project:
project = Project(name=name)
project.anchor = "Optional: describe the subjects or world shared by all scenes"
project.scenes = [Scene(text="Describe your first scene here…")]
return save(project)
def delete(project_id: str) -> bool:
p = _path(project_id)
if p.exists():
p.unlink()
return True
return False