Browse Source

seedance视频积分处理

feature/interaction
TianYun 1 month ago
parent
commit
aff9a78907
  1. 59
      src/components/__tests__/GenerationComposer.test.tsx
  2. 25
      src/components/composer/GenerationComposer.tsx

59
src/components/__tests__/GenerationComposer.test.tsx

@ -814,6 +814,65 @@ describe("GenerationComposer", () => {
}); });
}); });
it("sums connected video durations and rounds up for Seedance estimates", async () => {
const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
const url = String(input);
if (url.startsWith("/api/points/estimate")) {
return new Response(JSON.stringify({
success: true,
data: {
estimated_points: 234,
},
}), { status: 200 });
}
if (url.startsWith("/api/models")) {
return new Response(JSON.stringify({ success: true, models: [] }), { status: 200 });
}
throw new Error(`Unexpected fetch: ${url}`);
});
vi.stubGlobal("fetch", fetchMock);
useWorkflowStore.setState({
nodes: [
videoNode("source-1", false, {
outputVideo: "https://static.popi.art/source-1.mp4",
durationSeconds: 2.2,
}),
videoNode("source-2", false, {
outputVideo: "https://static.popi.art/source-2.mp4",
outputDuration: 3.1,
}),
videoNode("vid-1", true, {
durationSeconds: 5,
resolution: "720p",
selectedModel: {
provider: "newapiwg",
modelId: "doubao-seedance-2-0-260128",
displayName: "doubao-seedance-2-0-260128",
capabilities: ["text-to-video", "image-to-video"],
},
}),
],
edges: [
videoEdge("source-1", "vid-1", 0),
videoEdge("source-2", "vid-1", 1),
],
});
render(<GenerationComposer />);
await waitFor(() => {
expect(fetchMock.mock.calls.some(([input]) => String(input).startsWith("/api/points/estimate"))).toBe(true);
});
const estimateCall = fetchMock.mock.calls.find(([input]) => String(input).startsWith("/api/points/estimate"));
expect(estimateCall).toBeDefined();
expect(JSON.parse(String((estimateCall?.[1] as RequestInit).body))).toMatchObject({
seedanceVideo: {
input_video_duration_seconds: 6,
output_video_duration_seconds: 5,
},
});
});
it("uses PopiServer task price estimates for selected image models", async () => { it("uses PopiServer task price estimates for selected image models", async () => {
const fetchMock = vi.fn(async (input: RequestInfo | URL) => { const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
const url = String(input); const url = String(input);

25
src/components/composer/GenerationComposer.tsx

@ -619,19 +619,6 @@ function isVideoHandle(handleId: string | null | undefined): boolean {
return handleId === "video" || handleId.startsWith("video-") || handleId.includes("video"); return handleId === "video" || handleId.startsWith("video-") || handleId.includes("video");
} }
function getConnectedVideoDurationSeconds(
targetNodeId: string,
nodes: WorkflowNode[],
edges: Array<{ source: string; target: string; sourceHandle?: string | null; targetHandle?: string | null }>
): number {
const videoEdge = edges.find((edge) =>
edge.target === targetNodeId &&
(isVideoHandle(edge.targetHandle) || isVideoHandle(edge.sourceHandle))
);
if (!videoEdge) return 0;
return getNodeVideoDurationSeconds(nodes.find((node) => node.id === videoEdge.source));
}
function getConnectedVideoDurationList( function getConnectedVideoDurationList(
targetNodeId: string, targetNodeId: string,
nodes: WorkflowNode[], nodes: WorkflowNode[],
@ -1168,16 +1155,18 @@ export function GenerationComposer() {
return getConnectedInputs(context.node.id); return getConnectedInputs(context.node.id);
}, [context, getConnectedInputs, nodes, edges, dimmedNodeIds]); }, [context, getConnectedInputs, nodes, edges, dimmedNodeIds]);
const connectedVideoDurationSeconds = useMemo(() => {
if (context.mode !== "node-edit" || !context.node) return 0;
return getConnectedVideoDurationSeconds(context.node.id, nodes, edges);
}, [context, edges, nodes]);
const connectedVideoDurations = useMemo(() => { const connectedVideoDurations = useMemo(() => {
if (context.mode !== "node-edit" || !context.node) return []; if (context.mode !== "node-edit" || !context.node) return [];
return getConnectedVideoDurationList(context.node.id, nodes, edges); return getConnectedVideoDurationList(context.node.id, nodes, edges);
}, [context, edges, nodes]); }, [context, edges, nodes]);
const connectedVideoDurationSeconds = useMemo(() => {
const totalDuration = connectedVideoDurations.reduce((sum, duration) => {
return Number.isFinite(duration) && duration > 0 ? sum + duration : sum;
}, 0);
return Math.ceil(totalDuration);
}, [connectedVideoDurations]);
const estimatedReferenceImageCount = context.mode === "node-edit" && (connectedInputs?.images.length ?? 0) > 0 const estimatedReferenceImageCount = context.mode === "node-edit" && (connectedInputs?.images.length ?? 0) > 0
? connectedInputs?.images.length ?? 0 ? connectedInputs?.images.length ?? 0
: draft.inputImages.length; : draft.inputImages.length;

Loading…
Cancel
Save