fix: 优化架构
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user