Functions/GenXdev.AI.ComfyUI/Invoke-ComfyUIImageGeneration.ps1
<##############################################################################
Part of PowerShell module : GenXdev.AI.ComfyUI Original cmdlet filename : Invoke-ComfyUIImageGeneration.ps1 Original author : René Vaessen / GenXdev Version : 1.270.2025 ################################################################################ MIT License Copyright 2021-2025 GenXdev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ################################################################################> ############################################################################### <# .SYNOPSIS Comprehensive ComfyUI CLI for all image generation scenarios with automatic model management and multi-model batch processing .DESCRIPTION Advanced unified CLI wrapper for ComfyUI that handles all text-to-image and image-to-image workflows with intelligent model management and multi-model generation capabilities. Automatically detects, downloads, and installs required models, and supports batch generation across multiple models for comprehensive comparison workflows. KEY FEATURES: - Automatic model discovery and installation from supported list - Multi-model batch generation with the -UseAllModels parameter - Intelligent architecture detection (SD1.5 vs SDXL) with optimized settings - Robust error handling and recovery for uninterrupted batch processing - GPU and CPU optimization with architecture-specific defaults - Advanced image format conversion and organized output management MODEL MANAGEMENT: The function automatically ensures model availability by searching local ComfyUI installations and downloading from supported list when needed. The -UseAllModels switch enables comparison generation across all compatible installed models. MULTI-MODEL GENERATION: When -UseAllModels is specified, generates images using every compatible model found locally. Creates organized output directories with model-specific naming for easy comparison. Continues processing even if individual models fail, providing comprehensive batch generation with error resilience. Based on the proven CPU-optimized version with GPU support added. Automatically detects and connects to running ComfyUI instances, manages image processing, handles format conversions, and provides detailed progress feedback for both single and multi-model generation workflows. .PARAMETER Prompt The text prompt for image generation .PARAMETER NegativePrompt Negative prompt to exclude unwanted elements (default: "blurry, low quality, distorted") .PARAMETER InputImage Path to input image for img2img processing (optional - if not provided, does text-to-image) .PARAMETER OutFile Path to save the generated image (e.g., C:\temp\output.png). Defaults to '.\<input_filename>.out.jpg' for img2img or a prompt-based name for txt2img .PARAMETER Steps Number of denoising steps (default: 15 for CPU, 20 for GPU) .PARAMETER ImageWidth Output image width in pixels (default: 1024) .PARAMETER ImageHeight Output image height in pixels (default: 1024) .PARAMETER CfgScale CFG Scale for prompt adherence (default: 7.0, range: 1-20) .PARAMETER Strength Denoising strength for img2img (default: 0.75, range: 0.1-1.0) .PARAMETER Seed Random seed for reproducible results (default: random) .PARAMETER BatchSize Number of images to generate (default: 1, max: 4) .PARAMETER Model Model checkpoint(s) to use. Can specify a single model name or an array of multiple models. When multiple models are provided, they are combined with any models discovered through -UseAllModels. Default: Stable Diffusion 2.1 (improved quality over v1-5 with high downloads) .PARAMETER Sampler Sampling method (default: "euler", options: euler, dpmpp_2m, dpmpp_sde, ddim, etc.) .PARAMETER Scheduler Scheduler type (default: "normal", options: normal, karras, exponential, sgm_uniform) .PARAMETER CpuThreads Number of CPU threads to use (default: auto-detect, only used when UseGPU is false) .PARAMETER Timeout Timeout in seconds for ComfyUI operations (default: 600) .PARAMETER UseGPU Enable GPU acceleration (default: CPU-only for server-friendly operation) .PARAMETER NoResizeComfyInputImage Skip automatic resizing of input images to optimal dimensions .PARAMETER NoShowWindow Switch to not show the ComfyUI window .PARAMETER Monitor The monitor to use, 0 = default, -1 is discard .PARAMETER NoBorders Removes the borders of the window .PARAMETER Width The initial width of the window .PARAMETER Height The initial height of the window .PARAMETER X The initial X position of the window .PARAMETER Y The initial Y position of the window .PARAMETER Left Place window on the left side of the screen .PARAMETER Right Place window on the right side of the screen .PARAMETER Top Place window on the top side of the screen .PARAMETER Bottom Place window on the bottom side of the screen .PARAMETER Centered Place window in the center of the screen .PARAMETER FullScreen Sends F11 to the window .PARAMETER RestoreFocus Restore PowerShell window focus .PARAMETER SideBySide Position windows side by side .PARAMETER FocusWindow Focus the window after opening .PARAMETER SetForeground Set the window to foreground after opening .PARAMETER Maximize Maximize the window after positioning .PARAMETER KeysToSend Keys to send to the ComfyUI window .PARAMETER SendKeyEscape Escape control characters and modifiers when sending keys .PARAMETER SendKeyHoldKeyboardFocus Hold keyboard focus on target window when sending keys .PARAMETER SendKeyUseShiftEnter Use Shift+Enter instead of Enter when sending keys .PARAMETER SendKeyDelayMilliSeconds Delay in milliseconds between key sends .PARAMETER Force Force shutdown of any existing ComfyUI processes before starting .PARAMETER NoShutdown Keep ComfyUI running after image generation .PARAMETER UseAllModels Generate images using all compatible installed models instead of just the specified one. Uses all supported models from the hardcoded list. .PARAMETER ModelPath Optional custom path to set as the base path in extra_model_paths.yaml for the specified model(s). If provided, updates the ComfyUI configuration to use this path for checkpoints. .EXAMPLE Invoke-ComfyUIImageGeneration -Prompt "a cat in space" -OutFile "cat.jpg" Generates an image using the default model (Stable Diffusion 2.1). .EXAMPLE Invoke-ComfyUIImageGeneration "a dragon" "blurry" -Model "Juggernaut XL" ` -UseGPU -Steps 25 Uses the specified SDXL model with GPU acceleration. .EXAMPLE generateimage "beautiful landscape" -Model "DreamShaper", ` -UseAllModels Generates images with multiple models plus all others. #> function Invoke-ComfyUIImageGeneration { [CmdletBinding()] [Alias("generateimage")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] param( ####################################################################### [Parameter( Mandatory = $true, Position = 0, HelpMessage = "The text prompt for image generation" )] [ValidateNotNullOrEmpty()] [string] $Prompt, ####################################################################### [Parameter( Mandatory = $false, Position = 1, HelpMessage = "Negative prompt to exclude unwanted elements" )] [string] $NegativePrompt = "blurry, low quality, distorted", ####################################################################### [Parameter( Mandatory = $false, Position = 2, HelpMessage = "Path to input image for img2img processing" )] [string] $InputImage, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Path to save the generated image" )] [string] $OutFile, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Number of denoising steps (default: 15 for CPU, 20 for GPU)" )] [ValidateRange(1, 100)] [int] $Steps, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Output image width in pixels (default: 1024)" )] [ValidateRange(256, 2048)] [int] $ImageWidth, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Output image height in pixels (default: 1024)" )] [ValidateRange(256, 2048)] [int] $ImageHeight, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "CFG Scale for prompt adherence" )] [ValidateRange(1.0, 20.0)] [float] $CfgScale = 7.0, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Denoising strength for img2img" )] [ValidateRange(0.1, 1.0)] [float] $Strength = 0.75, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Random seed for reproducible results" )] [long] $Seed = -1, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Number of images to generate" )] [ValidateRange(1, 4)] [int] $BatchSize = 1, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Model checkpoint(s) to use" )] [ValidateSet("Stable Diffusion 1.5", "Stable Diffusion 2.1", "Analog Diffusion", "OpenJourney", "DreamShaper", "Protogen", "Juggernaut XL", "SDXL Base 1.0", "SDXL Turbo", "AbyssOrangeMix3")] [string[]] $Model = "Stable Diffusion 2.1", ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Sampling method" )] [string] $Sampler = "euler", ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Scheduler type" )] [string] $Scheduler = "normal", ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Number of CPU threads to use" )] [int] $CpuThreads, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Timeout in seconds for ComfyUI operations" )] [int] $Timeout = 360000, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Enable GPU acceleration" )] [switch] $UseGPU, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Skip automatic resizing of input images" )] [switch] $NoResizeComfyInputImage, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Do not show the ComfyUI window" )] [switch] $NoShowWindow, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "The monitor to use" )] [int] $Monitor = -2, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Removes the borders of the window" )] [switch] $NoBorders, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "The initial width of the window" )] [int] $Width, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "The initial height of the window" )] [int] $Height, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "The initial X position of the window" )] [int] $X, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "The initial Y position of the window" )] [int] $Y, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Place window on the left side of the screen" )] [switch] $Left, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Place window on the right side of the screen" )] [switch] $Right, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Place window on the top side of the screen" )] [switch] $Top, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Place window on the bottom side of the screen" )] [switch] $Bottom, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Place window in the center of the screen" )] [switch] $Centered, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Sends F11 to the window" )] [switch] $FullScreen, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Restore PowerShell window focus" )] [switch] $RestoreFocus, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Position windows side by side" )] [switch] $SideBySide, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Focus the window after opening" )] [switch] $FocusWindow, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Set the window to foreground after opening" )] [switch] $SetForeground, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Maximize the window after positioning" )] [switch] $Maximize, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Keys to send to the ComfyUI window" )] [string[]] $KeysToSend, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Escape control characters and modifiers when sending keys" )] [switch] $SendKeyEscape, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Hold keyboard focus on target window when sending keys" )] [switch] $SendKeyHoldKeyboardFocus, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Use Shift+Enter instead of Enter when sending keys" )] [switch] $SendKeyUseShiftEnter, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Delay in milliseconds between key sends" )] [int] $SendKeyDelayMilliSeconds, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Force shutdown of any existing ComfyUI processes before starting" )] [switch] $Force, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Keep ComfyUI running after image generation" )] [Alias('KeepComfyUIRunning')] [switch] $NoShutdown, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Generate images using all compatible installed models" )] [switch] $UseAllModels, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = "Custom path to set as the base path in extra_model_paths.yaml for the specified model(s)" )] [string] $ModelPath ####################################################################### ) begin { $ShowWindow = (-not $NoShowWindow) -and (-not (GenXdev.Helpers\Test-UnattendedMode -CallersInvocation $MyInvocation)) $callerInvocation = $MyInvocation # update extra_model_paths.yaml if modelpath provided if (-not [string]::IsNullOrWhiteSpace($ModelPath)) { $params = GenXdev.Helpers\Copy-IdenticalParamValues ` -BoundParameters $PSBoundParameters ` -FunctionName "GenXdev.AI\Set-ComfyUIModelPath" $params.ModelPath = $ModelPath GenXdev.AI\Set-ComfyUIModelPath @params } # determine defaults based on gpu $isgpu = $UseGPU.IsPresent if (-not $Steps) { $Steps = if ($isgpu) { 20 } else { 15 } } if (-not $ImageWidth) { $ImageWidth = 1024 } if (-not $ImageHeight) { $ImageHeight = 1024 } # validate seed if ($Seed -eq -1) { $Seed = Microsoft.PowerShell.Utility\Get-Random -Minimum 0 -Maximum ([long]::MaxValue) } # determine cpu threads if not specified if (-not $CpuThreads -and -not $UseGPU) { $CpuThreads = GenXdev.AI\Get-CPUCore } $comfyParams = GenXdev.Helpers\Copy-IdenticalParamValues ` -BoundParameters $PSBoundParameters ` -FunctionName 'EnsureComfyUI' ` -DefaultValues ( Microsoft.PowerShell.Utility\Get-Variable -Scope Local -ErrorAction SilentlyContinue ) # ensure comfyui is running GenXdev.AI\EnsureComfyUI @comfyParams # prepare output $allresults = @() # determine output path $baseoutputdir = $PWD.Path $utcTimestamp = "$(Microsoft.PowerShell.Utility\Get-Date -Format 'yyyyMMdd')_$(([System.DateTime]::UtcNow).Ticks)" $promptPart = $Prompt -replace '[^a-zA-Z0-9]', '_' if ($promptPart.Length -gt 20) { $promptPart = $promptPart.Substring(0, 20) } $basefilename = [GenXdev.Helpers.SecurityHelper]::SanitizeFileName("${utcTimestamp}_${promptPart}") $baseext = ".png" if ($OutFile) { $baseoutputdir = [System.IO.Path]::GetDirectoryName($OutFile) $basefilename = [System.IO.Path]::GetFileNameWithoutExtension($OutFile) $baseext = [System.IO.Path]::GetExtension($OutFile) } if (-not (Microsoft.PowerShell.Management\Test-Path -LiteralPath $baseoutputdir)) { $null = Microsoft.PowerShell.Management\New-Item ` -Path $baseoutputdir ` -ItemType Directory ` -Force } # prepare input image $processedimagepath = $InputImage if ($InputImage) { if (-not $NoResizeComfyInputImage) { $processedimagepath = GenXdev.AI\ResizeComfyInputImage -ImagePath $InputImage } } # load supported models from json $jsonpath = Microsoft.PowerShell.Management\Join-Path ` -Path $PSScriptRoot ` -ChildPath "SupportedComfyUIModels.json" $supportedModels = Microsoft.PowerShell.Management\Get-Content -LiteralPath $jsonpath -Raw | Microsoft.PowerShell.Utility\ConvertFrom-Json # first, detect which models are actually available in ComfyUI $availableModelFiles = @() try { $availableModelsResponse = Microsoft.PowerShell.Utility\Invoke-RestMethod ` -Verbose:$false ` -ProgressAction Continue ` -Uri "${script:comfyUIApiUrl}/object_info" ` -Method GET ` -TimeoutSec 5 if ($availableModelsResponse.CheckpointLoaderSimple -and $availableModelsResponse.CheckpointLoaderSimple.input -and $availableModelsResponse.CheckpointLoaderSimple.input.required -and $availableModelsResponse.CheckpointLoaderSimple.input.required.ckpt_name) { $availableModelFiles = $availableModelsResponse.CheckpointLoaderSimple.input.required.ckpt_name[0] Microsoft.PowerShell.Utility\Write-Verbose "Available model files in ComfyUI: $($availableModelFiles -join ', ')" } } catch { Microsoft.PowerShell.Utility\Write-Warning "Could not detect available models from ComfyUI: $_" } # determine models to use $modelstouse = @() if ($UseAllModels) { # when UseAllModels is specified, ensure all compatible supported models are available Microsoft.PowerShell.Utility\Write-Verbose "UseAllModels specified - ensuring all compatible models are available" # get all compatible supported models $allSupportedModels = $supportedModels | Microsoft.PowerShell.Core\Where-Object { $_.Compatible -eq $true } | Microsoft.PowerShell.Core\ForEach-Object { $_.Name } if ($allSupportedModels.Count -eq 0) { throw "No compatible models found in supported models list." } # ensure each model is available (download if needed) foreach ($modelName in $allSupportedModels) { try { Microsoft.PowerShell.Utility\Write-Verbose "Ensuring model '$modelName' is available..." if ($ModelPath) { $ensuredModel = GenXdev.AI\EnsureComfyUIModel -ModelName $modelName -ModelPath $ModelPath -ErrorAction Stop } else { $ensuredModel = GenXdev.AI\EnsureComfyUIModel -ModelName $modelName -ErrorAction Stop } if ($ensuredModel) { $modelstouse += $modelName Microsoft.PowerShell.Utility\Write-Verbose "Successfully ensured model: $modelName" } } catch { Microsoft.PowerShell.Utility\Write-Warning "Could not ensure model '$modelName' is available: $_" # Continue with other models instead of failing completely } } if ($modelstouse.Count -eq 0) { throw "Could not ensure any compatible models are available. Please check your internet connection and ComfyUI installation." } Microsoft.PowerShell.Utility\Write-Verbose "Successfully ensured $($modelstouse.Count) model(s) for UseAllModels" # Set multimodel flag for UseAllModels case $multimodel = $modelstouse.Count -gt 1 } else { # handle specific model selection or wildcards $filteredModels = @() foreach ($m in $Model) { if ($m -match '[\*\?]') { $filteredModels += $supportedModels.Name | Microsoft.PowerShell.Core\Where-Object { $_ -like $m } } else { $filteredModels += $m } } $modelstouse = $filteredModels | Microsoft.PowerShell.Utility\Select-Object -Unique # validate that specified models are available if we have that information if ($availableModelFiles.Count -gt 0) { $validatedModels = @() foreach ($modelName in $modelstouse) { $modelInfo = $supportedModels | Microsoft.PowerShell.Core\Where-Object { $_.Name -eq $modelName } if ($modelInfo -and $availableModelFiles -contains $modelInfo.FileName) { $validatedModels += $modelName } elseif ($modelInfo) { Microsoft.PowerShell.Utility\Write-Warning "Model '$modelName' (file: $($modelInfo.FileName)) is not available in ComfyUI. Skipping." } } if (($validatedModels.Count -eq 0) -and ($modelstouse.Count -gt 0)) { throw "None of the specified models are available in ComfyUI. Please ensure the models are installed." } $modelstouse = $validatedModels } $multimodel = $modelstouse.Count -gt 1 if ($modelstouse.Count -eq 0) { throw "No valid models available for generation." } Microsoft.PowerShell.Utility\Write-Verbose "Using models: $($modelstouse -join ', ')" } } process { # process each model foreach ($currentmodel in $modelstouse) { $modelInfo = $supportedModels | Microsoft.PowerShell.Core\Where-Object { $_.Name -eq $currentmodel } if (-not $modelInfo) { Microsoft.PowerShell.Utility\Write-Warning "Unsupported model: ${currentmodel}" continue } $modelstarttime = Microsoft.PowerShell.Utility\Get-Date # handle models that are already available or need to be ensured if ($UseAllModels) { # when UseAllModels was used, models were already ensured in begin block # just ensure the model by calling EnsureComfyUIModel again to get the correct file path try { if ($ModelPath) { $ensuredmodel = GenXdev.AI\EnsureComfyUIModel -ModelName $currentmodel -ModelPath $ModelPath -ErrorAction Stop } else { $ensuredmodel = GenXdev.AI\EnsureComfyUIModel -ModelName $currentmodel -ErrorAction Stop } Microsoft.PowerShell.Utility\Write-Verbose "Using pre-ensured model: $ensuredmodel" } catch { Microsoft.PowerShell.Utility\Write-Warning "Could not get model path for pre-ensured model '${currentmodel}': $_" continue } } else { # ensure the model is available (download if needed) try { if ($ModelPath) { $ensuredmodel = GenXdev.AI\EnsureComfyUIModel -ModelName $currentmodel -ModelPath $ModelPath -ErrorAction Stop } else { $ensuredmodel = GenXdev.AI\EnsureComfyUIModel -ModelName $currentmodel -ErrorAction Stop } } catch { Microsoft.PowerShell.Utility\Write-Warning "Could not ensure model '${currentmodel}' is available: $_" continue } } if ((-not $ensuredmodel) -or [string]::IsNullOrWhiteSpace($ensuredmodel)) { Microsoft.PowerShell.Utility\Write-Warning "Could not ensure model '${currentmodel}' is available" continue } # extract just the filename for ComfyUI $currentModelFileName = [System.IO.Path]::GetFileName($ensuredmodel) # validate model filename is not empty if ([string]::IsNullOrWhiteSpace($currentModelFileName)) { Microsoft.PowerShell.Utility\Write-Warning "Invalid model filename for '${currentmodel}', skipping to prevent fallback behavior" continue } Microsoft.PowerShell.Utility\Write-Verbose "Using model file: $currentModelFileName" # determine architecture for workflow creation from JSON data $workflowcreator = if ($modelInfo.Architecture -eq 'SDXL') { 'CreateComfySDXLWorkflow' } else { 'CreateComfyUniversalWorkflow' } $workflow = & "GenXdev.AI\$workflowcreator" ` -PromptText $Prompt ` -NegativePromptText $NegativePrompt ` -ImageName ([System.IO.Path]::GetFileName($processedimagepath)) ` -Steps $Steps ` -Width $ImageWidth ` -Height $ImageHeight ` -CfgScale $CfgScale ` -Strength $Strength ` -Seed $Seed ` -BatchSize $BatchSize ` -Model $currentModelFileName ` -Sampler $Sampler ` -Scheduler $Scheduler ` -FilenamePrefix $basefilename # queue workflow $promptid = GenXdev.AI\QueueComfyWorkflow -Workflow $workflow # wait for completion $results = GenXdev.AI\WaitForComfyCompletion ` -PromptId $promptid ` -Verbose:$false if ($results) { # download generated images $downloadedfiles = @(GenXdev.AI\DownloadComfyResults ` -HistoryData $results ` -OutputDirectory $env:TEMP) for ($i = 0; $i -lt $downloadedfiles.Count; $i++) { $originalfile = $downloadedfiles[$i] # use display name for file naming (clean up auto-detected names) $displayName = $currentmodel -replace '^(Available|Auto-Detected):\s*', '' $modelsuffix = if ($multimodel) { "_$($displayName -replace '\.[^\.]+$', '')" } else { '' } $indexsuffix = if ($downloadedfiles.Count -gt 1) { "_$i" } else { '' } $suffix = if ($modelsuffix -and $indexsuffix) { "${modelsuffix}${indexsuffix}" } ` elseif ($modelsuffix) { $modelsuffix } ` elseif ($indexsuffix) { $indexsuffix } ` else { '' } $currentoutfile = Microsoft.PowerShell.Management\Join-Path ` $baseoutputdir "${basefilename}${suffix}${baseext}" $targetext = [System.IO.Path]::GetExtension($currentoutfile) if ($targetext -ne [System.IO.Path]::GetExtension($originalfile)) { $targetformat = switch ($targetext) { ".jpg" { "Jpeg" } ".jpeg" { "Jpeg" } ".png" { "Png" } ".bmp" { "Bmp" } ".tiff" { "Tiff" } ".gif" { "Gif" } default { "Jpeg" } } $convertedfile = GenXdev.AI\ConvertComfyImageFormat ` -ImagePath $originalfile ` -OutputPath $currentoutfile ` -Format $targetformat if ($convertedfile -ne $originalfile) { $null = Microsoft.PowerShell.Management\Remove-Item ` -LiteralPath $originalfile ` -Force ` -ErrorAction SilentlyContinue } } else { Microsoft.PowerShell.Management\Move-Item ` -LiteralPath $originalfile ` -Destination $currentoutfile ` -Force } $allresults += $currentoutfile # Add metadata in the formats expected by Find-Image try { $displayModelName = $currentmodel -replace '^(Available|Auto-Detected):\s*', '' # Get file info for EXIF metadata $fileInfo = [System.IO.FileInfo]::new($currentoutfile) $fileExtension = $fileInfo.Extension.ToLowerInvariant() # Create EXIF metadata structure (for technical metadata like Invoke-ImageMetadataUpdate) $exifMetadata = @{ success = $true has_metadata = $true Basic = @{ Width = $ImageWidth Height = $ImageHeight Format = $fileExtension -replace '\.', '' FileName = $fileInfo.Name FileExtension = $fileExtension FileSizeBytes = $fileInfo.Length PixelFormat = "" HorizontalResolution = 96.0 VerticalResolution = 96.0 } Camera = @{ Make = "ComfyUI" Model = $displayModelName Software = "ComfyUI" } Other = @{ Software = "ComfyUI" ColorSpace = "sRGB" ResolutionUnit = "inch" } Author = @{ Artist = "ComfyUI AI Generation" Copyright = "" } DateTime = @{ DateTimeOriginal = (Microsoft.PowerShell.Utility\Get-Date).ToString("yyyy:MM:dd HH:mm:ss") DateTimeDigitized = (Microsoft.PowerShell.Utility\Get-Date).ToString("yyyy:MM:dd HH:mm:ss") } GPS = @{ Latitude = $null Longitude = $null Altitude = $null LatitudeDMS = "" LongitudeDMS = "" LatitudeError = "" LongitudeError = "" AltitudeError = "" } Exposure = @{ FNumber = $null ExposureTime = $null ISOSpeedRatings = $null FocalLength = $null ExposureProgram = $null MeteringMode = $null Flash = $null } } # Save EXIF metadata to EXIF.json stream $exifStream = GenXdev.FileSystem\Expand-Path "${currentoutfile}:EXIF.json" $exifJson = $exifMetadata | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 -Compress [System.IO.File]::WriteAllText($exifStream, $exifJson) # Create description metadata structure matching Invoke-ImageKeywordUpdate format $descriptionMetadata = @{ success = $true short_description = "AI-generated image: $Prompt" long_description = "Generated using ComfyUI with model '$displayModelName'. Prompt: '$Prompt'. Negative prompt: '$NegativePrompt'" keywords = @("AI-generated", "ComfyUI", ($displayModelName -replace '\s+', '-')) picture_type = if ($InputImage) { "img2img" } else { "txt2img" } style_type = "AI-generated" overall_mood_of_image = "AI-created" has_nudity = $false has_explicit_content = $false } # Save description metadata to description.json stream $descriptionStream = GenXdev.FileSystem\Expand-Path "${currentoutfile}:description.json" $descriptionJson = $descriptionMetadata | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 5 -Compress [System.IO.File]::WriteAllText($descriptionStream, $descriptionJson) # Create empty people metadata structure $peopleMetadata = @{ count = 0 faces = @() success = $false predictions = @() } # Save people metadata to people.json stream $peopleStream = GenXdev.FileSystem\Expand-Path "${currentoutfile}:people.json" $peopleJson = $peopleMetadata | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 5 -Compress [System.IO.File]::WriteAllText($peopleStream, $peopleJson) # Create empty objects metadata structure $objectsMetadata = @{ objects = @() object_counts = @{} count = 0 } # Save objects metadata to objects.json stream $objectsStream = GenXdev.FileSystem\Expand-Path "${currentoutfile}:objects.json" $objectsJson = $objectsMetadata | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 5 -Compress [System.IO.File]::WriteAllText($objectsStream, $objectsJson) Microsoft.PowerShell.Utility\Write-Verbose "Added complete Find-Image compatible metadata to: $currentoutfile" } catch { Microsoft.PowerShell.Utility\Write-Warning "Failed to write metadata for ${currentoutfile}: $($_.Exception.Message)" } } $modeltime = (Microsoft.PowerShell.Utility\Get-Date) - $modelstarttime $displayName = $currentmodel -replace '^(Available|Auto-Detected):\s*', '' Microsoft.PowerShell.Utility\Write-Verbose ` "${displayName} completed in $([Math]::Round($modeltime.TotalSeconds)) seconds" } else { $displayName = $currentmodel -replace '^(Available|Auto-Detected):\s*', '' Microsoft.PowerShell.Utility\Write-Warning ` "Generation failed for $displayName" } } } end { # clean temp image if ($processedimagepath -and $processedimagepath -ne $InputImage) { $null = Microsoft.PowerShell.Management\Remove-Item ` -LiteralPath $processedimagepath ` -Force ` -ErrorAction SilentlyContinue } # output results $allresults | GenXdev.FileSystem\WriteFileOutput ` -CallerInvocation $callerInvocation ` -Prefix "Generated: " # shutdown if not noshutdown and queue is empty if (-not $NoShutdown) { # check if the ComfyUI queue is empty before shutting down $queueEmpty = GenXdev.AI\Test-ComfyUIQueueEmpty if ($queueEmpty) { Microsoft.PowerShell.Utility\Write-Verbose ` "ComfyUI queue is empty, proceeding with shutdown" GenXdev.AI\Stop-ComfyUI -ErrorAction SilentlyContinue } else { Microsoft.PowerShell.Utility\Write-Warning ` "ComfyUI queue has pending tasks, skipping shutdown to avoid interrupting other workflows" Microsoft.PowerShell.Utility\Write-Warning ` "Use -NoShutdown parameter or stop ComfyUI manually when all tasks complete" } } } } ############################################################################### |