From 53483b3d39a8287239f4de1cdeaa16d41ae0e36b Mon Sep 17 00:00:00 2001 From: ddCat <2695092045@qq.com> Date: Fri, 31 Jul 2026 20:19:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(canvas):=20=E8=A7=86=E9=A2=91=E8=8A=82?= =?UTF-8?q?=E7=82=B9=20-=20=E6=8E=A5=E5=85=A5=20Vidstack=20=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E5=99=A8=E5=B9=B6=E6=94=B6=E6=95=9B=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E8=8C=83=E5=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/bun.lock | 5 + web/package.json | 1 + web/src/components/canvas/canvas-node.tsx | 9 +- web/src/components/video-player.css | 17 +++ web/src/components/video-player.tsx | 102 ++++++++++++++++++ .../canvas/canvas-project-status-dialogs.tsx | 3 +- .../canvas/use-canvas-generation-batches.ts | 17 ++- web/src/pages/canvas/use-canvas-generation.ts | 2 +- 8 files changed, 144 insertions(+), 12 deletions(-) create mode 100644 web/src/components/video-player.css create mode 100644 web/src/components/video-player.tsx diff --git a/web/bun.lock b/web/bun.lock index e4611ce89..64624a1af 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -23,6 +23,7 @@ "@tiptap/react": "^3.28.0", "@tiptap/starter-kit": "^3.28.0", "@uiw/react-codemirror": "^4.25.11", + "@vidstack/react": "1.15.6", "antd": "^6.5.1", "axios": "^1.18.1", "class-variance-authority": "^0.7.1", @@ -821,6 +822,8 @@ "@use-gesture/react": ["@use-gesture/react@10.3.1", "", { "dependencies": { "@use-gesture/core": "10.3.1" }, "peerDependencies": { "react": ">= 16.8.0" } }, "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g=="], + "@vidstack/react": ["@vidstack/react@1.15.6", "", { "dependencies": { "@floating-ui/dom": "^1.6.10", "media-captions": "^1.0.4" }, "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0" } }, "sha512-hO7cLld5yfxCdDnDCYqh5aS7JxmHfsmX43PmKBJWzDTwK40P3hh5qAdDQzl3qDtEJLYjuo3Sv+kr6tt/VRtJzQ=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.3", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg=="], "@xmldom/xmldom": ["@xmldom/xmldom@0.8.13", "", {}, "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw=="], @@ -1327,6 +1330,8 @@ "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + "media-captions": ["media-captions@1.0.4", "", {}, "sha512-cyDNmuZvvO4H27rcBq2Eudxo9IZRDCOX/I7VEyqbxsEiD2Ei7UYUhG/Sc5fvMZjmathgz3fEK7iAKqvpY+Ux1w=="], + "media-typer": ["media-typer@1.1.0", "https://registry.npmmirror.com/media-typer/-/media-typer-1.1.0.tgz", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], "merge-descriptors": ["merge-descriptors@2.0.0", "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], diff --git a/web/package.json b/web/package.json index 55ef902c9..8b3b22173 100644 --- a/web/package.json +++ b/web/package.json @@ -29,6 +29,7 @@ "@tiptap/react": "^3.28.0", "@tiptap/starter-kit": "^3.28.0", "@uiw/react-codemirror": "^4.25.11", + "@vidstack/react": "1.15.6", "antd": "^6.5.1", "axios": "^1.18.1", "class-variance-authority": "^0.7.1", diff --git a/web/src/components/canvas/canvas-node.tsx b/web/src/components/canvas/canvas-node.tsx index 75f4fe04a..f2503f132 100644 --- a/web/src/components/canvas/canvas-node.tsx +++ b/web/src/components/canvas/canvas-node.tsx @@ -17,6 +17,7 @@ import { CanvasNodeType, type CanvasNodeData, type Position } from "@/types/canv import type { CanvasResourceReference } from "@/lib/canvas/canvas-resource-references"; import { loadCanvasDrawingPreview } from "@/lib/canvas/canvas-drawing-storage"; import { MEDIA_NODE_MIN_SIZE } from "@/lib/canvas/canvas-node-size"; +import { VideoPlayer } from "@/components/video-player"; type ResizeCorner = "top-left" | "top-right" | "bottom-left" | "bottom-right"; type CanvasTheme = (typeof canvasThemes)[keyof typeof canvasThemes]; @@ -883,14 +884,8 @@ function EmptyImageContent({ node, theme, isBatchRoot, batchCount, batchExpanded } function VideoNodeContent({ node, theme, reduceMediaEffects }: NodeContentRendererProps) { - const videoRef = useRef(null); const playWhenReadyRef = useRef(false); const { url, loading, load } = useNodeResourceUrl(node, false); - useEffect(() => { - if (!url || !playWhenReadyRef.current) return; - playWhenReadyRef.current = false; - void videoRef.current?.play().catch(() => undefined); - }, [url]); if (!node.metadata?.content) return (
@@ -901,7 +896,7 @@ function VideoNodeContent({ node, theme, reduceMediaEffects }: NodeContentRender if (!url) { return : } label={loading ? "正在缓存视频" : "加载并缓存视频"} disabled={loading} onClick={() => { playWhenReadyRef.current = true; void load(); }} />; } - return