import { app } from "../../../scripts/app.js"; import { api } from "../../../scripts/api.js"; app.registerExtension({ name: "IF_VideoPrompts", async setup() { console.log("Video Prompt Generator extension setup"); }, async beforeRegisterNodeDef(nodeType, nodeData, app) { if (nodeType.comfyClass === "VideoPromptNode") { // Enhance the node's appearance and functionality // Override the onDrawBackground method to add processing information const origDrawBackground = nodeType.prototype.onDrawBackground; nodeType.prototype.onDrawBackground = function(ctx) { if (origDrawBackground) { origDrawBackground.apply(this, arguments); } if (this.widgets && this.widgets.length > 0) { // Display frame count if images are connected const images = this.getInputData(0); if (images && images.shape) { let frameCount = images.shape[0]; ctx.fillStyle = "#AAA"; ctx.font = "12px Arial"; ctx.fillText(`Sequence: ${frameCount} frame(s)`, 10, 35); // Display a visual indication of frame sampling const width = this.size[0]; const height = 10; const y = 45; // Draw background ctx.fillStyle = "#333"; ctx.fillRect(10, y, width - 20, height); // Draw frame markers ctx.fillStyle = "#6E6"; const spacing = (width - 20) / Math.min(frameCount, 20); for (let i = 0; i < Math.min(frameCount, 20); i++) { ctx.fillRect(10 + i * spacing, y, 2, height); } // Show which frames are sampled const samplesWidget = this.widgets.find(w => w.name === "frame_sample_count"); if (samplesWidget) { const sampleCount = samplesWidget.value; ctx.fillStyle = "#F66"; if (frameCount > sampleCount) { for (let i = 0; i < sampleCount; i++) { const idx = Math.floor(i * (frameCount-1) / (sampleCount-1)); const x = 10 + (idx / (frameCount-1)) * (width - 20); ctx.fillRect(x-2, y-2, 4, height+4); } } } } // Draw the model name const modelWidget = this.widgets.find(w => w.name === "model_name"); if (modelWidget) { ctx.fillStyle = "#AAA"; ctx.font = "12px Arial"; ctx.fillText(`Model: ${modelWidget.value}`, 10, 70); } } }; // Update the canvas when widget values change const origOnWidgetChange = nodeType.prototype.onWidgetChanged; nodeType.prototype.onWidgetChanged = function(widget, name, oldValue, newValue) { if (origOnWidgetChange) { origOnWidgetChange.apply(this, arguments); } if (name === "frame_sample_count" || name === "analysis_type" || name === "fps" || name === "model_name" || name === "input_mode") { this.setDirtyCanvas(true, false); } // Auto-update dependencies based on selected options if (name === "input_mode") { // Show/hide relevant widgets based on input mode const isFrameMode = newValue === "Frames"; const videoWidget = this.widgets.find(w => w.name === "video_file"); const imagesWidget = this.widgets.find(w => w.name === "images"); if (videoWidget) { videoWidget.hidden = isFrameMode; } if (imagesWidget) { imagesWidget.hidden = !isFrameMode; } } }; // Add info about execution time nodeType.prototype.onExecuted = function(message) { if (message && message.status === "executed") { this.execution_time = message.execution_time || 0; this.setDirtyCanvas(true, false); } }; // Add custom right-click menu options nodeType.prototype.getExtraMenuOptions = function(_, options) { options.push({ content: "📝 Analysis Options", callback: () => { const fpsWidget = this.widgets.find(w => w.name === "fps"); const sampleCountWidget = this.widgets.find(w => w.name === "frame_sample_count"); const maxPixelsWidget = this.widgets.find(w => w.name === "max_pixels"); const msg = `
FPS: ${fpsWidget ? fpsWidget.value : "N/A"} - Controls frame sampling rate
Frame Samples: ${sampleCountWidget ? sampleCountWidget.value : "N/A"} - Number of frames to analyze
Max Pixels: ${maxPixelsWidget ? maxPixelsWidget.value : "N/A"} - Image resolution limit
Higher FPS (8-16) gives better motion analysis but uses more memory
More frame samples (12-24) improves analysis quality but increases processing time