mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
Refactor utility functions and IndexedDB handling
Extracted and centralized common utility functions such as createCanvas, normalizeToUint8, and generateUniqueFileName in CommonUtils.js to reduce code duplication. Added createImageFromSource to ImageUtils.js. Refactored db.js to use a helper for IndexedDB requests, simplifying error handling and reducing boilerplate. Updated CanvasIO.js, CanvasLayers.js, and Canvas_view.js to use the new utilities.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import {saveImage, getImage, removeImage} from "./db.js";
|
||||
import {logger, LogLevel} from "./logger.js";
|
||||
import {createCanvas, normalizeToUint8} from "./CommonUtils.js";
|
||||
import {createImageFromSource} from "./ImageUtils.js";
|
||||
|
||||
// Inicjalizacja loggera dla modułu CanvasIO
|
||||
const log = {
|
||||
@@ -70,15 +72,8 @@ export class CanvasIO {
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
const maskCanvas = document.createElement('canvas');
|
||||
tempCanvas.width = this.canvas.width;
|
||||
tempCanvas.height = this.canvas.height;
|
||||
maskCanvas.width = this.canvas.width;
|
||||
maskCanvas.height = this.canvas.height;
|
||||
|
||||
const tempCtx = tempCanvas.getContext('2d');
|
||||
const maskCtx = maskCanvas.getContext('2d');
|
||||
const { canvas: tempCanvas, ctx: tempCtx } = createCanvas(this.canvas.width, this.canvas.height);
|
||||
const { canvas: maskCanvas, ctx: maskCtx } = createCanvas(this.canvas.width, this.canvas.height);
|
||||
|
||||
tempCtx.fillStyle = '#ffffff';
|
||||
tempCtx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
@@ -249,10 +244,7 @@ export class CanvasIO {
|
||||
try {
|
||||
log.debug("Adding input to canvas:", {inputImage});
|
||||
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
const tempCtx = tempCanvas.getContext('2d');
|
||||
tempCanvas.width = inputImage.width;
|
||||
tempCanvas.height = inputImage.height;
|
||||
const { canvas: tempCanvas, ctx: tempCtx } = createCanvas(inputImage.width, inputImage.height);
|
||||
|
||||
const imgData = new ImageData(
|
||||
inputImage.data,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {saveImage, getImage, removeImage} from "./db.js";
|
||||
import {createModuleLogger} from "./LoggerUtils.js";
|
||||
import {generateUUID, snapToGrid, getSnapAdjustment, worldToLocal, localToWorld} from "./CommonUtils.js";
|
||||
import {generateUUID, snapToGrid, getSnapAdjustment, worldToLocal, localToWorld, createCanvas, generateUniqueFileName} from "./CommonUtils.js";
|
||||
import {withErrorHandling, createValidationError, safeExecute} from "./ErrorHandler.js";
|
||||
import {createImageFromSource} from "./ImageUtils.js";
|
||||
|
||||
// Inicjalizacja loggera dla modułu CanvasLayers
|
||||
const log = createModuleLogger('CanvasLayers');
|
||||
@@ -557,21 +558,7 @@ export class CanvasLayers {
|
||||
// Funkcja fallback do zapisu
|
||||
const saveWithFallback = async (fileName) => {
|
||||
try {
|
||||
const getUniqueFileName = (baseName) => {
|
||||
// Sprawdź czy nazwa już zawiera identyfikator node-a (zapobiega nieskończonej pętli)
|
||||
const nodePattern = new RegExp(`_node_${this.canvas.node.id}(?:_node_\\d+)*`);
|
||||
if (nodePattern.test(baseName)) {
|
||||
// Usuń wszystkie poprzednie identyfikatory node-ów i dodaj tylko jeden
|
||||
const cleanName = baseName.replace(/_node_\d+/g, '');
|
||||
const extension = cleanName.split('.').pop();
|
||||
const nameWithoutExt = cleanName.replace(`.${extension}`, '');
|
||||
return `${nameWithoutExt}_node_${this.canvas.node.id}.${extension}`;
|
||||
}
|
||||
const extension = baseName.split('.').pop();
|
||||
const nameWithoutExt = baseName.replace(`.${extension}`, '');
|
||||
return `${nameWithoutExt}_node_${this.canvas.node.id}.${extension}`;
|
||||
};
|
||||
const uniqueFileName = getUniqueFileName(fileName);
|
||||
const uniqueFileName = generateUniqueFileName(fileName, this.canvas.node.id);
|
||||
return await this.canvas.saveToServer(uniqueFileName);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to save with unique name, falling back to original: ${fileName}`, error);
|
||||
|
||||
@@ -5,7 +5,8 @@ import {$el} from "../../scripts/ui.js";
|
||||
import {Canvas} from "./Canvas.js";
|
||||
import {clearAllCanvasStates} from "./db.js";
|
||||
import {ImageCache} from "./ImageCache.js";
|
||||
import {validateImageData, convertImageData, applyMaskToImageData, prepareImageForCanvas} from "./ImageUtils.js";
|
||||
import {validateImageData, convertImageData, applyMaskToImageData, prepareImageForCanvas, createImageFromSource} from "./ImageUtils.js";
|
||||
import {generateUniqueFileName} from "./CommonUtils.js";
|
||||
import {logger, LogLevel} from "./logger.js";
|
||||
|
||||
// Inicjalizacja loggera dla modułu Canvas_view
|
||||
@@ -930,31 +931,10 @@ async function createCanvasWidget(node, widget, app) {
|
||||
window.canvasExecutionStates = new Map();
|
||||
}
|
||||
|
||||
// Unikalna nazwa pliku dla każdego node-a
|
||||
const getUniqueFileName = (baseName) => {
|
||||
// Sprawdź czy nazwa już zawiera identyfikator node-a (zapobiega nieskończonej pętli)
|
||||
const nodePattern = new RegExp(`_node_${node.id}(?:_node_\\d+)*`);
|
||||
if (nodePattern.test(baseName)) {
|
||||
// Usuń wszystkie poprzednie identyfikatory node-ów i dodaj tylko jeden
|
||||
const cleanName = baseName.replace(/_node_\d+/g, '');
|
||||
const extension = cleanName.split('.').pop();
|
||||
const nameWithoutExt = cleanName.replace(`.${extension}`, '');
|
||||
return `${nameWithoutExt}_node_${node.id}.${extension}`;
|
||||
}
|
||||
const extension = baseName.split('.').pop();
|
||||
const nameWithoutExt = baseName.replace(`.${extension}`, '');
|
||||
return `${nameWithoutExt}_node_${node.id}.${extension}`;
|
||||
};
|
||||
|
||||
// Funkcja do czyszczenia nazwy pliku z identyfikatorów node-ów
|
||||
const getCleanFileName = (fileName) => {
|
||||
return fileName.replace(/_node_\d+/g, '');
|
||||
};
|
||||
|
||||
// Funkcja fallback w przypadku problemów z unikalną nazwą
|
||||
const saveWithFallback = async (fileName) => {
|
||||
try {
|
||||
const uniqueFileName = getUniqueFileName(fileName);
|
||||
const uniqueFileName = generateUniqueFileName(fileName, node.id);
|
||||
log.debug(`Attempting to save with unique name: ${uniqueFileName}`);
|
||||
return await canvas.saveToServer(uniqueFileName);
|
||||
} catch (error) {
|
||||
|
||||
@@ -174,17 +174,6 @@ export function throttle(func, limit) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza czy wartość jest w zakresie
|
||||
* @param {number} value - Wartość do sprawdzenia
|
||||
* @param {number} min - Minimalna wartość
|
||||
* @param {number} max - Maksymalna wartość
|
||||
* @returns {boolean} Czy wartość jest w zakresie
|
||||
*/
|
||||
export function isInRange(value, min, max) {
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ogranicza wartość do zakresu
|
||||
* @param {number} value - Wartość do ograniczenia
|
||||
@@ -226,15 +215,47 @@ export function radiansToDegrees(radians) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Oblicza odległość między dwoma punktami
|
||||
* @param {number} x1 - X pierwszego punktu
|
||||
* @param {number} y1 - Y pierwszego punktu
|
||||
* @param {number} x2 - X drugiego punktu
|
||||
* @param {number} y2 - Y drugiego punktu
|
||||
* @returns {number} Odległość
|
||||
* Tworzy canvas z kontekstem - eliminuje duplikaty w kodzie
|
||||
* @param {number} width - Szerokość canvas
|
||||
* @param {number} height - Wysokość canvas
|
||||
* @param {string} contextType - Typ kontekstu (domyślnie '2d')
|
||||
* @param {Object} contextOptions - Opcje kontekstu
|
||||
* @returns {Object} Obiekt z canvas i ctx
|
||||
*/
|
||||
export function distance(x1, y1, x2, y2) {
|
||||
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
|
||||
export function createCanvas(width, height, contextType = '2d', contextOptions = {}) {
|
||||
const canvas = document.createElement('canvas');
|
||||
if (width) canvas.width = width;
|
||||
if (height) canvas.height = height;
|
||||
const ctx = canvas.getContext(contextType, contextOptions);
|
||||
return { canvas, ctx };
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizuje wartość do zakresu Uint8 (0-255)
|
||||
* @param {number} value - Wartość do znormalizowania (0-1)
|
||||
* @returns {number} Wartość w zakresie 0-255
|
||||
*/
|
||||
export function normalizeToUint8(value) {
|
||||
return Math.max(0, Math.min(255, Math.round(value * 255)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generuje unikalną nazwę pliku z identyfikatorem node-a
|
||||
* @param {string} baseName - Podstawowa nazwa pliku
|
||||
* @param {string|number} nodeId - Identyfikator node-a
|
||||
* @returns {string} Unikalna nazwa pliku
|
||||
*/
|
||||
export function generateUniqueFileName(baseName, nodeId) {
|
||||
const nodePattern = new RegExp(`_node_${nodeId}(?:_node_\\d+)*`);
|
||||
if (nodePattern.test(baseName)) {
|
||||
const cleanName = baseName.replace(/_node_\d+/g, '');
|
||||
const extension = cleanName.split('.').pop();
|
||||
const nameWithoutExt = cleanName.replace(`.${extension}`, '');
|
||||
return `${nameWithoutExt}_node_${nodeId}.${extension}`;
|
||||
}
|
||||
const extension = baseName.split('.').pop();
|
||||
const nameWithoutExt = baseName.replace(`.${extension}`, '');
|
||||
return `${nameWithoutExt}_node_${nodeId}.${extension}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -356,6 +356,20 @@ export function getImageInfo(image) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Tworzy obraz z podanego źródła - eliminuje duplikaty w kodzie
|
||||
* @param {string} source - Źródło obrazu (URL, data URL, etc.)
|
||||
* @returns {Promise<HTMLImageElement>} Promise z obrazem
|
||||
*/
|
||||
export function createImageFromSource(source) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = reject;
|
||||
img.src = source;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tworzy pusty obraz o podanych wymiarach
|
||||
* @param {number} width - Szerokość
|
||||
|
||||
184
js/db.js
184
js/db.js
@@ -10,6 +10,48 @@ const DB_VERSION = 2; // Zwiększono wersję, aby wymusić aktualizację schemat
|
||||
|
||||
let db;
|
||||
|
||||
/**
|
||||
* Funkcja pomocnicza do tworzenia żądań IndexedDB z ujednoliconą obsługą błędów
|
||||
* @param {IDBObjectStore} store - Store IndexedDB
|
||||
* @param {string} operation - Nazwa operacji (get, put, delete, clear)
|
||||
* @param {*} data - Dane dla operacji (opcjonalne)
|
||||
* @param {string} errorMessage - Wiadomość błędu
|
||||
* @returns {Promise} Promise z wynikiem operacji
|
||||
*/
|
||||
function createDBRequest(store, operation, data, errorMessage) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request;
|
||||
|
||||
// Wybierz odpowiednią operację
|
||||
switch (operation) {
|
||||
case 'get':
|
||||
request = store.get(data);
|
||||
break;
|
||||
case 'put':
|
||||
request = store.put(data);
|
||||
break;
|
||||
case 'delete':
|
||||
request = store.delete(data);
|
||||
break;
|
||||
case 'clear':
|
||||
request = store.clear();
|
||||
break;
|
||||
default:
|
||||
reject(new Error(`Unknown operation: ${operation}`));
|
||||
return;
|
||||
}
|
||||
|
||||
request.onerror = (event) => {
|
||||
log.error(errorMessage, event.target.error);
|
||||
reject(errorMessage);
|
||||
};
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
resolve(event.target.result);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function openDB() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (db) {
|
||||
@@ -49,139 +91,71 @@ function openDB() {
|
||||
export async function getCanvasState(id) {
|
||||
log.info(`Getting state for id: ${id}`);
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STATE_STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(STATE_STORE_NAME);
|
||||
const request = store.get(id);
|
||||
|
||||
request.onerror = (event) => {
|
||||
log.error("Error getting canvas state:", event.target.error);
|
||||
reject("Error getting state.");
|
||||
};
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
log.debug(`Get success for id: ${id}`, event.target.result ? 'found' : 'not found');
|
||||
resolve(event.target.result ? event.target.result.state : null);
|
||||
};
|
||||
});
|
||||
const transaction = db.transaction([STATE_STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(STATE_STORE_NAME);
|
||||
|
||||
const result = await createDBRequest(store, 'get', id, "Error getting canvas state");
|
||||
log.debug(`Get success for id: ${id}`, result ? 'found' : 'not found');
|
||||
return result ? result.state : null;
|
||||
}
|
||||
|
||||
export async function setCanvasState(id, state) {
|
||||
log.info(`Setting state for id: ${id}`);
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE_NAME);
|
||||
const request = store.put({id, state});
|
||||
|
||||
request.onerror = (event) => {
|
||||
log.error("Error setting canvas state:", event.target.error);
|
||||
reject("Error setting state.");
|
||||
};
|
||||
|
||||
request.onsuccess = () => {
|
||||
log.debug(`Set success for id: ${id}`);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE_NAME);
|
||||
|
||||
await createDBRequest(store, 'put', {id, state}, "Error setting canvas state");
|
||||
log.debug(`Set success for id: ${id}`);
|
||||
}
|
||||
|
||||
export async function removeCanvasState(id) {
|
||||
log.info(`Removing state for id: ${id}`);
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE_NAME);
|
||||
const request = store.delete(id);
|
||||
|
||||
request.onerror = (event) => {
|
||||
log.error("Error removing canvas state:", event.target.error);
|
||||
reject("Error removing state.");
|
||||
};
|
||||
|
||||
request.onsuccess = () => {
|
||||
log.debug(`Remove success for id: ${id}`);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE_NAME);
|
||||
|
||||
await createDBRequest(store, 'delete', id, "Error removing canvas state");
|
||||
log.debug(`Remove success for id: ${id}`);
|
||||
}
|
||||
|
||||
export async function saveImage(imageId, imageSrc) {
|
||||
log.info(`Saving image with id: ${imageId}`);
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IMAGE_STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(IMAGE_STORE_NAME);
|
||||
const request = store.put({imageId, imageSrc});
|
||||
|
||||
request.onerror = (event) => {
|
||||
log.error("Error saving image:", event.target.error);
|
||||
reject("Error saving image.");
|
||||
};
|
||||
|
||||
request.onsuccess = () => {
|
||||
log.debug(`Image saved successfully for id: ${imageId}`);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
const transaction = db.transaction([IMAGE_STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(IMAGE_STORE_NAME);
|
||||
|
||||
await createDBRequest(store, 'put', {imageId, imageSrc}, "Error saving image");
|
||||
log.debug(`Image saved successfully for id: ${imageId}`);
|
||||
}
|
||||
|
||||
export async function getImage(imageId) {
|
||||
log.info(`Getting image with id: ${imageId}`);
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IMAGE_STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(IMAGE_STORE_NAME);
|
||||
const request = store.get(imageId);
|
||||
|
||||
request.onerror = (event) => {
|
||||
log.error("Error getting image:", event.target.error);
|
||||
reject("Error getting image.");
|
||||
};
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
log.debug(`Get image success for id: ${imageId}`, event.target.result ? 'found' : 'not found');
|
||||
resolve(event.target.result ? event.target.result.imageSrc : null);
|
||||
};
|
||||
});
|
||||
const transaction = db.transaction([IMAGE_STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(IMAGE_STORE_NAME);
|
||||
|
||||
const result = await createDBRequest(store, 'get', imageId, "Error getting image");
|
||||
log.debug(`Get image success for id: ${imageId}`, result ? 'found' : 'not found');
|
||||
return result ? result.imageSrc : null;
|
||||
}
|
||||
|
||||
export async function removeImage(imageId) {
|
||||
log.info(`Removing image with id: ${imageId}`);
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IMAGE_STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(IMAGE_STORE_NAME);
|
||||
const request = store.delete(imageId);
|
||||
|
||||
request.onerror = (event) => {
|
||||
log.error("Error removing image:", event.target.error);
|
||||
reject("Error removing image.");
|
||||
};
|
||||
|
||||
request.onsuccess = () => {
|
||||
log.debug(`Remove image success for id: ${imageId}`);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
const transaction = db.transaction([IMAGE_STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(IMAGE_STORE_NAME);
|
||||
|
||||
await createDBRequest(store, 'delete', imageId, "Error removing image");
|
||||
log.debug(`Remove image success for id: ${imageId}`);
|
||||
}
|
||||
|
||||
export async function clearAllCanvasStates() {
|
||||
log.info("Clearing all canvas states...");
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE_NAME);
|
||||
const request = store.clear();
|
||||
|
||||
request.onerror = (event) => {
|
||||
log.error("Error clearing canvas states:", event.target.error);
|
||||
reject("Error clearing states.");
|
||||
};
|
||||
|
||||
request.onsuccess = () => {
|
||||
log.info("All canvas states cleared successfully.");
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
const transaction = db.transaction([STATE_STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STATE_STORE_NAME);
|
||||
|
||||
await createDBRequest(store, 'clear', null, "Error clearing canvas states");
|
||||
log.info("All canvas states cleared successfully.");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user