feat(download): add Hugging Face model download to standalone UI wizard (#965, #977)

Integrate HF model downloading into the existing CivitAI-style wizard flow:
- URL type detection (civitai / hf-resolve / hf-repo / direct-http)
- Repo file explorer with checkbox-based file selection
- Batch/queue download with per-file WebSocket progress
- Aria2 backend support (respects download_backend setting)
- Scanner cache integration via create_default_metadata + add_model_to_cache
- i18n updates for all 10 locales
This commit is contained in:
Will Miao
2026-06-30 19:36:12 +08:00
parent 16f5222efd
commit 09ca91fc0e
20 changed files with 20207 additions and 19207 deletions

View File

@@ -7,6 +7,7 @@ import {
getCurrentModelType,
isValidModelType,
DOWNLOAD_ENDPOINTS,
HF_ENDPOINTS,
WS_ENDPOINTS
} from './apiConfig.js';
import { resetAndReload } from './modelApiFactory.js';
@@ -1243,6 +1244,48 @@ export class BaseModelApiClient {
}
}
async fetchHfRepoFiles(repo, revision = 'main') {
try {
const params = new URLSearchParams({ repo, revision });
const response = await fetch(`${HF_ENDPOINTS.repoFiles}?${params}`);
if (!response.ok) {
const err = await response.json().catch(() => ({}));
throw new Error(err.error || 'Failed to fetch HF repo files');
}
return await response.json();
} catch (error) {
console.error('Error fetching HF repo files:', error);
throw error;
}
}
async downloadHfModel({ repo, filename, revision, modelRoot, relativePath, useDefaultPaths, download_id }) {
try {
const response = await fetch(HF_ENDPOINTS.download, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
repo,
filename,
revision: revision || 'main',
model_root: modelRoot,
relative_path: relativePath || '',
use_default_paths: useDefaultPaths || false,
...(download_id ? { download_id } : {}),
})
});
if (!response.ok) {
throw new Error(await response.text());
}
return await response.json();
} catch (error) {
console.error('Error downloading HF model:', error);
throw error;
}
}
_buildQueryParams(baseParams, pageState) {
const params = new URLSearchParams(baseParams);
const isExcludedView = pageState.viewMode === 'excluded';