231 lines
6.4 KiB
Dart
231 lines
6.4 KiB
Dart
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<String, dynamic> 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();
|
|
}
|