refactor: unify model download system across all model types

- Add download-related methods to baseModelApi.js for fetching versions, roots, folders, and downloading models
- Replace separate download managers with a unified DownloadManager.js supporting all model types
- Create a single download_modals.html template that adapts to model type (LoRA, checkpoint, etc.)
- Remove old download modals from lora_modals.html and checkpoint_modals.html
- Update apiConfig.js to include civitaiVersions endpoints for each model type
- Centralize event handler binding in DownloadManager.js (no more inline HTML handlers)
- Modal UI and logic now auto-adapt to the current model type, making future extension easier
This commit is contained in:
Will Miao
2025-07-25 17:35:06 +08:00
parent e587189880
commit 7f205cdcc8
16 changed files with 337 additions and 785 deletions

View File

@@ -103,13 +103,12 @@ export const MODEL_SPECIFIC_ENDPOINTS = {
moveBulk: `/api/${MODEL_TYPES.LORA}/move_models_bulk`,
getTriggerWordsPost: `/api/${MODEL_TYPES.LORA}/get_trigger_words`,
civitaiModelByVersion: `/api/${MODEL_TYPES.LORA}/civitai/model/version`,
civitaiModelByHash: `/api/${MODEL_TYPES.LORA}/civitai/model/hash`
civitaiModelByHash: `/api/${MODEL_TYPES.LORA}/civitai/model/hash`,
},
[MODEL_TYPES.CHECKPOINT]: {
info: `/api/${MODEL_TYPES.CHECKPOINT}/info`
info: `/api/${MODEL_TYPES.CHECKPOINT}/info`,
},
[MODEL_TYPES.EMBEDDING]: {
// Future embedding-specific endpoints
}
};

View File

@@ -581,6 +581,86 @@ class ModelApiClient {
return successFilePaths;
}
/**
* Fetch Civitai model versions
*/
async fetchCivitaiVersions(modelId) {
try {
const response = await fetch(`${this.apiConfig.endpoints.civitaiVersions}/${modelId}`);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
if (errorData && errorData.error && errorData.error.includes('Model type mismatch')) {
throw new Error(`This model is not a ${this.apiConfig.config.displayName}. Please switch to the appropriate page to download this model type.`);
}
throw new Error('Failed to fetch model versions');
}
return await response.json();
} catch (error) {
console.error('Error fetching Civitai versions:', error);
throw error;
}
}
/**
* Fetch model roots
*/
async fetchModelRoots() {
try {
const response = await fetch(this.apiConfig.endpoints.roots);
if (!response.ok) {
throw new Error(`Failed to fetch ${this.apiConfig.config.displayName} roots`);
}
return await response.json();
} catch (error) {
console.error('Error fetching model roots:', error);
throw error;
}
}
/**
* Fetch model folders
*/
async fetchModelFolders() {
try {
const response = await fetch(this.apiConfig.endpoints.folders);
if (!response.ok) {
throw new Error(`Failed to fetch ${this.apiConfig.config.displayName} folders`);
}
return await response.json();
} catch (error) {
console.error('Error fetching model folders:', error);
throw error;
}
}
/**
* Download a model
*/
async downloadModel(modelId, versionId, modelRoot, relativePath, downloadId) {
try {
const response = await fetch(DOWNLOAD_ENDPOINTS.download, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model_id: modelId,
model_version_id: versionId,
model_root: modelRoot,
relative_path: relativePath,
download_id: downloadId
})
});
if (!response.ok) {
throw new Error(await response.text());
}
return await response.json();
} catch (error) {
console.error('Error downloading model:', error);
throw error;
}
}
/**
* Build query parameters for API requests
*/