import 'dart:io'; import 'package:comfyui_api_sdk/comfyui_api_sdk.dart'; import 'package:uuid/uuid.dart'; bool jobFinished = false; void main() async { // Create the API client final clientId = Uuid().v4().replaceAll('-', ''); final api = ComfyUiApi(host: 'http://mennos-server:7860', clientId: clientId); // Connect to the WebSocket for progress updates await api.connectWebSocket(); // Listen for progress updates api.onProgressChanged((progress) { print( '⏱️ Progress: ${progress.value} / ${progress.max} for ${progress.promptId}'); }); // Register for prompt start events api.onPromptStart((promptId) { print('🚀 Prompt started: $promptId'); }); // Register for prompt finished events api.onPromptFinished((promptId) async { print('✅ Prompt finished: $promptId'); // Fetch the history when a prompt finishes try { final history = await api.getHistory(maxItems: 5); print('Recent history:'); history.items.forEach((promptId, item) { print(' - $promptId: ${item.status.statusStr}'); }); final prompt = history.items[promptId]; if (prompt != null) { print('🖼 Prompt $promptId: ${prompt.status.statusStr}'); if (prompt.outputs.nodes.isNotEmpty) { for (var node in prompt.outputs.nodes.values) { for (var image in node.images) { print( ' - Image: ${image.filename} URL: ${api.host}/api/view?filename=${image.filename}'); // Write image to disk using image.fetchImageBytes(api.host) final bytes = await image.fetchImageBytes(api.host); // Ensure pwd/images exists Directory('images').createSync(); // We can save this to the disk under $pwd/images/${image.filename} final file = File('images/${image.filename}'); await file.writeAsBytes(bytes); } } } } } catch (e) { print('Error fetching history: $e'); } jobFinished = true; }); // Let's get a list of available checkpoints try { final checkpoints = await api.getCheckpoints(); print('📦 Checkpoints: ${checkpoints.length}'); for (var checkpoint in checkpoints) { print(' - ${checkpoint.name}'); } } catch (e) { print('❌ Error fetching checkpoints: $e'); } // Let's get a list of vaes try { final vaes = await api.getVaes(); print('🧠 VAES: ${vaes.length}'); for (var vae in vaes) { print(' - ${vae.name}'); } } catch (e) { print('❌ Error fetching vaes: $e'); } // Let's get a list of Loras try { final loras = await api.getLoras(); print('📡 Loras: ${loras.length}'); for (var lora in loras) { print(' - ${lora.name}'); } } catch (e) { print('❌ Error fetching loras: $e'); } // Let's get a list of possible samplers to use try { final samplers = await api.getKSamplers(); print('🎲 Samplers: ${samplers.length}'); for (var sampler in samplers) { print(' - ${sampler}'); } } catch (e) { print('❌ Error fetching samplers: $e'); } // Let's get a list of possible schedulers to use try { final schedulers = await api.getSchedulers(); print('📅 Schedulers: ${schedulers.length}'); for (var scheduler in schedulers) { print(' - ${scheduler}'); } } catch (e) { print('❌ Error fetching schedulers: $e'); } var randomSeed = DateTime.now().millisecondsSinceEpoch; // Fetch object info for validation (optional) Map objectInfo = {}; try { objectInfo = await api.getObjectInfo(); } catch (e) { print('❌ Error fetching object info: $e'); } // Use PromptBuilder to create a workflow final promptBuilder = PromptBuilder(clientId: api.clientId) ..addNode( "CheckpointLoaderSimple", {"ckpt_name": "Anime/prefectPonyXL_v50.safetensors"}, title: "Load Checkpoint", outputTags: {"MODEL": "model", "CLIP": "clip", "VAE": "vae"}, ) ..addNode( "EmptyLatentImage", {"width": 512, "height": 512, "batch_size": 1}, title: "Empty Latent Image", outputTags: {"LATENT": "latent_image"}, ) ..addNode( "CLIPTextEncode", { "text": "beautiful scenery nature glass bottle landscape", "clip": ["clip", 1] // Explicitly use the "clip" output }, title: "CLIP Text Encode (Positive Prompt)", outputTags: {"CONDITIONING": "positive_conditioning"}, ) ..addNode( "CLIPTextEncode", { "text": "negative, dark, scary, horror, spooky", "clip": ["clip", 1] // Explicitly use the "clip" output }, title: "CLIP Text Encode (Negative Prompt)", outputTags: {"CONDITIONING": "negative_conditioning"}, ) ..addNode( "KSampler", { "seed": randomSeed, "steps": 25, "cfg": 4, "sampler_name": "euler", "scheduler": "normal", "denoise": 1, "model": ["model", 0], // Explicitly use the "model" output "positive": [ "positive_conditioning", 0 ], // Explicitly use the positive conditioning "negative": [ "negative_conditioning", 0 ], // Explicitly use the negative conditioning "latent_image": ["latent_image", 0] // Explicitly use the latent image }, title: "KSampler", outputTags: {"LATENT": "denoised_latent"}, ) ..addNode( "VAEDecode", { "samples": ["denoised_latent", 0], // Explicitly use the denoised latent "vae": ["vae", 2] // Explicitly use the "vae" output }, title: "VAE Decode", outputTags: {"IMAGE": "final_image"}, ) ..addNode( "SaveImage", { "filename_prefix": "ComfyUI", "images": ["final_image", 0] // Explicitly use the final image }, title: "Save Image", ); // Validate the workflow (optional) try { promptBuilder.validate(objectInfo); } catch (e) { print('❌ Validation error: $e'); return; } // Submit the workflow try { final result = await api.submitPrompt(promptBuilder.build()); print('💼 Prompt submitted: ${result.promptId}'); } catch (e) { print('❌ Error submitting prompt: $e'); } // Wait until job is finished while (!jobFinished) { await Future.delayed(Duration(seconds: 1)); } // Clean up api.dispose(); }