diff --git a/canvas_node.py b/canvas_node.py index e3af507..2b861fc 100644 --- a/canvas_node.py +++ b/canvas_node.py @@ -190,18 +190,35 @@ class CanvasNode: print(f"Error in add_mask_to_canvas: {str(e)}") return None + # Zmienna blokująca równoczesne wykonania + _processing_lock = None + def process_canvas_image(self, canvas_image, trigger, output_switch, cache_enabled, input_image=None, input_mask=None): try: + # Sprawdź czy już trwa przetwarzanie + if self.__class__._processing_lock is not None: + print(f"[OUTPUT_LOG] Process already in progress, waiting for completion...") + return () # Zwróć pusty wynik, aby uniknąć równoczesnych przetworzeń + + # Ustaw blokadę + self.__class__._processing_lock = True + current_execution = self.get_execution_id() - print(f"Processing canvas image, execution ID: {current_execution}") + print(f"[OUTPUT_LOG] Starting process_canvas_image - execution ID: {current_execution}, trigger: {trigger}") + print(f"[OUTPUT_LOG] Canvas image filename: {canvas_image}") + print(f"[OUTPUT_LOG] Output switch: {output_switch}, Cache enabled: {cache_enabled}") + print(f"[OUTPUT_LOG] Input image provided: {input_image is not None}") + print(f"[OUTPUT_LOG] Input mask provided: {input_mask is not None}") if current_execution != self.__class__._canvas_cache['last_execution_id']: - print(f"New execution detected: {current_execution}") + print(f"[OUTPUT_LOG] New execution detected: {current_execution} (previous: {self.__class__._canvas_cache['last_execution_id']})") self.__class__._canvas_cache['image'] = None self.__class__._canvas_cache['mask'] = None self.__class__._canvas_cache['last_execution_id'] = current_execution + else: + print(f"[OUTPUT_LOG] Same execution ID, using cached data") if input_image is not None: print("Input image received, converting to PIL Image...") @@ -253,6 +270,7 @@ class CanvasNode: # Wczytaj obraz bez maski path_image_without_mask = folder_paths.get_annotated_filepath( canvas_image.replace('.png', '_without_mask.png')) + print(f"[OUTPUT_LOG] Loading image without mask from: {path_image_without_mask}") i = Image.open(path_image_without_mask) i = ImageOps.exif_transpose(i) if i.mode not in ['RGB', 'RGBA']: @@ -263,37 +281,56 @@ class CanvasNode: alpha = image[..., 3:] image = rgb * alpha + (1 - alpha) * 0.5 processed_image = torch.from_numpy(image)[None,] + print(f"[OUTPUT_LOG] Successfully loaded image without mask, shape: {processed_image.shape}") except Exception as e: - print(f"Error loading image without mask: {str(e)}") + print(f"[OUTPUT_LOG] Error loading image without mask: {str(e)}") processed_image = torch.ones((1, 512, 512, 3), dtype=torch.float32) + print(f"[OUTPUT_LOG] Using default image, shape: {processed_image.shape}") try: # Wczytaj maskę path_image = folder_paths.get_annotated_filepath(canvas_image) path_mask = path_image.replace('.png', '_mask.png') + print(f"[OUTPUT_LOG] Looking for mask at: {path_mask}") if os.path.exists(path_mask): + print(f"[OUTPUT_LOG] Mask file exists, loading...") mask = Image.open(path_mask).convert('L') mask = np.array(mask).astype(np.float32) / 255.0 processed_mask = torch.from_numpy(mask)[None,] + print(f"[OUTPUT_LOG] Successfully loaded mask, shape: {processed_mask.shape}") else: + print(f"[OUTPUT_LOG] Mask file does not exist, creating default mask") processed_mask = torch.ones((1, processed_image.shape[1], processed_image.shape[2]), dtype=torch.float32) + print(f"[OUTPUT_LOG] Default mask created, shape: {processed_mask.shape}") except Exception as e: - print(f"Error loading mask: {str(e)}") + print(f"[OUTPUT_LOG] Error loading mask: {str(e)}") processed_mask = torch.ones((1, processed_image.shape[1], processed_image.shape[2]), dtype=torch.float32) + print(f"[OUTPUT_LOG] Fallback mask created, shape: {processed_mask.shape}") if not output_switch: + print(f"[OUTPUT_LOG] Output switch is OFF, returning empty tuple") return () + print(f"[OUTPUT_LOG] About to return output - Image shape: {processed_image.shape}, Mask shape: {processed_mask.shape}") + print(f"[OUTPUT_LOG] Image tensor info - dtype: {processed_image.dtype}, device: {processed_image.device}") + print(f"[OUTPUT_LOG] Mask tensor info - dtype: {processed_mask.dtype}, device: {processed_mask.device}") + self.update_persistent_cache() - + + print(f"[OUTPUT_LOG] Successfully returning processed image and mask") return (processed_image, processed_mask) except Exception as e: print(f"Error in process_canvas_image: {str(e)}") traceback.print_exc() return () + + finally: + # Zwolnij blokadę + self.__class__._processing_lock = None + print(f"[OUTPUT_LOG] Process completed, lock released") def get_cached_data(self): return { @@ -572,8 +609,24 @@ class BiRefNetMatting: return m.hexdigest() +# Zmienna blokująca równoczesne wywołania matting +_matting_lock = None + @PromptServer.instance.routes.post("/matting") async def matting(request): + global _matting_lock + + # Sprawdź czy już trwa przetwarzanie + if _matting_lock is not None: + print("Matting already in progress, rejecting request") + return web.json_response({ + "error": "Another matting operation is in progress", + "details": "Please wait for the current operation to complete" + }, status=429) # 429 Too Many Requests + + # Ustaw blokadę + _matting_lock = True + try: print("Received matting request") data = await request.json() @@ -606,6 +659,10 @@ async def matting(request): "error": str(e), "details": traceback.format_exc() }, status=500) + finally: + # Zwolnij blokadę + _matting_lock = None + print("Matting lock released") def convert_base64_to_tensor(base64_str): diff --git a/js/Canvas.js b/js/Canvas.js index a68055a..9ff7791 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -99,12 +99,29 @@ export class Canvas { } async loadStateFromDB() { + // Sprawdź czy już trwa ładowanie + if (this._loadInProgress) { + console.log("Load already in progress, waiting..."); + return this._loadInProgress; + } + console.log("Attempting to load state from IndexedDB for node:", this.node.id); if (!this.node.id) { console.error("Node ID is not available for loading state from DB."); return false; } + this._loadInProgress = this._performLoad(); + + try { + const result = await this._loadInProgress; + return result; + } finally { + this._loadInProgress = null; + } + } + + async _performLoad() { try { const savedState = await getCanvasState(this.node.id); if (!savedState) { @@ -1713,6 +1730,28 @@ export class Canvas { async saveToServer(fileName) { + // Sprawdź czy już trwa zapis + if (this._saveInProgress) { + console.log(`[CANVAS_OUTPUT_LOG] Save already in progress, waiting...`); + return this._saveInProgress; + } + + console.log(`[CANVAS_OUTPUT_LOG] Starting saveToServer with fileName: ${fileName}`); + console.log(`[CANVAS_OUTPUT_LOG] Canvas dimensions: ${this.width}x${this.height}`); + console.log(`[CANVAS_OUTPUT_LOG] Number of layers: ${this.layers.length}`); + + // Utwórz Promise dla aktualnego zapisu + this._saveInProgress = this._performSave(fileName); + + try { + const result = await this._saveInProgress; + return result; + } finally { + this._saveInProgress = null; + } + } + + async _performSave(fileName) { // Zapisz stan do IndexedDB przed zapisem na serwer await this.saveStateToDB(true); @@ -1732,9 +1771,17 @@ export class Canvas { maskCtx.fillStyle = '#ffffff'; // Białe tło dla wolnych przestrzeni maskCtx.fillRect(0, 0, this.width, this.height); + + console.log(`[CANVAS_OUTPUT_LOG] Canvas contexts created, starting layer rendering`); // Rysowanie warstw - this.layers.sort((a, b) => a.zIndex - b.zIndex).forEach(layer => { + const sortedLayers = this.layers.sort((a, b) => a.zIndex - b.zIndex); + console.log(`[CANVAS_OUTPUT_LOG] Processing ${sortedLayers.length} layers in order`); + + sortedLayers.forEach((layer, index) => { + console.log(`[CANVAS_OUTPUT_LOG] Processing layer ${index}: zIndex=${layer.zIndex}, size=${layer.width}x${layer.height}, pos=(${layer.x},${layer.y})`); + console.log(`[CANVAS_OUTPUT_LOG] Layer ${index}: blendMode=${layer.blendMode || 'normal'}, opacity=${layer.opacity !== undefined ? layer.opacity : 1}`); + tempCtx.save(); tempCtx.globalCompositeOperation = layer.blendMode || 'normal'; tempCtx.globalAlpha = layer.opacity !== undefined ? layer.opacity : 1; @@ -1742,6 +1789,8 @@ export class Canvas { tempCtx.rotate(layer.rotation * Math.PI / 180); tempCtx.drawImage(layer.image, -layer.width / 2, -layer.height / 2, layer.width, layer.height); tempCtx.restore(); + + console.log(`[CANVAS_OUTPUT_LOG] Layer ${index} rendered successfully`); maskCtx.save(); maskCtx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2); @@ -1826,23 +1875,29 @@ export class Canvas { // Zapisz obraz bez maski const fileNameWithoutMask = fileName.replace('.png', '_without_mask.png'); + console.log(`[CANVAS_OUTPUT_LOG] Saving image without mask as: ${fileNameWithoutMask}`); + tempCanvas.toBlob(async (blobWithoutMask) => { + console.log(`[CANVAS_OUTPUT_LOG] Created blob for image without mask, size: ${blobWithoutMask.size} bytes`); const formDataWithoutMask = new FormData(); formDataWithoutMask.append("image", blobWithoutMask, fileNameWithoutMask); formDataWithoutMask.append("overwrite", "true"); try { - await fetch("/upload/image", { + const response = await fetch("/upload/image", { method: "POST", body: formDataWithoutMask, }); + console.log(`[CANVAS_OUTPUT_LOG] Image without mask upload response: ${response.status}`); } catch (error) { - console.error("Error uploading image without mask:", error); + console.error(`[CANVAS_OUTPUT_LOG] Error uploading image without mask:`, error); } }, "image/png"); // Zapisz obraz z maską + console.log(`[CANVAS_OUTPUT_LOG] Saving main image as: ${fileName}`); tempCanvas.toBlob(async (blob) => { + console.log(`[CANVAS_OUTPUT_LOG] Created blob for main image, size: ${blob.size} bytes`); const formData = new FormData(); formData.append("image", blob, fileName); formData.append("overwrite", "true"); @@ -1852,11 +1907,15 @@ export class Canvas { method: "POST", body: formData, }); + console.log(`[CANVAS_OUTPUT_LOG] Main image upload response: ${resp.status}`); if (resp.status === 200) { + const maskFileName = fileName.replace('.png', '_mask.png'); + console.log(`[CANVAS_OUTPUT_LOG] Saving mask as: ${maskFileName}`); + maskCanvas.toBlob(async (maskBlob) => { + console.log(`[CANVAS_OUTPUT_LOG] Created blob for mask, size: ${maskBlob.size} bytes`); const maskFormData = new FormData(); - const maskFileName = fileName.replace('.png', '_mask.png'); maskFormData.append("image", maskBlob, maskFileName); maskFormData.append("overwrite", "true"); @@ -1865,26 +1924,28 @@ export class Canvas { method: "POST", body: maskFormData, }); + console.log(`[CANVAS_OUTPUT_LOG] Mask upload response: ${maskResp.status}`); if (maskResp.status === 200) { const data = await resp.json(); this.widget.value = data.name; + console.log(`[CANVAS_OUTPUT_LOG] All files saved successfully, widget value set to: ${data.name}`); resolve(true); } else { - console.error("Error saving mask: " + maskResp.status); + console.error(`[CANVAS_OUTPUT_LOG] Error saving mask: ${maskResp.status}`); resolve(false); } } catch (error) { - console.error("Error saving mask:", error); + console.error(`[CANVAS_OUTPUT_LOG] Error saving mask:`, error); resolve(false); } }, "image/png"); } else { - console.error(resp.status + " - " + resp.statusText); + console.error(`[CANVAS_OUTPUT_LOG] Main image upload failed: ${resp.status} - ${resp.statusText}`); resolve(false); } } catch (error) { - console.error(error); + console.error(`[CANVAS_OUTPUT_LOG] Error uploading main image:`, error); resolve(false); } }, "image/png"); diff --git a/js/Canvas_view.js b/js/Canvas_view.js index cf2023d..b29ef34 100644 --- a/js/Canvas_view.js +++ b/js/Canvas_view.js @@ -906,22 +906,53 @@ async function createCanvasWidget(node, widget, app) { } }; + // Zmienna do śledzenia czy wykonanie jest w trakcie + let executionInProgress = false; + api.addEventListener("execution_start", async () => { + console.log(`[CANVAS_VIEW_LOG] Execution start event for node ${node.id}`); + console.log(`[CANVAS_VIEW_LOG] Widget value: ${widget.value}`); + console.log(`[CANVAS_VIEW_LOG] Node inputs: ${node.inputs?.length || 0}`); + + // Sprawdź czy już trwa wykonanie + if (executionInProgress) { + console.log(`[CANVAS_VIEW_LOG] Execution already in progress, skipping...`); + return; + } + + // Ustaw flagę wykonania + executionInProgress = true; + + try { + await canvas.saveToServer(widget.value); + console.log(`[CANVAS_VIEW_LOG] Canvas saved to server`); - await canvas.saveToServer(widget.value); - - if (node.inputs[0].link) { - const linkId = node.inputs[0].link; - const inputData = app.nodeOutputs[linkId]; - if (inputData) { - imageCache.set(linkId, inputData); + if (node.inputs[0]?.link) { + const linkId = node.inputs[0].link; + const inputData = app.nodeOutputs[linkId]; + console.log(`[CANVAS_VIEW_LOG] Input link ${linkId} has data: ${!!inputData}`); + if (inputData) { + imageCache.set(linkId, inputData); + console.log(`[CANVAS_VIEW_LOG] Input data cached for link ${linkId}`); + } + } else { + console.log(`[CANVAS_VIEW_LOG] No input link found`); } + } catch (error) { + console.error(`[CANVAS_VIEW_LOG] Error during execution:`, error); + } finally { + // Zwolnij flagę wykonania + executionInProgress = false; + console.log(`[CANVAS_VIEW_LOG] Execution completed, flag released`); } }); const originalSaveToServer = canvas.saveToServer; canvas.saveToServer = async function (fileName) { + console.log(`[CANVAS_VIEW_LOG] saveToServer called with fileName: ${fileName}`); + console.log(`[CANVAS_VIEW_LOG] Current execution context - node ID: ${node.id}`); const result = await originalSaveToServer.call(this, fileName); + console.log(`[CANVAS_VIEW_LOG] saveToServer completed, result: ${result}`); return result; };