mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
Add logging system
This commit is contained in:
316
README_LOGGER.md
Normal file
316
README_LOGGER.md
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
# System Logowania dla ComfyUI-LayerForge
|
||||||
|
|
||||||
|
Ten dokument opisuje system logowania zaimplementowany dla projektu ComfyUI-LayerForge, który umożliwia łatwe zarządzanie debugowaniem kodu zarówno w części JavaScript, jak i Python.
|
||||||
|
|
||||||
|
## Spis treści
|
||||||
|
|
||||||
|
1. [Wprowadzenie](#wprowadzenie)
|
||||||
|
2. [Konfiguracja](#konfiguracja)
|
||||||
|
3. [Użycie w JavaScript](#użycie-w-javascript)
|
||||||
|
4. [Użycie w Python](#użycie-w-python)
|
||||||
|
5. [Poziomy logowania](#poziomy-logowania)
|
||||||
|
6. [Zarządzanie logami](#zarządzanie-logami)
|
||||||
|
7. [Przykłady](#przykłady)
|
||||||
|
|
||||||
|
## Wprowadzenie
|
||||||
|
|
||||||
|
System logowania ComfyUI-LayerForge został zaprojektowany, aby zapewnić:
|
||||||
|
|
||||||
|
- Spójne logowanie w całym projekcie (JavaScript i Python)
|
||||||
|
- Możliwość włączania/wyłączania logów globalnie lub per moduł
|
||||||
|
- Różne poziomy logowania (DEBUG, INFO, WARN, ERROR)
|
||||||
|
- Kolorowe logi w konsoli
|
||||||
|
- Możliwość zapisywania logów do plików
|
||||||
|
- Eksport logów do analizy
|
||||||
|
|
||||||
|
## Konfiguracja
|
||||||
|
|
||||||
|
### Konfiguracja JavaScript
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Importuj logger
|
||||||
|
import {logger, LogLevel} from "./logger.js";
|
||||||
|
|
||||||
|
// Konfiguracja globalna
|
||||||
|
logger.configure({
|
||||||
|
globalLevel: LogLevel.INFO, // Domyślny poziom logowania
|
||||||
|
useColors: true, // Kolorowe logi w konsoli
|
||||||
|
saveToStorage: true, // Zapisywanie logów do localStorage
|
||||||
|
maxStoredLogs: 1000 // Maksymalna liczba przechowywanych logów
|
||||||
|
});
|
||||||
|
|
||||||
|
// Konfiguracja per moduł
|
||||||
|
logger.setModuleLevel('Canvas', LogLevel.DEBUG);
|
||||||
|
logger.setModuleLevel('API', LogLevel.WARN);
|
||||||
|
|
||||||
|
// Włączanie/wyłączanie globalnie
|
||||||
|
logger.setEnabled(true); // Włącz wszystkie logi
|
||||||
|
logger.setEnabled(false); // Wyłącz wszystkie logi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Konfiguracja Python
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Importuj logger
|
||||||
|
from python.logger import logger, LogLevel, set_debug, set_file_logging
|
||||||
|
|
||||||
|
# Konfiguracja globalna
|
||||||
|
logger.configure({
|
||||||
|
'global_level': LogLevel.INFO,
|
||||||
|
'use_colors': True,
|
||||||
|
'log_to_file': True,
|
||||||
|
'log_dir': 'logs',
|
||||||
|
'max_file_size_mb': 10,
|
||||||
|
'backup_count': 5
|
||||||
|
})
|
||||||
|
|
||||||
|
# Konfiguracja per moduł
|
||||||
|
logger.set_module_level('canvas_node', LogLevel.DEBUG)
|
||||||
|
logger.set_module_level('api', LogLevel.WARN)
|
||||||
|
|
||||||
|
# Szybkie włączanie debugowania
|
||||||
|
set_debug(True) # Ustawia globalny poziom na DEBUG
|
||||||
|
set_debug(False) # Ustawia globalny poziom na INFO
|
||||||
|
|
||||||
|
# Włączanie/wyłączanie logowania do pliku
|
||||||
|
set_file_logging(True, 'custom_logs')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Konfiguracja przez zmienne środowiskowe (Python)
|
||||||
|
|
||||||
|
Możesz również skonfigurować logger Python za pomocą zmiennych środowiskowych:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Poziom globalny (DEBUG, INFO, WARN, ERROR, NONE)
|
||||||
|
export LAYERFORGE_LOG_LEVEL=DEBUG
|
||||||
|
|
||||||
|
# Ustawienia modułów (format JSON)
|
||||||
|
export LAYERFORGE_MODULE_LEVELS='{"canvas_node": "DEBUG", "api": "WARN"}'
|
||||||
|
|
||||||
|
# Inne ustawienia
|
||||||
|
export LAYERFORGE_USE_COLORS=true
|
||||||
|
export LAYERFORGE_LOG_TO_FILE=true
|
||||||
|
export LAYERFORGE_LOG_DIR=logs
|
||||||
|
export LAYERFORGE_MAX_FILE_SIZE_MB=10
|
||||||
|
export LAYERFORGE_BACKUP_COUNT=5
|
||||||
|
```
|
||||||
|
|
||||||
|
## Użycie w JavaScript
|
||||||
|
|
||||||
|
### Podstawowe użycie
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {logger, LogLevel} from "./logger.js";
|
||||||
|
|
||||||
|
// Inicjalizacja loggera dla modułu
|
||||||
|
const log = {
|
||||||
|
debug: (...args) => logger.debug('MojModul', ...args),
|
||||||
|
info: (...args) => logger.info('MojModul', ...args),
|
||||||
|
warn: (...args) => logger.warn('MojModul', ...args),
|
||||||
|
error: (...args) => logger.error('MojModul', ...args)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Używanie loggera
|
||||||
|
log.debug("To jest wiadomość debugowania");
|
||||||
|
log.info("To jest informacja");
|
||||||
|
log.warn("To jest ostrzeżenie");
|
||||||
|
log.error("To jest błąd");
|
||||||
|
|
||||||
|
// Logowanie obiektów
|
||||||
|
log.debug("Dane użytkownika:", { id: 123, name: "Jan Kowalski" });
|
||||||
|
|
||||||
|
// Logowanie błędów
|
||||||
|
try {
|
||||||
|
// Kod, który może rzucić wyjątek
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Wystąpił błąd:", error);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dostęp z konsoli przeglądarki
|
||||||
|
|
||||||
|
Logger jest dostępny globalnie w przeglądarce jako `window.LayerForgeLogger`, co umożliwia łatwe debugowanie:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// W konsoli przeglądarki
|
||||||
|
LayerForgeLogger.setGlobalLevel(LayerForgeLogger.LogLevel.DEBUG);
|
||||||
|
LayerForgeLogger.setModuleLevel('Canvas', LayerForgeLogger.LogLevel.DEBUG);
|
||||||
|
LayerForgeLogger.exportLogs('json'); // Eksportuje logi do pliku JSON
|
||||||
|
```
|
||||||
|
|
||||||
|
## Użycie w Python
|
||||||
|
|
||||||
|
### Podstawowe użycie
|
||||||
|
|
||||||
|
```python
|
||||||
|
from python.logger import debug, info, warn, error, exception
|
||||||
|
|
||||||
|
# Używanie funkcji pomocniczych
|
||||||
|
debug('canvas_node', 'To jest wiadomość debugowania')
|
||||||
|
info('canvas_node', 'To jest informacja')
|
||||||
|
warn('canvas_node', 'To jest ostrzeżenie')
|
||||||
|
error('canvas_node', 'To jest błąd')
|
||||||
|
|
||||||
|
# Logowanie wyjątków
|
||||||
|
try:
|
||||||
|
# Kod, który może rzucić wyjątek
|
||||||
|
except Exception as e:
|
||||||
|
exception('canvas_node', f'Wystąpił błąd: {str(e)}')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Używanie funkcji pomocniczych w module
|
||||||
|
|
||||||
|
```python
|
||||||
|
from python.logger import logger, LogLevel, debug, info, warn, error, exception
|
||||||
|
|
||||||
|
# Funkcje pomocnicze dla modułu
|
||||||
|
log_debug = lambda *args, **kwargs: debug('moj_modul', *args, **kwargs)
|
||||||
|
log_info = lambda *args, **kwargs: info('moj_modul', *args, **kwargs)
|
||||||
|
log_warn = lambda *args, **kwargs: warn('moj_modul', *args, **kwargs)
|
||||||
|
log_error = lambda *args, **kwargs: error('moj_modul', *args, **kwargs)
|
||||||
|
log_exception = lambda *args: exception('moj_modul', *args)
|
||||||
|
|
||||||
|
# Używanie funkcji pomocniczych
|
||||||
|
log_debug("To jest wiadomość debugowania")
|
||||||
|
log_info("To jest informacja")
|
||||||
|
log_warn("To jest ostrzeżenie")
|
||||||
|
log_error("To jest błąd")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Poziomy logowania
|
||||||
|
|
||||||
|
System logowania obsługuje następujące poziomy:
|
||||||
|
|
||||||
|
- **DEBUG** - Szczegółowe informacje, przydatne podczas debugowania
|
||||||
|
- **INFO** - Ogólne informacje o działaniu aplikacji
|
||||||
|
- **WARN** - Ostrzeżenia, które nie powodują błędów, ale mogą prowadzić do problemów
|
||||||
|
- **ERROR** - Błędy, które uniemożliwiają wykonanie operacji
|
||||||
|
- **NONE** - Wyłącza wszystkie logi
|
||||||
|
|
||||||
|
## Zarządzanie logami
|
||||||
|
|
||||||
|
### JavaScript
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Eksport logów do pliku
|
||||||
|
logger.exportLogs('json'); // Eksportuje logi do pliku JSON
|
||||||
|
logger.exportLogs('txt'); // Eksportuje logi do pliku tekstowego
|
||||||
|
|
||||||
|
// Czyszczenie logów
|
||||||
|
logger.clearLogs();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
Logi Python są automatycznie zapisywane do plików w katalogu `logs` (lub innym skonfigurowanym), jeśli włączono opcję `log_to_file`. Pliki logów są rotowane, gdy osiągną określony rozmiar.
|
||||||
|
|
||||||
|
## Przykłady
|
||||||
|
|
||||||
|
### Przykład użycia w JavaScript
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {logger, LogLevel} from "./logger.js";
|
||||||
|
|
||||||
|
// Inicjalizacja loggera dla modułu Canvas
|
||||||
|
const log = {
|
||||||
|
debug: (...args) => logger.debug('Canvas', ...args),
|
||||||
|
info: (...args) => logger.info('Canvas', ...args),
|
||||||
|
warn: (...args) => logger.warn('Canvas', ...args),
|
||||||
|
error: (...args) => logger.error('Canvas', ...args)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Konfiguracja loggera dla modułu Canvas
|
||||||
|
logger.setModuleLevel('Canvas', LogLevel.DEBUG);
|
||||||
|
|
||||||
|
class Canvas {
|
||||||
|
constructor() {
|
||||||
|
log.info("Inicjalizacja Canvas");
|
||||||
|
this.width = 512;
|
||||||
|
this.height = 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
log.debug(`Renderowanie canvas o wymiarach ${this.width}x${this.height}`);
|
||||||
|
// Kod renderowania...
|
||||||
|
log.info("Renderowanie zakończone");
|
||||||
|
}
|
||||||
|
|
||||||
|
saveToServer(fileName) {
|
||||||
|
log.info(`Zapisywanie do serwera: ${fileName}`);
|
||||||
|
try {
|
||||||
|
// Kod zapisywania...
|
||||||
|
log.debug("Zapisano pomyślnie");
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Błąd podczas zapisywania:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Przykład użycia w Python
|
||||||
|
|
||||||
|
```python
|
||||||
|
from python.logger import logger, LogLevel, debug, info, warn, error, exception
|
||||||
|
|
||||||
|
# Konfiguracja loggera dla modułu canvas_node
|
||||||
|
logger.set_module_level('canvas_node', LogLevel.DEBUG)
|
||||||
|
|
||||||
|
# Funkcje pomocnicze dla modułu
|
||||||
|
log_debug = lambda *args, **kwargs: debug('canvas_node', *args, **kwargs)
|
||||||
|
log_info = lambda *args, **kwargs: info('canvas_node', *args, **kwargs)
|
||||||
|
log_warn = lambda *args, **kwargs: warn('canvas_node', *args, **kwargs)
|
||||||
|
log_error = lambda *args, **kwargs: error('canvas_node', *args, **kwargs)
|
||||||
|
log_exception = lambda *args: exception('canvas_node', *args)
|
||||||
|
|
||||||
|
class CanvasNode:
|
||||||
|
def __init__(self):
|
||||||
|
log_info("Inicjalizacja CanvasNode")
|
||||||
|
self.flow_id = "123456"
|
||||||
|
|
||||||
|
def process_canvas_image(self, canvas_image, trigger, output_switch, cache_enabled, input_image=None, input_mask=None):
|
||||||
|
try:
|
||||||
|
log_info(f"Przetwarzanie obrazu canvas - ID wykonania: {self.flow_id}, trigger: {trigger}")
|
||||||
|
log_debug(f"Nazwa pliku canvas: {canvas_image}")
|
||||||
|
log_debug(f"Output switch: {output_switch}, Cache enabled: {cache_enabled}")
|
||||||
|
|
||||||
|
# Kod przetwarzania...
|
||||||
|
|
||||||
|
log_info("Pomyślnie zwrócono przetworzony obraz i maskę")
|
||||||
|
return (processed_image, processed_mask)
|
||||||
|
except Exception as e:
|
||||||
|
log_exception(f"Błąd w process_canvas_image: {str(e)}")
|
||||||
|
return ()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Podsumowanie
|
||||||
|
|
||||||
|
System logowania ComfyUI-LayerForge zapewnia spójne i elastyczne rozwiązanie do debugowania i monitorowania aplikacji. Dzięki możliwości konfiguracji poziomów logowania per moduł, możesz skupić się na konkretnych częściach aplikacji bez zaśmiecania konsoli niepotrzebnymi informacjami.
|
||||||
|
|
||||||
|
Aby włączyć pełne debugowanie, ustaw poziom globalny na `DEBUG`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// JavaScript
|
||||||
|
logger.setGlobalLevel(LogLevel.DEBUG);
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Python
|
||||||
|
logger.set_global_level(LogLevel.DEBUG)
|
||||||
|
# lub
|
||||||
|
set_debug(True)
|
||||||
|
```
|
||||||
|
|
||||||
|
Aby wyłączyć wszystkie logi:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// JavaScript
|
||||||
|
logger.setEnabled(false);
|
||||||
|
// lub
|
||||||
|
logger.setGlobalLevel(LogLevel.NONE);
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Python
|
||||||
|
logger.set_global_level(LogLevel.NONE)
|
||||||
184
canvas_node.py
184
canvas_node.py
@@ -16,6 +16,45 @@ import time
|
|||||||
import base64
|
import base64
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import io
|
import io
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Dodaj ścieżkę do katalogu python/ do sys.path
|
||||||
|
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'python'))
|
||||||
|
|
||||||
|
# Importuj logger
|
||||||
|
try:
|
||||||
|
from python.logger import logger, LogLevel, debug, info, warn, error, exception
|
||||||
|
|
||||||
|
# Konfiguracja loggera dla modułu canvas_node
|
||||||
|
logger.set_module_level('canvas_node', LogLevel.INFO) # Domyślnie INFO, można zmienić na DEBUG
|
||||||
|
|
||||||
|
# Włącz logowanie do pliku
|
||||||
|
logger.configure({
|
||||||
|
'log_to_file': True,
|
||||||
|
'log_dir': os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logs')
|
||||||
|
})
|
||||||
|
|
||||||
|
# Funkcje pomocnicze dla modułu
|
||||||
|
log_debug = lambda *args, **kwargs: debug('canvas_node', *args, **kwargs)
|
||||||
|
log_info = lambda *args, **kwargs: info('canvas_node', *args, **kwargs)
|
||||||
|
log_warn = lambda *args, **kwargs: warn('canvas_node', *args, **kwargs)
|
||||||
|
log_error = lambda *args, **kwargs: error('canvas_node', *args, **kwargs)
|
||||||
|
log_exception = lambda *args: exception('canvas_node', *args)
|
||||||
|
|
||||||
|
log_info("Logger initialized for canvas_node")
|
||||||
|
except ImportError as e:
|
||||||
|
# Fallback jeśli logger nie jest dostępny
|
||||||
|
print(f"Warning: Logger module not available: {e}")
|
||||||
|
|
||||||
|
# Proste funkcje zastępcze
|
||||||
|
def log_debug(*args): print("[DEBUG]", *args)
|
||||||
|
def log_info(*args): print("[INFO]", *args)
|
||||||
|
def log_warn(*args): print("[WARN]", *args)
|
||||||
|
def log_error(*args): print("[ERROR]", *args)
|
||||||
|
def log_exception(*args):
|
||||||
|
print("[ERROR]", *args)
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
torch.set_float32_matmul_precision('high')
|
torch.set_float32_matmul_precision('high')
|
||||||
|
|
||||||
@@ -74,7 +113,7 @@ class CanvasNode:
|
|||||||
current_execution = self.get_execution_id()
|
current_execution = self.get_execution_id()
|
||||||
|
|
||||||
if current_execution != self.__class__._canvas_cache['last_execution_id']:
|
if current_execution != self.__class__._canvas_cache['last_execution_id']:
|
||||||
print(f"New execution detected: {current_execution}")
|
log_info(f"New execution detected: {current_execution}")
|
||||||
self.__class__._canvas_cache['image'] = None
|
self.__class__._canvas_cache['image'] = None
|
||||||
self.__class__._canvas_cache['mask'] = None
|
self.__class__._canvas_cache['mask'] = None
|
||||||
self.__class__._canvas_cache['last_execution_id'] = current_execution
|
self.__class__._canvas_cache['last_execution_id'] = current_execution
|
||||||
@@ -82,12 +121,12 @@ class CanvasNode:
|
|||||||
|
|
||||||
if persistent.get('image') is not None:
|
if persistent.get('image') is not None:
|
||||||
self.__class__._canvas_cache['image'] = persistent['image']
|
self.__class__._canvas_cache['image'] = persistent['image']
|
||||||
print("Restored image from persistent cache")
|
log_info("Restored image from persistent cache")
|
||||||
if persistent.get('mask') is not None:
|
if persistent.get('mask') is not None:
|
||||||
self.__class__._canvas_cache['mask'] = persistent['mask']
|
self.__class__._canvas_cache['mask'] = persistent['mask']
|
||||||
print("Restored mask from persistent cache")
|
log_info("Restored mask from persistent cache")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error restoring cache: {str(e)}")
|
log_error(f"Error restoring cache: {str(e)}")
|
||||||
|
|
||||||
def get_execution_id(self):
|
def get_execution_id(self):
|
||||||
|
|
||||||
@@ -95,7 +134,7 @@ class CanvasNode:
|
|||||||
|
|
||||||
return str(int(time.time() * 1000))
|
return str(int(time.time() * 1000))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error getting execution ID: {str(e)}")
|
log_error(f"Error getting execution ID: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def update_persistent_cache(self):
|
def update_persistent_cache(self):
|
||||||
@@ -105,9 +144,9 @@ class CanvasNode:
|
|||||||
'image': self.__class__._canvas_cache['image'],
|
'image': self.__class__._canvas_cache['image'],
|
||||||
'mask': self.__class__._canvas_cache['mask']
|
'mask': self.__class__._canvas_cache['mask']
|
||||||
}
|
}
|
||||||
print("Updated persistent cache")
|
log_debug("Updated persistent cache")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error updating persistent cache: {str(e)}")
|
log_error(f"Error updating persistent cache: {str(e)}")
|
||||||
|
|
||||||
def track_data_flow(self, stage, status, data_info=None):
|
def track_data_flow(self, stage, status, data_info=None):
|
||||||
|
|
||||||
@@ -117,9 +156,9 @@ class CanvasNode:
|
|||||||
'status': status,
|
'status': status,
|
||||||
'data_info': data_info
|
'data_info': data_info
|
||||||
}
|
}
|
||||||
print(f"Data Flow [{self.flow_id}] - Stage: {stage}, Status: {status}")
|
log_debug(f"Data Flow [{self.flow_id}] - Stage: {stage}, Status: {status}")
|
||||||
if data_info:
|
if data_info:
|
||||||
print(f"Data Info: {data_info}")
|
log_debug(f"Data Info: {data_info}")
|
||||||
|
|
||||||
self.__class__._canvas_cache['data_flow_status'][self.flow_id] = flow_status
|
self.__class__._canvas_cache['data_flow_status'][self.flow_id] = flow_status
|
||||||
|
|
||||||
@@ -159,7 +198,7 @@ class CanvasNode:
|
|||||||
return input_image
|
return input_image
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in add_image_to_canvas: {str(e)}")
|
log_error(f"Error in add_image_to_canvas: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def add_mask_to_canvas(self, input_mask, input_image):
|
def add_mask_to_canvas(self, input_mask, input_image):
|
||||||
@@ -187,7 +226,7 @@ class CanvasNode:
|
|||||||
return input_mask
|
return input_mask
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in add_mask_to_canvas: {str(e)}")
|
log_error(f"Error in add_mask_to_canvas: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Zmienna blokująca równoczesne wykonania
|
# Zmienna blokująca równoczesne wykonania
|
||||||
@@ -198,30 +237,30 @@ class CanvasNode:
|
|||||||
try:
|
try:
|
||||||
# Sprawdź czy już trwa przetwarzanie
|
# Sprawdź czy już trwa przetwarzanie
|
||||||
if self.__class__._processing_lock is not None:
|
if self.__class__._processing_lock is not None:
|
||||||
print(f"[OUTPUT_LOG] Process already in progress, waiting for completion...")
|
log_warn(f"Process already in progress, waiting for completion...")
|
||||||
return () # Zwróć pusty wynik, aby uniknąć równoczesnych przetworzeń
|
return () # Zwróć pusty wynik, aby uniknąć równoczesnych przetworzeń
|
||||||
|
|
||||||
# Ustaw blokadę
|
# Ustaw blokadę
|
||||||
self.__class__._processing_lock = True
|
self.__class__._processing_lock = True
|
||||||
|
|
||||||
current_execution = self.get_execution_id()
|
current_execution = self.get_execution_id()
|
||||||
print(f"[OUTPUT_LOG] Starting process_canvas_image - execution ID: {current_execution}, trigger: {trigger}")
|
log_info(f"Starting process_canvas_image - execution ID: {current_execution}, trigger: {trigger}")
|
||||||
print(f"[OUTPUT_LOG] Canvas image filename: {canvas_image}")
|
log_debug(f"Canvas image filename: {canvas_image}")
|
||||||
print(f"[OUTPUT_LOG] Output switch: {output_switch}, Cache enabled: {cache_enabled}")
|
log_debug(f"Output switch: {output_switch}, Cache enabled: {cache_enabled}")
|
||||||
print(f"[OUTPUT_LOG] Input image provided: {input_image is not None}")
|
log_debug(f"Input image provided: {input_image is not None}")
|
||||||
print(f"[OUTPUT_LOG] Input mask provided: {input_mask is not None}")
|
log_debug(f"Input mask provided: {input_mask is not None}")
|
||||||
|
|
||||||
if current_execution != self.__class__._canvas_cache['last_execution_id']:
|
if current_execution != self.__class__._canvas_cache['last_execution_id']:
|
||||||
print(f"[OUTPUT_LOG] New execution detected: {current_execution} (previous: {self.__class__._canvas_cache['last_execution_id']})")
|
log_info(f"New execution detected: {current_execution} (previous: {self.__class__._canvas_cache['last_execution_id']})")
|
||||||
|
|
||||||
self.__class__._canvas_cache['image'] = None
|
self.__class__._canvas_cache['image'] = None
|
||||||
self.__class__._canvas_cache['mask'] = None
|
self.__class__._canvas_cache['mask'] = None
|
||||||
self.__class__._canvas_cache['last_execution_id'] = current_execution
|
self.__class__._canvas_cache['last_execution_id'] = current_execution
|
||||||
else:
|
else:
|
||||||
print(f"[OUTPUT_LOG] Same execution ID, using cached data")
|
log_debug(f"Same execution ID, using cached data")
|
||||||
|
|
||||||
if input_image is not None:
|
if input_image is not None:
|
||||||
print("Input image received, converting to PIL Image...")
|
log_info("Input image received, converting to PIL Image...")
|
||||||
|
|
||||||
if isinstance(input_image, torch.Tensor):
|
if isinstance(input_image, torch.Tensor):
|
||||||
if input_image.dim() == 4:
|
if input_image.dim() == 4:
|
||||||
@@ -240,17 +279,17 @@ class CanvasNode:
|
|||||||
try:
|
try:
|
||||||
|
|
||||||
pil_image = Image.fromarray(image_array, 'RGB')
|
pil_image = Image.fromarray(image_array, 'RGB')
|
||||||
print("Successfully converted to PIL Image")
|
log_debug("Successfully converted to PIL Image")
|
||||||
|
|
||||||
self.__class__._canvas_cache['image'] = pil_image
|
self.__class__._canvas_cache['image'] = pil_image
|
||||||
print(f"Image stored in cache with size: {pil_image.size}")
|
log_debug(f"Image stored in cache with size: {pil_image.size}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error converting to PIL Image: {str(e)}")
|
log_error(f"Error converting to PIL Image: {str(e)}")
|
||||||
print(f"Array shape: {image_array.shape}, dtype: {image_array.dtype}")
|
log_debug(f"Array shape: {image_array.shape}, dtype: {image_array.dtype}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if input_mask is not None:
|
if input_mask is not None:
|
||||||
print("Input mask received, converting to PIL Image...")
|
log_info("Input mask received, converting to PIL Image...")
|
||||||
if isinstance(input_mask, torch.Tensor):
|
if isinstance(input_mask, torch.Tensor):
|
||||||
if input_mask.dim() == 4:
|
if input_mask.dim() == 4:
|
||||||
input_mask = input_mask.squeeze(0)
|
input_mask = input_mask.squeeze(0)
|
||||||
@@ -259,10 +298,10 @@ class CanvasNode:
|
|||||||
|
|
||||||
mask_array = (input_mask.cpu().numpy() * 255).astype(np.uint8)
|
mask_array = (input_mask.cpu().numpy() * 255).astype(np.uint8)
|
||||||
pil_mask = Image.fromarray(mask_array, 'L')
|
pil_mask = Image.fromarray(mask_array, 'L')
|
||||||
print("Successfully converted mask to PIL Image")
|
log_debug("Successfully converted mask to PIL Image")
|
||||||
|
|
||||||
self.__class__._canvas_cache['mask'] = pil_mask
|
self.__class__._canvas_cache['mask'] = pil_mask
|
||||||
print(f"Mask stored in cache with size: {pil_mask.size}")
|
log_debug(f"Mask stored in cache with size: {pil_mask.size}")
|
||||||
|
|
||||||
self.__class__._canvas_cache['cache_enabled'] = cache_enabled
|
self.__class__._canvas_cache['cache_enabled'] = cache_enabled
|
||||||
|
|
||||||
@@ -270,7 +309,7 @@ class CanvasNode:
|
|||||||
# Wczytaj obraz bez maski
|
# Wczytaj obraz bez maski
|
||||||
path_image_without_mask = folder_paths.get_annotated_filepath(
|
path_image_without_mask = folder_paths.get_annotated_filepath(
|
||||||
canvas_image.replace('.png', '_without_mask.png'))
|
canvas_image.replace('.png', '_without_mask.png'))
|
||||||
print(f"[OUTPUT_LOG] Loading image without mask from: {path_image_without_mask}")
|
log_debug(f"Loading image without mask from: {path_image_without_mask}")
|
||||||
i = Image.open(path_image_without_mask)
|
i = Image.open(path_image_without_mask)
|
||||||
i = ImageOps.exif_transpose(i)
|
i = ImageOps.exif_transpose(i)
|
||||||
if i.mode not in ['RGB', 'RGBA']:
|
if i.mode not in ['RGB', 'RGBA']:
|
||||||
@@ -281,56 +320,55 @@ class CanvasNode:
|
|||||||
alpha = image[..., 3:]
|
alpha = image[..., 3:]
|
||||||
image = rgb * alpha + (1 - alpha) * 0.5
|
image = rgb * alpha + (1 - alpha) * 0.5
|
||||||
processed_image = torch.from_numpy(image)[None,]
|
processed_image = torch.from_numpy(image)[None,]
|
||||||
print(f"[OUTPUT_LOG] Successfully loaded image without mask, shape: {processed_image.shape}")
|
log_debug(f"Successfully loaded image without mask, shape: {processed_image.shape}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[OUTPUT_LOG] Error loading image without mask: {str(e)}")
|
log_error(f"Error loading image without mask: {str(e)}")
|
||||||
processed_image = torch.ones((1, 512, 512, 3), dtype=torch.float32)
|
processed_image = torch.ones((1, 512, 512, 3), dtype=torch.float32)
|
||||||
print(f"[OUTPUT_LOG] Using default image, shape: {processed_image.shape}")
|
log_debug(f"Using default image, shape: {processed_image.shape}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Wczytaj maskę
|
# Wczytaj maskę
|
||||||
path_image = folder_paths.get_annotated_filepath(canvas_image)
|
path_image = folder_paths.get_annotated_filepath(canvas_image)
|
||||||
path_mask = path_image.replace('.png', '_mask.png')
|
path_mask = path_image.replace('.png', '_mask.png')
|
||||||
print(f"[OUTPUT_LOG] Looking for mask at: {path_mask}")
|
log_debug(f"Looking for mask at: {path_mask}")
|
||||||
if os.path.exists(path_mask):
|
if os.path.exists(path_mask):
|
||||||
print(f"[OUTPUT_LOG] Mask file exists, loading...")
|
log_debug(f"Mask file exists, loading...")
|
||||||
mask = Image.open(path_mask).convert('L')
|
mask = Image.open(path_mask).convert('L')
|
||||||
mask = np.array(mask).astype(np.float32) / 255.0
|
mask = np.array(mask).astype(np.float32) / 255.0
|
||||||
processed_mask = torch.from_numpy(mask)[None,]
|
processed_mask = torch.from_numpy(mask)[None,]
|
||||||
print(f"[OUTPUT_LOG] Successfully loaded mask, shape: {processed_mask.shape}")
|
log_debug(f"Successfully loaded mask, shape: {processed_mask.shape}")
|
||||||
else:
|
else:
|
||||||
print(f"[OUTPUT_LOG] Mask file does not exist, creating default mask")
|
log_debug(f"Mask file does not exist, creating default mask")
|
||||||
processed_mask = torch.ones((1, processed_image.shape[1], processed_image.shape[2]),
|
processed_mask = torch.ones((1, processed_image.shape[1], processed_image.shape[2]),
|
||||||
dtype=torch.float32)
|
dtype=torch.float32)
|
||||||
print(f"[OUTPUT_LOG] Default mask created, shape: {processed_mask.shape}")
|
log_debug(f"Default mask created, shape: {processed_mask.shape}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[OUTPUT_LOG] Error loading mask: {str(e)}")
|
log_error(f"Error loading mask: {str(e)}")
|
||||||
processed_mask = torch.ones((1, processed_image.shape[1], processed_image.shape[2]),
|
processed_mask = torch.ones((1, processed_image.shape[1], processed_image.shape[2]),
|
||||||
dtype=torch.float32)
|
dtype=torch.float32)
|
||||||
print(f"[OUTPUT_LOG] Fallback mask created, shape: {processed_mask.shape}")
|
log_debug(f"Fallback mask created, shape: {processed_mask.shape}")
|
||||||
|
|
||||||
if not output_switch:
|
if not output_switch:
|
||||||
print(f"[OUTPUT_LOG] Output switch is OFF, returning empty tuple")
|
log_debug(f"Output switch is OFF, returning empty tuple")
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
print(f"[OUTPUT_LOG] About to return output - Image shape: {processed_image.shape}, Mask shape: {processed_mask.shape}")
|
log_debug(f"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}")
|
log_debug(f"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}")
|
log_debug(f"Mask tensor info - dtype: {processed_mask.dtype}, device: {processed_mask.device}")
|
||||||
|
|
||||||
self.update_persistent_cache()
|
self.update_persistent_cache()
|
||||||
|
|
||||||
print(f"[OUTPUT_LOG] Successfully returning processed image and mask")
|
log_info(f"Successfully returning processed image and mask")
|
||||||
return (processed_image, processed_mask)
|
return (processed_image, processed_mask)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in process_canvas_image: {str(e)}")
|
log_exception(f"Error in process_canvas_image: {str(e)}")
|
||||||
traceback.print_exc()
|
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Zwolnij blokadę
|
# Zwolnij blokadę
|
||||||
self.__class__._processing_lock = None
|
self.__class__._processing_lock = None
|
||||||
print(f"[OUTPUT_LOG] Process completed, lock released")
|
log_debug(f"Process completed, lock released")
|
||||||
|
|
||||||
def get_cached_data(self):
|
def get_cached_data(self):
|
||||||
return {
|
return {
|
||||||
@@ -378,11 +416,11 @@ class CanvasNode:
|
|||||||
async def get_canvas_data(request):
|
async def get_canvas_data(request):
|
||||||
try:
|
try:
|
||||||
node_id = request.match_info["node_id"]
|
node_id = request.match_info["node_id"]
|
||||||
print(f"Received request for node: {node_id}")
|
log_debug(f"Received request for node: {node_id}")
|
||||||
|
|
||||||
cache_data = cls._canvas_cache
|
cache_data = cls._canvas_cache
|
||||||
print(f"Cache content: {cache_data}")
|
log_debug(f"Cache content: {cache_data}")
|
||||||
print(f"Image in cache: {cache_data['image'] is not None}")
|
log_debug(f"Image in cache: {cache_data['image'] is not None}")
|
||||||
|
|
||||||
response_data = {
|
response_data = {
|
||||||
'success': True,
|
'success': True,
|
||||||
@@ -409,7 +447,7 @@ class CanvasNode:
|
|||||||
return web.json_response(response_data)
|
return web.json_response(response_data)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in get_canvas_data: {str(e)}")
|
log_error(f"Error in get_canvas_data: {str(e)}")
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
'success': False,
|
'success': False,
|
||||||
'error': str(e)
|
'error': str(e)
|
||||||
@@ -471,7 +509,7 @@ class BiRefNetMatting:
|
|||||||
|
|
||||||
full_model_path = os.path.join(self.base_path, "BiRefNet")
|
full_model_path = os.path.join(self.base_path, "BiRefNet")
|
||||||
|
|
||||||
print(f"Loading BiRefNet model from {full_model_path}...")
|
log_info(f"Loading BiRefNet model from {full_model_path}...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
@@ -486,23 +524,23 @@ class BiRefNetMatting:
|
|||||||
self.model = self.model.cuda()
|
self.model = self.model.cuda()
|
||||||
|
|
||||||
self.model_cache[model_path] = self.model
|
self.model_cache[model_path] = self.model
|
||||||
print("Model loaded successfully from Hugging Face")
|
log_info("Model loaded successfully from Hugging Face")
|
||||||
print(f"Model type: {type(self.model)}")
|
log_debug(f"Model type: {type(self.model)}")
|
||||||
print(f"Model device: {next(self.model.parameters()).device}")
|
log_debug(f"Model device: {next(self.model.parameters()).device}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to load model: {str(e)}")
|
log_error(f"Failed to load model: {str(e)}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.model = self.model_cache[model_path]
|
self.model = self.model_cache[model_path]
|
||||||
print("Using cached model")
|
log_debug("Using cached model")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error loading model: {str(e)}")
|
log_error(f"Error loading model: {str(e)}")
|
||||||
traceback.print_exc()
|
log_exception("Model loading failed")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def preprocess_image(self, image):
|
def preprocess_image(self, image):
|
||||||
@@ -528,7 +566,7 @@ class BiRefNetMatting:
|
|||||||
|
|
||||||
return image_tensor
|
return image_tensor
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error preprocessing image: {str(e)}")
|
log_error(f"Error preprocessing image: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def execute(self, image, model_path, threshold=0.5, refinement=1):
|
def execute(self, image, model_path, threshold=0.5, refinement=1):
|
||||||
@@ -544,25 +582,25 @@ class BiRefNetMatting:
|
|||||||
else:
|
else:
|
||||||
original_size = image.size[::-1]
|
original_size = image.size[::-1]
|
||||||
|
|
||||||
print(f"Original size: {original_size}")
|
log_debug(f"Original size: {original_size}")
|
||||||
|
|
||||||
processed_image = self.preprocess_image(image)
|
processed_image = self.preprocess_image(image)
|
||||||
if processed_image is None:
|
if processed_image is None:
|
||||||
raise Exception("Failed to preprocess image")
|
raise Exception("Failed to preprocess image")
|
||||||
|
|
||||||
print(f"Processed image shape: {processed_image.shape}")
|
log_debug(f"Processed image shape: {processed_image.shape}")
|
||||||
|
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
outputs = self.model(processed_image)
|
outputs = self.model(processed_image)
|
||||||
result = outputs[-1].sigmoid().cpu()
|
result = outputs[-1].sigmoid().cpu()
|
||||||
print(f"Model output shape: {result.shape}")
|
log_debug(f"Model output shape: {result.shape}")
|
||||||
|
|
||||||
if result.dim() == 3:
|
if result.dim() == 3:
|
||||||
result = result.unsqueeze(1) # 添加通道维度
|
result = result.unsqueeze(1) # 添加通道维度
|
||||||
elif result.dim() == 2:
|
elif result.dim() == 2:
|
||||||
result = result.unsqueeze(0).unsqueeze(0) # 添加batch和通道维度
|
result = result.unsqueeze(0).unsqueeze(0) # 添加batch和通道维度
|
||||||
|
|
||||||
print(f"Reshaped result shape: {result.shape}")
|
log_debug(f"Reshaped result shape: {result.shape}")
|
||||||
|
|
||||||
result = F.interpolate(
|
result = F.interpolate(
|
||||||
result,
|
result,
|
||||||
@@ -570,7 +608,7 @@ class BiRefNetMatting:
|
|||||||
mode='bilinear',
|
mode='bilinear',
|
||||||
align_corners=True
|
align_corners=True
|
||||||
)
|
)
|
||||||
print(f"Resized result shape: {result.shape}")
|
log_debug(f"Resized result shape: {result.shape}")
|
||||||
|
|
||||||
result = result.squeeze() # 移除多余的维度
|
result = result.squeeze() # 移除多余的维度
|
||||||
ma = torch.max(result)
|
ma = torch.max(result)
|
||||||
@@ -618,7 +656,7 @@ async def matting(request):
|
|||||||
|
|
||||||
# Sprawdź czy już trwa przetwarzanie
|
# Sprawdź czy już trwa przetwarzanie
|
||||||
if _matting_lock is not None:
|
if _matting_lock is not None:
|
||||||
print("Matting already in progress, rejecting request")
|
log_warn("Matting already in progress, rejecting request")
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
"error": "Another matting operation is in progress",
|
"error": "Another matting operation is in progress",
|
||||||
"details": "Please wait for the current operation to complete"
|
"details": "Please wait for the current operation to complete"
|
||||||
@@ -628,13 +666,13 @@ async def matting(request):
|
|||||||
_matting_lock = True
|
_matting_lock = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print("Received matting request")
|
log_info("Received matting request")
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
|
|
||||||
matting = BiRefNetMatting()
|
matting = BiRefNetMatting()
|
||||||
|
|
||||||
image_tensor, original_alpha = convert_base64_to_tensor(data["image"])
|
image_tensor, original_alpha = convert_base64_to_tensor(data["image"])
|
||||||
print(f"Input image shape: {image_tensor.shape}")
|
log_debug(f"Input image shape: {image_tensor.shape}")
|
||||||
|
|
||||||
matted_image, alpha_mask = matting.execute(
|
matted_image, alpha_mask = matting.execute(
|
||||||
image_tensor,
|
image_tensor,
|
||||||
@@ -652,9 +690,7 @@ async def matting(request):
|
|||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in matting endpoint: {str(e)}")
|
log_exception(f"Error in matting endpoint: {str(e)}")
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
"details": traceback.format_exc()
|
"details": traceback.format_exc()
|
||||||
@@ -662,7 +698,7 @@ async def matting(request):
|
|||||||
finally:
|
finally:
|
||||||
# Zwolnij blokadę
|
# Zwolnij blokadę
|
||||||
_matting_lock = None
|
_matting_lock = None
|
||||||
print("Matting lock released")
|
log_debug("Matting lock released")
|
||||||
|
|
||||||
|
|
||||||
def convert_base64_to_tensor(base64_str):
|
def convert_base64_to_tensor(base64_str):
|
||||||
@@ -695,7 +731,7 @@ def convert_base64_to_tensor(base64_str):
|
|||||||
return img_tensor, None
|
return img_tensor, None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in convert_base64_to_tensor: {str(e)}")
|
log_error(f"Error in convert_base64_to_tensor: {str(e)}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
@@ -742,6 +778,6 @@ def convert_tensor_to_base64(tensor, alpha_mask=None, original_alpha=None):
|
|||||||
return f"data:image/png;base64,{img_str}"
|
return f"data:image/png;base64,{img_str}"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in convert_tensor_to_base64: {str(e)}")
|
log_error(f"Error in convert_tensor_to_base64: {str(e)}")
|
||||||
print(f"Tensor shape: {tensor.shape}, dtype: {tensor.dtype}")
|
log_debug(f"Tensor shape: {tensor.shape}, dtype: {tensor.dtype}")
|
||||||
raise
|
raise
|
||||||
|
|||||||
211
js/Canvas.js
211
js/Canvas.js
@@ -1,5 +1,17 @@
|
|||||||
import {getCanvasState, setCanvasState, removeCanvasState, saveImage, getImage, removeImage} from "./db.js";
|
import {getCanvasState, setCanvasState, removeCanvasState, saveImage, getImage, removeImage} from "./db.js";
|
||||||
import {MaskTool} from "./Mask_tool.js";
|
import {MaskTool} from "./Mask_tool.js";
|
||||||
|
import {logger, LogLevel} from "./logger.js";
|
||||||
|
|
||||||
|
// Inicjalizacja loggera dla modułu Canvas
|
||||||
|
const log = {
|
||||||
|
debug: (...args) => logger.debug('Canvas', ...args),
|
||||||
|
info: (...args) => logger.info('Canvas', ...args),
|
||||||
|
warn: (...args) => logger.warn('Canvas', ...args),
|
||||||
|
error: (...args) => logger.error('Canvas', ...args)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Konfiguracja loggera dla modułu Canvas
|
||||||
|
logger.setModuleLevel('Canvas', LogLevel.DEBUG); // Domyślnie INFO, można zmienić na DEBUG dla szczegółowych logów
|
||||||
|
|
||||||
// Prosta funkcja generująca UUID
|
// Prosta funkcja generująca UUID
|
||||||
function generateUUID() {
|
function generateUUID() {
|
||||||
@@ -101,13 +113,13 @@ export class Canvas {
|
|||||||
async loadStateFromDB() {
|
async loadStateFromDB() {
|
||||||
// Sprawdź czy już trwa ładowanie
|
// Sprawdź czy już trwa ładowanie
|
||||||
if (this._loadInProgress) {
|
if (this._loadInProgress) {
|
||||||
console.log("Load already in progress, waiting...");
|
log.warn("Load already in progress, waiting...");
|
||||||
return this._loadInProgress;
|
return this._loadInProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Attempting to load state from IndexedDB for node:", this.node.id);
|
log.info("Attempting to load state from IndexedDB for node:", this.node.id);
|
||||||
if (!this.node.id) {
|
if (!this.node.id) {
|
||||||
console.error("Node ID is not available for loading state from DB.");
|
log.error("Node ID is not available for loading state from DB.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,35 +137,35 @@ export class Canvas {
|
|||||||
try {
|
try {
|
||||||
const savedState = await getCanvasState(this.node.id);
|
const savedState = await getCanvasState(this.node.id);
|
||||||
if (!savedState) {
|
if (!savedState) {
|
||||||
console.log("No saved state found in IndexedDB for node:", this.node.id);
|
log.info("No saved state found in IndexedDB for node:", this.node.id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
console.log("Found saved state in IndexedDB.");
|
log.info("Found saved state in IndexedDB.");
|
||||||
|
|
||||||
this.width = savedState.width || 512;
|
this.width = savedState.width || 512;
|
||||||
this.height = savedState.height || 512;
|
this.height = savedState.height || 512;
|
||||||
this.viewport = savedState.viewport || {x: -(this.width / 4), y: -(this.height / 4), zoom: 0.8};
|
this.viewport = savedState.viewport || {x: -(this.width / 4), y: -(this.height / 4), zoom: 0.8};
|
||||||
|
|
||||||
this.updateCanvasSize(this.width, this.height, false);
|
this.updateCanvasSize(this.width, this.height, false);
|
||||||
console.log(`Canvas resized to ${this.width}x${this.height} and viewport set.`);
|
log.debug(`Canvas resized to ${this.width}x${this.height} and viewport set.`);
|
||||||
|
|
||||||
const imagePromises = savedState.layers.map((layerData, index) => {
|
const imagePromises = savedState.layers.map((layerData, index) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
if (layerData.imageId) {
|
if (layerData.imageId) {
|
||||||
console.log(`Layer ${index}: Loading image with id: ${layerData.imageId}`);
|
log.debug(`Layer ${index}: Loading image with id: ${layerData.imageId}`);
|
||||||
// Sprawdź, czy obraz jest już w pamięci podręcznej
|
// Sprawdź, czy obraz jest już w pamięci podręcznej
|
||||||
if (this.imageCache.has(layerData.imageId)) {
|
if (this.imageCache.has(layerData.imageId)) {
|
||||||
console.log(`Layer ${index}: Image found in cache.`);
|
log.debug(`Layer ${index}: Image found in cache.`);
|
||||||
const imageSrc = this.imageCache.get(layerData.imageId);
|
const imageSrc = this.imageCache.get(layerData.imageId);
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
console.log(`Layer ${index}: Image loaded successfully.`);
|
log.debug(`Layer ${index}: Image loaded successfully.`);
|
||||||
const newLayer = {...layerData, image: img};
|
const newLayer = {...layerData, image: img};
|
||||||
delete newLayer.imageId;
|
delete newLayer.imageId;
|
||||||
resolve(newLayer);
|
resolve(newLayer);
|
||||||
};
|
};
|
||||||
img.onerror = () => {
|
img.onerror = () => {
|
||||||
console.error(`Layer ${index}: Failed to load image from src.`);
|
log.error(`Layer ${index}: Failed to load image from src.`);
|
||||||
resolve(null);
|
resolve(null);
|
||||||
};
|
};
|
||||||
img.src = imageSrc;
|
img.src = imageSrc;
|
||||||
@@ -161,54 +173,54 @@ export class Canvas {
|
|||||||
// Wczytaj obraz z IndexedDB
|
// Wczytaj obraz z IndexedDB
|
||||||
getImage(layerData.imageId).then(imageSrc => {
|
getImage(layerData.imageId).then(imageSrc => {
|
||||||
if (imageSrc) {
|
if (imageSrc) {
|
||||||
console.log(`Layer ${index}: Loading image from data:URL...`);
|
log.debug(`Layer ${index}: Loading image from data:URL...`);
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
console.log(`Layer ${index}: Image loaded successfully.`);
|
log.debug(`Layer ${index}: Image loaded successfully.`);
|
||||||
this.imageCache.set(layerData.imageId, imageSrc); // Zapisz w pamięci podręcznej jako imageSrc
|
this.imageCache.set(layerData.imageId, imageSrc); // Zapisz w pamięci podręcznej jako imageSrc
|
||||||
const newLayer = {...layerData, image: img};
|
const newLayer = {...layerData, image: img};
|
||||||
delete newLayer.imageId;
|
delete newLayer.imageId;
|
||||||
resolve(newLayer);
|
resolve(newLayer);
|
||||||
};
|
};
|
||||||
img.onerror = () => {
|
img.onerror = () => {
|
||||||
console.error(`Layer ${index}: Failed to load image from src.`);
|
log.error(`Layer ${index}: Failed to load image from src.`);
|
||||||
resolve(null);
|
resolve(null);
|
||||||
};
|
};
|
||||||
img.src = imageSrc;
|
img.src = imageSrc;
|
||||||
} else {
|
} else {
|
||||||
console.error(`Layer ${index}: Image not found in IndexedDB.`);
|
log.error(`Layer ${index}: Image not found in IndexedDB.`);
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error(`Layer ${index}: Error loading image from IndexedDB:`, err);
|
log.error(`Layer ${index}: Error loading image from IndexedDB:`, err);
|
||||||
resolve(null);
|
resolve(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (layerData.imageSrc) {
|
} else if (layerData.imageSrc) {
|
||||||
// Obsługa starego formatu z imageSrc
|
// Obsługa starego formatu z imageSrc
|
||||||
console.log(`Layer ${index}: Found imageSrc, converting to new format with imageId.`);
|
log.info(`Layer ${index}: Found imageSrc, converting to new format with imageId.`);
|
||||||
const imageId = generateUUID();
|
const imageId = generateUUID();
|
||||||
saveImage(imageId, layerData.imageSrc).then(() => {
|
saveImage(imageId, layerData.imageSrc).then(() => {
|
||||||
console.log(`Layer ${index}: Image saved to IndexedDB with id: ${imageId}`);
|
log.info(`Layer ${index}: Image saved to IndexedDB with id: ${imageId}`);
|
||||||
this.imageCache.set(imageId, layerData.imageSrc); // Zapisz w pamięci podręcznej jako imageSrc
|
this.imageCache.set(imageId, layerData.imageSrc); // Zapisz w pamięci podręcznej jako imageSrc
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
console.log(`Layer ${index}: Image loaded successfully from imageSrc.`);
|
log.debug(`Layer ${index}: Image loaded successfully from imageSrc.`);
|
||||||
const newLayer = {...layerData, image: img, imageId};
|
const newLayer = {...layerData, image: img, imageId};
|
||||||
delete newLayer.imageSrc;
|
delete newLayer.imageSrc;
|
||||||
resolve(newLayer);
|
resolve(newLayer);
|
||||||
};
|
};
|
||||||
img.onerror = () => {
|
img.onerror = () => {
|
||||||
console.error(`Layer ${index}: Failed to load image from imageSrc.`);
|
log.error(`Layer ${index}: Failed to load image from imageSrc.`);
|
||||||
resolve(null);
|
resolve(null);
|
||||||
};
|
};
|
||||||
img.src = layerData.imageSrc;
|
img.src = layerData.imageSrc;
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error(`Layer ${index}: Error saving image to IndexedDB:`, err);
|
log.error(`Layer ${index}: Error saving image to IndexedDB:`, err);
|
||||||
resolve(null);
|
resolve(null);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error(`Layer ${index}: No imageId or imageSrc found, skipping layer.`);
|
log.error(`Layer ${index}: No imageId or imageSrc found, skipping layer.`);
|
||||||
resolve(null); // Pomiń warstwy bez obrazu
|
resolve(null); // Pomiń warstwy bez obrazu
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -216,35 +228,35 @@ export class Canvas {
|
|||||||
|
|
||||||
const loadedLayers = await Promise.all(imagePromises);
|
const loadedLayers = await Promise.all(imagePromises);
|
||||||
this.layers = loadedLayers.filter(l => l !== null);
|
this.layers = loadedLayers.filter(l => l !== null);
|
||||||
console.log(`Loaded ${this.layers.length} layers.`);
|
log.info(`Loaded ${this.layers.length} layers.`);
|
||||||
|
|
||||||
if (this.layers.length === 0) {
|
if (this.layers.length === 0) {
|
||||||
console.warn("No valid layers loaded, state may be corrupted.");
|
log.warn("No valid layers loaded, state may be corrupted.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateSelectionAfterHistory();
|
this.updateSelectionAfterHistory();
|
||||||
this.render();
|
this.render();
|
||||||
console.log("Canvas state loaded successfully from IndexedDB for node", this.node.id);
|
log.info("Canvas state loaded successfully from IndexedDB for node", this.node.id);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error loading canvas state from IndexedDB:", e);
|
log.error("Error loading canvas state from IndexedDB:", e);
|
||||||
await removeCanvasState(this.node.id).catch(err => console.error("Failed to remove corrupted state:", err));
|
await removeCanvasState(this.node.id).catch(err => log.error("Failed to remove corrupted state:", err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveStateToDB(immediate = false) {
|
async saveStateToDB(immediate = false) {
|
||||||
console.log("Preparing to save state to IndexedDB for node:", this.node.id);
|
log.info("Preparing to save state to IndexedDB for node:", this.node.id);
|
||||||
if (!this.node.id) {
|
if (!this.node.id) {
|
||||||
console.error("Node ID is not available for saving state to DB.");
|
log.error("Node ID is not available for saving state to DB.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oblicz sygnaturę obecnego stanu
|
// Oblicz sygnaturę obecnego stanu
|
||||||
const currentStateSignature = this.getStateSignature(this.layers);
|
const currentStateSignature = this.getStateSignature(this.layers);
|
||||||
if (this.lastSavedStateSignature === currentStateSignature) {
|
if (this.lastSavedStateSignature === currentStateSignature) {
|
||||||
console.log("State unchanged, skipping save to IndexedDB.");
|
log.debug("State unchanged, skipping save to IndexedDB.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +271,7 @@ export class Canvas {
|
|||||||
layers: await Promise.all(this.layers.map(async (layer, index) => {
|
layers: await Promise.all(this.layers.map(async (layer, index) => {
|
||||||
const newLayer = {...layer};
|
const newLayer = {...layer};
|
||||||
if (layer.image instanceof HTMLImageElement) {
|
if (layer.image instanceof HTMLImageElement) {
|
||||||
console.log(`Layer ${index}: Using imageId instead of serializing image.`);
|
log.debug(`Layer ${index}: Using imageId instead of serializing image.`);
|
||||||
if (!layer.imageId) {
|
if (!layer.imageId) {
|
||||||
// Jeśli obraz nie ma jeszcze imageId, zapisz go do IndexedDB
|
// Jeśli obraz nie ma jeszcze imageId, zapisz go do IndexedDB
|
||||||
layer.imageId = generateUUID();
|
layer.imageId = generateUUID();
|
||||||
@@ -268,7 +280,7 @@ export class Canvas {
|
|||||||
}
|
}
|
||||||
newLayer.imageId = layer.imageId;
|
newLayer.imageId = layer.imageId;
|
||||||
} else if (!layer.imageId) {
|
} else if (!layer.imageId) {
|
||||||
console.error(`Layer ${index}: No image or imageId found, skipping layer.`);
|
log.error(`Layer ${index}: No image or imageId found, skipping layer.`);
|
||||||
return null; // Pomiń warstwy bez obrazu
|
return null; // Pomiń warstwy bez obrazu
|
||||||
}
|
}
|
||||||
delete newLayer.image;
|
delete newLayer.image;
|
||||||
@@ -281,14 +293,14 @@ export class Canvas {
|
|||||||
// Filtruj warstwy, które nie mają obrazu
|
// Filtruj warstwy, które nie mają obrazu
|
||||||
state.layers = state.layers.filter(layer => layer !== null);
|
state.layers = state.layers.filter(layer => layer !== null);
|
||||||
if (state.layers.length === 0) {
|
if (state.layers.length === 0) {
|
||||||
console.warn("No valid layers to save, skipping save to IndexedDB.");
|
log.warn("No valid layers to save, skipping save to IndexedDB.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await setCanvasState(this.node.id, state);
|
await setCanvasState(this.node.id, state);
|
||||||
console.log("Canvas state saved to IndexedDB.");
|
log.info("Canvas state saved to IndexedDB.");
|
||||||
this.lastSavedStateSignature = currentStateSignature; // Zaktualizuj sygnaturę zapisanego stanu
|
this.lastSavedStateSignature = currentStateSignature; // Zaktualizuj sygnaturę zapisanego stanu
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error saving canvas state to IndexedDB:", e);
|
log.error("Error saving canvas state to IndexedDB:", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -302,10 +314,10 @@ export class Canvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadInitialState() {
|
async loadInitialState() {
|
||||||
console.log("Loading initial state for node:", this.node.id);
|
log.info("Loading initial state for node:", this.node.id);
|
||||||
const loaded = await this.loadStateFromDB();
|
const loaded = await this.loadStateFromDB();
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
console.log("No saved state found, initializing from node data.");
|
log.info("No saved state found, initializing from node data.");
|
||||||
await this.initNodeData();
|
await this.initNodeData();
|
||||||
}
|
}
|
||||||
this.saveState(); // Save initial state to undo stack
|
this.saveState(); // Save initial state to undo stack
|
||||||
@@ -508,16 +520,16 @@ export class Canvas {
|
|||||||
async copySelectedLayers() {
|
async copySelectedLayers() {
|
||||||
if (this.selectedLayers.length === 0) return;
|
if (this.selectedLayers.length === 0) return;
|
||||||
this.internalClipboard = this.selectedLayers.map(layer => ({...layer}));
|
this.internalClipboard = this.selectedLayers.map(layer => ({...layer}));
|
||||||
console.log(`Copied ${this.internalClipboard.length} layer(s) to internal clipboard.`);
|
log.info(`Copied ${this.internalClipboard.length} layer(s) to internal clipboard.`);
|
||||||
try {
|
try {
|
||||||
const blob = await this.getFlattenedSelectionAsBlob();
|
const blob = await this.getFlattenedSelectionAsBlob();
|
||||||
if (blob) {
|
if (blob) {
|
||||||
const item = new ClipboardItem({'image/png': blob});
|
const item = new ClipboardItem({'image/png': blob});
|
||||||
await navigator.clipboard.write([item]);
|
await navigator.clipboard.write([item]);
|
||||||
console.log("Flattened selection copied to the system clipboard.");
|
log.info("Flattened selection copied to the system clipboard.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to copy image to system clipboard:", error);
|
log.error("Failed to copy image to system clipboard:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,14 +553,14 @@ export class Canvas {
|
|||||||
|
|
||||||
this.updateSelection(newLayers);
|
this.updateSelection(newLayers);
|
||||||
this.render();
|
this.render();
|
||||||
console.log(`Pasted ${newLayers.length} layer(s).`);
|
log.info(`Pasted ${newLayers.length} layer(s).`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async handlePaste() {
|
async handlePaste() {
|
||||||
try {
|
try {
|
||||||
if (!navigator.clipboard?.read) {
|
if (!navigator.clipboard?.read) {
|
||||||
console.log("Browser does not support clipboard read API. Falling back to internal paste.");
|
log.info("Browser does not support clipboard read API. Falling back to internal paste.");
|
||||||
this.pasteLayers();
|
this.pasteLayers();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -582,7 +594,7 @@ export class Canvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Paste operation failed, falling back to internal paste. Error:", err);
|
log.error("Paste operation failed, falling back to internal paste. Error:", err);
|
||||||
this.pasteLayers();
|
this.pasteLayers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1202,7 +1214,7 @@ export class Canvas {
|
|||||||
|
|
||||||
async addLayerWithImage(image, layerProps = {}) {
|
async addLayerWithImage(image, layerProps = {}) {
|
||||||
try {
|
try {
|
||||||
console.log("Adding layer with image:", image);
|
log.debug("Adding layer with image:", image);
|
||||||
|
|
||||||
// Wygeneruj unikalny identyfikator dla obrazu i zapisz go do IndexedDB
|
// Wygeneruj unikalny identyfikator dla obrazu i zapisz go do IndexedDB
|
||||||
const imageId = generateUUID();
|
const imageId = generateUUID();
|
||||||
@@ -1228,10 +1240,10 @@ export class Canvas {
|
|||||||
this.render();
|
this.render();
|
||||||
this.saveState();
|
this.saveState();
|
||||||
|
|
||||||
console.log("Layer added successfully");
|
log.info("Layer added successfully");
|
||||||
return layer;
|
return layer;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error adding layer:", error);
|
log.error("Error adding layer:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1732,13 +1744,13 @@ export class Canvas {
|
|||||||
async saveToServer(fileName) {
|
async saveToServer(fileName) {
|
||||||
// Sprawdź czy już trwa zapis
|
// Sprawdź czy już trwa zapis
|
||||||
if (this._saveInProgress) {
|
if (this._saveInProgress) {
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Save already in progress, waiting...`);
|
log.warn(`Save already in progress, waiting...`);
|
||||||
return this._saveInProgress;
|
return this._saveInProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Starting saveToServer with fileName: ${fileName}`);
|
log.info(`Starting saveToServer with fileName: ${fileName}`);
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Canvas dimensions: ${this.width}x${this.height}`);
|
log.debug(`Canvas dimensions: ${this.width}x${this.height}`);
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Number of layers: ${this.layers.length}`);
|
log.debug(`Number of layers: ${this.layers.length}`);
|
||||||
|
|
||||||
// Utwórz Promise dla aktualnego zapisu
|
// Utwórz Promise dla aktualnego zapisu
|
||||||
this._saveInProgress = this._performSave(fileName);
|
this._saveInProgress = this._performSave(fileName);
|
||||||
@@ -1748,6 +1760,7 @@ export class Canvas {
|
|||||||
return result;
|
return result;
|
||||||
} finally {
|
} finally {
|
||||||
this._saveInProgress = null;
|
this._saveInProgress = null;
|
||||||
|
log.debug(`Save completed, lock released`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1772,15 +1785,15 @@ export class Canvas {
|
|||||||
maskCtx.fillStyle = '#ffffff'; // Białe tło dla wolnych przestrzeni
|
maskCtx.fillStyle = '#ffffff'; // Białe tło dla wolnych przestrzeni
|
||||||
maskCtx.fillRect(0, 0, this.width, this.height);
|
maskCtx.fillRect(0, 0, this.width, this.height);
|
||||||
|
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Canvas contexts created, starting layer rendering`);
|
log.debug(`Canvas contexts created, starting layer rendering`);
|
||||||
|
|
||||||
// Rysowanie warstw
|
// Rysowanie warstw
|
||||||
const sortedLayers = this.layers.sort((a, b) => a.zIndex - b.zIndex);
|
const sortedLayers = this.layers.sort((a, b) => a.zIndex - b.zIndex);
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Processing ${sortedLayers.length} layers in order`);
|
log.debug(`Processing ${sortedLayers.length} layers in order`);
|
||||||
|
|
||||||
sortedLayers.forEach((layer, index) => {
|
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})`);
|
log.debug(`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}`);
|
log.debug(`Layer ${index}: blendMode=${layer.blendMode || 'normal'}, opacity=${layer.opacity !== undefined ? layer.opacity : 1}`);
|
||||||
|
|
||||||
tempCtx.save();
|
tempCtx.save();
|
||||||
tempCtx.globalCompositeOperation = layer.blendMode || 'normal';
|
tempCtx.globalCompositeOperation = layer.blendMode || 'normal';
|
||||||
@@ -1790,7 +1803,7 @@ export class Canvas {
|
|||||||
tempCtx.drawImage(layer.image, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
tempCtx.drawImage(layer.image, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||||
tempCtx.restore();
|
tempCtx.restore();
|
||||||
|
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Layer ${index} rendered successfully`);
|
log.debug(`Layer ${index} rendered successfully`);
|
||||||
|
|
||||||
maskCtx.save();
|
maskCtx.save();
|
||||||
maskCtx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
|
maskCtx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
|
||||||
@@ -1875,10 +1888,10 @@ export class Canvas {
|
|||||||
|
|
||||||
// Zapisz obraz bez maski
|
// Zapisz obraz bez maski
|
||||||
const fileNameWithoutMask = fileName.replace('.png', '_without_mask.png');
|
const fileNameWithoutMask = fileName.replace('.png', '_without_mask.png');
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Saving image without mask as: ${fileNameWithoutMask}`);
|
log.info(`Saving image without mask as: ${fileNameWithoutMask}`);
|
||||||
|
|
||||||
tempCanvas.toBlob(async (blobWithoutMask) => {
|
tempCanvas.toBlob(async (blobWithoutMask) => {
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Created blob for image without mask, size: ${blobWithoutMask.size} bytes`);
|
log.debug(`Created blob for image without mask, size: ${blobWithoutMask.size} bytes`);
|
||||||
const formDataWithoutMask = new FormData();
|
const formDataWithoutMask = new FormData();
|
||||||
formDataWithoutMask.append("image", blobWithoutMask, fileNameWithoutMask);
|
formDataWithoutMask.append("image", blobWithoutMask, fileNameWithoutMask);
|
||||||
formDataWithoutMask.append("overwrite", "true");
|
formDataWithoutMask.append("overwrite", "true");
|
||||||
@@ -1888,16 +1901,16 @@ export class Canvas {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: formDataWithoutMask,
|
body: formDataWithoutMask,
|
||||||
});
|
});
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Image without mask upload response: ${response.status}`);
|
log.debug(`Image without mask upload response: ${response.status}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[CANVAS_OUTPUT_LOG] Error uploading image without mask:`, error);
|
log.error(`Error uploading image without mask:`, error);
|
||||||
}
|
}
|
||||||
}, "image/png");
|
}, "image/png");
|
||||||
|
|
||||||
// Zapisz obraz z maską
|
// Zapisz obraz z maską
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Saving main image as: ${fileName}`);
|
log.info(`Saving main image as: ${fileName}`);
|
||||||
tempCanvas.toBlob(async (blob) => {
|
tempCanvas.toBlob(async (blob) => {
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Created blob for main image, size: ${blob.size} bytes`);
|
log.debug(`Created blob for main image, size: ${blob.size} bytes`);
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("image", blob, fileName);
|
formData.append("image", blob, fileName);
|
||||||
formData.append("overwrite", "true");
|
formData.append("overwrite", "true");
|
||||||
@@ -1907,14 +1920,14 @@ export class Canvas {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
body: formData,
|
||||||
});
|
});
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Main image upload response: ${resp.status}`);
|
log.debug(`Main image upload response: ${resp.status}`);
|
||||||
|
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const maskFileName = fileName.replace('.png', '_mask.png');
|
const maskFileName = fileName.replace('.png', '_mask.png');
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Saving mask as: ${maskFileName}`);
|
log.info(`Saving mask as: ${maskFileName}`);
|
||||||
|
|
||||||
maskCanvas.toBlob(async (maskBlob) => {
|
maskCanvas.toBlob(async (maskBlob) => {
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Created blob for mask, size: ${maskBlob.size} bytes`);
|
log.debug(`Created blob for mask, size: ${maskBlob.size} bytes`);
|
||||||
const maskFormData = new FormData();
|
const maskFormData = new FormData();
|
||||||
maskFormData.append("image", maskBlob, maskFileName);
|
maskFormData.append("image", maskBlob, maskFileName);
|
||||||
maskFormData.append("overwrite", "true");
|
maskFormData.append("overwrite", "true");
|
||||||
@@ -1924,28 +1937,28 @@ export class Canvas {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: maskFormData,
|
body: maskFormData,
|
||||||
});
|
});
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] Mask upload response: ${maskResp.status}`);
|
log.debug(`Mask upload response: ${maskResp.status}`);
|
||||||
|
|
||||||
if (maskResp.status === 200) {
|
if (maskResp.status === 200) {
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
this.widget.value = data.name;
|
this.widget.value = data.name;
|
||||||
console.log(`[CANVAS_OUTPUT_LOG] All files saved successfully, widget value set to: ${data.name}`);
|
log.info(`All files saved successfully, widget value set to: ${data.name}`);
|
||||||
resolve(true);
|
resolve(true);
|
||||||
} else {
|
} else {
|
||||||
console.error(`[CANVAS_OUTPUT_LOG] Error saving mask: ${maskResp.status}`);
|
log.error(`Error saving mask: ${maskResp.status}`);
|
||||||
resolve(false);
|
resolve(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[CANVAS_OUTPUT_LOG] Error saving mask:`, error);
|
log.error(`Error saving mask:`, error);
|
||||||
resolve(false);
|
resolve(false);
|
||||||
}
|
}
|
||||||
}, "image/png");
|
}, "image/png");
|
||||||
} else {
|
} else {
|
||||||
console.error(`[CANVAS_OUTPUT_LOG] Main image upload failed: ${resp.status} - ${resp.statusText}`);
|
log.error(`Main image upload failed: ${resp.status} - ${resp.statusText}`);
|
||||||
resolve(false);
|
resolve(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[CANVAS_OUTPUT_LOG] Error uploading main image:`, error);
|
log.error(`Error uploading main image:`, error);
|
||||||
resolve(false);
|
resolve(false);
|
||||||
}
|
}
|
||||||
}, "image/png");
|
}, "image/png");
|
||||||
@@ -2241,7 +2254,7 @@ export class Canvas {
|
|||||||
|
|
||||||
return dataUrl;
|
return dataUrl;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error getting layer image data:", error);
|
log.error("Error getting layer image data:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2289,7 +2302,7 @@ export class Canvas {
|
|||||||
|
|
||||||
async addInputToCanvas(inputImage, inputMask) {
|
async addInputToCanvas(inputImage, inputMask) {
|
||||||
try {
|
try {
|
||||||
console.log("Adding input to canvas:", {inputImage});
|
log.debug("Adding input to canvas:", {inputImage});
|
||||||
|
|
||||||
const tempCanvas = document.createElement('canvas');
|
const tempCanvas = document.createElement('canvas');
|
||||||
const tempCtx = tempCanvas.getContext('2d');
|
const tempCtx = tempCanvas.getContext('2d');
|
||||||
@@ -2326,18 +2339,18 @@ export class Canvas {
|
|||||||
layer.mask = inputMask.data;
|
layer.mask = inputMask.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Layer added successfully");
|
log.info("Layer added successfully");
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in addInputToCanvas:", error);
|
log.error("Error in addInputToCanvas:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async convertTensorToImage(tensor) {
|
async convertTensorToImage(tensor) {
|
||||||
try {
|
try {
|
||||||
console.log("Converting tensor to image:", tensor);
|
log.debug("Converting tensor to image:", tensor);
|
||||||
|
|
||||||
if (!tensor || !tensor.data || !tensor.width || !tensor.height) {
|
if (!tensor || !tensor.data || !tensor.width || !tensor.height) {
|
||||||
throw new Error("Invalid tensor data");
|
throw new Error("Invalid tensor data");
|
||||||
@@ -2363,7 +2376,7 @@ export class Canvas {
|
|||||||
img.src = canvas.toDataURL();
|
img.src = canvas.toDataURL();
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error converting tensor to image:", error);
|
log.error("Error converting tensor to image:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2383,10 +2396,10 @@ export class Canvas {
|
|||||||
|
|
||||||
async initNodeData() {
|
async initNodeData() {
|
||||||
try {
|
try {
|
||||||
console.log("Starting node data initialization...");
|
log.info("Starting node data initialization...");
|
||||||
|
|
||||||
if (!this.node || !this.node.inputs) {
|
if (!this.node || !this.node.inputs) {
|
||||||
console.log("Node or inputs not ready");
|
log.debug("Node or inputs not ready");
|
||||||
return this.scheduleDataCheck();
|
return this.scheduleDataCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2395,11 +2408,11 @@ export class Canvas {
|
|||||||
const imageData = app.nodeOutputs[imageLinkId];
|
const imageData = app.nodeOutputs[imageLinkId];
|
||||||
|
|
||||||
if (imageData) {
|
if (imageData) {
|
||||||
console.log("Found image data:", imageData);
|
log.debug("Found image data:", imageData);
|
||||||
await this.processImageData(imageData);
|
await this.processImageData(imageData);
|
||||||
this.dataInitialized = true;
|
this.dataInitialized = true;
|
||||||
} else {
|
} else {
|
||||||
console.log("Image data not available yet");
|
log.debug("Image data not available yet");
|
||||||
return this.scheduleDataCheck();
|
return this.scheduleDataCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2409,13 +2422,13 @@ export class Canvas {
|
|||||||
const maskData = app.nodeOutputs[maskLinkId];
|
const maskData = app.nodeOutputs[maskLinkId];
|
||||||
|
|
||||||
if (maskData) {
|
if (maskData) {
|
||||||
console.log("Found mask data:", maskData);
|
log.debug("Found mask data:", maskData);
|
||||||
await this.processMaskData(maskData);
|
await this.processMaskData(maskData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in initNodeData:", error);
|
log.error("Error in initNodeData:", error);
|
||||||
return this.scheduleDataCheck();
|
return this.scheduleDataCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2437,7 +2450,7 @@ export class Canvas {
|
|||||||
try {
|
try {
|
||||||
if (!imageData) return;
|
if (!imageData) return;
|
||||||
|
|
||||||
console.log("Processing image data:", {
|
log.debug("Processing image data:", {
|
||||||
type: typeof imageData,
|
type: typeof imageData,
|
||||||
isArray: Array.isArray(imageData),
|
isArray: Array.isArray(imageData),
|
||||||
shape: imageData.shape,
|
shape: imageData.shape,
|
||||||
@@ -2465,10 +2478,10 @@ export class Canvas {
|
|||||||
const image = await this.createImageFromData(convertedData);
|
const image = await this.createImageFromData(convertedData);
|
||||||
|
|
||||||
this.addScaledLayer(image, scale);
|
this.addScaledLayer(image, scale);
|
||||||
console.log("Image layer added successfully with scale:", scale);
|
log.info("Image layer added successfully with scale:", scale);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error processing image data:", error);
|
log.error("Error processing image data:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2494,13 +2507,13 @@ export class Canvas {
|
|||||||
this.selectedLayer = layer;
|
this.selectedLayer = layer;
|
||||||
this.render();
|
this.render();
|
||||||
|
|
||||||
console.log("Scaled layer added:", {
|
log.debug("Scaled layer added:", {
|
||||||
originalSize: `${image.width}x${image.height}`,
|
originalSize: `${image.width}x${image.height}`,
|
||||||
scaledSize: `${scaledWidth}x${scaledHeight}`,
|
scaledSize: `${scaledWidth}x${scaledHeight}`,
|
||||||
scale: scale
|
scale: scale
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error adding scaled layer:", error);
|
log.error("Error adding scaled layer:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2512,7 +2525,7 @@ export class Canvas {
|
|||||||
const width = shape[2];
|
const width = shape[2];
|
||||||
const channels = shape[3];
|
const channels = shape[3];
|
||||||
|
|
||||||
console.log("Converting tensor:", {
|
log.debug("Converting tensor:", {
|
||||||
shape: shape,
|
shape: shape,
|
||||||
dataRange: {
|
dataRange: {
|
||||||
min: tensor.min_val,
|
min: tensor.min_val,
|
||||||
@@ -2543,7 +2556,7 @@ export class Canvas {
|
|||||||
imageData.data.set(data);
|
imageData.data.set(data);
|
||||||
return imageData;
|
return imageData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error converting tensor:", error);
|
log.error("Error converting tensor:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2569,20 +2582,20 @@ export class Canvas {
|
|||||||
await this.initNodeData();
|
await this.initNodeData();
|
||||||
return;
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Retry ${i + 1}/${maxRetries} failed:`, error);
|
log.warn(`Retry ${i + 1}/${maxRetries} failed:`, error);
|
||||||
if (i < maxRetries - 1) {
|
if (i < maxRetries - 1) {
|
||||||
await new Promise(resolve => setTimeout(resolve, delay));
|
await new Promise(resolve => setTimeout(resolve, delay));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.error("Failed to load data after", maxRetries, "retries");
|
log.error("Failed to load data after", maxRetries, "retries");
|
||||||
}
|
}
|
||||||
|
|
||||||
async processMaskData(maskData) {
|
async processMaskData(maskData) {
|
||||||
try {
|
try {
|
||||||
if (!maskData) return;
|
if (!maskData) return;
|
||||||
|
|
||||||
console.log("Processing mask data:", maskData);
|
log.debug("Processing mask data:", maskData);
|
||||||
|
|
||||||
if (Array.isArray(maskData)) {
|
if (Array.isArray(maskData)) {
|
||||||
maskData = maskData[0];
|
maskData = maskData[0];
|
||||||
@@ -2596,10 +2609,10 @@ export class Canvas {
|
|||||||
const maskTensor = await this.convertTensorToMask(maskData);
|
const maskTensor = await this.convertTensorToMask(maskData);
|
||||||
this.selectedLayer.mask = maskTensor;
|
this.selectedLayer.mask = maskTensor;
|
||||||
this.render();
|
this.render();
|
||||||
console.log("Mask applied to selected layer");
|
log.info("Mask applied to selected layer");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error processing mask data:", error);
|
log.error("Error processing mask data:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2614,7 +2627,7 @@ export class Canvas {
|
|||||||
|
|
||||||
async importImage(cacheData) {
|
async importImage(cacheData) {
|
||||||
try {
|
try {
|
||||||
console.log("Starting image import with cache data");
|
log.info("Starting image import with cache data");
|
||||||
const img = await this.loadImageFromCache(cacheData.image);
|
const img = await this.loadImageFromCache(cacheData.image);
|
||||||
const mask = cacheData.mask ? await this.loadImageFromCache(cacheData.mask) : null;
|
const mask = cacheData.mask ? await this.loadImageFromCache(cacheData.mask) : null;
|
||||||
|
|
||||||
@@ -2667,18 +2680,18 @@ export class Canvas {
|
|||||||
this.render();
|
this.render();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error importing image:', error);
|
log.error('Error importing image:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async importLatestImage() {
|
async importLatestImage() {
|
||||||
try {
|
try {
|
||||||
console.log("Fetching latest image from server...");
|
log.info("Fetching latest image from server...");
|
||||||
const response = await fetch('/ycnode/get_latest_image');
|
const response = await fetch('/ycnode/get_latest_image');
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.success && result.image_data) {
|
if (result.success && result.image_data) {
|
||||||
console.log("Latest image received, adding to canvas.");
|
log.info("Latest image received, adding to canvas.");
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
img.onload = resolve;
|
img.onload = resolve;
|
||||||
@@ -2692,13 +2705,13 @@ export class Canvas {
|
|||||||
width: this.width,
|
width: this.width,
|
||||||
height: this.height,
|
height: this.height,
|
||||||
});
|
});
|
||||||
console.log("Latest image imported and placed on canvas successfully.");
|
log.info("Latest image imported and placed on canvas successfully.");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(result.error || "Failed to fetch the latest image.");
|
throw new Error(result.error || "Failed to fetch the latest image.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error importing latest image:", error);
|
log.error("Error importing latest image:", error);
|
||||||
alert(`Failed to import latest image: ${error.message}`);
|
alert(`Failed to import latest image: ${error.message}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,19 @@ import {$el} from "../../scripts/ui.js";
|
|||||||
import {Canvas} from "./Canvas.js";
|
import {Canvas} from "./Canvas.js";
|
||||||
import {clearAllCanvasStates} from "./db.js";
|
import {clearAllCanvasStates} from "./db.js";
|
||||||
import {ImageCache} from "./ImageCache.js";
|
import {ImageCache} from "./ImageCache.js";
|
||||||
import { validateImageData, convertImageData, applyMaskToImageData, prepareImageForCanvas } from "./ImageUtils.js";
|
import {validateImageData, convertImageData, applyMaskToImageData, prepareImageForCanvas} from "./ImageUtils.js";
|
||||||
|
import {logger, LogLevel} from "./logger.js";
|
||||||
|
|
||||||
|
// Inicjalizacja loggera dla modułu Canvas_view
|
||||||
|
const log = {
|
||||||
|
debug: (...args) => logger.debug('Canvas_view', ...args),
|
||||||
|
info: (...args) => logger.info('Canvas_view', ...args),
|
||||||
|
warn: (...args) => logger.warn('Canvas_view', ...args),
|
||||||
|
error: (...args) => logger.error('Canvas_view', ...args)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
async function createCanvasWidget(node, widget, app) {
|
async function createCanvasWidget(node, widget, app) {
|
||||||
const canvas = new Canvas(node, widget);
|
const canvas = new Canvas(node, widget);
|
||||||
@@ -580,7 +592,7 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
await canvas.saveToServer(widget.value);
|
await canvas.saveToServer(widget.value);
|
||||||
app.graph.runStep();
|
app.graph.runStep();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Matting error:", error);
|
log.error("Matting error:", error);
|
||||||
alert(`Error during matting process: ${error.message}`);
|
alert(`Error during matting process: ${error.message}`);
|
||||||
} finally {
|
} finally {
|
||||||
button.classList.remove('loading');
|
button.classList.remove('loading');
|
||||||
@@ -683,7 +695,7 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
await clearAllCanvasStates();
|
await clearAllCanvasStates();
|
||||||
alert("Canvas cache cleared successfully!");
|
alert("Canvas cache cleared successfully!");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to clear canvas cache:", e);
|
log.error("Failed to clear canvas cache:", e);
|
||||||
alert("Error clearing canvas cache. Check the console for details.");
|
alert("Error clearing canvas cache. Check the console for details.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -788,18 +800,18 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
}
|
}
|
||||||
}, [controlPanel, canvasContainer]);
|
}, [controlPanel, canvasContainer]);
|
||||||
const handleFileLoad = async (file) => {
|
const handleFileLoad = async (file) => {
|
||||||
console.log("File dropped:", file.name);
|
log.info("File dropped:", file.name);
|
||||||
if (!file.type.startsWith('image/')) {
|
if (!file.type.startsWith('image/')) {
|
||||||
console.log("Dropped file is not an image.");
|
log.info("Dropped file is not an image.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = async (event) => {
|
reader.onload = async (event) => {
|
||||||
console.log("FileReader finished loading dropped file as data:URL.");
|
log.debug("FileReader finished loading dropped file as data:URL.");
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = async () => {
|
img.onload = async () => {
|
||||||
console.log("Image object loaded from dropped data:URL.");
|
log.debug("Image object loaded from dropped data:URL.");
|
||||||
const scale = Math.min(
|
const scale = Math.min(
|
||||||
canvas.width / img.width,
|
canvas.width / img.width,
|
||||||
canvas.height / img.height
|
canvas.height / img.height
|
||||||
@@ -821,7 +833,7 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
canvas.updateSelection([layer]);
|
canvas.updateSelection([layer]);
|
||||||
canvas.render();
|
canvas.render();
|
||||||
canvas.saveState();
|
canvas.saveState();
|
||||||
console.log("Dropped layer added and state saved.");
|
log.info("Dropped layer added and state saved.");
|
||||||
await updateOutput();
|
await updateOutput();
|
||||||
};
|
};
|
||||||
img.src = event.target.result;
|
img.src = event.target.result;
|
||||||
@@ -885,7 +897,7 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
|
|
||||||
originalParent = mainContainer.parentNode;
|
originalParent = mainContainer.parentNode;
|
||||||
if (!originalParent) {
|
if (!originalParent) {
|
||||||
console.error("Could not find original parent of the canvas container!");
|
log.error("Could not find original parent of the canvas container!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -910,13 +922,13 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
let executionInProgress = false;
|
let executionInProgress = false;
|
||||||
|
|
||||||
api.addEventListener("execution_start", async () => {
|
api.addEventListener("execution_start", async () => {
|
||||||
console.log(`[CANVAS_VIEW_LOG] Execution start event for node ${node.id}`);
|
log.info(`Execution start event for node ${node.id}`);
|
||||||
console.log(`[CANVAS_VIEW_LOG] Widget value: ${widget.value}`);
|
log.debug(`Widget value: ${widget.value}`);
|
||||||
console.log(`[CANVAS_VIEW_LOG] Node inputs: ${node.inputs?.length || 0}`);
|
log.debug(`Node inputs: ${node.inputs?.length || 0}`);
|
||||||
|
|
||||||
// Sprawdź czy już trwa wykonanie
|
// Sprawdź czy już trwa wykonanie
|
||||||
if (executionInProgress) {
|
if (executionInProgress) {
|
||||||
console.log(`[CANVAS_VIEW_LOG] Execution already in progress, skipping...`);
|
log.warn(`Execution already in progress, skipping...`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -925,34 +937,34 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await canvas.saveToServer(widget.value);
|
await canvas.saveToServer(widget.value);
|
||||||
console.log(`[CANVAS_VIEW_LOG] Canvas saved to server`);
|
log.info(`Canvas saved to server`);
|
||||||
|
|
||||||
if (node.inputs[0]?.link) {
|
if (node.inputs[0]?.link) {
|
||||||
const linkId = node.inputs[0].link;
|
const linkId = node.inputs[0].link;
|
||||||
const inputData = app.nodeOutputs[linkId];
|
const inputData = app.nodeOutputs[linkId];
|
||||||
console.log(`[CANVAS_VIEW_LOG] Input link ${linkId} has data: ${!!inputData}`);
|
log.debug(`Input link ${linkId} has data: ${!!inputData}`);
|
||||||
if (inputData) {
|
if (inputData) {
|
||||||
imageCache.set(linkId, inputData);
|
imageCache.set(linkId, inputData);
|
||||||
console.log(`[CANVAS_VIEW_LOG] Input data cached for link ${linkId}`);
|
log.debug(`Input data cached for link ${linkId}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`[CANVAS_VIEW_LOG] No input link found`);
|
log.debug(`No input link found`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[CANVAS_VIEW_LOG] Error during execution:`, error);
|
log.error(`Error during execution:`, error);
|
||||||
} finally {
|
} finally {
|
||||||
// Zwolnij flagę wykonania
|
// Zwolnij flagę wykonania
|
||||||
executionInProgress = false;
|
executionInProgress = false;
|
||||||
console.log(`[CANVAS_VIEW_LOG] Execution completed, flag released`);
|
log.debug(`Execution completed, flag released`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const originalSaveToServer = canvas.saveToServer;
|
const originalSaveToServer = canvas.saveToServer;
|
||||||
canvas.saveToServer = async function (fileName) {
|
canvas.saveToServer = async function (fileName) {
|
||||||
console.log(`[CANVAS_VIEW_LOG] saveToServer called with fileName: ${fileName}`);
|
log.debug(`saveToServer called with fileName: ${fileName}`);
|
||||||
console.log(`[CANVAS_VIEW_LOG] Current execution context - node ID: ${node.id}`);
|
log.debug(`Current execution context - node ID: ${node.id}`);
|
||||||
const result = await originalSaveToServer.call(this, fileName);
|
const result = await originalSaveToServer.call(this, fileName);
|
||||||
console.log(`[CANVAS_VIEW_LOG] saveToServer completed, result: ${result}`);
|
log.debug(`saveToServer completed, result: ${result}`);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -975,11 +987,11 @@ app.registerExtension({
|
|||||||
if (nodeType.comfyClass === "CanvasNode") {
|
if (nodeType.comfyClass === "CanvasNode") {
|
||||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||||
nodeType.prototype.onNodeCreated = async function () {
|
nodeType.prototype.onNodeCreated = async function () {
|
||||||
console.log("CanvasNode created, ID:", this.id);
|
log.info("CanvasNode created, ID:", this.id);
|
||||||
const r = onNodeCreated?.apply(this, arguments);
|
const r = onNodeCreated?.apply(this, arguments);
|
||||||
|
|
||||||
const widget = this.widgets.find(w => w.name === "canvas_image");
|
const widget = this.widgets.find(w => w.name === "canvas_image");
|
||||||
console.log("Found canvas_image widget:", widget);
|
log.debug("Found canvas_image widget:", widget);
|
||||||
await createCanvasWidget(this, widget, app);
|
await createCanvasWidget(this, widget, app);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
@@ -1017,7 +1029,7 @@ app.registerExtension({
|
|||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error opening image:", e);
|
log.error("Error opening image:", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1028,9 +1040,9 @@ app.registerExtension({
|
|||||||
const blob = await self.canvasWidget.getFlattenedCanvasAsBlob();
|
const blob = await self.canvasWidget.getFlattenedCanvasAsBlob();
|
||||||
const item = new ClipboardItem({'image/png': blob});
|
const item = new ClipboardItem({'image/png': blob});
|
||||||
await navigator.clipboard.write([item]);
|
await navigator.clipboard.write([item]);
|
||||||
console.log("Image copied to clipboard.");
|
log.info("Image copied to clipboard.");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error copying image:", e);
|
log.error("Error copying image:", e);
|
||||||
alert("Failed to copy image to clipboard.");
|
alert("Failed to copy image to clipboard.");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1049,7 +1061,7 @@ app.registerExtension({
|
|||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error saving image:", e);
|
log.error("Error saving image:", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,16 +1,29 @@
|
|||||||
|
import {logger, LogLevel} from "./logger.js";
|
||||||
|
|
||||||
|
// Inicjalizacja loggera dla modułu ImageCache
|
||||||
|
const log = {
|
||||||
|
debug: (...args) => logger.debug('ImageCache', ...args),
|
||||||
|
info: (...args) => logger.info('ImageCache', ...args),
|
||||||
|
warn: (...args) => logger.warn('ImageCache', ...args),
|
||||||
|
error: (...args) => logger.error('ImageCache', ...args)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Konfiguracja loggera dla modułu ImageCache
|
||||||
|
logger.setModuleLevel('ImageCache', LogLevel.INFO);
|
||||||
|
|
||||||
export class ImageCache {
|
export class ImageCache {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.cache = new Map();
|
this.cache = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
set(key, imageData) {
|
set(key, imageData) {
|
||||||
console.log("Caching image data for key:", key);
|
log.info("Caching image data for key:", key);
|
||||||
this.cache.set(key, imageData);
|
this.cache.set(key, imageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(key) {
|
get(key) {
|
||||||
const data = this.cache.get(key);
|
const data = this.cache.get(key);
|
||||||
console.log("Retrieved cached data for key:", key, !!data);
|
log.debug("Retrieved cached data for key:", key, !!data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,7 +32,7 @@ export class ImageCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
console.log("Clearing image cache");
|
log.info("Clearing image cache");
|
||||||
this.cache.clear();
|
this.cache.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,18 @@
|
|||||||
|
import {logger, LogLevel} from "./logger.js";
|
||||||
|
|
||||||
|
// Inicjalizacja loggera dla modułu ImageUtils
|
||||||
|
const log = {
|
||||||
|
debug: (...args) => logger.debug('ImageUtils', ...args),
|
||||||
|
info: (...args) => logger.info('ImageUtils', ...args),
|
||||||
|
warn: (...args) => logger.warn('ImageUtils', ...args),
|
||||||
|
error: (...args) => logger.error('ImageUtils', ...args)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Konfiguracja loggera dla modułu ImageUtils
|
||||||
|
logger.setModuleLevel('ImageUtils', LogLevel.INFO);
|
||||||
|
|
||||||
export function validateImageData(data) {
|
export function validateImageData(data) {
|
||||||
console.log("Validating data structure:", {
|
log.debug("Validating data structure:", {
|
||||||
hasData: !!data,
|
hasData: !!data,
|
||||||
type: typeof data,
|
type: typeof data,
|
||||||
isArray: Array.isArray(data),
|
isArray: Array.isArray(data),
|
||||||
@@ -10,22 +23,22 @@ export function validateImageData(data) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
console.log("Data is null or undefined");
|
log.info("Data is null or undefined");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
console.log("Data is array, getting first element");
|
log.debug("Data is array, getting first element");
|
||||||
data = data[0];
|
data = data[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data || typeof data !== 'object') {
|
if (!data || typeof data !== 'object') {
|
||||||
console.log("Invalid data type");
|
log.info("Invalid data type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.data) {
|
if (!data.data) {
|
||||||
console.log("Missing data property");
|
log.info("Missing data property");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +46,7 @@ export function validateImageData(data) {
|
|||||||
try {
|
try {
|
||||||
data.data = new Float32Array(data.data);
|
data.data = new Float32Array(data.data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Failed to convert data to Float32Array:", e);
|
log.error("Failed to convert data to Float32Array:", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +55,7 @@ export function validateImageData(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function convertImageData(data) {
|
export function convertImageData(data) {
|
||||||
console.log("Converting image data:", data);
|
log.info("Converting image data:", data);
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
data = data[0];
|
data = data[0];
|
||||||
@@ -54,7 +67,7 @@ export function convertImageData(data) {
|
|||||||
const channels = shape[3];
|
const channels = shape[3];
|
||||||
const floatData = new Float32Array(data.data);
|
const floatData = new Float32Array(data.data);
|
||||||
|
|
||||||
console.log("Processing dimensions:", {height, width, channels});
|
log.debug("Processing dimensions:", {height, width, channels});
|
||||||
|
|
||||||
const rgbaData = new Uint8ClampedArray(width * height * 4);
|
const rgbaData = new Uint8ClampedArray(width * height * 4);
|
||||||
|
|
||||||
@@ -80,7 +93,7 @@ export function convertImageData(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function applyMaskToImageData(imageData, maskData) {
|
export function applyMaskToImageData(imageData, maskData) {
|
||||||
console.log("Applying mask to image data");
|
log.info("Applying mask to image data");
|
||||||
|
|
||||||
const rgbaData = new Uint8ClampedArray(imageData.data);
|
const rgbaData = new Uint8ClampedArray(imageData.data);
|
||||||
const width = imageData.width;
|
const width = imageData.width;
|
||||||
@@ -89,7 +102,7 @@ export function applyMaskToImageData(imageData, maskData) {
|
|||||||
const maskShape = maskData.shape;
|
const maskShape = maskData.shape;
|
||||||
const maskFloatData = new Float32Array(maskData.data);
|
const maskFloatData = new Float32Array(maskData.data);
|
||||||
|
|
||||||
console.log(`Applying mask of shape: ${maskShape}`);
|
log.debug(`Applying mask of shape: ${maskShape}`);
|
||||||
|
|
||||||
for (let h = 0; h < height; h++) {
|
for (let h = 0; h < height; h++) {
|
||||||
for (let w = 0; w < width; w++) {
|
for (let w = 0; w < width; w++) {
|
||||||
@@ -101,7 +114,7 @@ export function applyMaskToImageData(imageData, maskData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Mask application completed");
|
log.info("Mask application completed");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: rgbaData,
|
data: rgbaData,
|
||||||
@@ -111,7 +124,7 @@ export function applyMaskToImageData(imageData, maskData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function prepareImageForCanvas(inputImage) {
|
export function prepareImageForCanvas(inputImage) {
|
||||||
console.log("Preparing image for canvas:", inputImage);
|
log.info("Preparing image for canvas:", inputImage);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (Array.isArray(inputImage)) {
|
if (Array.isArray(inputImage)) {
|
||||||
@@ -128,7 +141,7 @@ export function prepareImageForCanvas(inputImage) {
|
|||||||
const channels = shape[3];
|
const channels = shape[3];
|
||||||
const floatData = new Float32Array(inputImage.data);
|
const floatData = new Float32Array(inputImage.data);
|
||||||
|
|
||||||
console.log("Image dimensions:", {height, width, channels});
|
log.debug("Image dimensions:", {height, width, channels});
|
||||||
|
|
||||||
const rgbaData = new Uint8ClampedArray(width * height * 4);
|
const rgbaData = new Uint8ClampedArray(width * height * 4);
|
||||||
|
|
||||||
@@ -152,7 +165,7 @@ export function prepareImageForCanvas(inputImage) {
|
|||||||
height: height
|
height: height
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error preparing image:", error);
|
log.error("Error preparing image:", error);
|
||||||
throw new Error(`Failed to prepare image: ${error.message}`);
|
throw new Error(`Failed to prepare image: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,16 @@
|
|||||||
|
import {logger, LogLevel} from "./logger.js";
|
||||||
|
|
||||||
|
// Inicjalizacja loggera dla modułu Mask_tool
|
||||||
|
const log = {
|
||||||
|
debug: (...args) => logger.debug('Mask_tool', ...args),
|
||||||
|
info: (...args) => logger.info('Mask_tool', ...args),
|
||||||
|
warn: (...args) => logger.warn('Mask_tool', ...args),
|
||||||
|
error: (...args) => logger.error('Mask_tool', ...args)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Konfiguracja loggera dla modułu Mask_tool
|
||||||
|
logger.setModuleLevel('Mask_tool', LogLevel.INFO);
|
||||||
|
|
||||||
export class MaskTool {
|
export class MaskTool {
|
||||||
constructor(canvasInstance) {
|
constructor(canvasInstance) {
|
||||||
this.canvasInstance = canvasInstance;
|
this.canvasInstance = canvasInstance;
|
||||||
@@ -28,13 +41,13 @@ export class MaskTool {
|
|||||||
activate() {
|
activate() {
|
||||||
this.isActive = true;
|
this.isActive = true;
|
||||||
this.canvasInstance.interaction.mode = 'drawingMask';
|
this.canvasInstance.interaction.mode = 'drawingMask';
|
||||||
console.log("Mask tool activated");
|
log.info("Mask tool activated");
|
||||||
}
|
}
|
||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
this.isActive = false;
|
this.isActive = false;
|
||||||
this.canvasInstance.interaction.mode = 'none';
|
this.canvasInstance.interaction.mode = 'none';
|
||||||
console.log("Mask tool deactivated");
|
log.info("Mask tool deactivated");
|
||||||
}
|
}
|
||||||
|
|
||||||
setBrushSize(size) {
|
setBrushSize(size) {
|
||||||
|
|||||||
67
js/db.js
67
js/db.js
@@ -1,3 +1,16 @@
|
|||||||
|
import {logger, LogLevel} from "./logger.js";
|
||||||
|
|
||||||
|
// Inicjalizacja loggera dla modułu db
|
||||||
|
const log = {
|
||||||
|
debug: (...args) => logger.debug('db', ...args),
|
||||||
|
info: (...args) => logger.info('db', ...args),
|
||||||
|
warn: (...args) => logger.warn('db', ...args),
|
||||||
|
error: (...args) => logger.error('db', ...args)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Konfiguracja loggera dla modułu db
|
||||||
|
logger.setModuleLevel('db', LogLevel.INFO);
|
||||||
|
|
||||||
const DB_NAME = 'CanvasNodeDB';
|
const DB_NAME = 'CanvasNodeDB';
|
||||||
const STATE_STORE_NAME = 'CanvasState';
|
const STATE_STORE_NAME = 'CanvasState';
|
||||||
const IMAGE_STORE_NAME = 'CanvasImages';
|
const IMAGE_STORE_NAME = 'CanvasImages';
|
||||||
@@ -12,37 +25,37 @@ function openDB() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Opening IndexedDB...");
|
log.info("Opening IndexedDB...");
|
||||||
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
||||||
|
|
||||||
request.onerror = (event) => {
|
request.onerror = (event) => {
|
||||||
console.error("IndexedDB error:", event.target.error);
|
log.error("IndexedDB error:", event.target.error);
|
||||||
reject("Error opening IndexedDB.");
|
reject("Error opening IndexedDB.");
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
request.onsuccess = (event) => {
|
||||||
db = event.target.result;
|
db = event.target.result;
|
||||||
console.log("IndexedDB opened successfully.");
|
log.info("IndexedDB opened successfully.");
|
||||||
resolve(db);
|
resolve(db);
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onupgradeneeded = (event) => {
|
request.onupgradeneeded = (event) => {
|
||||||
console.log("Upgrading IndexedDB...");
|
log.info("Upgrading IndexedDB...");
|
||||||
const db = event.target.result;
|
const db = event.target.result;
|
||||||
if (!db.objectStoreNames.contains(STATE_STORE_NAME)) {
|
if (!db.objectStoreNames.contains(STATE_STORE_NAME)) {
|
||||||
db.createObjectStore(STATE_STORE_NAME, {keyPath: 'id'});
|
db.createObjectStore(STATE_STORE_NAME, {keyPath: 'id'});
|
||||||
console.log("Object store created:", STATE_STORE_NAME);
|
log.info("Object store created:", STATE_STORE_NAME);
|
||||||
}
|
}
|
||||||
if (!db.objectStoreNames.contains(IMAGE_STORE_NAME)) {
|
if (!db.objectStoreNames.contains(IMAGE_STORE_NAME)) {
|
||||||
db.createObjectStore(IMAGE_STORE_NAME, {keyPath: 'imageId'});
|
db.createObjectStore(IMAGE_STORE_NAME, {keyPath: 'imageId'});
|
||||||
console.log("Object store created:", IMAGE_STORE_NAME);
|
log.info("Object store created:", IMAGE_STORE_NAME);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCanvasState(id) {
|
export async function getCanvasState(id) {
|
||||||
console.log(`DB: Getting state for id: ${id}`);
|
log.info(`Getting state for id: ${id}`);
|
||||||
const db = await openDB();
|
const db = await openDB();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const transaction = db.transaction([STATE_STORE_NAME], 'readonly');
|
const transaction = db.transaction([STATE_STORE_NAME], 'readonly');
|
||||||
@@ -50,19 +63,19 @@ export async function getCanvasState(id) {
|
|||||||
const request = store.get(id);
|
const request = store.get(id);
|
||||||
|
|
||||||
request.onerror = (event) => {
|
request.onerror = (event) => {
|
||||||
console.error("DB: Error getting canvas state:", event.target.error);
|
log.error("Error getting canvas state:", event.target.error);
|
||||||
reject("Error getting state.");
|
reject("Error getting state.");
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
request.onsuccess = (event) => {
|
||||||
console.log(`DB: Get success for id: ${id}`, event.target.result ? 'found' : 'not found');
|
log.debug(`Get success for id: ${id}`, event.target.result ? 'found' : 'not found');
|
||||||
resolve(event.target.result ? event.target.result.state : null);
|
resolve(event.target.result ? event.target.result.state : null);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setCanvasState(id, state) {
|
export async function setCanvasState(id, state) {
|
||||||
console.log(`DB: Setting state for id: ${id}`);
|
log.info(`Setting state for id: ${id}`);
|
||||||
const db = await openDB();
|
const db = await openDB();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
||||||
@@ -70,19 +83,19 @@ export async function setCanvasState(id, state) {
|
|||||||
const request = store.put({id, state});
|
const request = store.put({id, state});
|
||||||
|
|
||||||
request.onerror = (event) => {
|
request.onerror = (event) => {
|
||||||
console.error("DB: Error setting canvas state:", event.target.error);
|
log.error("Error setting canvas state:", event.target.error);
|
||||||
reject("Error setting state.");
|
reject("Error setting state.");
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = () => {
|
request.onsuccess = () => {
|
||||||
console.log(`DB: Set success for id: ${id}`);
|
log.debug(`Set success for id: ${id}`);
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeCanvasState(id) {
|
export async function removeCanvasState(id) {
|
||||||
console.log(`DB: Removing state for id: ${id}`);
|
log.info(`Removing state for id: ${id}`);
|
||||||
const db = await openDB();
|
const db = await openDB();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
||||||
@@ -90,19 +103,19 @@ export async function removeCanvasState(id) {
|
|||||||
const request = store.delete(id);
|
const request = store.delete(id);
|
||||||
|
|
||||||
request.onerror = (event) => {
|
request.onerror = (event) => {
|
||||||
console.error("DB: Error removing canvas state:", event.target.error);
|
log.error("Error removing canvas state:", event.target.error);
|
||||||
reject("Error removing state.");
|
reject("Error removing state.");
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = () => {
|
request.onsuccess = () => {
|
||||||
console.log(`DB: Remove success for id: ${id}`);
|
log.debug(`Remove success for id: ${id}`);
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveImage(imageId, imageSrc) {
|
export async function saveImage(imageId, imageSrc) {
|
||||||
console.log(`DB: Saving image with id: ${imageId}`);
|
log.info(`Saving image with id: ${imageId}`);
|
||||||
const db = await openDB();
|
const db = await openDB();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const transaction = db.transaction([IMAGE_STORE_NAME], 'readwrite');
|
const transaction = db.transaction([IMAGE_STORE_NAME], 'readwrite');
|
||||||
@@ -110,19 +123,19 @@ export async function saveImage(imageId, imageSrc) {
|
|||||||
const request = store.put({imageId, imageSrc});
|
const request = store.put({imageId, imageSrc});
|
||||||
|
|
||||||
request.onerror = (event) => {
|
request.onerror = (event) => {
|
||||||
console.error("DB: Error saving image:", event.target.error);
|
log.error("Error saving image:", event.target.error);
|
||||||
reject("Error saving image.");
|
reject("Error saving image.");
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = () => {
|
request.onsuccess = () => {
|
||||||
console.log(`DB: Image saved successfully for id: ${imageId}`);
|
log.debug(`Image saved successfully for id: ${imageId}`);
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getImage(imageId) {
|
export async function getImage(imageId) {
|
||||||
console.log(`DB: Getting image with id: ${imageId}`);
|
log.info(`Getting image with id: ${imageId}`);
|
||||||
const db = await openDB();
|
const db = await openDB();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const transaction = db.transaction([IMAGE_STORE_NAME], 'readonly');
|
const transaction = db.transaction([IMAGE_STORE_NAME], 'readonly');
|
||||||
@@ -130,19 +143,19 @@ export async function getImage(imageId) {
|
|||||||
const request = store.get(imageId);
|
const request = store.get(imageId);
|
||||||
|
|
||||||
request.onerror = (event) => {
|
request.onerror = (event) => {
|
||||||
console.error("DB: Error getting image:", event.target.error);
|
log.error("Error getting image:", event.target.error);
|
||||||
reject("Error getting image.");
|
reject("Error getting image.");
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
request.onsuccess = (event) => {
|
||||||
console.log(`DB: Get image success for id: ${imageId}`, event.target.result ? 'found' : 'not found');
|
log.debug(`Get image success for id: ${imageId}`, event.target.result ? 'found' : 'not found');
|
||||||
resolve(event.target.result ? event.target.result.imageSrc : null);
|
resolve(event.target.result ? event.target.result.imageSrc : null);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeImage(imageId) {
|
export async function removeImage(imageId) {
|
||||||
console.log(`DB: Removing image with id: ${imageId}`);
|
log.info(`Removing image with id: ${imageId}`);
|
||||||
const db = await openDB();
|
const db = await openDB();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const transaction = db.transaction([IMAGE_STORE_NAME], 'readwrite');
|
const transaction = db.transaction([IMAGE_STORE_NAME], 'readwrite');
|
||||||
@@ -150,19 +163,19 @@ export async function removeImage(imageId) {
|
|||||||
const request = store.delete(imageId);
|
const request = store.delete(imageId);
|
||||||
|
|
||||||
request.onerror = (event) => {
|
request.onerror = (event) => {
|
||||||
console.error("DB: Error removing image:", event.target.error);
|
log.error("Error removing image:", event.target.error);
|
||||||
reject("Error removing image.");
|
reject("Error removing image.");
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = () => {
|
request.onsuccess = () => {
|
||||||
console.log(`DB: Remove image success for id: ${imageId}`);
|
log.debug(`Remove image success for id: ${imageId}`);
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function clearAllCanvasStates() {
|
export async function clearAllCanvasStates() {
|
||||||
console.log("DB: Clearing all canvas states...");
|
log.info("Clearing all canvas states...");
|
||||||
const db = await openDB();
|
const db = await openDB();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
||||||
@@ -170,12 +183,12 @@ export async function clearAllCanvasStates() {
|
|||||||
const request = store.clear();
|
const request = store.clear();
|
||||||
|
|
||||||
request.onerror = (event) => {
|
request.onerror = (event) => {
|
||||||
console.error("DB: Error clearing canvas states:", event.target.error);
|
log.error("Error clearing canvas states:", event.target.error);
|
||||||
reject("Error clearing states.");
|
reject("Error clearing states.");
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = () => {
|
request.onsuccess = () => {
|
||||||
console.log("DB: All canvas states cleared successfully.");
|
log.info("All canvas states cleared successfully.");
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
371
js/logger.js
Normal file
371
js/logger.js
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
/**
|
||||||
|
* Logger - Centralny system logowania dla ComfyUI-LayerForge
|
||||||
|
*
|
||||||
|
* Funkcje:
|
||||||
|
* - Różne poziomy logowania (DEBUG, INFO, WARN, ERROR)
|
||||||
|
* - Możliwość włączania/wyłączania logów globalnie lub per moduł
|
||||||
|
* - Kolorowe logi w konsoli
|
||||||
|
* - Możliwość zapisywania logów do localStorage
|
||||||
|
* - Możliwość eksportu logów
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Poziomy logowania
|
||||||
|
export const LogLevel = {
|
||||||
|
DEBUG: 0,
|
||||||
|
INFO: 1,
|
||||||
|
WARN: 2,
|
||||||
|
ERROR: 3,
|
||||||
|
NONE: 4
|
||||||
|
};
|
||||||
|
|
||||||
|
// Konfiguracja domyślna
|
||||||
|
const DEFAULT_CONFIG = {
|
||||||
|
globalLevel: LogLevel.INFO, // Domyślny poziom logowania
|
||||||
|
moduleSettings: {}, // Ustawienia per moduł
|
||||||
|
useColors: true, // Kolorowe logi w konsoli
|
||||||
|
saveToStorage: false, // Zapisywanie logów do localStorage
|
||||||
|
maxStoredLogs: 1000, // Maksymalna liczba przechowywanych logów
|
||||||
|
timestampFormat: 'HH:mm:ss', // Format znacznika czasu
|
||||||
|
storageKey: 'layerforge_logs' // Klucz localStorage
|
||||||
|
};
|
||||||
|
|
||||||
|
// Kolory dla różnych poziomów logowania
|
||||||
|
const COLORS = {
|
||||||
|
[LogLevel.DEBUG]: '#9e9e9e', // Szary
|
||||||
|
[LogLevel.INFO]: '#2196f3', // Niebieski
|
||||||
|
[LogLevel.WARN]: '#ff9800', // Pomarańczowy
|
||||||
|
[LogLevel.ERROR]: '#f44336', // Czerwony
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nazwy poziomów logowania
|
||||||
|
const LEVEL_NAMES = {
|
||||||
|
[LogLevel.DEBUG]: 'DEBUG',
|
||||||
|
[LogLevel.INFO]: 'INFO',
|
||||||
|
[LogLevel.WARN]: 'WARN',
|
||||||
|
[LogLevel.ERROR]: 'ERROR',
|
||||||
|
};
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
constructor() {
|
||||||
|
this.config = { ...DEFAULT_CONFIG };
|
||||||
|
this.logs = [];
|
||||||
|
this.enabled = true;
|
||||||
|
|
||||||
|
// Załaduj konfigurację z localStorage, jeśli istnieje
|
||||||
|
this.loadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Konfiguracja loggera
|
||||||
|
* @param {Object} config - Obiekt konfiguracyjny
|
||||||
|
*/
|
||||||
|
configure(config) {
|
||||||
|
this.config = { ...this.config, ...config };
|
||||||
|
this.saveConfig();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Włącz/wyłącz logger globalnie
|
||||||
|
* @param {boolean} enabled - Czy logger ma być włączony
|
||||||
|
*/
|
||||||
|
setEnabled(enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ustaw globalny poziom logowania
|
||||||
|
* @param {LogLevel} level - Poziom logowania
|
||||||
|
*/
|
||||||
|
setGlobalLevel(level) {
|
||||||
|
this.config.globalLevel = level;
|
||||||
|
this.saveConfig();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ustaw poziom logowania dla konkretnego modułu
|
||||||
|
* @param {string} module - Nazwa modułu
|
||||||
|
* @param {LogLevel} level - Poziom logowania
|
||||||
|
*/
|
||||||
|
setModuleLevel(module, level) {
|
||||||
|
this.config.moduleSettings[module] = level;
|
||||||
|
this.saveConfig();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sprawdź, czy dany poziom logowania jest aktywny dla modułu
|
||||||
|
* @param {string} module - Nazwa modułu
|
||||||
|
* @param {LogLevel} level - Poziom logowania do sprawdzenia
|
||||||
|
* @returns {boolean} - Czy poziom jest aktywny
|
||||||
|
*/
|
||||||
|
isLevelEnabled(module, level) {
|
||||||
|
if (!this.enabled) return false;
|
||||||
|
|
||||||
|
// Sprawdź ustawienia modułu, jeśli istnieją
|
||||||
|
if (this.config.moduleSettings[module] !== undefined) {
|
||||||
|
return level >= this.config.moduleSettings[module];
|
||||||
|
}
|
||||||
|
|
||||||
|
// W przeciwnym razie użyj globalnego poziomu
|
||||||
|
return level >= this.config.globalLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatuj znacznik czasu
|
||||||
|
* @returns {string} - Sformatowany znacznik czasu
|
||||||
|
*/
|
||||||
|
formatTimestamp() {
|
||||||
|
const now = new Date();
|
||||||
|
const format = this.config.timestampFormat;
|
||||||
|
|
||||||
|
// Prosty formatter - można rozszerzyć o więcej opcji
|
||||||
|
return format
|
||||||
|
.replace('HH', String(now.getHours()).padStart(2, '0'))
|
||||||
|
.replace('mm', String(now.getMinutes()).padStart(2, '0'))
|
||||||
|
.replace('ss', String(now.getSeconds()).padStart(2, '0'))
|
||||||
|
.replace('SSS', String(now.getMilliseconds()).padStart(3, '0'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zapisz log
|
||||||
|
* @param {string} module - Nazwa modułu
|
||||||
|
* @param {LogLevel} level - Poziom logowania
|
||||||
|
* @param {Array} args - Argumenty do zalogowania
|
||||||
|
*/
|
||||||
|
log(module, level, ...args) {
|
||||||
|
if (!this.isLevelEnabled(module, level)) return;
|
||||||
|
|
||||||
|
const timestamp = this.formatTimestamp();
|
||||||
|
const levelName = LEVEL_NAMES[level];
|
||||||
|
|
||||||
|
// Przygotuj dane logu
|
||||||
|
const logData = {
|
||||||
|
timestamp,
|
||||||
|
module,
|
||||||
|
level,
|
||||||
|
levelName,
|
||||||
|
args,
|
||||||
|
time: new Date()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dodaj do pamięci, jeśli zapisywanie jest włączone
|
||||||
|
if (this.config.saveToStorage) {
|
||||||
|
this.logs.push(logData);
|
||||||
|
|
||||||
|
// Ogranicz liczbę przechowywanych logów
|
||||||
|
if (this.logs.length > this.config.maxStoredLogs) {
|
||||||
|
this.logs.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zapisz do localStorage
|
||||||
|
this.saveLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wyświetl w konsoli
|
||||||
|
this.printToConsole(logData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wyświetl log w konsoli
|
||||||
|
* @param {Object} logData - Dane logu
|
||||||
|
*/
|
||||||
|
printToConsole(logData) {
|
||||||
|
const { timestamp, module, level, levelName, args } = logData;
|
||||||
|
|
||||||
|
// Przygotuj prefix logu
|
||||||
|
const prefix = `[${timestamp}] [${module}] [${levelName}]`;
|
||||||
|
|
||||||
|
// Użyj kolorów, jeśli są włączone
|
||||||
|
if (this.config.useColors && typeof console.log === 'function') {
|
||||||
|
const color = COLORS[level] || '#000000';
|
||||||
|
console.log(`%c${prefix}`, `color: ${color}; font-weight: bold;`, ...args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback bez kolorów
|
||||||
|
console.log(prefix, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zapisz logi do localStorage
|
||||||
|
*/
|
||||||
|
saveLogs() {
|
||||||
|
if (typeof localStorage !== 'undefined' && this.config.saveToStorage) {
|
||||||
|
try {
|
||||||
|
// Zapisz tylko niezbędne informacje
|
||||||
|
const simplifiedLogs = this.logs.map(log => ({
|
||||||
|
t: log.timestamp,
|
||||||
|
m: log.module,
|
||||||
|
l: log.level,
|
||||||
|
a: log.args.map(arg => {
|
||||||
|
// Konwertuj obiekty na stringi
|
||||||
|
if (typeof arg === 'object') {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(arg);
|
||||||
|
} catch (e) {
|
||||||
|
return String(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arg;
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
localStorage.setItem(this.config.storageKey, JSON.stringify(simplifiedLogs));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to save logs to localStorage:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Załaduj logi z localStorage
|
||||||
|
*/
|
||||||
|
loadLogs() {
|
||||||
|
if (typeof localStorage !== 'undefined' && this.config.saveToStorage) {
|
||||||
|
try {
|
||||||
|
const storedLogs = localStorage.getItem(this.config.storageKey);
|
||||||
|
if (storedLogs) {
|
||||||
|
this.logs = JSON.parse(storedLogs);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load logs from localStorage:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zapisz konfigurację do localStorage
|
||||||
|
*/
|
||||||
|
saveConfig() {
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
try {
|
||||||
|
localStorage.setItem('layerforge_logger_config', JSON.stringify(this.config));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to save logger config to localStorage:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Załaduj konfigurację z localStorage
|
||||||
|
*/
|
||||||
|
loadConfig() {
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
try {
|
||||||
|
const storedConfig = localStorage.getItem('layerforge_logger_config');
|
||||||
|
if (storedConfig) {
|
||||||
|
this.config = { ...this.config, ...JSON.parse(storedConfig) };
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load logger config from localStorage:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wyczyść wszystkie logi
|
||||||
|
*/
|
||||||
|
clearLogs() {
|
||||||
|
this.logs = [];
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
localStorage.removeItem(this.config.storageKey);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eksportuj logi do pliku
|
||||||
|
* @param {string} format - Format eksportu ('json' lub 'txt')
|
||||||
|
*/
|
||||||
|
exportLogs(format = 'json') {
|
||||||
|
if (this.logs.length === 0) {
|
||||||
|
console.warn('No logs to export');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content;
|
||||||
|
let mimeType;
|
||||||
|
let extension;
|
||||||
|
|
||||||
|
if (format === 'json') {
|
||||||
|
content = JSON.stringify(this.logs, null, 2);
|
||||||
|
mimeType = 'application/json';
|
||||||
|
extension = 'json';
|
||||||
|
} else {
|
||||||
|
// Format tekstowy
|
||||||
|
content = this.logs.map(log =>
|
||||||
|
`[${log.timestamp}] [${log.module}] [${log.levelName}] ${log.args.join(' ')}`
|
||||||
|
).join('\n');
|
||||||
|
mimeType = 'text/plain';
|
||||||
|
extension = 'txt';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utwórz link do pobrania
|
||||||
|
const blob = new Blob([content], { type: mimeType });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = `layerforge_logs_${new Date().toISOString().replace(/[:.]/g, '-')}.${extension}`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metody pomocnicze dla różnych poziomów logowania
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log na poziomie DEBUG
|
||||||
|
* @param {string} module - Nazwa modułu
|
||||||
|
* @param {...any} args - Argumenty do zalogowania
|
||||||
|
*/
|
||||||
|
debug(module, ...args) {
|
||||||
|
this.log(module, LogLevel.DEBUG, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log na poziomie INFO
|
||||||
|
* @param {string} module - Nazwa modułu
|
||||||
|
* @param {...any} args - Argumenty do zalogowania
|
||||||
|
*/
|
||||||
|
info(module, ...args) {
|
||||||
|
this.log(module, LogLevel.INFO, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log na poziomie WARN
|
||||||
|
* @param {string} module - Nazwa modułu
|
||||||
|
* @param {...any} args - Argumenty do zalogowania
|
||||||
|
*/
|
||||||
|
warn(module, ...args) {
|
||||||
|
this.log(module, LogLevel.WARN, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log na poziomie ERROR
|
||||||
|
* @param {string} module - Nazwa modułu
|
||||||
|
* @param {...any} args - Argumenty do zalogowania
|
||||||
|
*/
|
||||||
|
error(module, ...args) {
|
||||||
|
this.log(module, LogLevel.ERROR, ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eksportuj singleton
|
||||||
|
export const logger = new Logger();
|
||||||
|
|
||||||
|
// Eksportuj funkcje pomocnicze
|
||||||
|
export const debug = (module, ...args) => logger.debug(module, ...args);
|
||||||
|
export const info = (module, ...args) => logger.info(module, ...args);
|
||||||
|
export const warn = (module, ...args) => logger.warn(module, ...args);
|
||||||
|
export const error = (module, ...args) => logger.error(module, ...args);
|
||||||
|
|
||||||
|
// Dodaj do window dla łatwego dostępu z konsoli
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.LayerForgeLogger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default logger;
|
||||||
299
python/logger.py
Normal file
299
python/logger.py
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
"""
|
||||||
|
Logger - Centralny system logowania dla ComfyUI-LayerForge (Python)
|
||||||
|
|
||||||
|
Funkcje:
|
||||||
|
- Różne poziomy logowania (DEBUG, INFO, WARN, ERROR)
|
||||||
|
- Możliwość włączania/wyłączania logów globalnie lub per moduł
|
||||||
|
- Kolorowe logi w konsoli
|
||||||
|
- Rotacja plików logów
|
||||||
|
- Konfiguracja przez zmienne środowiskowe
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
from enum import IntEnum
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
# Poziomy logowania
|
||||||
|
class LogLevel(IntEnum):
|
||||||
|
DEBUG = 10
|
||||||
|
INFO = 20
|
||||||
|
WARN = 30
|
||||||
|
ERROR = 40
|
||||||
|
NONE = 100
|
||||||
|
|
||||||
|
# Mapowanie poziomów logowania
|
||||||
|
LEVEL_MAP = {
|
||||||
|
LogLevel.DEBUG: logging.DEBUG,
|
||||||
|
LogLevel.INFO: logging.INFO,
|
||||||
|
LogLevel.WARN: logging.WARNING,
|
||||||
|
LogLevel.ERROR: logging.ERROR,
|
||||||
|
LogLevel.NONE: logging.CRITICAL + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Kolory ANSI dla różnych poziomów logowania
|
||||||
|
COLORS = {
|
||||||
|
LogLevel.DEBUG: '\033[90m', # Szary
|
||||||
|
LogLevel.INFO: '\033[94m', # Niebieski
|
||||||
|
LogLevel.WARN: '\033[93m', # Żółty
|
||||||
|
LogLevel.ERROR: '\033[91m', # Czerwony
|
||||||
|
'RESET': '\033[0m' # Reset
|
||||||
|
}
|
||||||
|
|
||||||
|
# Konfiguracja domyślna
|
||||||
|
DEFAULT_CONFIG = {
|
||||||
|
'global_level': LogLevel.INFO,
|
||||||
|
'module_settings': {},
|
||||||
|
'use_colors': True,
|
||||||
|
'log_to_file': False,
|
||||||
|
'log_dir': 'logs',
|
||||||
|
'max_file_size_mb': 10,
|
||||||
|
'backup_count': 5,
|
||||||
|
'timestamp_format': '%H:%M:%S',
|
||||||
|
}
|
||||||
|
|
||||||
|
class ColoredFormatter(logging.Formatter):
|
||||||
|
"""Formatter dodający kolory do logów w konsoli"""
|
||||||
|
|
||||||
|
def __init__(self, fmt=None, datefmt=None, use_colors=True):
|
||||||
|
super().__init__(fmt, datefmt)
|
||||||
|
self.use_colors = use_colors
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
levelname = record.levelname
|
||||||
|
message = super().format(record)
|
||||||
|
|
||||||
|
if self.use_colors and hasattr(LogLevel, levelname):
|
||||||
|
level = getattr(LogLevel, levelname)
|
||||||
|
color = COLORS.get(level, '')
|
||||||
|
reset = COLORS['RESET']
|
||||||
|
return f"{color}{message}{reset}"
|
||||||
|
|
||||||
|
return message
|
||||||
|
|
||||||
|
class LayerForgeLogger:
|
||||||
|
"""Główna klasa loggera dla LayerForge"""
|
||||||
|
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
def __new__(cls):
|
||||||
|
if cls._instance is None:
|
||||||
|
cls._instance = super(LayerForgeLogger, cls).__new__(cls)
|
||||||
|
cls._instance._initialized = False
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if self._initialized:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.config = DEFAULT_CONFIG.copy()
|
||||||
|
self.enabled = True
|
||||||
|
self.loggers = {}
|
||||||
|
|
||||||
|
# Załaduj konfigurację ze zmiennych środowiskowych
|
||||||
|
self._load_config_from_env()
|
||||||
|
|
||||||
|
# Utwórz katalog logów, jeśli nie istnieje
|
||||||
|
if self.config['log_to_file']:
|
||||||
|
os.makedirs(self.config['log_dir'], exist_ok=True)
|
||||||
|
|
||||||
|
self._initialized = True
|
||||||
|
|
||||||
|
def _load_config_from_env(self):
|
||||||
|
"""Załaduj konfigurację ze zmiennych środowiskowych"""
|
||||||
|
|
||||||
|
# Poziom globalny
|
||||||
|
if 'LAYERFORGE_LOG_LEVEL' in os.environ:
|
||||||
|
level_name = os.environ['LAYERFORGE_LOG_LEVEL'].upper()
|
||||||
|
if hasattr(LogLevel, level_name):
|
||||||
|
self.config['global_level'] = getattr(LogLevel, level_name)
|
||||||
|
|
||||||
|
# Ustawienia modułów
|
||||||
|
if 'LAYERFORGE_MODULE_LEVELS' in os.environ:
|
||||||
|
try:
|
||||||
|
module_settings = json.loads(os.environ['LAYERFORGE_MODULE_LEVELS'])
|
||||||
|
for module, level_name in module_settings.items():
|
||||||
|
if hasattr(LogLevel, level_name.upper()):
|
||||||
|
self.config['module_settings'][module] = getattr(LogLevel, level_name.upper())
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Inne ustawienia
|
||||||
|
if 'LAYERFORGE_USE_COLORS' in os.environ:
|
||||||
|
self.config['use_colors'] = os.environ['LAYERFORGE_USE_COLORS'].lower() == 'true'
|
||||||
|
|
||||||
|
if 'LAYERFORGE_LOG_TO_FILE' in os.environ:
|
||||||
|
self.config['log_to_file'] = os.environ['LAYERFORGE_LOG_TO_FILE'].lower() == 'true'
|
||||||
|
|
||||||
|
if 'LAYERFORGE_LOG_DIR' in os.environ:
|
||||||
|
self.config['log_dir'] = os.environ['LAYERFORGE_LOG_DIR']
|
||||||
|
|
||||||
|
if 'LAYERFORGE_MAX_FILE_SIZE_MB' in os.environ:
|
||||||
|
try:
|
||||||
|
self.config['max_file_size_mb'] = int(os.environ['LAYERFORGE_MAX_FILE_SIZE_MB'])
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if 'LAYERFORGE_BACKUP_COUNT' in os.environ:
|
||||||
|
try:
|
||||||
|
self.config['backup_count'] = int(os.environ['LAYERFORGE_BACKUP_COUNT'])
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def configure(self, config):
|
||||||
|
"""Konfiguracja loggera"""
|
||||||
|
self.config.update(config)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_enabled(self, enabled):
|
||||||
|
"""Włącz/wyłącz logger globalnie"""
|
||||||
|
self.enabled = enabled
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_global_level(self, level):
|
||||||
|
"""Ustaw globalny poziom logowania"""
|
||||||
|
self.config['global_level'] = level
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_module_level(self, module, level):
|
||||||
|
"""Ustaw poziom logowania dla konkretnego modułu"""
|
||||||
|
self.config['module_settings'][module] = level
|
||||||
|
return self
|
||||||
|
|
||||||
|
def is_level_enabled(self, module, level):
|
||||||
|
"""Sprawdź, czy dany poziom logowania jest aktywny dla modułu"""
|
||||||
|
if not self.enabled:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Sprawdź ustawienia modułu, jeśli istnieją
|
||||||
|
if module in self.config['module_settings']:
|
||||||
|
return level >= self.config['module_settings'][module]
|
||||||
|
|
||||||
|
# W przeciwnym razie użyj globalnego poziomu
|
||||||
|
return level >= self.config['global_level']
|
||||||
|
|
||||||
|
def _get_logger(self, module):
|
||||||
|
"""Pobierz lub utwórz logger dla modułu"""
|
||||||
|
if module in self.loggers:
|
||||||
|
return self.loggers[module]
|
||||||
|
|
||||||
|
# Utwórz nowy logger
|
||||||
|
logger = logging.getLogger(f"layerforge.{module}")
|
||||||
|
logger.setLevel(logging.DEBUG) # Ustaw najniższy poziom, filtrowanie będzie później
|
||||||
|
logger.propagate = False
|
||||||
|
|
||||||
|
# Dodaj handler dla konsoli
|
||||||
|
console_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
console_formatter = ColoredFormatter(
|
||||||
|
fmt='[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s',
|
||||||
|
datefmt=self.config['timestamp_format'],
|
||||||
|
use_colors=self.config['use_colors']
|
||||||
|
)
|
||||||
|
console_handler.setFormatter(console_formatter)
|
||||||
|
logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
# Dodaj handler dla pliku, jeśli włączono logowanie do pliku
|
||||||
|
if self.config['log_to_file']:
|
||||||
|
log_file = os.path.join(self.config['log_dir'], f"layerforge_{module}.log")
|
||||||
|
file_handler = RotatingFileHandler(
|
||||||
|
log_file,
|
||||||
|
maxBytes=self.config['max_file_size_mb'] * 1024 * 1024,
|
||||||
|
backupCount=self.config['backup_count']
|
||||||
|
)
|
||||||
|
file_formatter = logging.Formatter(
|
||||||
|
fmt='[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s',
|
||||||
|
datefmt='%Y-%m-%d %H:%M:%S'
|
||||||
|
)
|
||||||
|
file_handler.setFormatter(file_formatter)
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
self.loggers[module] = logger
|
||||||
|
return logger
|
||||||
|
|
||||||
|
def log(self, module, level, *args, **kwargs):
|
||||||
|
"""Zapisz log"""
|
||||||
|
if not self.is_level_enabled(module, level):
|
||||||
|
return
|
||||||
|
|
||||||
|
logger = self._get_logger(module)
|
||||||
|
|
||||||
|
# Konwertuj argumenty na string
|
||||||
|
message = " ".join(str(arg) for arg in args)
|
||||||
|
|
||||||
|
# Dodaj informacje o wyjątku, jeśli podano
|
||||||
|
exc_info = kwargs.get('exc_info', None)
|
||||||
|
|
||||||
|
# Mapuj poziom LogLevel na poziom logging
|
||||||
|
log_level = LEVEL_MAP.get(level, logging.INFO)
|
||||||
|
|
||||||
|
# Zapisz log
|
||||||
|
logger.log(log_level, message, exc_info=exc_info)
|
||||||
|
|
||||||
|
def debug(self, module, *args, **kwargs):
|
||||||
|
"""Log na poziomie DEBUG"""
|
||||||
|
self.log(module, LogLevel.DEBUG, *args, **kwargs)
|
||||||
|
|
||||||
|
def info(self, module, *args, **kwargs):
|
||||||
|
"""Log na poziomie INFO"""
|
||||||
|
self.log(module, LogLevel.INFO, *args, **kwargs)
|
||||||
|
|
||||||
|
def warn(self, module, *args, **kwargs):
|
||||||
|
"""Log na poziomie WARN"""
|
||||||
|
self.log(module, LogLevel.WARN, *args, **kwargs)
|
||||||
|
|
||||||
|
def error(self, module, *args, **kwargs):
|
||||||
|
"""Log na poziomie ERROR"""
|
||||||
|
self.log(module, LogLevel.ERROR, *args, **kwargs)
|
||||||
|
|
||||||
|
def exception(self, module, *args):
|
||||||
|
"""Log wyjątku na poziomie ERROR"""
|
||||||
|
self.log(module, LogLevel.ERROR, *args, exc_info=True)
|
||||||
|
|
||||||
|
# Singleton
|
||||||
|
logger = LayerForgeLogger()
|
||||||
|
|
||||||
|
# Funkcje pomocnicze
|
||||||
|
def debug(module, *args, **kwargs):
|
||||||
|
"""Log na poziomie DEBUG"""
|
||||||
|
logger.debug(module, *args, **kwargs)
|
||||||
|
|
||||||
|
def info(module, *args, **kwargs):
|
||||||
|
"""Log na poziomie INFO"""
|
||||||
|
logger.info(module, *args, **kwargs)
|
||||||
|
|
||||||
|
def warn(module, *args, **kwargs):
|
||||||
|
"""Log na poziomie WARN"""
|
||||||
|
logger.warn(module, *args, **kwargs)
|
||||||
|
|
||||||
|
def error(module, *args, **kwargs):
|
||||||
|
"""Log na poziomie ERROR"""
|
||||||
|
logger.error(module, *args, **kwargs)
|
||||||
|
|
||||||
|
def exception(module, *args):
|
||||||
|
"""Log wyjątku na poziomie ERROR"""
|
||||||
|
logger.exception(module, *args)
|
||||||
|
|
||||||
|
# Funkcja do szybkiego włączania/wyłączania debugowania
|
||||||
|
def set_debug(enabled=True):
|
||||||
|
"""Włącz/wyłącz debugowanie globalnie"""
|
||||||
|
if enabled:
|
||||||
|
logger.set_global_level(LogLevel.DEBUG)
|
||||||
|
else:
|
||||||
|
logger.set_global_level(LogLevel.INFO)
|
||||||
|
return logger
|
||||||
|
|
||||||
|
# Funkcja do włączania/wyłączania logowania do pliku
|
||||||
|
def set_file_logging(enabled=True, log_dir=None):
|
||||||
|
"""Włącz/wyłącz logowanie do pliku"""
|
||||||
|
logger.config['log_to_file'] = enabled
|
||||||
|
if log_dir:
|
||||||
|
logger.config['log_dir'] = log_dir
|
||||||
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Zresetuj loggery, aby zastosować nowe ustawienia
|
||||||
|
logger.loggers = {}
|
||||||
|
return logger
|
||||||
Reference in New Issue
Block a user