fix: 优化架构

This commit is contained in:
Daniel
2026-03-25 19:35:37 +08:00
parent 34786b37c7
commit 508c28ce31
184 changed files with 2199 additions and 241 deletions

View File

@@ -12,13 +12,14 @@ from typing import Any
from moviepy import ImageClip
from PIL import Image, ImageDraw, ImageFont
from engine.audio_gen import synthesize_scenes
from engine.model_factory import get_model
from engine.prompt_injector import inject_prompt
from engine.adapters.image.mock_adapter import MockImageGen
from engine.assembler import assemble_clips
from engine.comfy_client import ComfyClient
from engine.config import AppConfig
from engine.director import scenes_to_shots
from engine.shot_executor import render_shot
from engine.script_gen import generate_scenes, refine_scene
from engine.task_store import create_task, update_shot_status, update_task_status
from engine.types import Scene
from engine.video_editor import Segment, render_final
@@ -28,13 +29,15 @@ def _emit(line: str) -> None:
print(line, flush=True)
def _emit_scene(scene_idx: int, scene: Scene) -> None:
def _emit_scene(scene_idx: int, scene: Scene, extra: dict[str, Any] | None = None) -> None:
payload = {
"index": scene_idx,
"image_prompt": scene.image_prompt,
"video_motion": scene.video_motion,
"narration": scene.narration,
}
if extra:
payload.update(extra)
_emit("SCENE_JSON " + json.dumps(payload, ensure_ascii=False))
@@ -136,9 +139,50 @@ def _fallback_scenes(prompt: str) -> list[Scene]:
]
def _generate_scene_preview(
*,
cfg: AppConfig,
out_dir: Path,
image_prompt: str,
style: str | None,
character: str | None,
) -> str | None:
try:
image_gen = get_model("image", cfg)
except Exception:
image_gen = get_model("image_fallback", cfg)
global_cfg = dict(cfg.get("global", {}) or {})
if style:
global_cfg["style"] = style
if character:
global_cfg["character"] = character
prompt_obj = inject_prompt(global_cfg, {"prompt": image_prompt})
try:
image_path = image_gen.generate(prompt_obj, out_dir)
except Exception:
try:
image_path = get_model("image_fallback", cfg).generate(prompt_obj, out_dir)
except Exception:
# Last-resort hard fallback: never block script stage due to preview failures.
image_path = MockImageGen().generate(prompt_obj, out_dir)
p = Path(str(image_path))
if not p.exists():
return None
return f"/api/static/{out_dir.name}/{p.name}"
def _has_llm_key(cfg: AppConfig) -> bool:
api_key_env = str(cfg.get("openai.api_key_env", "OPENAI_API_KEY"))
return bool(os.environ.get(api_key_env))
api_key_env = str(cfg.get("openai.api_key_env", "OPENAI_API_KEY") or "OPENAI_API_KEY").strip()
# Env var name case.
if os.environ.get(api_key_env):
return True
# Literal key case (DashScope / OpenAI-compatible).
if api_key_env.startswith("sk-"):
return True
return False
def _parse_scenes_from_obj(obj: Any) -> list[Scene]:
@@ -239,7 +283,8 @@ def step_script(prompt: str, cfg: AppConfig, mock: bool, *, style: str | None, c
# fallback scenes still should include global injection
scenes = _fallback_scenes(prompt)
else:
scenes = generate_scenes(prompt2, cfg)
llm = get_model("llm", cfg)
scenes = llm.generate_script(prompt2, context=None)
out_dir.mkdir(parents=True, exist_ok=True)
_emit("SCRIPT_BEGIN")
@@ -249,7 +294,14 @@ def step_script(prompt: str, cfg: AppConfig, mock: bool, *, style: str | None, c
video_motion=s.video_motion,
narration=s.narration,
)
_emit_scene(idx, s2)
preview_url = _generate_scene_preview(
cfg=cfg,
out_dir=out_dir,
image_prompt=s2.image_prompt,
style=style,
character=character,
)
_emit_scene(idx, s2, extra={"preview_url": preview_url or ""})
_emit("SCRIPT_END")
(out_dir / "scenes.json").write_text(
json.dumps(
@@ -292,8 +344,9 @@ def step_refine(
narration=(s.narration + "(更凝练)")[:30],
)
else:
# Ensure globals are visible to LLM, and inject to output image prompt.
refined0 = refine_scene(prompt=prompt2, scenes=scenes, target_index=target_index, cfg=cfg)
llm = get_model("llm", cfg)
# Context carries prompt + scenes for consistent refinement.
refined0 = llm.refine_scene(scenes[target_index - 1], context={"prompt": prompt2, "scenes": scenes, "target_index": target_index})
refined = Scene(
image_prompt=_decorate_image_prompt(refined0.image_prompt, style=style, character=character),
video_motion=refined0.video_motion,
@@ -301,7 +354,14 @@ def step_refine(
)
# Keep the original index for frontend replacement.
_emit_scene(scene_index, refined)
preview_url = _generate_scene_preview(
cfg=cfg,
out_dir=out_dir,
image_prompt=refined.image_prompt,
style=style,
character=character,
)
_emit_scene(scene_index, refined, extra={"preview_url": preview_url or ""})
out_dir.mkdir(parents=True, exist_ok=True)
(out_dir / f"refine_scene_{scene_index}.json").write_text(
json.dumps(