diff --git a/locales/de.json b/locales/de.json index 316d661c..6e2294a3 100644 --- a/locales/de.json +++ b/locales/de.json @@ -1466,6 +1466,7 @@ "folderTreeFailed": "Fehler beim Laden des Ordnerbaums", "folderTreeError": "Fehler beim Laden des Ordnerbaums", "imagesImported": "Beispielbilder erfolgreich importiert", + "imagesPartial": "{success} Bild(er) importiert, {failed} fehlgeschlagen", "importFailed": "Fehler beim Importieren der Beispielbilder: {message}" }, "triggerWords": { diff --git a/locales/en.json b/locales/en.json index 970549ae..d3e2f06d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1466,6 +1466,7 @@ "folderTreeFailed": "Failed to load folder tree", "folderTreeError": "Error loading folder tree", "imagesImported": "Example images imported successfully", + "imagesPartial": "{success} image(s) imported, {failed} failed", "importFailed": "Failed to import example images: {message}" }, "triggerWords": { diff --git a/locales/es.json b/locales/es.json index 904c8878..0a9d436f 100644 --- a/locales/es.json +++ b/locales/es.json @@ -1466,6 +1466,7 @@ "folderTreeFailed": "Error al cargar árbol de carpetas", "folderTreeError": "Error al cargar árbol de carpetas", "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}" }, "triggerWords": { diff --git a/locales/fr.json b/locales/fr.json index edca5d25..397dd488 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1466,6 +1466,7 @@ "folderTreeFailed": "Échec 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", + "imagesPartial": "{success} image(s) importée(s), {failed} échouée(s)", "importFailed": "Échec de l'importation des images d'exemple : {message}" }, "triggerWords": { diff --git a/locales/he.json b/locales/he.json index 24f5a6c8..734b956d 100644 --- a/locales/he.json +++ b/locales/he.json @@ -1466,6 +1466,7 @@ "folderTreeFailed": "טעינת עץ התיקיות נכשלה", "folderTreeError": "שגיאה בטעינת עץ התיקיות", "imagesImported": "תמונות הדוגמה יובאו בהצלחה", + "imagesPartial": "{success} תמונה/ות יובאו, {failed} נכשלו", "importFailed": "ייבוא תמונות הדוגמה נכשל: {message}" }, "triggerWords": { diff --git a/locales/ja.json b/locales/ja.json index d357391c..7de32774 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -1466,6 +1466,7 @@ "folderTreeFailed": "フォルダツリーの読み込みに失敗しました", "folderTreeError": "フォルダツリー読み込みエラー", "imagesImported": "例画像が正常にインポートされました", + "imagesPartial": "{success} 件の画像をインポート、{failed} 件失敗", "importFailed": "例画像のインポートに失敗しました:{message}" }, "triggerWords": { diff --git a/locales/ko.json b/locales/ko.json index ddd44127..d7af5309 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -1466,6 +1466,7 @@ "folderTreeFailed": "폴더 트리 로딩 실패", "folderTreeError": "폴더 트리 로딩 오류", "imagesImported": "예시 이미지가 성공적으로 가져와졌습니다", + "imagesPartial": "{success}개 이미지 가져오기 성공, {failed}개 실패", "importFailed": "예시 이미지 가져오기 실패: {message}" }, "triggerWords": { diff --git a/locales/ru.json b/locales/ru.json index c810423d..4e03e2b4 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1466,6 +1466,7 @@ "folderTreeFailed": "Не удалось загрузить дерево папок", "folderTreeError": "Ошибка загрузки дерева папок", "imagesImported": "Примеры изображений успешно импортированы", + "imagesPartial": "{success} изображ. импортировано, {failed} не удалось", "importFailed": "Не удалось импортировать примеры изображений: {message}" }, "triggerWords": { diff --git a/locales/zh-CN.json b/locales/zh-CN.json index a3cb1c86..916040c3 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -1466,6 +1466,7 @@ "folderTreeFailed": "加载文件夹树失败", "folderTreeError": "加载文件夹树出错", "imagesImported": "示例图片导入成功", + "imagesPartial": "成功导入 {success} 张图片,{failed} 张失败", "importFailed": "导入示例图片失败:{message}" }, "triggerWords": { diff --git a/locales/zh-TW.json b/locales/zh-TW.json index b9c662fc..256c5a1f 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -1466,6 +1466,7 @@ "folderTreeFailed": "載入資料夾樹狀結構失敗", "folderTreeError": "載入資料夾樹狀結構錯誤", "imagesImported": "範例圖片匯入成功", + "imagesPartial": "成功匯入 {success} 張圖片,{failed} 張失敗", "importFailed": "匯入範例圖片失敗:{message}" }, "triggerWords": { diff --git a/py/routes/handlers/example_images_handlers.py b/py/routes/handlers/example_images_handlers.py index 9db58188..3100fee2 100644 --- a/py/routes/handlers/example_images_handlers.py +++ b/py/routes/handlers/example_images_handlers.py @@ -1,11 +1,14 @@ """Handler set for example image routes.""" from __future__ import annotations +import logging from dataclasses import dataclass from typing import Callable, Mapping from aiohttp import web +logger = logging.getLogger(__name__) + from ...services.use_cases.example_images import ( DownloadExampleImagesConfigurationError, DownloadExampleImagesInProgressError, @@ -122,6 +125,9 @@ class ExampleImagesManagementHandler: return web.json_response({'success': False, 'error': str(exc)}, status=400) except ExampleImagesImportError as exc: 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: return await self._processor.delete_custom_image(request) diff --git a/standalone.py b/standalone.py index 8c60cf4c..62806cb0 100644 --- a/standalone.py +++ b/standalone.py @@ -154,6 +154,7 @@ class StandaloneServer: self.app = web.Application( logger=logger, middlewares=[cache_control], + client_max_size=256 * 1024 * 1024, handler_args={ "max_field_size": HEADER_SIZE_LIMIT, "max_line_size": HEADER_SIZE_LIMIT, diff --git a/static/js/components/shared/showcase/ShowcaseView.js b/static/js/components/shared/showcase/ShowcaseView.js index 093422a9..434f5108 100644 --- a/static/js/components/shared/showcase/ShowcaseView.js +++ b/static/js/components/shared/showcase/ShowcaseView.js @@ -455,34 +455,49 @@ async function handleImportFiles(files, modelHash, importContainer) { } try { - // Use FormData to upload files - const formData = new FormData(); - formData.append('model_hash', modelHash); - - validFiles.forEach(file => { - formData.append('files', file); - }); - - // Call API to import files - const response = await fetch('/api/lm/import-example-images', { - method: 'POST', - body: formData - }); - - const result = await response.json(); - - if (!result.success) { - throw new Error(result.error || 'Failed to import example files'); + // Upload files one at a time to avoid exceeding server size limits + let lastSuccessResult = null; + let successCount = 0; + const errors = []; + + for (const file of validFiles) { + try { + const formData = new FormData(); + formData.append('model_hash', modelHash); + formData.append('files', file); + + const response = await fetch('/api/lm/import-example-images', { + method: 'POST', + body: formData + }); + + const result = await response.json(); + + if (!result.success) { + 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 const updatedFilesResponse = await fetch(`/api/lm/example-image-files?model_hash=${modelHash}`); const updatedFilesResult = await updatedFilesResponse.json(); - + if (!updatedFilesResult.success) { throw new Error(updatedFilesResult.error || 'Failed to get updated file list'); } - + // Re-render the showcase content const showcaseTab = document.getElementById('showcase-tab'); if (showcaseTab) { @@ -492,18 +507,22 @@ async function handleImportFiles(files, modelHash, importContainer) { // Combine both arrays for rendering const allImages = [...regularImages, ...customImages]; showcaseTab.innerHTML = renderShowcaseContent(allImages, updatedFilesResult.files, true); - + // Re-initialize showcase functionality const carousel = showcaseTab.querySelector('.carousel'); if (carousel && !carousel.classList.contains('collapsed')) { initShowcaseContent(carousel); } - + // Initialize the import UI for the new content 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 if (state.virtualScroller && result.model_file_path) { // Create an update object with only the necessary properties @@ -513,7 +532,7 @@ async function handleImportFiles(files, modelHash, importContainer) { customImages: customImages } }; - + // Update the item in the virtual scroller state.virtualScroller.updateSingleItem(result.model_file_path, updateData); }