Files
arcum42-ComfyUI_SageUtils/js/shared/imageLoader.js
T

173 lines
5.6 KiB
JavaScript

/**
* Centralized Image Loading Service
* Provides consistent image loading across all gallery features
* Uses the correct API endpoints and handles errors consistently
*/
/**
* Loads a full-size image using the sage_utils API
* @param {Object|string} imageInput - Image object or path string
* @returns {Promise<string>} Promise that resolves to a blob URL
*/
export async function loadFullImage(imageInput) {
const imagePath = typeof imageInput === 'string'
? imageInput
: (imageInput.path || imageInput.relative_path || imageInput.name);
if (!imagePath) {
throw new Error('No valid image path provided');
}
try {
const response = await fetch('/sage_utils/image', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
image_path: imagePath
})
});
if (!response.ok) {
throw new Error(`Failed to load image: ${response.status} ${response.statusText}`);
}
const blob = await response.blob();
const imageUrl = URL.createObjectURL(blob);
return imageUrl;
} catch (error) {
console.error('Error loading full image:', error);
throw error;
}
}
/**
* Loads a thumbnail image using the sage_utils API
* @param {Object|string} imageInput - Image object or path string
* @param {string} size - Thumbnail size ('small', 'medium', 'large')
* @returns {Promise<string>} Promise that resolves to a blob URL
*/
export async function loadThumbnail(imageInput, size = 'large') {
const imagePath = typeof imageInput === 'string'
? imageInput
: (imageInput.path || imageInput.relative_path || imageInput.name);
if (!imagePath) {
throw new Error('No valid image path provided');
}
try {
const response = await fetch('/sage_utils/thumbnail', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
image_path: imagePath,
size: size
})
});
if (!response.ok) {
throw new Error(`Failed to load thumbnail: ${response.status} ${response.statusText}`);
}
const blob = await response.blob();
const imageUrl = URL.createObjectURL(blob);
return imageUrl;
} catch (error) {
console.error('Error loading thumbnail:', error);
throw error;
}
}
/**
* Creates a fallback SVG image for error states
* @param {string} message - Error message to display
* @param {number} width - Image width
* @param {number} height - Image height
* @returns {string} Data URL for fallback SVG
*/
export function createFallbackImage(message = 'Failed to load image', width = 400, height = 300) {
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
<rect width="${width}" height="${height}" fill="#333"/>
<text x="${width/2}" y="${height/2}" text-anchor="middle" font-size="16" fill="#999">${message}</text>
</svg>`;
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
}
/**
* Loads an image with automatic fallback handling
* @param {Object|string} imageInput - Image object or path string
* @param {Object} options - Loading options
* @param {boolean} options.thumbnail - Whether to load as thumbnail
* @param {string} options.thumbnailSize - Thumbnail size if thumbnail is true
* @param {boolean} options.fallbackOnError - Whether to return fallback image on error
* @returns {Promise<string>} Promise that resolves to image URL or fallback
*/
export async function loadImageWithFallback(imageInput, options = {}) {
const {
thumbnail = false,
thumbnailSize = 'large',
fallbackOnError = true
} = options;
try {
if (thumbnail) {
return await loadThumbnail(imageInput, thumbnailSize);
} else {
return await loadFullImage(imageInput);
}
} catch (error) {
if (fallbackOnError) {
console.warn('Image loading failed, using fallback:', error);
return createFallbackImage('Error loading image');
} else {
throw error;
}
}
}
/**
* Opens an image in a new browser tab
* @param {Object|string} imageInput - Image object or path string
*/
export async function openImageInNewTab(imageInput) {
try {
const imageUrl = await loadFullImage(imageInput);
window.open(imageUrl, '_blank');
} catch (error) {
console.error('Failed to open image in new tab:', error);
// Could show a user notification here
}
}
/**
* Utility to clean up blob URLs to prevent memory leaks
* @param {string} blobUrl - The blob URL to revoke
*/
export function cleanupImageUrl(blobUrl) {
if (blobUrl && blobUrl.startsWith('blob:')) {
URL.revokeObjectURL(blobUrl);
}
}
/**
* Preloads an image for faster display
* @param {Object|string} imageInput - Image object or path string
* @param {Object} options - Loading options (same as loadImageWithFallback)
* @returns {Promise<HTMLImageElement>} Promise that resolves to loaded image element
*/
export async function preloadImage(imageInput, options = {}) {
const imageUrl = await loadImageWithFallback(imageInput, options);
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error('Failed to preload image'));
img.src = imageUrl;
});
}