mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-22 15:29:44 +00:00
Fix/vue nodes video (#5870)
## Summary Fix the video preview widget and associated dropdown to load and select videos. Fixes: - https://www.notion.so/comfy-org/Video-thumbnails-not-being-used-in-asset-explorer-dialog-27e6d73d365080ec8a3ee7c7ec413657?source=copy_link - https://www.notion.so/comfy-org/Image-Video-upload-dialog-doesnt-set-mime-type-27e6d73d365080c5bffdf08842855ba0?source=copy_link - https://www.notion.so/comfy-org/Video-Previews-are-not-displayed-2756d73d365080b2bfb9e0004e9d784d?source=copy_link - https://www.notion.so/comfy-org/Cannot-load-video-in-Load-Video-node-2756d73d365080009c21d3a67add96c4?source=copy_link ## Screenshots (if applicable) https://github.com/user-attachments/assets/b71dbecb-c9a7-4feb-83a3-c3e044a9c93c ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5870-Fix-vue-nodes-video-27e6d73d36508182b44bef8e90ef4018) by [Unito](https://www.unito.io) --------- Co-authored-by: JakeSchroeder <jake@axiom.co> Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Jake Schroeder <jake.schroeder@isophex.com> Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe>
This commit is contained in:
@@ -101,7 +101,7 @@
|
||||
<NodeContent
|
||||
v-if="hasCustomContent"
|
||||
:node-data="nodeData"
|
||||
:image-urls="nodeImageUrls"
|
||||
:media="nodeMedia"
|
||||
/>
|
||||
<!-- Live preview image -->
|
||||
<div v-if="shouldShowPreviewImg" class="px-4">
|
||||
@@ -267,10 +267,10 @@ onMounted(() => {
|
||||
// Track collapsed state
|
||||
const isCollapsed = computed(() => nodeData.flags?.collapsed ?? false)
|
||||
|
||||
// Check if node has custom content (like image outputs)
|
||||
// Check if node has custom content (like image/video outputs)
|
||||
const hasCustomContent = computed(() => {
|
||||
// Show custom content if node has image outputs
|
||||
return nodeImageUrls.value.length > 0
|
||||
// Show custom content if node has media outputs
|
||||
return !!nodeMedia.value && nodeMedia.value.urls.length > 0
|
||||
})
|
||||
|
||||
// Computed classes and conditions for better reusability
|
||||
@@ -340,26 +340,29 @@ const nodeOutputs = useNodeOutputStore()
|
||||
const nodeOutputLocatorId = computed(() =>
|
||||
nodeData.subgraphId ? `${nodeData.subgraphId}:${nodeData.id}` : nodeData.id
|
||||
)
|
||||
const nodeImageUrls = computed(() => {
|
||||
const newOutputs = nodeOutputs.nodeOutputs[nodeOutputLocatorId.value]
|
||||
|
||||
const lgraphNode = computed(() => {
|
||||
const locatorId = getLocatorIdFromNodeData(nodeData)
|
||||
|
||||
// Use root graph for getNodeByLocatorId since it needs to traverse from root
|
||||
const rootGraph = app.graph?.rootGraph || app.graph
|
||||
if (!rootGraph) {
|
||||
return []
|
||||
}
|
||||
if (!rootGraph) return null
|
||||
return getNodeByLocatorId(rootGraph, locatorId)
|
||||
})
|
||||
|
||||
const node = getNodeByLocatorId(rootGraph, locatorId)
|
||||
const nodeMedia = computed(() => {
|
||||
const newOutputs = nodeOutputs.nodeOutputs[nodeOutputLocatorId.value]
|
||||
const node = lgraphNode.value
|
||||
|
||||
// Note: Despite the field name "images", videos are also included.
|
||||
// The actual media type is determined by node.previewMediaType
|
||||
// TODO: fix the backend to return videos using the vidoes key instead of the images key
|
||||
if (node && newOutputs?.images?.length) {
|
||||
const urls = nodeOutputs.getNodeImageUrls(node)
|
||||
if (urls) {
|
||||
return urls
|
||||
if (urls && urls.length > 0) {
|
||||
const type = node.previewMediaType === 'video' ? 'video' : 'image'
|
||||
return { type, urls } as const
|
||||
}
|
||||
}
|
||||
// Clear URLs if no outputs or no images
|
||||
return []
|
||||
return undefined
|
||||
})
|
||||
|
||||
const nodeContainerRef = ref()
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
<div v-else class="lg-node-content">
|
||||
<!-- Default slot for custom content -->
|
||||
<slot>
|
||||
<VideoPreview
|
||||
v-if="hasMedia && media?.type === 'video'"
|
||||
:image-urls="media.urls"
|
||||
:node-id="nodeId"
|
||||
class="mt-2"
|
||||
/>
|
||||
<ImagePreview
|
||||
v-if="hasImages"
|
||||
:image-urls="props.imageUrls || []"
|
||||
v-else-if="hasMedia && media?.type === 'image'"
|
||||
:image-urls="media.urls"
|
||||
:node-id="nodeId"
|
||||
class="mt-2"
|
||||
/>
|
||||
@@ -20,24 +26,24 @@ import { computed, onErrorCaptured, ref } from 'vue'
|
||||
|
||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
|
||||
import VideoPreview from '../VideoPreview.vue'
|
||||
import ImagePreview from './ImagePreview.vue'
|
||||
|
||||
interface NodeContentProps {
|
||||
node?: LGraphNode // For backwards compatibility
|
||||
nodeData?: VueNodeData // New clean data structure
|
||||
imageUrls?: string[]
|
||||
nodeData?: VueNodeData
|
||||
media?: {
|
||||
type: 'image' | 'video'
|
||||
urls: string[]
|
||||
}
|
||||
}
|
||||
|
||||
const props = defineProps<NodeContentProps>()
|
||||
|
||||
const hasImages = computed(() => props.imageUrls && props.imageUrls.length > 0)
|
||||
const hasMedia = computed(() => props.media && props.media.urls.length > 0)
|
||||
|
||||
// Get node ID from nodeData or node prop
|
||||
const nodeId = computed(() => {
|
||||
return props.nodeData?.id?.toString() || props.node?.id?.toString()
|
||||
})
|
||||
// Get node ID from nodeData
|
||||
const nodeId = computed(() => props.nodeData?.id?.toString())
|
||||
|
||||
// Error boundary implementation
|
||||
const renderError = ref<string | null>(null)
|
||||
|
||||
Reference in New Issue
Block a user