Implement example file to showcase how the library works
This commit is contained in:
parent
8dad2b57b3
commit
2bbbd38b93
231
example/example.dart
Normal file
231
example/example.dart
Normal file
@ -0,0 +1,231 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:comfyui_api_sdk/comfyui_api_sdk.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:comfyui_api_sdk/src/prompt_builder.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();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user