diff --git a/canvas_node.py b/canvas_node.py index 22471a1..72b31a0 100644 --- a/canvas_node.py +++ b/canvas_node.py @@ -307,9 +307,25 @@ class CanvasNode: try: # Wczytaj obraz bez maski - path_image_without_mask = folder_paths.get_annotated_filepath( - canvas_image.replace('.png', '_without_mask.png')) - log_debug(f"Loading image without mask from: {path_image_without_mask}") + image_without_mask_name = canvas_image.replace('.png', '_without_mask.png') + path_image_without_mask = folder_paths.get_annotated_filepath(image_without_mask_name) + log_debug(f"Canvas image name: {canvas_image}") + log_debug(f"Looking for image without mask: {image_without_mask_name}") + log_debug(f"Full path: {path_image_without_mask}") + + # Sprawdź czy plik istnieje + if not os.path.exists(path_image_without_mask): + log_warn(f"Image without mask not found at: {path_image_without_mask}") + # Spróbuj znaleźć plik w katalogu input + input_dir = folder_paths.get_input_directory() + alternative_path = os.path.join(input_dir, image_without_mask_name) + log_debug(f"Trying alternative path: {alternative_path}") + if os.path.exists(alternative_path): + path_image_without_mask = alternative_path + log_info(f"Found image at alternative path: {alternative_path}") + else: + raise FileNotFoundError(f"Image file not found: {image_without_mask_name}") + i = Image.open(path_image_without_mask) i = ImageOps.exif_transpose(i) if i.mode not in ['RGB', 'RGBA']: @@ -330,7 +346,21 @@ class CanvasNode: # Wczytaj maskę path_image = folder_paths.get_annotated_filepath(canvas_image) path_mask = path_image.replace('.png', '_mask.png') + log_debug(f"Canvas image path: {path_image}") log_debug(f"Looking for mask at: {path_mask}") + + # Sprawdź czy plik maski istnieje + if not os.path.exists(path_mask): + log_warn(f"Mask not found at: {path_mask}") + # Spróbuj znaleźć plik w katalogu input + input_dir = folder_paths.get_input_directory() + mask_name = canvas_image.replace('.png', '_mask.png') + alternative_mask_path = os.path.join(input_dir, mask_name) + log_debug(f"Trying alternative mask path: {alternative_mask_path}") + if os.path.exists(alternative_mask_path): + path_mask = alternative_mask_path + log_info(f"Found mask at alternative path: {alternative_mask_path}") + if os.path.exists(path_mask): log_debug(f"Mask file exists, loading...") mask = Image.open(path_mask).convert('L') diff --git a/js/Canvas.js b/js/Canvas.js index 9b97f25..01c0651 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -604,32 +604,56 @@ export class Canvas { async saveToServer(fileName) { - // Sprawdź czy już trwa zapis - if (this._saveInProgress) { - log.warn(`Save already in progress, waiting...`); - return this._saveInProgress; + // Globalna mapa do śledzenia zapisów dla wszystkich node-ów + if (!window.canvasSaveStates) { + window.canvasSaveStates = new Map(); + } + + const nodeId = this.node.id; + const saveKey = `${nodeId}_${fileName}`; + + // Sprawdź czy już trwa zapis dla tego node-a i pliku + if (this._saveInProgress || window.canvasSaveStates.get(saveKey)) { + log.warn(`Save already in progress for node ${nodeId}, waiting...`); + return this._saveInProgress || window.canvasSaveStates.get(saveKey); } - log.info(`Starting saveToServer with fileName: ${fileName}`); + log.info(`Starting saveToServer with fileName: ${fileName} for node: ${nodeId}`); log.debug(`Canvas dimensions: ${this.width}x${this.height}`); log.debug(`Number of layers: ${this.layers.length}`); // Utwórz Promise dla aktualnego zapisu this._saveInProgress = this._performSave(fileName); + window.canvasSaveStates.set(saveKey, this._saveInProgress); try { const result = await this._saveInProgress; return result; } finally { this._saveInProgress = null; - log.debug(`Save completed, lock released`); + window.canvasSaveStates.delete(saveKey); + log.debug(`Save completed for node ${nodeId}, lock released`); } } async _performSave(fileName) { + // Sprawdź czy są warstwy do zapisania + if (this.layers.length === 0) { + log.warn(`Node ${this.node.id} has no layers, creating empty canvas`); + // Zwróć sukces ale nie zapisuj pustego canvas-a na serwer + return Promise.resolve(true); + } + // Zapisz stan do IndexedDB przed zapisem na serwer await this.saveStateToDB(true); + // Dodaj krótkie opóźnienie dla różnych node-ów, aby uniknąć konfliktów + const nodeId = this.node.id; + const delay = (nodeId % 10) * 50; // 0-450ms opóźnienia w zależności od ID node-a + if (delay > 0) { + await new Promise(resolve => setTimeout(resolve, delay)); + } + return new Promise((resolve) => { const tempCanvas = document.createElement('canvas'); const maskCanvas = document.createElement('canvas'); @@ -803,8 +827,10 @@ export class Canvas { if (maskResp.status === 200) { const data = await resp.json(); - this.widget.value = data.name; - log.info(`All files saved successfully, widget value set to: ${data.name}`); + // Ustaw widget.value na rzeczywistą nazwę zapisanego pliku (unikalną) + // aby node zwracał właściwy plik + this.widget.value = fileName; + log.info(`All files saved successfully, widget value set to: ${fileName}`); resolve(true); } else { log.error(`Error saving mask: ${maskResp.status}`); diff --git a/js/CanvasInteractions.js b/js/CanvasInteractions.js index a8ac270..653a856 100644 --- a/js/CanvasInteractions.js +++ b/js/CanvasInteractions.js @@ -9,7 +9,7 @@ const log = { }; // Konfiguracja loggera dla modułu CanvasInteractions -logger.setModuleLevel('CanvasInteractions', LogLevel.INFO); +logger.setModuleLevel('CanvasInteractions', LogLevel.DEBUG); export class CanvasInteractions { constructor(canvas) { diff --git a/js/CanvasLayers.js b/js/CanvasLayers.js index 9aa775f..866a1e1 100644 --- a/js/CanvasLayers.js +++ b/js/CanvasLayers.js @@ -10,7 +10,7 @@ const log = { }; // Konfiguracja loggera dla modułu CanvasLayers -logger.setModuleLevel('CanvasLayers', LogLevel.INFO); // Domyślnie INFO, można zmienić na DEBUG dla szczegółowych logów +logger.setModuleLevel('CanvasLayers', LogLevel.DEBUG); // Domyślnie INFO, można zmienić na DEBUG dla szczegółowych logów export class CanvasLayers { constructor(canvas) { @@ -628,7 +628,32 @@ export class CanvasLayers { this.canvas.selectedLayer.opacity = slider.value / 100; this.canvas.render(); - await this.canvas.saveToServer(this.canvas.widget.value); + // Funkcja fallback do zapisu + const saveWithFallback = async (fileName) => { + try { + const getUniqueFileName = (baseName) => { + // Sprawdź czy nazwa już zawiera identyfikator node-a (zapobiega nieskończonej pętli) + const nodePattern = new RegExp(`_node_${this.canvas.node.id}(?:_node_\\d+)*`); + if (nodePattern.test(baseName)) { + // Usuń wszystkie poprzednie identyfikatory node-ów i dodaj tylko jeden + const cleanName = baseName.replace(/_node_\d+/g, ''); + const extension = cleanName.split('.').pop(); + const nameWithoutExt = cleanName.replace(`.${extension}`, ''); + return `${nameWithoutExt}_node_${this.canvas.node.id}.${extension}`; + } + const extension = baseName.split('.').pop(); + const nameWithoutExt = baseName.replace(`.${extension}`, ''); + return `${nameWithoutExt}_node_${this.canvas.node.id}.${extension}`; + }; + const uniqueFileName = getUniqueFileName(fileName); + return await this.canvas.saveToServer(uniqueFileName); + } catch (error) { + console.warn(`Failed to save with unique name, falling back to original: ${fileName}`, error); + return await this.canvas.saveToServer(fileName); + } + }; + + await saveWithFallback(this.canvas.widget.value); if (this.canvas.node) { app.graph.runStep(); } diff --git a/js/CanvasState.js b/js/CanvasState.js index c6b1be6..24ed359 100644 --- a/js/CanvasState.js +++ b/js/CanvasState.js @@ -10,7 +10,7 @@ const log = { }; // Konfiguracja loggera dla modułu CanvasState -logger.setModuleLevel('CanvasState', LogLevel.INFO); +logger.setModuleLevel('CanvasState', LogLevel.DEBUG); // Prosta funkcja generująca UUID function generateUUID() { diff --git a/js/Canvas_view.js b/js/Canvas_view.js index 2c241c2..09e057d 100644 --- a/js/Canvas_view.js +++ b/js/Canvas_view.js @@ -17,7 +17,7 @@ const log = { }; // Konfiguracja loggera dla modułu Canvas_view -logger.setModuleLevel('Canvas_view', LogLevel.INFO); // Domyślnie INFO, można zmienić na DEBUG dla szczegółowych logów +logger.setModuleLevel('Canvas_view', LogLevel.DEBUG); // Domyślnie INFO, można zmienić na DEBUG dla szczegółowych logów async function createCanvasWidget(node, widget, app) { const canvas = new Canvas(node, widget); @@ -387,7 +387,7 @@ async function createCanvasWidget(node, widget, app) { const img = new Image(); img.onload = async () => { canvas.addLayer(img); - await canvas.saveToServer(widget.value); + await saveWithFallback(widget.value); app.graph.runStep(); }; img.src = event.target.result; @@ -402,7 +402,7 @@ async function createCanvasWidget(node, widget, app) { textContent: "Import Input", onclick: async () => { if (await canvas.importLatestImage()) { - await canvas.saveToServer(widget.value); + await saveWithFallback(widget.value); app.graph.runStep(); } } @@ -589,7 +589,7 @@ async function createCanvasWidget(node, widget, app) { canvas.updateSelection([newLayer]); canvas.render(); canvas.saveState(); - await canvas.saveToServer(widget.value); + await saveWithFallback(widget.value); app.graph.runStep(); } catch (error) { log.error("Matting error:", error); @@ -743,7 +743,8 @@ async function createCanvasWidget(node, widget, app) { const triggerWidget = node.widgets.find(w => w.name === "trigger"); const updateOutput = async () => { - await canvas.saveToServer(widget.value); + // Użyj funkcji fallback do zapisu + await saveWithFallback(widget.value); triggerWidget.value = (triggerWidget.value + 1) % 99999999; app.graph.runStep(); }; @@ -918,26 +919,73 @@ async function createCanvasWidget(node, widget, app) { } }; - // Zmienna do śledzenia czy wykonanie jest w trakcie - let executionInProgress = false; + // Globalna mapa do śledzenia wykonania dla każdego node-a + if (!window.canvasExecutionStates) { + window.canvasExecutionStates = new Map(); + } - api.addEventListener("execution_start", async () => { - log.info(`Execution start event for node ${node.id}`); + // Unikalna nazwa pliku dla każdego node-a + const getUniqueFileName = (baseName) => { + // Sprawdź czy nazwa już zawiera identyfikator node-a (zapobiega nieskończonej pętli) + const nodePattern = new RegExp(`_node_${node.id}(?:_node_\\d+)*`); + if (nodePattern.test(baseName)) { + // Usuń wszystkie poprzednie identyfikatory node-ów i dodaj tylko jeden + const cleanName = baseName.replace(/_node_\d+/g, ''); + const extension = cleanName.split('.').pop(); + const nameWithoutExt = cleanName.replace(`.${extension}`, ''); + return `${nameWithoutExt}_node_${node.id}.${extension}`; + } + const extension = baseName.split('.').pop(); + const nameWithoutExt = baseName.replace(`.${extension}`, ''); + return `${nameWithoutExt}_node_${node.id}.${extension}`; + }; + + // Funkcja do czyszczenia nazwy pliku z identyfikatorów node-ów + const getCleanFileName = (fileName) => { + return fileName.replace(/_node_\d+/g, ''); + }; + + // Funkcja fallback w przypadku problemów z unikalną nazwą + const saveWithFallback = async (fileName) => { + try { + const uniqueFileName = getUniqueFileName(fileName); + log.debug(`Attempting to save with unique name: ${uniqueFileName}`); + return await canvas.saveToServer(uniqueFileName); + } catch (error) { + log.warn(`Failed to save with unique name, falling back to original: ${fileName}`, error); + return await canvas.saveToServer(fileName); + } + }; + + api.addEventListener("execution_start", async (event) => { + // Sprawdź czy event dotyczy tego konkretnego node-a + const executionData = event.detail || {}; + const currentPromptId = executionData.prompt_id; + + log.info(`Execution start event for node ${node.id}, prompt_id: ${currentPromptId}`); log.debug(`Widget value: ${widget.value}`); log.debug(`Node inputs: ${node.inputs?.length || 0}`); + log.debug(`Canvas layers count: ${canvas.layers.length}`); - // Sprawdź czy już trwa wykonanie - if (executionInProgress) { - log.warn(`Execution already in progress, skipping...`); + // Sprawdź czy już trwa wykonanie dla tego node-a + if (window.canvasExecutionStates.get(node.id)) { + log.warn(`Execution already in progress for node ${node.id}, skipping...`); return; } - // Ustaw flagę wykonania - executionInProgress = true; + // Ustaw flagę wykonania dla tego node-a + window.canvasExecutionStates.set(node.id, true); try { - await canvas.saveToServer(widget.value); - log.info(`Canvas saved to server`); + // Sprawdź czy canvas ma jakiekolwiek warstwy przed zapisem + if (canvas.layers.length === 0) { + log.warn(`Node ${node.id} has no layers, skipping save to server`); + // Nie zapisuj pustego canvas-a, ale nadal przetwórz dane wejściowe + } else { + // Użyj funkcji fallback do zapisu tylko jeśli są warstwy + await saveWithFallback(widget.value); + log.info(`Canvas saved to server for node ${node.id}`); + } if (node.inputs[0]?.link) { const linkId = node.inputs[0].link; @@ -951,11 +999,11 @@ async function createCanvasWidget(node, widget, app) { log.debug(`No input link found`); } } catch (error) { - log.error(`Error during execution:`, error); + log.error(`Error during execution for node ${node.id}:`, error); } finally { - // Zwolnij flagę wykonania - executionInProgress = false; - log.debug(`Execution completed, flag released`); + // Zwolnij flagę wykonania dla tego node-a + window.canvasExecutionStates.set(node.id, false); + log.debug(`Execution completed for node ${node.id}, flag released`); } }); diff --git a/js/ImageCache.js b/js/ImageCache.js index 0654df3..c1446b0 100644 --- a/js/ImageCache.js +++ b/js/ImageCache.js @@ -9,7 +9,7 @@ const log = { }; // Konfiguracja loggera dla modułu ImageCache -logger.setModuleLevel('ImageCache', LogLevel.INFO); +logger.setModuleLevel('ImageCache', LogLevel.DEBUG); export class ImageCache { constructor() { diff --git a/js/ImageUtils.js b/js/ImageUtils.js index e76fcf9..2e07e84 100644 --- a/js/ImageUtils.js +++ b/js/ImageUtils.js @@ -9,7 +9,7 @@ const log = { }; // Konfiguracja loggera dla modułu ImageUtils -logger.setModuleLevel('ImageUtils', LogLevel.INFO); +logger.setModuleLevel('ImageUtils', LogLevel.DEBUG); export function validateImageData(data) { log.debug("Validating data structure:", { diff --git a/js/Mask_tool.js b/js/Mask_tool.js index 401b396..1abba1a 100644 --- a/js/Mask_tool.js +++ b/js/Mask_tool.js @@ -9,7 +9,7 @@ const log = { }; // Konfiguracja loggera dla modułu Mask_tool -logger.setModuleLevel('Mask_tool', LogLevel.INFO); +logger.setModuleLevel('Mask_tool', LogLevel.DEBUG); export class MaskTool { constructor(canvasInstance) { diff --git a/js/db.js b/js/db.js index 6593d82..389930b 100644 --- a/js/db.js +++ b/js/db.js @@ -9,7 +9,7 @@ const log = { }; // Konfiguracja loggera dla modułu db -logger.setModuleLevel('db', LogLevel.INFO); +logger.setModuleLevel('db', LogLevel.DEBUG); const DB_NAME = 'CanvasNodeDB'; const STATE_STORE_NAME = 'CanvasState';