测试极速成片

This commit is contained in:
Azmat@qq.com
2026-08-26 15:18:18 +08:00
parent 245525ec53
commit 0ee498d807
30 changed files with 1963 additions and 367 deletions
+39 -7
View File
@@ -21,6 +21,7 @@ import type {
} from "./types";
import { publicModelDisplayName } from "./model-display";
import { generationErrorText } from "./generation-error";
import { isQuickCreateBusy, lockedQuickCreateProject, rememberQuickCreateJob, withQuickCreateStatus } from "./quick-create-lock";
import { AccountMenu, CornerMarks, Decorations, ModeTabs, openCommandPalette, Sidebar, ToastLike, topModuleForPage } from "./components/app-shell";
import { SystemLoading } from "./components/loading";
import {
@@ -415,6 +416,17 @@ export function App() {
};
}, [authed, page, activeProjectId, detailRetry]);
useEffect(() => {
if (!authed || page !== "pipeline" || !activeProjectId) return;
const listed = projects.find((item) => item.id === activeProjectId);
const detailed = projectDetail && projectDetail.id === activeProjectId ? projectDetail : null;
const locked = lockedQuickCreateProject(listed, detailed);
if (!locked) return;
rememberQuickCreateJob(locked.quick_create_job_id);
setNotice({ type: "info", text: "该项目正在极速成片中,请在极速成片页查看进度" });
navigate("quickCreate", { productId: locked.product, replace: true });
}, [authed, page, activeProjectId, projects, projectDetail]);
// 静默轮询运行中的视频段(本机无 Celery worker,由前端驱动 poll-video-segment),实时刷新管线进度,不弹 toast。
// 资源账:旧实现每轮「GET 项目 → 逐段串行 POST → 再 GET 项目」,4 段在途时一轮 = 2 个 26KB GET + 4 个串行
// ARK 轮询(总耗时随段数线性涨)。现用内存态定位在途段(省前置 GET),段间 Promise.all 并行,一轮只回读一次。
@@ -516,12 +528,36 @@ export function App() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeProjectId, refreshExport]);
function applyQuickCreateStatus(projectId: string, status: string) {
setProjects((items) => withQuickCreateStatus(items, projectId, status));
setProjectDetail((current) => (
current && current.id === projectId ? { ...current, quick_create_status: status } : current
));
}
function navigate(next: Page, options: NavigateOptions = {}) {
// 已知为子账号时在发起导航前拦截;与直接访问 URL 的 layout guard 共用同一规则。
if (role && !isOwner && isOwnerOnlyPage(next)) {
setNotice({ type: "info", text: "当前账号暂无访问权限" });
return;
}
if (next === "pipeline") {
const targetId = options.projectId ?? activeProjectId;
const listed = projects.find((item) => item.id === targetId);
const detailed = projectDetail && projectDetail.id === targetId ? projectDetail : null;
const released = options.quickCreateStatus || "failed";
const canForce = Boolean(options.forcePipeline && targetId && !isQuickCreateBusy({ quick_create_status: released }));
if (canForce && targetId) {
applyQuickCreateStatus(targetId, released);
}
const locked = canForce ? null : lockedQuickCreateProject(listed, detailed);
if (locked) {
rememberQuickCreateJob(locked.quick_create_job_id);
setNotice({ type: "info", text: "该项目正在极速成片中,请在极速成片页查看进度" });
next = "quickCreate";
options = { ...options, productId: locked.product, replace: options.replace };
}
}
// 图片创作 / 极速成片只有显式入口才携带商品;从工作台或视频创作进入时不能继承全局当前商品。
const productId = next === "imageOptimize" || next === "quickCreate" ? options.productId : (options.productId ?? activeProductId);
const projectId = options.projectId ?? activeProjectId;
@@ -1064,6 +1100,7 @@ export function App() {
modelConfigs={modelConfigs}
onNotify={(type, text) => setNotice({ type, text })}
onProjectCreated={() => { void loadData(); }}
onQuickCreateStatus={applyQuickCreateStatus}
/>
);
case "videoRemix":
@@ -1151,19 +1188,14 @@ export function App() {
onAdoptVideoVersion={(segmentId, versionId) => action(() => api.adoptVideoVersion(pipelineProject.id, { video_segment_id: segmentId, version_id: versionId }), "已采用该版本")}
onGenerateVoiceover={(payload) => action(() => api.generateVoiceover(pipelineProject.id, payload), "配音已生成")}
onGenerateBaseAsset={async (kind, prompt, label, referenceAssetId) => {
// 异步:提交→轮询出图→刷新。角色立绘成功后立刻据该立绘出三视图,用户不必再进详情点一次。
// 异步:提交→轮询出图→刷新。角色三视图由后端在立绘落库后自动接力,
// 避免页面刷新/离开时漏掉,也避免这里重复创建第二个三视图任务。
// referenceAssetId:角色重跑时传当前立绘 → 后端走 image_edit 参考它,保持人物一致
const assetId = await submitAndPollAsset(
() => api.generateBaseAsset(pipelineProject.id, { kind, prompt, label, reference_asset_id: referenceAssetId }),
kind === "person" ? "" : "基础资产已生成",
);
if (!assetId) return null;
if (kind === "person") {
await submitAndPollAsset(
() => api.generateTriview(pipelineProject.id, { portrait_asset_id: assetId }),
"角色立绘与三视图已生成",
);
}
return { adopted_asset: assetId };
}}
onGenerateStoryboard={(prompt) =>