mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-25 14:25:44 -03:00
Add support for importing multiple latest images
Introduces a new backend route and method to fetch all images created since a given timestamp, and updates the frontend to import all new images as layers on auto-refresh. This improves workflow by allowing multiple images generated in a single execution to be imported at once, rather than only the most recent image.
This commit is contained in:
@@ -333,6 +333,24 @@ class CanvasNode:
|
|||||||
latest_image_path = max(image_files, key=os.path.getctime)
|
latest_image_path = max(image_files, key=os.path.getctime)
|
||||||
return latest_image_path
|
return latest_image_path
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_latest_images(cls, since_timestamp=0):
|
||||||
|
output_dir = folder_paths.get_output_directory()
|
||||||
|
files = []
|
||||||
|
for f_name in os.listdir(output_dir):
|
||||||
|
file_path = os.path.join(output_dir, f_name)
|
||||||
|
if os.path.isfile(file_path) and file_path.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
|
||||||
|
try:
|
||||||
|
mtime = os.path.getmtime(file_path)
|
||||||
|
if mtime > since_timestamp:
|
||||||
|
files.append((mtime, file_path))
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
files.sort(key=lambda x: x[0])
|
||||||
|
|
||||||
|
return [f[1] for f in files]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_flow_status(cls, flow_id=None):
|
def get_flow_status(cls, flow_id=None):
|
||||||
|
|
||||||
@@ -454,6 +472,30 @@ class CanvasNode:
|
|||||||
'error': str(e)
|
'error': str(e)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@PromptServer.instance.routes.get("/layerforge/get-latest-images/{since}")
|
||||||
|
async def get_latest_images_route(request):
|
||||||
|
try:
|
||||||
|
since_timestamp = float(request.match_info.get('since', 0))
|
||||||
|
# JS Timestamps are in milliseconds, Python's are in seconds
|
||||||
|
latest_image_paths = cls.get_latest_images(since_timestamp / 1000.0)
|
||||||
|
|
||||||
|
images_data = []
|
||||||
|
for image_path in latest_image_paths:
|
||||||
|
with open(image_path, "rb") as f:
|
||||||
|
encoded_string = base64.b64encode(f.read()).decode('utf-8')
|
||||||
|
images_data.append(f"data:image/png;base64,{encoded_string}")
|
||||||
|
|
||||||
|
return web.json_response({
|
||||||
|
'success': True,
|
||||||
|
'images': images_data
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
log_error(f"Error in get_latest_images_route: {str(e)}")
|
||||||
|
return web.json_response({
|
||||||
|
'success': False,
|
||||||
|
'error': str(e)
|
||||||
|
}, status=500)
|
||||||
|
|
||||||
@PromptServer.instance.routes.get("/ycnode/get_latest_image")
|
@PromptServer.instance.routes.get("/ycnode/get_latest_image")
|
||||||
async def get_latest_image_route(request):
|
async def get_latest_image_route(request):
|
||||||
try:
|
try:
|
||||||
|
|||||||
14
js/Canvas.js
14
js/Canvas.js
@@ -459,11 +459,17 @@ export class Canvas {
|
|||||||
|
|
||||||
_addAutoRefreshToggle() {
|
_addAutoRefreshToggle() {
|
||||||
let autoRefreshEnabled = false;
|
let autoRefreshEnabled = false;
|
||||||
|
let lastExecutionStartTime = 0;
|
||||||
|
|
||||||
|
const handleExecutionStart = () => {
|
||||||
|
lastExecutionStartTime = Date.now();
|
||||||
|
log.debug(`Execution started, timestamp set to: ${lastExecutionStartTime}`);
|
||||||
|
};
|
||||||
|
|
||||||
const handleExecutionSuccess = () => {
|
const handleExecutionSuccess = () => {
|
||||||
if (autoRefreshEnabled) {
|
if (autoRefreshEnabled) {
|
||||||
log.info('Auto-refresh triggered, importing latest image.');
|
log.info('Auto-refresh triggered, importing latest images.');
|
||||||
this.importLatestImage();
|
this.canvasIO.importLatestImages(lastExecutionStartTime);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -479,10 +485,12 @@ export class Canvas {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
api.addEventListener('execution_start', handleExecutionStart);
|
||||||
api.addEventListener('execution_success', handleExecutionSuccess);
|
api.addEventListener('execution_success', handleExecutionSuccess);
|
||||||
|
|
||||||
this.node.onRemoved = useChainCallback(this.node.onRemoved, () => {
|
this.node.onRemoved = useChainCallback(this.node.onRemoved, () => {
|
||||||
log.info('Node removed, cleaning up auto-refresh listener.');
|
log.info('Node removed, cleaning up auto-refresh listeners.');
|
||||||
|
api.removeEventListener('execution_start', handleExecutionStart);
|
||||||
api.removeEventListener('execution_success', handleExecutionSuccess);
|
api.removeEventListener('execution_success', handleExecutionSuccess);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -744,12 +744,7 @@ export class CanvasIO {
|
|||||||
img.src = result.image_data;
|
img.src = result.image_data;
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.canvas.canvasLayers.addLayerWithImage(img, {
|
await this.canvas.canvasLayers.addLayerWithImage(img, {}, 'fit');
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: this.canvas.width,
|
|
||||||
height: this.canvas.height,
|
|
||||||
});
|
|
||||||
log.info("Latest image imported and placed on canvas successfully.");
|
log.info("Latest image imported and placed on canvas successfully.");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -761,4 +756,39 @@ export class CanvasIO {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async importLatestImages(sinceTimestamp) {
|
||||||
|
try {
|
||||||
|
log.info(`Fetching latest images since ${sinceTimestamp}...`);
|
||||||
|
const response = await fetch(`/layerforge/get-latest-images/${sinceTimestamp}`);
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success && result.images && result.images.length > 0) {
|
||||||
|
log.info(`Received ${result.images.length} new images, adding to canvas.`);
|
||||||
|
|
||||||
|
for (const imageData of result.images) {
|
||||||
|
const img = new Image();
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
img.onload = resolve;
|
||||||
|
img.onerror = reject;
|
||||||
|
img.src = imageData;
|
||||||
|
});
|
||||||
|
await this.canvas.canvasLayers.addLayerWithImage(img, {}, 'fit');
|
||||||
|
}
|
||||||
|
log.info("All new images imported and placed on canvas successfully.");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else if (result.success) {
|
||||||
|
log.info("No new images found since last generation.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error(result.error || "Failed to fetch latest images.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Error importing latest images:", error);
|
||||||
|
alert(`Failed to import latest images: ${error.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user