测试视频复刻

This commit is contained in:
Azmat@qq.com
2026-08-27 18:27:08 +08:00
parent b3bf159e08
commit 52c5cdae1d
7 changed files with 299 additions and 106 deletions
+2 -44
View File
@@ -34,26 +34,6 @@ const KIND_LABEL: Record<string, string> = { product: "商品", person: "角色"
// 脚本来源 → 「来源」brief pill 文案
// 入口收敛为「脚本辅助生成 / 上传脚本」两种。theme 键保留:历史稿的 source 仍可能是旧的「一句话主题」。
const SOURCE_LABEL: Record<string, string> = { ai: "脚本辅助生成", theme: "脚本辅助生成", manual: "上传脚本", video: "上传视频提炼" };
type DirectorBeat = { time: string; direction: string; narration: string };
const DIRECTOR_BEAT_RE = /(?:^|\n)\s*(\d{1,2})\s*[-–—~到至]\s*(\d{1,2})\s*(?:s|秒)?\s*[::]*\s*([^\n]+)/g;
function buildDirectorBeats(visual: string, narration: string, duration: number): DirectorBeat[] {
const matches = [...(visual || "").matchAll(DIRECTOR_BEAT_RE)];
const narrationLines = (narration || "").split(/(?<=[。!?!?])/).map((line) => line.trim()).filter(Boolean);
if (matches.length) {
return matches.slice(0, 5).map((match, index) => ({
time: `${match[1]}–${match[2]}s`,
direction: match[3].trim(),
narration: narrationLines[index] || (index === 0 ? narration.trim() : ""),
}));
}
const count = Math.min(3, Math.max(1, narrationLines.length || 1));
const step = duration / count;
return Array.from({ length: count }, (_, index) => ({
time: `${Math.round(index * step)}–${Math.round((index + 1) * step)}s`,
direction: visual.trim() || "按本镜脚本完成关键动作与商品露出",
narration: narrationLines[index] || (index === 0 ? narration.trim() : ""),
}));
}
// 旁白配音音色预设(语音合成经典版试用包实测可用,与后端 VOICEOVER_VOICES 对齐)
const VO_VOICES = [
{ key: "BV700_streaming", label: "灿灿 · 活力女声" },
@@ -2070,7 +2050,7 @@ export function PipelinePage(props: {
document.body.style.userSelect = "";
};
}, [gutterDragging]);
const SB_PROMPT_DEFAULT = "统一商品、人物、场景风格,生成干净、可直接指导视频的关键帧;导演分镜说明按脚本自动生成";
const SB_PROMPT_DEFAULT = "统一商品、人物、场景风格,生成完整、可直接指导视频的导演故事板";
// 整张风格提示词:项目级(原 StoryboardVersion.prompt 的去处),重跑时生效
const sbSavedPrompt = (project.metadata as Record<string, unknown> | undefined)?.storyboard_prompt as string | undefined;
const [storyboardPrompt, setStoryboardPrompt] = useState(sbSavedPrompt || SB_PROMPT_DEFAULT);
@@ -3871,13 +3851,6 @@ export function PipelinePage(props: {
const sbViewedIsAdopted = !sbViewedVer || sbViewedVer.is_adopted || sbViewedVer.id === sbActiveShot?.adopted_version;
const mainUrl = sbViewedVer ? (sbViewedVer.asset_url || assetUrl(sbViewedVer.asset)) : shotImg(sbActiveShot);
const mainAid = sbViewedVer?.asset || sbActiveShot?.adopted_asset || "";
const activeScriptShot = sbActiveShot ? shots[sbActiveShot.sort_order] : null;
const directorBeats = buildDirectorBeats(
activeScriptShot?.visual_prompt || "",
activeScriptShot?.narration || "",
activeScriptShot?.duration_seconds || SEGMENT_DURATION_MAX,
);
const directorRange = sceneTimes[sbSelected] || "—";
return (
<section className="stage active" data-stage-pane="3">
<div className="stage-storyboard">
@@ -3949,22 +3922,7 @@ export function PipelinePage(props: {
<span>{sbActiveShot.error_message}</span>
</div>
)}
<div className="muted-2" style={{ fontSize: "12px", lineHeight: 1.55, marginBottom: "10px" }}>每个场生成一张干净视频关键帧;下方导演稿按本镜真实脚本展开,视频只使用关键帧,不会带入文字或边框。</div>
<div className="sb-director-board" aria-label="本场导演故事板">
<div className="sb-director-head">
<div><span className="mono">DIRECTOR BOARD</span><strong>场 {sbSelected + 1} · {directorRange}</strong></div>
<span className="pill pill-l3 neutral"><span className="dot" />9:16 · 关键帧</span>
</div>
{directorBeats.length ? directorBeats.map((beat, index) => (
<div className="sb-director-beat" key={`${beat.time}-${index}`}>
<div className="sb-director-time">{beat.time}</div>
<div className="sb-director-copy">
<div className="sb-director-direction">{beat.direction}</div>
{beat.narration && <div className="sb-director-narration">口播/旁白:{beat.narration}</div>}
</div>
</div>
)) : <div className="sb-director-empty">脚本生成后,这里会自动展示每段时间、机位、动作和旁白。</div>}
</div>
<div className="muted-2" style={{ fontSize: "12px", lineHeight: 1.55, marginBottom: "10px" }}>每个场由 image-2 直接生成一张完整导演故事板图片;可单独「重跑本场」只重出这一张,每场各自留历史版本,互不影响。</div>
<div className="muted mono" style={{ fontSize: "12px", fontWeight: 500, marginBottom: "6px", letterSpacing: ".04em" }}>整张风格提示词(重跑时生效,可编辑)</div>
<PromptBox
className="prompt-edit"