From 919dcffaf254fbd38555333cba8e6c53d6b6fcda Mon Sep 17 00:00:00 2001 From: Suzu008 Date: Sun, 31 May 2026 18:02:55 +0800 Subject: [PATCH] fix: reuse video preview widgets to support Vue Nodes 2.0 mode - SaveVideo/PreviewVideo: reuse existing video-preview widgets instead of removing and recreating them on each execution, preventing WidgetDOM from losing its mounted element reference in VueNodes mode - UploadVideo: remove duplicate updateVideoWidget call in handleVideoUpload that caused parallel fetch/decrypt race on the same widget element --- .gitignore | 3 ++- ts/cryptioSaveVideo.ts | 55 +++++++++++++++++++++++++--------------- ts/cryptioUploadVideo.ts | 12 ++------- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index f9163e7..2f1badb 100644 --- a/.gitignore +++ b/.gitignore @@ -114,4 +114,5 @@ yarn-error.log* node.zip .vscode/ .claude/ -.*/ \ No newline at end of file +.*/ +opencode.json \ No newline at end of file diff --git a/ts/cryptioSaveVideo.ts b/ts/cryptioSaveVideo.ts index ae72f4a..28fbdf0 100644 --- a/ts/cryptioSaveVideo.ts +++ b/ts/cryptioSaveVideo.ts @@ -55,38 +55,53 @@ app.registerExtension({ if (message?.cryptio_images) { const autoDownload = this.widgets.find((n: any) => n.name === "auto_download")?.value; - // Remove existing video preview widgets + // Get existing video preview widgets (reuse them to avoid re-mount in VueNodes mode) const existingWidgets = this.widgets.filter((w: any) => w.type === "video-preview" ); - for (const widget of existingWidgets) { - clearVideoWidget(widget); - this.removeWidget(widget); + + // Remove excess widgets if fewer videos than existing widgets + for (let i = message.cryptio_images.length; i < existingWidgets.length; i++) { + clearVideoWidget(existingWidgets[i]); + this.removeWidget(existingWidgets[i]); } - // Create widgets and decrypt videos in parallel + // Reuse existing widgets or create new ones as needed const updatePromises: Promise[] = []; for (let i = 0; i < message.cryptio_images.length; i++) { const videoInfo = message.cryptio_images[i]; - if (videoInfo.filename && videoInfo.filename.endsWith(".encrypted")) { - const widget = createVideoWidget(this, { + if (!videoInfo.filename || !videoInfo.filename.endsWith(".encrypted")) continue; + + let widget = existingWidgets[i]; + if (widget) { + // Clean up previous video content before reusing the widget + if (widget.videoElement) { + widget.videoElement.pause(); + widget.videoElement.src = ""; + } + if (widget.videoUrl) { + URL.revokeObjectURL(widget.videoUrl); + } + widget.videoInfo = videoInfo; + widget.value = { ...widget.value, videoInfo }; + } else { + widget = createVideoWidget(this, { widgetName: `video_${i}`, videoInfo, }); - - updatePromises.push( - updateVideoWidget(widget, videoInfo) - .then((videoUrl) => { - if (autoDownload) { - downloadDecryptedVideo(videoUrl, videoInfo.filename); - console.log(`Auto-downloaded decrypted video: ${videoInfo.filename.replace(/\.encrypted$/, "")}`); - } - }) - .catch((error) => { - console.error("Failed to load video:", error); - }) - ); } + + updatePromises.push( + updateVideoWidget(widget, videoInfo) + .then((videoUrl) => { + if (autoDownload) { + downloadDecryptedVideo(videoUrl, videoInfo.filename); + } + }) + .catch((error) => { + console.error("Failed to load video:", error); + }) + ); } await Promise.allSettled(updatePromises); } diff --git a/ts/cryptioUploadVideo.ts b/ts/cryptioUploadVideo.ts index 6f5a00c..a1e57f9 100644 --- a/ts/cryptioUploadVideo.ts +++ b/ts/cryptioUploadVideo.ts @@ -48,21 +48,13 @@ async function handleVideoUpload(node: CryptIONode, file: File, app: CryptIOApp) // Upload encrypted video const result = await uploadEncryptedFile(file); - // Update video widget value + // Update video widget value (callback handles preview update) const videoWidget = node.widgets.find( (w: any) => w.name === "video" ); if (videoWidget) { videoWidget.value = result.name; - videoWidget.callback?.call(videoWidget, result.name); - } - - // Update preview - const previewWidget = node.widgets.find( - (w: any) => w.type === "video-preview" - ) as VideoPreviewWidget; - if (previewWidget) { - await updateVideoWidget(previewWidget, result.name); + await videoWidget.callback?.call(videoWidget, result.name); } if (app.rootGraph) {