adds proper support for fetching images

This commit is contained in:
Menno van Leeuwen 2025-03-30 18:40:31 +02:00
parent 47562afde0
commit 28a0014fa0
Signed by: vleeuwenmenno
SSH Key Fingerprint: SHA256:OJFmjANpakwD3F2Rsws4GLtbdz1TJ5tkQF0RZmF0TRE
4 changed files with 44 additions and 7 deletions

View File

@ -42,10 +42,19 @@ class ComfyApiHttpEndpoints {
return HistoryResponse.fromJson(jsonDecode(response.body));
}
/// Gets image data by filename
Future<List<int>> getImage(String filename) async {
final response =
await _httpClient.get(Uri.parse('$host/api/view?filename=$filename'));
/// Gets image data by filename, optionally specifying subfolder and type
Future<List<int>> getImage(String filename,
{String? subfolder, String? type}) async {
// Build query parameters dynamically
final queryParameters = {
'filename': filename,
if (subfolder != null && subfolder.isNotEmpty) 'subfolder': subfolder,
if (type != null && type.isNotEmpty) 'type': type,
};
final uri =
Uri.parse('$host/api/view').replace(queryParameters: queryParameters);
logger.d('Fetching image from URI: $uri'); // Log the final URI
final response = await _httpClient.get(uri);
_validateResponse(response);
return response.bodyBytes;
}

View File

@ -96,9 +96,10 @@ class ComfyUiApi {
Future<HistoryResponse> getHistory({int maxItems = 64}) =>
_httpEndpoints.getHistory(maxItems: maxItems);
/// Gets image data by filename
Future<List<int>> getImage(String filename) =>
_httpEndpoints.getImage(filename);
/// Gets image data by filename, optionally specifying subfolder and type
Future<List<int>> getImage(String filename,
{String? subfolder, String? type}) =>
_httpEndpoints.getImage(filename, subfolder: subfolder, type: type);
/// Gets a list of all available models
Future<Map<String, dynamic>> getModels() => _httpEndpoints.getModels();

View File

@ -2,20 +2,29 @@ class ExecutionEvent {
final String promptId;
final int timestamp;
final String? node;
final List<Map<String, dynamic>>? outputImages;
final Map<String, dynamic>? extra;
const ExecutionEvent({
required this.promptId,
required this.timestamp,
this.node,
this.outputImages,
this.extra,
});
factory ExecutionEvent.fromJson(Map<String, dynamic> json) {
// Note: This factory is not directly used by the handler,
// but kept for potential future use or consistency.
// The handler now constructs the event directly.
return ExecutionEvent(
promptId: json['prompt_id'] as String,
timestamp: json['timestamp'] as int,
node: json['node'] as String?,
// Parsing logic moved to the handler for direct use.
// outputImages: (json['output']?['images'] as List<dynamic>?)
// ?.map((e) => e as Map<String, dynamic>)
// .toList(),
extra: json['extra'] as Map<String, dynamic>?,
);
}
@ -25,6 +34,7 @@ class ExecutionEvent {
'prompt_id': promptId,
'timestamp': timestamp,
'node': node,
'outputImages': outputImages, // Added for completeness
'extra': extra,
};
}

View File

@ -38,11 +38,28 @@ class WebSocketEventHandler {
) {
if (event.data.containsKey('prompt_id')) {
try {
List<Map<String, dynamic>>? outputImages;
if (event.data.containsKey('output') &&
event.data['output'] is Map &&
(event.data['output'] as Map).containsKey('images') &&
event.data['output']['images'] is List) {
// Ensure correct casting
try {
outputImages = (event.data['output']['images'] as List<dynamic>)
.map((e) => Map<String, dynamic>.from(e as Map))
.toList();
} catch (e) {
print('Error parsing output images: $e');
outputImages = null; // Set to null if parsing fails
}
}
final executionEvent = ExecutionEvent(
promptId: event.data['prompt_id'] as String,
timestamp: event.data['timestamp'] as int? ??
DateTime.now().millisecondsSinceEpoch,
node: event.data['node']?.toString(),
outputImages: outputImages, // Pass the extracted images
extra: event.data['extra'] as Map<String, dynamic>?,
);
executionEventController.add(executionEvent);