perf(canvas): 画布媒体 - 优化媒体加载、播放器与节点交互 (#357)
Merge origin/main at fb089b2 and retain canvas media performance, player layout, and interaction updates.
Co-authored-by: ddCat <2695092045@qq.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { buildNodeMentionReferences, canvasResourceMentionToken, collectUpstreamVideoNodes } from "../src/lib/canvas/canvas-resource-references";
|
||||
import { buildAssetMentionReferences, buildNodeMentionReferences, buildOrderedCanvasResourceReferences, canvasResourceMentionToken, collectUpstreamVideoNodes } from "../src/lib/canvas/canvas-resource-references";
|
||||
import { canvasNodeToAsset } from "../src/lib/canvas/canvas-node-asset";
|
||||
import { buildNodeGenerationInputs } from "../src/components/canvas/canvas-node-generation";
|
||||
import { CanvasNodeType, type CanvasConnection, type CanvasNodeData } from "../src/types/canvas";
|
||||
|
||||
function videoNode(id: string): CanvasNodeData {
|
||||
@@ -76,6 +78,89 @@ describe("collectUpstreamVideoNodes", () => {
|
||||
});
|
||||
|
||||
describe("canvas resource mention slots", () => {
|
||||
test("素材库视频优先使用封面,没有封面时保留首帧视频回退源", () => {
|
||||
const poster = buildAssetMentionReferences([{
|
||||
id: "video-with-poster",
|
||||
kind: "video",
|
||||
title: "带封面视频",
|
||||
coverUrl: "https://cdn.example.com/poster.jpg",
|
||||
tags: [],
|
||||
createdAt: "2026-08-31T00:00:00.000Z",
|
||||
updatedAt: "2026-08-31T00:00:00.000Z",
|
||||
data: { url: "https://cdn.example.com/video.mp4", storageKey: "resource:video", width: 1280, height: 720, bytes: 1, mimeType: "video/mp4" },
|
||||
}])[0];
|
||||
expect(poster?.previewUrl).toBe("https://cdn.example.com/poster.jpg");
|
||||
expect(poster?.mediaUrl).toBeUndefined();
|
||||
|
||||
const legacy = buildAssetMentionReferences([{
|
||||
id: "legacy-video",
|
||||
kind: "video",
|
||||
title: "旧视频",
|
||||
coverUrl: "https://cdn.example.com/video.mp4",
|
||||
tags: [],
|
||||
createdAt: "2026-08-31T00:00:00.000Z",
|
||||
updatedAt: "2026-08-31T00:00:00.000Z",
|
||||
data: { url: "https://cdn.example.com/video.mp4", storageKey: "resource:legacy-video", width: 1280, height: 720, bytes: 1, mimeType: "video/mp4" },
|
||||
}])[0];
|
||||
expect(legacy?.previewUrl).toBe("");
|
||||
expect(legacy?.mediaUrl).toBe("https://cdn.example.com/video.mp4");
|
||||
});
|
||||
|
||||
test("保存画布视频资产时保留节点已有的静态首帧", () => {
|
||||
const node = videoNode("video-with-poster");
|
||||
node.metadata = {
|
||||
content: "https://cdn.example.com/video.mp4",
|
||||
videoPreview: { content: "https://cdn.example.com/poster.jpg", storageKey: "resource:poster" },
|
||||
};
|
||||
const asset = canvasNodeToAsset(node, { canvasId: "canvas", source: "canvas-upload" });
|
||||
expect(asset?.kind).toBe("video");
|
||||
expect(asset && "coverUrl" in asset ? asset.coverUrl : "").toBe("https://cdn.example.com/poster.jpg");
|
||||
});
|
||||
|
||||
test("上传视频作为生成设置引用时携带首帧预览,而不是播放器地址", () => {
|
||||
const source = videoNode("uploaded-video");
|
||||
source.metadata = {
|
||||
content: "https://cdn.example.com/video.mp4",
|
||||
storageKey: "video:user:uploaded",
|
||||
videoPreview: { content: "blob:poster", storageKey: "image:user:poster", width: 400, height: 225 },
|
||||
};
|
||||
const config: CanvasNodeData = {
|
||||
id: "config",
|
||||
type: CanvasNodeType.Config,
|
||||
title: "生成设置",
|
||||
position: { x: 0, y: 0 },
|
||||
width: 320,
|
||||
height: 180,
|
||||
metadata: {},
|
||||
};
|
||||
const inputs = buildNodeGenerationInputs(config.id, [source, config], [connection(source.id, config.id)]);
|
||||
expect(inputs).toHaveLength(1);
|
||||
expect(inputs[0]?.type).toBe("video");
|
||||
expect(inputs[0]?.previewUrl).toBe("blob:poster");
|
||||
expect(inputs[0]?.previewUrl).not.toBe(source.metadata.content);
|
||||
});
|
||||
|
||||
test("视频引用只暴露静态首帧,不把原视频当缩略图", () => {
|
||||
const genericVideo = videoNode("generic-video");
|
||||
genericVideo.metadata = { content: "https://example.com/video.mp4" };
|
||||
const libtvVideo = videoNode("libtv-video");
|
||||
libtvVideo.metadata = { content: "https://libtv-res.liblib.art/path/video.mp4" };
|
||||
|
||||
const references = buildOrderedCanvasResourceReferences([genericVideo, libtvVideo]);
|
||||
expect(references[0]?.previewUrl).toBe("");
|
||||
expect(references[1]?.previewUrl).toContain("video%2Fsnapshot");
|
||||
|
||||
const uploaded = videoNode("uploaded-video");
|
||||
uploaded.metadata = {
|
||||
content: "https://cdn.example.com/video.mp4",
|
||||
storageKey: "video:user:source",
|
||||
videoPreview: { content: "https://cdn.example.com/poster.jpg", storageKey: "image:user:poster" },
|
||||
};
|
||||
const [uploadedReference] = buildOrderedCanvasResourceReferences([uploaded]);
|
||||
expect(uploadedReference?.previewStorageKey).toBe("image:user:poster");
|
||||
expect(uploadedReference?.storageKey).toBe("video:user:source");
|
||||
});
|
||||
|
||||
test("画布节点引用只保存类型位置,不保存节点 ID", () => {
|
||||
const target = videoNode("target");
|
||||
const image = imageNode("image-a");
|
||||
|
||||
Reference in New Issue
Block a user