diff --git a/locales/de.json b/locales/de.json index db83bcbe..eadb5d06 100644 --- a/locales/de.json +++ b/locales/de.json @@ -535,6 +535,16 @@ "defaultUnetRootHelp": "Legen Sie den Standard-Diffusion-Modell-(UNET)-Stammordner für Downloads, Importe und Verschiebungen fest", "defaultEmbeddingRoot": "Embedding-Stammordner", "defaultEmbeddingRootHelp": "Legen Sie den Standard-Embedding-Stammordner für Downloads, Importe und Verschiebungen fest", + "defaultVaeRoot": "[TODO: Translate] VAE Root", + "defaultVaeRootHelp": "[TODO: Translate] Set default VAE root directory for downloads, imports and moves", + "defaultUpscalerRoot": "[TODO: Translate] Upscaler Root", + "defaultUpscalerRootHelp": "[TODO: Translate] Set default upscaler root directory for downloads, imports and moves", + "defaultTextEncoderRoot": "[TODO: Translate] Text Encoder Root", + "defaultTextEncoderRootHelp": "[TODO: Translate] Set default text encoder root directory for downloads, imports and moves", + "defaultClipVisionRoot": "[TODO: Translate] CLIP Vision Root", + "defaultClipVisionRootHelp": "[TODO: Translate] Set default CLIP vision root directory for downloads, imports and moves", + "defaultControlnetRoot": "[TODO: Translate] ControlNet Root", + "defaultControlnetRootHelp": "[TODO: Translate] Set default ControlNet root directory for downloads, imports and moves", "recipesPath": "Rezepte-Speicherpfad", "recipesPathHelp": "Optionales benutzerdefiniertes Verzeichnis für gespeicherte Rezepte. Leer lassen, um den recipes-Ordner im ersten LoRA-Stammverzeichnis zu verwenden.", "recipesPathPlaceholder": "/path/to/recipes", @@ -2342,6 +2352,7 @@ "checkpointRootsFailed": "Fehler beim Laden der Checkpoint-Stammverzeichnisse: {message}", "unetRootsFailed": "Fehler beim Laden der Diffusion-Modell-Stammverzeichnisse: {message}", "embeddingRootsFailed": "Fehler beim Laden der Embedding-Stammverzeichnisse: {message}", + "otherRootsFailed": "[TODO: Translate] Failed to load other model roots: {message}", "mappingsUpdated": "Basismodell-Pfad-Zuordnungen aktualisiert ({count})", "mappingsCleared": "Basismodell-Pfad-Zuordnungen gelöscht", "mappingSaveFailed": "Fehler beim Speichern der Basismodell-Zuordnungen: {message}", diff --git a/locales/en.json b/locales/en.json index c5e45b63..1f32af8f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -535,6 +535,16 @@ "defaultUnetRootHelp": "Set default diffusion model (UNET) root directory for downloads, imports and moves", "defaultEmbeddingRoot": "Embedding Root", "defaultEmbeddingRootHelp": "Set default embedding root directory for downloads, imports and moves", + "defaultVaeRoot": "VAE Root", + "defaultVaeRootHelp": "Set default VAE root directory for downloads, imports and moves", + "defaultUpscalerRoot": "Upscaler Root", + "defaultUpscalerRootHelp": "Set default upscaler root directory for downloads, imports and moves", + "defaultTextEncoderRoot": "Text Encoder Root", + "defaultTextEncoderRootHelp": "Set default text encoder root directory for downloads, imports and moves", + "defaultClipVisionRoot": "CLIP Vision Root", + "defaultClipVisionRootHelp": "Set default CLIP vision root directory for downloads, imports and moves", + "defaultControlnetRoot": "ControlNet Root", + "defaultControlnetRootHelp": "Set default ControlNet root directory for downloads, imports and moves", "recipesPath": "Recipes Storage Path", "recipesPathHelp": "Optional custom directory for stored recipes. Leave empty to use the first LoRA root's recipes folder.", "recipesPathPlaceholder": "/path/to/recipes", @@ -2342,6 +2352,7 @@ "checkpointRootsFailed": "Failed to load checkpoint roots: {message}", "unetRootsFailed": "Failed to load diffusion model roots: {message}", "embeddingRootsFailed": "Failed to load embedding roots: {message}", + "otherRootsFailed": "Failed to load other model roots: {message}", "mappingsUpdated": "Base model path mappings updated ({count} mapping{plural})", "mappingsCleared": "Base model path mappings cleared", "mappingSaveFailed": "Failed to save base model mappings: {message}", diff --git a/locales/es.json b/locales/es.json index 7d32f6ac..fe83b1f9 100644 --- a/locales/es.json +++ b/locales/es.json @@ -535,6 +535,16 @@ "defaultUnetRootHelp": "Establecer el directorio raíz predeterminado de Diffusion Model (UNET) para descargas, importaciones y movimientos", "defaultEmbeddingRoot": "Raíz de embedding", "defaultEmbeddingRootHelp": "Establecer el directorio raíz predeterminado de embedding para descargas, importaciones y movimientos", + "defaultVaeRoot": "[TODO: Translate] VAE Root", + "defaultVaeRootHelp": "[TODO: Translate] Set default VAE root directory for downloads, imports and moves", + "defaultUpscalerRoot": "[TODO: Translate] Upscaler Root", + "defaultUpscalerRootHelp": "[TODO: Translate] Set default upscaler root directory for downloads, imports and moves", + "defaultTextEncoderRoot": "[TODO: Translate] Text Encoder Root", + "defaultTextEncoderRootHelp": "[TODO: Translate] Set default text encoder root directory for downloads, imports and moves", + "defaultClipVisionRoot": "[TODO: Translate] CLIP Vision Root", + "defaultClipVisionRootHelp": "[TODO: Translate] Set default CLIP vision root directory for downloads, imports and moves", + "defaultControlnetRoot": "[TODO: Translate] ControlNet Root", + "defaultControlnetRootHelp": "[TODO: Translate] Set default ControlNet root directory for downloads, imports and moves", "recipesPath": "Ruta de almacenamiento de recetas", "recipesPathHelp": "Directorio personalizado opcional para las recetas guardadas. Déjalo vacío para usar la carpeta recipes del primer directorio raíz de LoRA.", "recipesPathPlaceholder": "/path/to/recipes", @@ -2342,6 +2352,7 @@ "checkpointRootsFailed": "Error al cargar raíces de checkpoint: {message}", "unetRootsFailed": "Error al cargar raíces de Diffusion Model: {message}", "embeddingRootsFailed": "Error al cargar raíces de embedding: {message}", + "otherRootsFailed": "[TODO: Translate] Failed to load other model roots: {message}", "mappingsUpdated": "Mapeos de rutas de modelo base actualizados ({count} mapeo{plural})", "mappingsCleared": "Mapeos de rutas de modelo base limpiados", "mappingSaveFailed": "Error al guardar mapeos de modelo base: {message}", diff --git a/locales/fr.json b/locales/fr.json index 9d3464f3..dc69d10c 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -535,6 +535,16 @@ "defaultUnetRootHelp": "Définir le répertoire racine Diffusion Model (UNET) par défaut pour les téléchargements, imports et déplacements", "defaultEmbeddingRoot": "Racine Embedding", "defaultEmbeddingRootHelp": "Définir le répertoire racine embedding par défaut pour les téléchargements, imports et déplacements", + "defaultVaeRoot": "[TODO: Translate] VAE Root", + "defaultVaeRootHelp": "[TODO: Translate] Set default VAE root directory for downloads, imports and moves", + "defaultUpscalerRoot": "[TODO: Translate] Upscaler Root", + "defaultUpscalerRootHelp": "[TODO: Translate] Set default upscaler root directory for downloads, imports and moves", + "defaultTextEncoderRoot": "[TODO: Translate] Text Encoder Root", + "defaultTextEncoderRootHelp": "[TODO: Translate] Set default text encoder root directory for downloads, imports and moves", + "defaultClipVisionRoot": "[TODO: Translate] CLIP Vision Root", + "defaultClipVisionRootHelp": "[TODO: Translate] Set default CLIP vision root directory for downloads, imports and moves", + "defaultControlnetRoot": "[TODO: Translate] ControlNet Root", + "defaultControlnetRootHelp": "[TODO: Translate] Set default ControlNet root directory for downloads, imports and moves", "recipesPath": "Chemin de stockage des Recipes", "recipesPathHelp": "Dossier personnalisé facultatif pour les Recipes enregistrées. Laissez vide pour utiliser le dossier recipes de la première racine LoRA.", "recipesPathPlaceholder": "/path/to/recipes", @@ -2342,6 +2352,7 @@ "checkpointRootsFailed": "Échec du chargement des racines checkpoint : {message}", "unetRootsFailed": "Échec du chargement des racines Diffusion Model : {message}", "embeddingRootsFailed": "Échec du chargement des racines embedding : {message}", + "otherRootsFailed": "[TODO: Translate] Failed to load other model roots: {message}", "mappingsUpdated": "Mappages de chemin de modèle de base mis à jour ({count} mappage{plural})", "mappingsCleared": "Mappages de chemin de modèle de base effacés", "mappingSaveFailed": "Échec de la sauvegarde des mappages de modèle de base : {message}", diff --git a/locales/he.json b/locales/he.json index d0da7ac1..cb91c56a 100644 --- a/locales/he.json +++ b/locales/he.json @@ -535,6 +535,16 @@ "defaultUnetRootHelp": "הגדר את ספריית השורש המוגדרת כברירת מחדל של Diffusion Model (UNET) להורדות, ייבוא והעברות", "defaultEmbeddingRoot": "תיקיית שורש Embedding", "defaultEmbeddingRootHelp": "הגדר את ספריית השורש המוגדרת כברירת מחדל של embedding להורדות, ייבוא והעברות", + "defaultVaeRoot": "[TODO: Translate] VAE Root", + "defaultVaeRootHelp": "[TODO: Translate] Set default VAE root directory for downloads, imports and moves", + "defaultUpscalerRoot": "[TODO: Translate] Upscaler Root", + "defaultUpscalerRootHelp": "[TODO: Translate] Set default upscaler root directory for downloads, imports and moves", + "defaultTextEncoderRoot": "[TODO: Translate] Text Encoder Root", + "defaultTextEncoderRootHelp": "[TODO: Translate] Set default text encoder root directory for downloads, imports and moves", + "defaultClipVisionRoot": "[TODO: Translate] CLIP Vision Root", + "defaultClipVisionRootHelp": "[TODO: Translate] Set default CLIP vision root directory for downloads, imports and moves", + "defaultControlnetRoot": "[TODO: Translate] ControlNet Root", + "defaultControlnetRootHelp": "[TODO: Translate] Set default ControlNet root directory for downloads, imports and moves", "recipesPath": "נתיב אחסון מתכונים", "recipesPathHelp": "ספרייה מותאמת אישית אופציונלית למתכונים שנשמרו. השאר ריק כדי להשתמש בתיקיית recipes של שורש LoRA הראשון.", "recipesPathPlaceholder": "/path/to/recipes", @@ -2342,6 +2352,7 @@ "checkpointRootsFailed": "טעינת שורשי checkpoint נכשלה: {message}", "unetRootsFailed": "טעינת שורשי Diffusion Model נכשלה: {message}", "embeddingRootsFailed": "טעינת שורשי embedding נכשלה: {message}", + "otherRootsFailed": "[TODO: Translate] Failed to load other model roots: {message}", "mappingsUpdated": "מיפויי נתיבי מודל בסיס עודכנו ({count})", "mappingsCleared": "מיפויי נתיבי מודל בסיס נוקו", "mappingSaveFailed": "שמירת מיפויי מודל בסיס נכשלה: {message}", diff --git a/locales/ja.json b/locales/ja.json index afc1edbb..69339323 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -535,6 +535,16 @@ "defaultUnetRootHelp": "ダウンロード、インポート、移動用のデフォルトDiffusion Model (UNET)ルートディレクトリを設定", "defaultEmbeddingRoot": "Embeddingルート", "defaultEmbeddingRootHelp": "ダウンロード、インポート、移動用のデフォルトembeddingルートディレクトリを設定", + "defaultVaeRoot": "[TODO: Translate] VAE Root", + "defaultVaeRootHelp": "[TODO: Translate] Set default VAE root directory for downloads, imports and moves", + "defaultUpscalerRoot": "[TODO: Translate] Upscaler Root", + "defaultUpscalerRootHelp": "[TODO: Translate] Set default upscaler root directory for downloads, imports and moves", + "defaultTextEncoderRoot": "[TODO: Translate] Text Encoder Root", + "defaultTextEncoderRootHelp": "[TODO: Translate] Set default text encoder root directory for downloads, imports and moves", + "defaultClipVisionRoot": "[TODO: Translate] CLIP Vision Root", + "defaultClipVisionRootHelp": "[TODO: Translate] Set default CLIP vision root directory for downloads, imports and moves", + "defaultControlnetRoot": "[TODO: Translate] ControlNet Root", + "defaultControlnetRootHelp": "[TODO: Translate] Set default ControlNet root directory for downloads, imports and moves", "recipesPath": "レシピ保存先", "recipesPathHelp": "保存済みレシピ用の任意のカスタムディレクトリです。空欄にすると最初のLoRAルートのrecipesフォルダーを使用します。", "recipesPathPlaceholder": "/path/to/recipes", @@ -2342,6 +2352,7 @@ "checkpointRootsFailed": "Checkpointルートの読み込みに失敗しました:{message}", "unetRootsFailed": "Diffusion Modelルートの読み込みに失敗しました:{message}", "embeddingRootsFailed": "embeddingルートの読み込みに失敗しました:{message}", + "otherRootsFailed": "[TODO: Translate] Failed to load other model roots: {message}", "mappingsUpdated": "ベースモデルパスマッピングが更新されました({count} マッピング)", "mappingsCleared": "ベースモデルパスマッピングがクリアされました", "mappingSaveFailed": "ベースモデルマッピングの保存に失敗しました:{message}", diff --git a/locales/ko.json b/locales/ko.json index b32d16da..4215737e 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -535,6 +535,16 @@ "defaultUnetRootHelp": "다운로드, 가져오기 및 이동을 위한 기본 Diffusion Model (UNET) 루트 디렉토리를 설정합니다", "defaultEmbeddingRoot": "Embedding 루트", "defaultEmbeddingRootHelp": "다운로드, 가져오기 및 이동을 위한 기본 Embedding 루트 디렉토리를 설정합니다", + "defaultVaeRoot": "[TODO: Translate] VAE Root", + "defaultVaeRootHelp": "[TODO: Translate] Set default VAE root directory for downloads, imports and moves", + "defaultUpscalerRoot": "[TODO: Translate] Upscaler Root", + "defaultUpscalerRootHelp": "[TODO: Translate] Set default upscaler root directory for downloads, imports and moves", + "defaultTextEncoderRoot": "[TODO: Translate] Text Encoder Root", + "defaultTextEncoderRootHelp": "[TODO: Translate] Set default text encoder root directory for downloads, imports and moves", + "defaultClipVisionRoot": "[TODO: Translate] CLIP Vision Root", + "defaultClipVisionRootHelp": "[TODO: Translate] Set default CLIP vision root directory for downloads, imports and moves", + "defaultControlnetRoot": "[TODO: Translate] ControlNet Root", + "defaultControlnetRootHelp": "[TODO: Translate] Set default ControlNet root directory for downloads, imports and moves", "recipesPath": "레시피 저장 경로", "recipesPathHelp": "저장된 레시피를 위한 선택적 사용자 지정 디렉터리입니다. 비워 두면 첫 번째 LoRA 루트의 recipes 폴더를 사용합니다.", "recipesPathPlaceholder": "/path/to/recipes", @@ -2342,6 +2352,7 @@ "checkpointRootsFailed": "Checkpoint 루트 로딩 실패: {message}", "unetRootsFailed": "Diffusion Model 루트 로딩 실패: {message}", "embeddingRootsFailed": "Embedding 루트 로딩 실패: {message}", + "otherRootsFailed": "[TODO: Translate] Failed to load other model roots: {message}", "mappingsUpdated": "베이스 모델 경로 매핑이 업데이트되었습니다 ({count}개 매핑)", "mappingsCleared": "베이스 모델 경로 매핑이 지워졌습니다", "mappingSaveFailed": "베이스 모델 매핑 저장 실패: {message}", diff --git a/locales/ru.json b/locales/ru.json index bbdbd5a7..6bf627b4 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -535,6 +535,16 @@ "defaultUnetRootHelp": "Установить корневую папку Diffusion Model (UNET) по умолчанию для загрузок, импорта и перемещений", "defaultEmbeddingRoot": "Корневая папка Embedding", "defaultEmbeddingRootHelp": "Установить корневую папку embedding по умолчанию для загрузок, импорта и перемещений", + "defaultVaeRoot": "[TODO: Translate] VAE Root", + "defaultVaeRootHelp": "[TODO: Translate] Set default VAE root directory for downloads, imports and moves", + "defaultUpscalerRoot": "[TODO: Translate] Upscaler Root", + "defaultUpscalerRootHelp": "[TODO: Translate] Set default upscaler root directory for downloads, imports and moves", + "defaultTextEncoderRoot": "[TODO: Translate] Text Encoder Root", + "defaultTextEncoderRootHelp": "[TODO: Translate] Set default text encoder root directory for downloads, imports and moves", + "defaultClipVisionRoot": "[TODO: Translate] CLIP Vision Root", + "defaultClipVisionRootHelp": "[TODO: Translate] Set default CLIP vision root directory for downloads, imports and moves", + "defaultControlnetRoot": "[TODO: Translate] ControlNet Root", + "defaultControlnetRootHelp": "[TODO: Translate] Set default ControlNet root directory for downloads, imports and moves", "recipesPath": "Путь хранения рецептов", "recipesPathHelp": "Дополнительный пользовательский каталог для сохранённых рецептов. Оставьте пустым, чтобы использовать папку recipes в первом корне LoRA.", "recipesPathPlaceholder": "/path/to/recipes", @@ -2342,6 +2352,7 @@ "checkpointRootsFailed": "Не удалось загрузить корни checkpoint: {message}", "unetRootsFailed": "Не удалось загрузить корни Diffusion Model: {message}", "embeddingRootsFailed": "Не удалось загрузить корни embedding: {message}", + "otherRootsFailed": "[TODO: Translate] Failed to load other model roots: {message}", "mappingsUpdated": "Сопоставления путей базовых моделей обновлены ({count})", "mappingsCleared": "Сопоставления путей базовых моделей очищены", "mappingSaveFailed": "Не удалось сохранить сопоставления базовых моделей: {message}", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 817bdd54..c3f19342 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -535,6 +535,16 @@ "defaultUnetRootHelp": "设置下载、导入和移动时的默认 Diffusion Model (UNET) 根目录", "defaultEmbeddingRoot": "Embedding 根目录", "defaultEmbeddingRootHelp": "设置下载、导入和移动时的默认 Embedding 根目录", + "defaultVaeRoot": "[TODO: Translate] VAE Root", + "defaultVaeRootHelp": "[TODO: Translate] Set default VAE root directory for downloads, imports and moves", + "defaultUpscalerRoot": "[TODO: Translate] Upscaler Root", + "defaultUpscalerRootHelp": "[TODO: Translate] Set default upscaler root directory for downloads, imports and moves", + "defaultTextEncoderRoot": "[TODO: Translate] Text Encoder Root", + "defaultTextEncoderRootHelp": "[TODO: Translate] Set default text encoder root directory for downloads, imports and moves", + "defaultClipVisionRoot": "[TODO: Translate] CLIP Vision Root", + "defaultClipVisionRootHelp": "[TODO: Translate] Set default CLIP vision root directory for downloads, imports and moves", + "defaultControlnetRoot": "[TODO: Translate] ControlNet Root", + "defaultControlnetRootHelp": "[TODO: Translate] Set default ControlNet root directory for downloads, imports and moves", "recipesPath": "配方存储路径", "recipesPathHelp": "已保存配方的可选自定义目录。留空则使用第一个 LoRA 根目录下的 recipes 文件夹。", "recipesPathPlaceholder": "/path/to/recipes", @@ -2342,6 +2352,7 @@ "checkpointRootsFailed": "加载 Checkpoint 根目录失败:{message}", "unetRootsFailed": "加载 Diffusion Model 根目录失败:{message}", "embeddingRootsFailed": "加载 Embedding 根目录失败:{message}", + "otherRootsFailed": "[TODO: Translate] Failed to load other model roots: {message}", "mappingsUpdated": "基础模型路径映射已更新({count} 条映射)", "mappingsCleared": "基础模型路径映射已清除", "mappingSaveFailed": "保存基础模型映射失败:{message}", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index a79b4c45..2b917517 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -535,6 +535,16 @@ "defaultUnetRootHelp": "設定下載、匯入和移動時的預設 Diffusion Model (UNET) 根目錄", "defaultEmbeddingRoot": "Embedding 根目錄", "defaultEmbeddingRootHelp": "設定下載、匯入和移動時的預設 Embedding 根目錄", + "defaultVaeRoot": "[TODO: Translate] VAE Root", + "defaultVaeRootHelp": "[TODO: Translate] Set default VAE root directory for downloads, imports and moves", + "defaultUpscalerRoot": "[TODO: Translate] Upscaler Root", + "defaultUpscalerRootHelp": "[TODO: Translate] Set default upscaler root directory for downloads, imports and moves", + "defaultTextEncoderRoot": "[TODO: Translate] Text Encoder Root", + "defaultTextEncoderRootHelp": "[TODO: Translate] Set default text encoder root directory for downloads, imports and moves", + "defaultClipVisionRoot": "[TODO: Translate] CLIP Vision Root", + "defaultClipVisionRootHelp": "[TODO: Translate] Set default CLIP vision root directory for downloads, imports and moves", + "defaultControlnetRoot": "[TODO: Translate] ControlNet Root", + "defaultControlnetRootHelp": "[TODO: Translate] Set default ControlNet root directory for downloads, imports and moves", "recipesPath": "配方儲存路徑", "recipesPathHelp": "已儲存配方的可選自訂目錄。留空則使用第一個 LoRA 根目錄下的 recipes 資料夾。", "recipesPathPlaceholder": "/path/to/recipes", @@ -2342,6 +2352,7 @@ "checkpointRootsFailed": "載入 checkpoint 根目錄失敗:{message}", "unetRootsFailed": "載入 Diffusion Model 根目錄失敗:{message}", "embeddingRootsFailed": "載入 embedding 根目錄失敗:{message}", + "otherRootsFailed": "[TODO: Translate] Failed to load other model roots: {message}", "mappingsUpdated": "基礎模型路徑對應已更新({count} 個對應)", "mappingsCleared": "基礎模型路徑對應已清除", "mappingSaveFailed": "儲存基礎模型對應失敗:{message}", diff --git a/static/js/api/apiConfig.js b/static/js/api/apiConfig.js index d1bce2cc..7fd6d508 100644 --- a/static/js/api/apiConfig.js +++ b/static/js/api/apiConfig.js @@ -146,6 +146,7 @@ export const MODEL_SPECIFIC_ENDPOINTS = { }, [MODEL_TYPES.OTHER]: { metadata: `/api/lm/${MODEL_TYPES.OTHER}/metadata`, + roots_by_subtype: `/api/lm/${MODEL_TYPES.OTHER}/roots_by_subtype`, } }; diff --git a/static/js/api/otherApi.js b/static/js/api/otherApi.js index eecf72d2..d42425c0 100644 --- a/static/js/api/otherApi.js +++ b/static/js/api/otherApi.js @@ -4,4 +4,42 @@ import { BaseModelApiClient } from './baseModelApi.js'; * Other-models-specific API client (VAE, upscalers, text encoders, etc.) */ export class OtherApiClient extends BaseModelApiClient { + /** + * Get other-model roots, optionally narrowed to one sub_type + * (vae/upscaler/text_encoder/clip_vision/controlnet). + * + * Without a sub_type this falls back to the merged roots list + * (GET /api/lm/other/roots); with one it reads the grouped + * roots_by_subtype map and extracts the matching list. + */ + async fetchModelRoots(subType = null) { + if (!subType) { + return super.fetchModelRoots(); + } + + const data = await this.fetchRootsBySubType(); + const groupedRoots = data.roots_by_subtype || {}; + return { + success: data.success !== false, + roots: groupedRoots[subType] || [], + }; + } + + /** + * Get other-model roots grouped by sub_type. + * GET /api/lm/other/roots_by_subtype + * -> { success, roots_by_subtype: {sub_type: [...]} } + */ + async fetchRootsBySubType() { + try { + const response = await fetch(this.apiConfig.endpoints.specific.roots_by_subtype); + if (!response.ok) { + throw new Error('Failed to fetch other-model roots by sub_type'); + } + return await response.json(); + } catch (error) { + console.error('Error fetching other-model roots by sub_type:', error); + throw error; + } + } } diff --git a/static/js/components/controls/OtherControls.js b/static/js/components/controls/OtherControls.js index e7a19388..c9bb6d60 100644 --- a/static/js/components/controls/OtherControls.js +++ b/static/js/components/controls/OtherControls.js @@ -2,6 +2,7 @@ import { PageControls } from './PageControls.js'; import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js'; import { showToast } from '../../utils/uiHelpers.js'; +import { downloadManager } from '../../managers/DownloadManager.js'; /** * OtherControls class - Extends PageControls for the Other Models page @@ -39,6 +40,11 @@ export class OtherControls extends PageControls { return await getModelApiClient().fetchCivitaiMetadata(); }, + // Add show download modal functionality + showDownloadModal: () => { + downloadManager.showDownloadModal(); + }, + toggleBulkMode: () => { if (window.bulkManager) { window.bulkManager.toggleBulkMode(); diff --git a/static/js/managers/DownloadManager.js b/static/js/managers/DownloadManager.js index 6831ee64..885becef 100644 --- a/static/js/managers/DownloadManager.js +++ b/static/js/managers/DownloadManager.js @@ -8,6 +8,7 @@ import { isModelWeightFile } from '../utils/modelFileTypes.js'; import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js'; import { FolderTreeManager } from '../components/FolderTreeManager.js'; import { translate } from '../utils/i18nHelpers.js'; +import { MODEL_SUBTYPE_DISPLAY_NAMES } from '../utils/constants.js'; import { buildCivitaiUrl, extractCivitaiModelUrlParts, normalizeCivitaiPageHost } from '../utils/civitaiUtils.js'; import { formatFileSize } from '../utils/formatters.js'; import { showDownloadBatchSummary } from '../components/DownloadBatchSummaryModal.js'; @@ -956,11 +957,17 @@ export class DownloadManager { try { this._isDiffusionModel = await this._resolveIsDiffusionModel(); + this._otherSubType = await this._resolveOtherSubType(); let rootsData; if (this._isDiffusionModel && this.apiClient.modelType === 'checkpoints') { rootsData = await this.apiClient.fetchModelRoots('diffusion_model'); + } else if (this.apiClient.modelType === 'other' && this._otherSubType) { + rootsData = await this.apiClient.fetchModelRoots(this._otherSubType); } else { + // An undecidable other sub_type (null) intentionally lands + // here: fetchModelRoots() lists all other roots so the user + // can pick manually. rootsData = await this.apiClient.fetchModelRoots(); } const modelRoot = document.getElementById('modelRoot'); @@ -968,19 +975,29 @@ export class DownloadManager { `` ).join(''); - const singularType = this._isDiffusionModel - ? 'unet' - : this.apiClient.modelType.replace(/s$/, ''); - const defaultRootKey = `default_${singularType}_root`; - const defaultRoot = state.global.settings[defaultRootKey]; - console.log(`Default root for ${singularType}:`, defaultRoot); + let defaultRoot; + let subtypeDisplay; + if (this.apiClient.modelType === 'other') { + const otherDefaultRoots = state.global.settings.default_other_roots || {}; + defaultRoot = this._otherSubType ? (otherDefaultRoots[this._otherSubType] || '') : ''; + subtypeDisplay = this._otherSubType + ? (MODEL_SUBTYPE_DISPLAY_NAMES[this._otherSubType] || this._otherSubType) + : this.apiClient.apiConfig.config.displayName; + } else { + const singularType = this._isDiffusionModel + ? 'unet' + : this.apiClient.modelType.replace(/s$/, ''); + const defaultRootKey = `default_${singularType}_root`; + defaultRoot = state.global.settings[defaultRootKey]; + subtypeDisplay = this._isDiffusionModel ? 'Diffusion Model' : this.apiClient.apiConfig.config.displayName; + } + console.log('Default root:', defaultRoot); console.log('Available roots:', rootsData.roots); if (defaultRoot && rootsData.roots.includes(defaultRoot)) { console.log(`Setting default root: ${defaultRoot}`); modelRoot.value = defaultRoot; } - const subtypeDisplay = this._isDiffusionModel ? 'Diffusion Model' : this.apiClient.apiConfig.config.displayName; document.getElementById('modelRootLabel').textContent = translate('modals.download.selectTypeRoot', { type: subtypeDisplay }); @@ -1065,6 +1082,50 @@ export class DownloadManager { return localFileTypeCheck; } + /** + * Resolve which other-page sub_type (vae/upscaler/text_encoder/ + * clip_vision/controlnet) this download routes to. The backend owns the + * routing rule (explicit file pick first, model.type next, file.type + * fallback), so the location step sends both the picked file's type + * (selected_file_type) and the version's full file-type list and lets + * the backend apply its priority chain. Returns null when the sub_type + * cannot be decided; the location step then lists all other roots for + * manual selection instead of guessing a folder. + */ + async _resolveOtherSubType() { + // Only other-page downloads route by sub_type; without version + // metadata (e.g. Hugging Face downloads) there is nothing to route on. + if (this.apiClient.modelType !== 'other' + || (!this.selectedFile && !this.currentVersion)) { + return null; + } + + try { + const fileTypes = (this.currentVersion?.files || []).map(f => f.type); + const response = await fetch(DOWNLOAD_ENDPOINTS.routing, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model_type: 'other', + base_model: this.currentVersion?.baseModel || '', + file_types: fileTypes, + ...(this.selectedFile + ? { selected_file_type: this.selectedFile.type } + : {}), + }), + }); + if (!response.ok) { + throw new Error(`routing endpoint returned ${response.status}`); + } + const data = await response.json(); + return data.sub_type || null; + } catch (error) { + console.warn('[download] other routing endpoint unavailable, ' + + 'falling back to manual root selection:', error); + return null; + } + } + loadDefaultPathSetting() { const modelType = this.apiClient.modelType; const storageKey = `use_default_path_${modelType}`; diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index ec5f2dc9..5c4ba1f0 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -1153,6 +1153,9 @@ export class SettingsManager { // Load default unet root await this.loadUnetRoots(); + // Load default other-model roots (per sub_type) + await this.loadOtherRoots(); + // Load extra folder paths this.loadExtraFolderPaths(); @@ -1658,6 +1661,51 @@ export class SettingsManager { } } + async loadOtherRoots() { + const selects = document.querySelectorAll('select[data-other-root-subtype]'); + if (!selects.length) return; + + try { + // Fetch other-model roots grouped by sub_type + const response = await fetch('/api/lm/other/roots_by_subtype'); + if (!response.ok) { + throw new Error('Failed to fetch other model roots'); + } + + const data = await response.json(); + const groupedRoots = data.roots_by_subtype || {}; + const defaultRoots = state.global.settings.default_other_roots || {}; + + selects.forEach((select) => { + const subType = select.dataset.otherRootSubtype; + const roots = groupedRoots[subType] || []; + if (!roots.length) { + this.showNoRootsPlaceholder(select); + return; + } + + select.innerHTML = ''; + select.disabled = false; + + // Add options for each root + roots.forEach(root => { + const option = document.createElement('option'); + option.value = root; + option.textContent = root; + select.appendChild(option); + }); + + const defaultRoot = defaultRoots[subType] || ''; + select.value = roots.includes(defaultRoot) ? defaultRoot : roots[0]; + }); + + } catch (error) { + console.error('Error loading other model roots:', error); + selects.forEach((select) => this.showNoRootsPlaceholder(select)); + showToast('toast.settings.otherRootsFailed', { message: error.message }, 'error'); + } + } + async loadEmbeddingRoots() { const defaultEmbeddingRootSelect = document.getElementById('defaultEmbeddingRoot'); if (!defaultEmbeddingRootSelect) return; @@ -2339,6 +2387,27 @@ export class SettingsManager { } } + /** + * Save one sub_type entry of the default_other_roots dict setting + * (read-modify-write: the backend stores the whole mapping). + */ + async saveOtherRootSetting(subType, value) { + try { + const defaultRoots = { ...(state.global.settings.default_other_roots || {}) }; + if (value) { + defaultRoots[subType] = value; + } else { + delete defaultRoots[subType]; + } + + await this.saveSetting('default_other_roots', defaultRoots); + + showToast('toast.settings.settingsUpdated', { setting: `default ${subType} root` }, 'success'); + } catch (error) { + showToast('toast.settings.settingSaveFailed', { message: error.message }, 'error'); + } + } + /** * Save the recipes page layout (grid | masonry) and rebuild the scroller. * Shared entry point for the settings modal segmented control and the diff --git a/static/js/state/index.js b/static/js/state/index.js index e20b82d4..f54858f3 100644 --- a/static/js/state/index.js +++ b/static/js/state/index.js @@ -24,6 +24,7 @@ const DEFAULT_SETTINGS_BASE = Object.freeze({ default_lora_root: '', default_checkpoint_root: '', default_embedding_root: '', + default_other_roots: {}, recipes_path: '', base_model_path_mappings: {}, download_path_templates: {}, @@ -72,6 +73,7 @@ export function createDefaultSettings() { base_model_path_mappings: {}, download_path_templates: { ...DEFAULT_PATH_TEMPLATES }, priority_tags: { ...DEFAULT_PRIORITY_TAG_CONFIG }, + default_other_roots: {}, }; } diff --git a/templates/components/controls.html b/templates/components/controls.html index ef83941b..9a7dc1db 100644 --- a/templates/components/controls.html +++ b/templates/components/controls.html @@ -80,14 +80,12 @@