mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
fix: improve example image upload reliability and error handling, #804
- Sequential per-file upload to avoid client_max_size limits - Add backend exception handler with proper 500 responses - Increase standalone server upload limit to 256MB - Add partial success localization support
This commit is contained in:
@@ -1466,6 +1466,7 @@
|
|||||||
"folderTreeFailed": "Fehler beim Laden des Ordnerbaums",
|
"folderTreeFailed": "Fehler beim Laden des Ordnerbaums",
|
||||||
"folderTreeError": "Fehler beim Laden des Ordnerbaums",
|
"folderTreeError": "Fehler beim Laden des Ordnerbaums",
|
||||||
"imagesImported": "Beispielbilder erfolgreich importiert",
|
"imagesImported": "Beispielbilder erfolgreich importiert",
|
||||||
|
"imagesPartial": "{success} Bild(er) importiert, {failed} fehlgeschlagen",
|
||||||
"importFailed": "Fehler beim Importieren der Beispielbilder: {message}"
|
"importFailed": "Fehler beim Importieren der Beispielbilder: {message}"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
|
|||||||
@@ -1466,6 +1466,7 @@
|
|||||||
"folderTreeFailed": "Failed to load folder tree",
|
"folderTreeFailed": "Failed to load folder tree",
|
||||||
"folderTreeError": "Error loading folder tree",
|
"folderTreeError": "Error loading folder tree",
|
||||||
"imagesImported": "Example images imported successfully",
|
"imagesImported": "Example images imported successfully",
|
||||||
|
"imagesPartial": "{success} image(s) imported, {failed} failed",
|
||||||
"importFailed": "Failed to import example images: {message}"
|
"importFailed": "Failed to import example images: {message}"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
|
|||||||
@@ -1466,6 +1466,7 @@
|
|||||||
"folderTreeFailed": "Error al cargar árbol de carpetas",
|
"folderTreeFailed": "Error al cargar árbol de carpetas",
|
||||||
"folderTreeError": "Error al cargar árbol de carpetas",
|
"folderTreeError": "Error al cargar árbol de carpetas",
|
||||||
"imagesImported": "Imágenes de ejemplo importadas exitosamente",
|
"imagesImported": "Imágenes de ejemplo importadas exitosamente",
|
||||||
|
"imagesPartial": "{success} imagen(es) importada(s), {failed} fallida(s)",
|
||||||
"importFailed": "Error al importar imágenes de ejemplo: {message}"
|
"importFailed": "Error al importar imágenes de ejemplo: {message}"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
|
|||||||
@@ -1466,6 +1466,7 @@
|
|||||||
"folderTreeFailed": "Échec du chargement de l'arborescence des dossiers",
|
"folderTreeFailed": "Échec du chargement de l'arborescence des dossiers",
|
||||||
"folderTreeError": "Erreur lors du chargement de l'arborescence des dossiers",
|
"folderTreeError": "Erreur lors du chargement de l'arborescence des dossiers",
|
||||||
"imagesImported": "Images d'exemple importées avec succès",
|
"imagesImported": "Images d'exemple importées avec succès",
|
||||||
|
"imagesPartial": "{success} image(s) importée(s), {failed} échouée(s)",
|
||||||
"importFailed": "Échec de l'importation des images d'exemple : {message}"
|
"importFailed": "Échec de l'importation des images d'exemple : {message}"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
|
|||||||
@@ -1466,6 +1466,7 @@
|
|||||||
"folderTreeFailed": "טעינת עץ התיקיות נכשלה",
|
"folderTreeFailed": "טעינת עץ התיקיות נכשלה",
|
||||||
"folderTreeError": "שגיאה בטעינת עץ התיקיות",
|
"folderTreeError": "שגיאה בטעינת עץ התיקיות",
|
||||||
"imagesImported": "תמונות הדוגמה יובאו בהצלחה",
|
"imagesImported": "תמונות הדוגמה יובאו בהצלחה",
|
||||||
|
"imagesPartial": "{success} תמונה/ות יובאו, {failed} נכשלו",
|
||||||
"importFailed": "ייבוא תמונות הדוגמה נכשל: {message}"
|
"importFailed": "ייבוא תמונות הדוגמה נכשל: {message}"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
|
|||||||
@@ -1466,6 +1466,7 @@
|
|||||||
"folderTreeFailed": "フォルダツリーの読み込みに失敗しました",
|
"folderTreeFailed": "フォルダツリーの読み込みに失敗しました",
|
||||||
"folderTreeError": "フォルダツリー読み込みエラー",
|
"folderTreeError": "フォルダツリー読み込みエラー",
|
||||||
"imagesImported": "例画像が正常にインポートされました",
|
"imagesImported": "例画像が正常にインポートされました",
|
||||||
|
"imagesPartial": "{success} 件の画像をインポート、{failed} 件失敗",
|
||||||
"importFailed": "例画像のインポートに失敗しました:{message}"
|
"importFailed": "例画像のインポートに失敗しました:{message}"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
|
|||||||
@@ -1466,6 +1466,7 @@
|
|||||||
"folderTreeFailed": "폴더 트리 로딩 실패",
|
"folderTreeFailed": "폴더 트리 로딩 실패",
|
||||||
"folderTreeError": "폴더 트리 로딩 오류",
|
"folderTreeError": "폴더 트리 로딩 오류",
|
||||||
"imagesImported": "예시 이미지가 성공적으로 가져와졌습니다",
|
"imagesImported": "예시 이미지가 성공적으로 가져와졌습니다",
|
||||||
|
"imagesPartial": "{success}개 이미지 가져오기 성공, {failed}개 실패",
|
||||||
"importFailed": "예시 이미지 가져오기 실패: {message}"
|
"importFailed": "예시 이미지 가져오기 실패: {message}"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
|
|||||||
@@ -1466,6 +1466,7 @@
|
|||||||
"folderTreeFailed": "Не удалось загрузить дерево папок",
|
"folderTreeFailed": "Не удалось загрузить дерево папок",
|
||||||
"folderTreeError": "Ошибка загрузки дерева папок",
|
"folderTreeError": "Ошибка загрузки дерева папок",
|
||||||
"imagesImported": "Примеры изображений успешно импортированы",
|
"imagesImported": "Примеры изображений успешно импортированы",
|
||||||
|
"imagesPartial": "{success} изображ. импортировано, {failed} не удалось",
|
||||||
"importFailed": "Не удалось импортировать примеры изображений: {message}"
|
"importFailed": "Не удалось импортировать примеры изображений: {message}"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
|
|||||||
@@ -1466,6 +1466,7 @@
|
|||||||
"folderTreeFailed": "加载文件夹树失败",
|
"folderTreeFailed": "加载文件夹树失败",
|
||||||
"folderTreeError": "加载文件夹树出错",
|
"folderTreeError": "加载文件夹树出错",
|
||||||
"imagesImported": "示例图片导入成功",
|
"imagesImported": "示例图片导入成功",
|
||||||
|
"imagesPartial": "成功导入 {success} 张图片,{failed} 张失败",
|
||||||
"importFailed": "导入示例图片失败:{message}"
|
"importFailed": "导入示例图片失败:{message}"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
|
|||||||
@@ -1466,6 +1466,7 @@
|
|||||||
"folderTreeFailed": "載入資料夾樹狀結構失敗",
|
"folderTreeFailed": "載入資料夾樹狀結構失敗",
|
||||||
"folderTreeError": "載入資料夾樹狀結構錯誤",
|
"folderTreeError": "載入資料夾樹狀結構錯誤",
|
||||||
"imagesImported": "範例圖片匯入成功",
|
"imagesImported": "範例圖片匯入成功",
|
||||||
|
"imagesPartial": "成功匯入 {success} 張圖片,{failed} 張失敗",
|
||||||
"importFailed": "匯入範例圖片失敗:{message}"
|
"importFailed": "匯入範例圖片失敗:{message}"
|
||||||
},
|
},
|
||||||
"triggerWords": {
|
"triggerWords": {
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
"""Handler set for example image routes."""
|
"""Handler set for example image routes."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Callable, Mapping
|
from typing import Callable, Mapping
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from ...services.use_cases.example_images import (
|
from ...services.use_cases.example_images import (
|
||||||
DownloadExampleImagesConfigurationError,
|
DownloadExampleImagesConfigurationError,
|
||||||
DownloadExampleImagesInProgressError,
|
DownloadExampleImagesInProgressError,
|
||||||
@@ -122,6 +125,9 @@ class ExampleImagesManagementHandler:
|
|||||||
return web.json_response({'success': False, 'error': str(exc)}, status=400)
|
return web.json_response({'success': False, 'error': str(exc)}, status=400)
|
||||||
except ExampleImagesImportError as exc:
|
except ExampleImagesImportError as exc:
|
||||||
return web.json_response({'success': False, 'error': str(exc)}, status=500)
|
return web.json_response({'success': False, 'error': str(exc)}, status=500)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.exception("Unexpected error importing example images")
|
||||||
|
return web.json_response({'success': False, 'error': str(exc)}, status=500)
|
||||||
|
|
||||||
async def delete_example_image(self, request: web.Request) -> web.StreamResponse:
|
async def delete_example_image(self, request: web.Request) -> web.StreamResponse:
|
||||||
return await self._processor.delete_custom_image(request)
|
return await self._processor.delete_custom_image(request)
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ class StandaloneServer:
|
|||||||
self.app = web.Application(
|
self.app = web.Application(
|
||||||
logger=logger,
|
logger=logger,
|
||||||
middlewares=[cache_control],
|
middlewares=[cache_control],
|
||||||
|
client_max_size=256 * 1024 * 1024,
|
||||||
handler_args={
|
handler_args={
|
||||||
"max_field_size": HEADER_SIZE_LIMIT,
|
"max_field_size": HEADER_SIZE_LIMIT,
|
||||||
"max_line_size": HEADER_SIZE_LIMIT,
|
"max_line_size": HEADER_SIZE_LIMIT,
|
||||||
|
|||||||
@@ -455,26 +455,41 @@ async function handleImportFiles(files, modelHash, importContainer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use FormData to upload files
|
// Upload files one at a time to avoid exceeding server size limits
|
||||||
const formData = new FormData();
|
let lastSuccessResult = null;
|
||||||
formData.append('model_hash', modelHash);
|
let successCount = 0;
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
validFiles.forEach(file => {
|
for (const file of validFiles) {
|
||||||
formData.append('files', file);
|
try {
|
||||||
});
|
const formData = new FormData();
|
||||||
|
formData.append('model_hash', modelHash);
|
||||||
|
formData.append('files', file);
|
||||||
|
|
||||||
// Call API to import files
|
const response = await fetch('/api/lm/import-example-images', {
|
||||||
const response = await fetch('/api/lm/import-example-images', {
|
method: 'POST',
|
||||||
method: 'POST',
|
body: formData
|
||||||
body: formData
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(result.error || 'Failed to import example files');
|
errors.push(`${file.name}: ${result.error || 'Unknown error'}`);
|
||||||
|
} else {
|
||||||
|
lastSuccessResult = result;
|
||||||
|
successCount++;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
errors.push(`${file.name}: ${err.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (successCount === 0) {
|
||||||
|
throw new Error(errors.join('; '));
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = lastSuccessResult;
|
||||||
|
|
||||||
// Get updated local files
|
// Get updated local files
|
||||||
const updatedFilesResponse = await fetch(`/api/lm/example-image-files?model_hash=${modelHash}`);
|
const updatedFilesResponse = await fetch(`/api/lm/example-image-files?model_hash=${modelHash}`);
|
||||||
const updatedFilesResult = await updatedFilesResponse.json();
|
const updatedFilesResult = await updatedFilesResponse.json();
|
||||||
@@ -502,7 +517,11 @@ async function handleImportFiles(files, modelHash, importContainer) {
|
|||||||
// Initialize the import UI for the new content
|
// Initialize the import UI for the new content
|
||||||
initExampleImport(modelHash, showcaseTab);
|
initExampleImport(modelHash, showcaseTab);
|
||||||
|
|
||||||
showToast('toast.import.imagesImported', {}, 'success');
|
if (errors.length > 0) {
|
||||||
|
showToast('toast.import.imagesPartial', { success: successCount, failed: errors.length }, 'warning');
|
||||||
|
} else {
|
||||||
|
showToast('toast.import.imagesImported', {}, 'success');
|
||||||
|
}
|
||||||
|
|
||||||
// Update VirtualScroller if available
|
// Update VirtualScroller if available
|
||||||
if (state.virtualScroller && result.model_file_path) {
|
if (state.virtualScroller && result.model_file_path) {
|
||||||
|
|||||||
Reference in New Issue
Block a user