mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-20 18:51:26 -03:00
38d4c59b4c
ModelScope became a linkable source, but downloading from it was impossible:
the URL picker only recognised huggingface.co, the file listing hit a
huggingface-only endpoint, the resolve URL was hardcoded, and the default
path template always wrote into a `huggingface/` directory.
Move the download knowledge into the providers so the handlers stay generic:
- `ModelSource` gains `list_files()`, `file_download_url()`,
`default_revision` and `default_subdir`. `HuggingFaceSource` keeps the Hub
tree API (`/api/models/{id}/tree/{rev}`, LFS-aware sizes, `main`).
`ModelScopeSource` uses `/api/v1/models/{id}/repo/files?Revision=master`
— which reports real byte sizes for LFS files, so no HEAD probe is needed,
and which only accepts `master` (an HF-imported repo still 404s on `main`)
— and downloads through `/models/{id}/resolve/{rev}/{path}`. That URL
redirects to a CDN target carrying a time-limited `auth_key`, so it is
rebuilt on every request and never cached, which is also what keeps
resumable Range requests working.
- `hf_handlers.py`/`HfHandler` become `model_source_handlers.py`/
`ModelSourceHandler` with `list_model_source_files` and
`download_model_source`. New routes `/api/lm/model-source-files` and
`/api/lm/download-model-source`; the old `/api/lm/hf-repo-files` and
`/api/lm/download-hf-model` paths stay as aliases, and a payload without
`platform` still means Hugging Face, so existing callers are unaffected.
- A downloaded sidecar now records `source_platform` + `source_url` (with the
`hf_url` alias only for Hugging Face) instead of always writing `hf_url`,
and `use_default_paths` files ModelScope downloads under
`modelscope/<owner>/<repo>`. The now-unused shared HF aiohttp session and
its shutdown hook are gone; providers open short-lived sessions.
- Frontend: `detectUrlType` returns the platform-neutral
`model-source-repo` / `model-source-file` plus an explicit `platform`, the
DownloadManager's `hf*` state and methods are renamed to `source*`, every
`source === 'huggingface'` check becomes `isExternalModelSource()`, and
batch groups are keyed by `platform:repo` so the same `owner/name` on two
sites renders as two groups. A bare `owner/name` still means Hugging Face.
- `is_valid_source_id()` centralises repo-id validation (exactly
`owner/name`, no traversal, no leading dot). This also fixes the old HF
download check that rejected any dot in the name, i.e. legitimate repos
such as `black-forest-labs/FLUX.1-dev`.
Verified against the live APIs: the example repo lists 8 weight files with
correct sizes, and a ranged GET of the built resolve URL returns 206 after
following the redirect to the CDN. Backend 2853 passed; frontend 1143 JS +
91 Vue passed. The nine locales carry the refreshed download copy in the
next commit.
224 lines
7.9 KiB
JavaScript
224 lines
7.9 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,
|
|
templateName: 'loras.html'
|
|
},
|
|
[MODEL_TYPES.CHECKPOINT]: {
|
|
displayName: 'Checkpoint',
|
|
singularName: 'checkpoint',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: false,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
templateName: 'checkpoints.html'
|
|
},
|
|
[MODEL_TYPES.EMBEDDING]: {
|
|
displayName: 'Embedding',
|
|
singularName: 'embedding',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: true,
|
|
supportsBulkOperations: true,
|
|
supportsMove: true,
|
|
templateName: 'embeddings.html'
|
|
},
|
|
[MODEL_TYPES.OTHER]: {
|
|
displayName: 'Other Model',
|
|
singularName: 'other',
|
|
defaultPageSize: 100,
|
|
supportsLetterFilter: false,
|
|
supportsBulkOperations: true,
|
|
supportsMove: 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`,
|
|
|
|
// 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'
|
|
};
|