mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-20 18:51:26 -03:00
4938faa049
Folders created from the sidebar had no in-app way back out: the only
removal path was to leave ComfyUI, delete the directory by hand and
rescan. A typo'd folder also polluted the move/download destination
picker permanently, since it reads the same all_folders source.
Adds POST /api/lm/{prefix}/delete-folder, restricted to directories
whose subtree holds no model weight files — a folder-level cascade would
bypass the per-model lifecycle bookkeeping (metadata sidecars, previews,
cache entries, pending-delete staging, recipe references). The service
walks the directory itself instead of trusting the possibly stale cache,
reports what it would remove (models / files / subfolders / symlinks),
and refuses library roots, top-level symlinks (shutil.rmtree rejects
those) and folders holding a staged delete, whose manifest would be
invalidated by the move. Symbolic links inside the subtree are counted
but never followed.
ModelScanner.remove_known_folder mirrors add_known_folder: the removed
subtree leaves all_folders while ancestors are kept (every recorded
ancestor exists on disk in its own right), stale cache entries under the
prefix are purged and the folder list recomputed. The handler broadcasts
models_changed so destination pickers drop the folder too.
The sidebar entry is a destructive context-menu item. The modal opens in
a confirm state for model-free folders and an explanatory one when the
subtree still holds models, decided from the models-only set that already
dims empty nodes; a stale tree is caught by the 409 not_empty/busy
conflict. Truly empty folders get the existing 20s undo affordance,
implemented by re-creating the directory.
230 lines
8.1 KiB
JavaScript
230 lines
8.1 KiB
JavaScript
import { state } from '../state/index.js';
|
|
|
|
/**
|
|
* API Configuration
|
|
* Centralized configuration for all model types and their endpoints
|
|
*/
|
|
|
|
// Model type definitions
|
|
export const MODEL_TYPES = {
|
|
LORA: 'loras',
|
|
CHECKPOINT: 'checkpoints',
|
|
EMBEDDING: 'embeddings',
|
|
OTHER: 'other'
|
|
};
|
|
|
|
// Base API configuration for each model type
|
|
export const MODEL_CONFIG = {
|
|
[MODEL_TYPES.LORA]: {
|
|
displayName: 'LoRA',
|
|
singularName: 'lora',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: true,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
supportsFolderManagement: true,
|
|
templateName: 'loras.html'
|
|
},
|
|
[MODEL_TYPES.CHECKPOINT]: {
|
|
displayName: 'Checkpoint',
|
|
singularName: 'checkpoint',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: false,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
supportsFolderManagement: true,
|
|
templateName: 'checkpoints.html'
|
|
},
|
|
[MODEL_TYPES.EMBEDDING]: {
|
|
displayName: 'Embedding',
|
|
singularName: 'embedding',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: true,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
supportsFolderManagement: true,
|
|
templateName: 'embeddings.html'
|
|
},
|
|
[MODEL_TYPES.OTHER]: {
|
|
displayName: 'Other Model',
|
|
singularName: 'other',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: false,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
supportsFolderManagement: true,
|
|
templateName: 'other.html'
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Generate API endpoints for a given model type
|
|
* @param {string} modelType - The model type (e.g., 'loras', 'checkpoints')
|
|
* @returns {Object} Object containing all API endpoints for the model type
|
|
*/
|
|
export function getApiEndpoints(modelType) {
|
|
return {
|
|
// Base CRUD operations
|
|
list: `/api/lm/${modelType}/list`,
|
|
excluded: `/api/lm/${modelType}/excluded`,
|
|
delete: `/api/lm/${modelType}/delete`,
|
|
exclude: `/api/lm/${modelType}/exclude`,
|
|
unexclude: `/api/lm/${modelType}/unexclude`,
|
|
rename: `/api/lm/${modelType}/rename`,
|
|
save: `/api/lm/${modelType}/save-metadata`,
|
|
cancelTask: `/api/lm/${modelType}/cancel-task`,
|
|
|
|
// Bulk operations
|
|
bulkDelete: `/api/lm/${modelType}/bulk-delete`,
|
|
|
|
// Tag operations
|
|
addTags: `/api/lm/${modelType}/add-tags`,
|
|
|
|
// Move operations (now common for all model types that support move)
|
|
moveModel: `/api/lm/${modelType}/move_model`,
|
|
moveBulk: `/api/lm/${modelType}/move_models_bulk`,
|
|
createFolder: `/api/lm/${modelType}/create-folder`,
|
|
deleteFolder: `/api/lm/${modelType}/delete-folder`,
|
|
|
|
// CivitAI integration
|
|
fetchCivitai: `/api/lm/${modelType}/fetch-civitai`,
|
|
fetchAllCivitai: `/api/lm/${modelType}/fetch-all-civitai`,
|
|
relinkCivitai: `/api/lm/${modelType}/relink-civitai`,
|
|
civitaiVersions: `/api/lm/${modelType}/civitai/versions`,
|
|
refreshUpdates: `/api/lm/${modelType}/updates/refresh`,
|
|
fetchMissingLicenses: `/api/lm/${modelType}/updates/fetch-missing-license`,
|
|
modelUpdateStatus: `/api/lm/${modelType}/updates/status`,
|
|
modelUpdateVersions: `/api/lm/${modelType}/updates/versions`,
|
|
ignoreModelUpdate: `/api/lm/${modelType}/updates/ignore`,
|
|
ignoreVersionUpdate: `/api/lm/${modelType}/updates/ignore-version`,
|
|
|
|
// Preview management
|
|
replacePreview: `/api/lm/${modelType}/replace-preview`,
|
|
setPreviewFromUrl: `/api/lm/${modelType}/set-preview-from-url`,
|
|
|
|
// Query operations
|
|
scan: `/api/lm/${modelType}/scan`,
|
|
topTags: `/api/lm/${modelType}/top-tags`,
|
|
searchTags: `/api/lm/${modelType}/search-tags`,
|
|
baseModels: `/api/lm/${modelType}/base-models`,
|
|
roots: `/api/lm/${modelType}/roots`,
|
|
folders: `/api/lm/${modelType}/folders`,
|
|
folderTree: `/api/lm/${modelType}/folder-tree`,
|
|
unifiedFolderTree: `/api/lm/${modelType}/unified-folder-tree`,
|
|
duplicates: `/api/lm/${modelType}/find-duplicates`,
|
|
conflicts: `/api/lm/${modelType}/find-filename-conflicts`,
|
|
verify: `/api/lm/${modelType}/verify-duplicates`,
|
|
metadata: `/api/lm/${modelType}/metadata`,
|
|
modelDescription: `/api/lm/${modelType}/model-description`,
|
|
|
|
// Auto-organize operations
|
|
autoOrganize: `/api/lm/${modelType}/auto-organize`,
|
|
autoOrganizeProgress: `/api/lm/${modelType}/auto-organize-progress`,
|
|
|
|
// Model-specific endpoints (will be merged with specific configs)
|
|
specific: {}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Model-specific endpoint configurations
|
|
*/
|
|
export const MODEL_SPECIFIC_ENDPOINTS = {
|
|
[MODEL_TYPES.LORA]: {
|
|
letterCounts: `/api/lm/${MODEL_TYPES.LORA}/letter-counts`,
|
|
notes: `/api/lm/${MODEL_TYPES.LORA}/get-notes`,
|
|
triggerWords: `/api/lm/${MODEL_TYPES.LORA}/get-trigger-words`,
|
|
previewUrl: `/api/lm/${MODEL_TYPES.LORA}/preview-url`,
|
|
civitaiUrl: `/api/lm/${MODEL_TYPES.LORA}/civitai-url`,
|
|
metadata: `/api/lm/${MODEL_TYPES.LORA}/metadata`,
|
|
getTriggerWordsPost: `/api/lm/${MODEL_TYPES.LORA}/get_trigger_words`,
|
|
civitaiModelByVersion: `/api/lm/${MODEL_TYPES.LORA}/civitai/model/version`,
|
|
civitaiModelByHash: `/api/lm/${MODEL_TYPES.LORA}/civitai/model/hash`,
|
|
},
|
|
[MODEL_TYPES.CHECKPOINT]: {
|
|
info: `/api/lm/${MODEL_TYPES.CHECKPOINT}/info`,
|
|
checkpoints_roots: `/api/lm/${MODEL_TYPES.CHECKPOINT}/checkpoints_roots`,
|
|
unet_roots: `/api/lm/${MODEL_TYPES.CHECKPOINT}/unet_roots`,
|
|
metadata: `/api/lm/${MODEL_TYPES.CHECKPOINT}/metadata`,
|
|
},
|
|
[MODEL_TYPES.EMBEDDING]: {
|
|
metadata: `/api/lm/${MODEL_TYPES.EMBEDDING}/metadata`,
|
|
},
|
|
[MODEL_TYPES.OTHER]: {
|
|
metadata: `/api/lm/${MODEL_TYPES.OTHER}/metadata`,
|
|
roots_by_subtype: `/api/lm/${MODEL_TYPES.OTHER}/roots_by_subtype`,
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get complete API configuration for a model type
|
|
* @param {string} modelType - The model type
|
|
* @returns {Object} Complete API configuration
|
|
*/
|
|
export function getCompleteApiConfig(modelType) {
|
|
const baseEndpoints = getApiEndpoints(modelType);
|
|
const specificEndpoints = MODEL_SPECIFIC_ENDPOINTS[modelType] || {};
|
|
const config = MODEL_CONFIG[modelType];
|
|
|
|
return {
|
|
modelType,
|
|
config,
|
|
endpoints: {
|
|
...baseEndpoints,
|
|
specific: specificEndpoints
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Validate if a model type is supported
|
|
* @param {string} modelType - The model type to validate
|
|
* @returns {boolean} True if valid, false otherwise
|
|
*/
|
|
export function isValidModelType(modelType) {
|
|
return Object.values(MODEL_TYPES).includes(modelType);
|
|
}
|
|
|
|
/**
|
|
* Get model type from current page or explicit parameter
|
|
* @param {string} [explicitType] - Explicitly provided model type
|
|
* @returns {string} The model type
|
|
*/
|
|
export function getCurrentModelType(explicitType = null) {
|
|
if (explicitType && isValidModelType(explicitType)) {
|
|
return explicitType;
|
|
}
|
|
|
|
return state.currentPageType || MODEL_TYPES.LORA;
|
|
}
|
|
|
|
// Download API endpoints (shared across all model types)
|
|
export const DOWNLOAD_ENDPOINTS = {
|
|
download: '/api/lm/download-model',
|
|
downloadGet: '/api/lm/download-model-get',
|
|
cancelGet: '/api/lm/cancel-download-get',
|
|
progress: '/api/lm/download-progress',
|
|
routing: '/api/lm/download/routing',
|
|
exampleImages: '/api/lm/force-download-example-images', // Re-process example images ignoring previous status
|
|
exampleImagesMissing: '/api/lm/download-example-images' // Download only missing example images
|
|
};
|
|
|
|
// External model source endpoints (Hugging Face / ModelScope).
|
|
// The hf-* paths are the historical names, kept as server-side aliases.
|
|
export const MODEL_SOURCE_ENDPOINTS = {
|
|
repoFiles: '/api/lm/model-source-files',
|
|
download: '/api/lm/download-model-source',
|
|
sources: '/api/lm/model-sources',
|
|
};
|
|
|
|
/** @deprecated use MODEL_SOURCE_ENDPOINTS */
|
|
export const HF_ENDPOINTS = {
|
|
repoFiles: MODEL_SOURCE_ENDPOINTS.repoFiles,
|
|
download: MODEL_SOURCE_ENDPOINTS.download,
|
|
};
|
|
|
|
// WebSocket endpoints
|
|
export const WS_ENDPOINTS = {
|
|
fetchProgress: '/ws/fetch-progress'
|
|
};
|