fix(download): accept mirror/www Civitai hosts and clarify linked-file errors
Follow-up to #1152: - Accept civitai.green and www-prefixed hosts in isSupportedCivitaiPageHost, aligning with py/utils/civitai_utils.py and extensionReimportBridge.js, so download URLs on those hosts resolve too - Drop the duplicate civitaiModelByVersion entry from the LoRA-specific endpoint map; the shared getApiEndpoints map now provides it for all types - Report a distinct error when a linked fileId exists in the version but is not a downloadable model weight file (new linkedFileNotWeightFile locale key)
This commit is contained in:
@@ -1723,6 +1723,7 @@
|
||||
"invalidUrl": "Ungültiges Modell-URL-Format",
|
||||
"noVersions": "Keine Versionen für dieses Modell verfügbar",
|
||||
"linkedFileNotFound": "[TODO: Translate] File {fileId} was not found in the requested model version.",
|
||||
"linkedFileNotWeightFile": "[TODO: Translate] File {fileId} is not a downloadable model file.",
|
||||
"mixedSources": "CivitAI- und Hugging Face-/ModelScope-URLs können nicht in derselben Charge gemischt werden.",
|
||||
"noModelFiles": "In diesem Repository wurden keine Modelldateien gefunden."
|
||||
},
|
||||
|
||||
@@ -1723,6 +1723,7 @@
|
||||
"invalidUrl": "Invalid model URL format",
|
||||
"noVersions": "No versions available for this model",
|
||||
"linkedFileNotFound": "File {fileId} was not found in the requested model version.",
|
||||
"linkedFileNotWeightFile": "File {fileId} is not a downloadable model file.",
|
||||
"mixedSources": "Cannot mix CivitAI and Hugging Face / ModelScope URLs in the same batch.",
|
||||
"noModelFiles": "No model files found in this repository."
|
||||
},
|
||||
|
||||
@@ -1723,6 +1723,7 @@
|
||||
"invalidUrl": "Formato de URL de modelo inválido",
|
||||
"noVersions": "No hay versiones disponibles para este modelo",
|
||||
"linkedFileNotFound": "[TODO: Translate] File {fileId} was not found in the requested model version.",
|
||||
"linkedFileNotWeightFile": "[TODO: Translate] File {fileId} is not a downloadable model file.",
|
||||
"mixedSources": "No se pueden mezclar URL de CivitAI y Hugging Face / ModelScope en el mismo lote.",
|
||||
"noModelFiles": "No se encontraron archivos de modelo en este repositorio."
|
||||
},
|
||||
|
||||
@@ -1723,6 +1723,7 @@
|
||||
"invalidUrl": "Format d'URL de modèle invalide",
|
||||
"noVersions": "Aucune version disponible pour ce modèle",
|
||||
"linkedFileNotFound": "[TODO: Translate] File {fileId} was not found in the requested model version.",
|
||||
"linkedFileNotWeightFile": "[TODO: Translate] File {fileId} is not a downloadable model file.",
|
||||
"mixedSources": "Impossible de mélanger les URL CivitAI et Hugging Face / ModelScope dans le même lot.",
|
||||
"noModelFiles": "Aucun fichier de modèle trouvé dans ce dépôt."
|
||||
},
|
||||
|
||||
@@ -1723,6 +1723,7 @@
|
||||
"invalidUrl": "פורמט URL של מודל לא חוקי",
|
||||
"noVersions": "אין גרסאות זמינות למודל זה",
|
||||
"linkedFileNotFound": "[TODO: Translate] File {fileId} was not found in the requested model version.",
|
||||
"linkedFileNotWeightFile": "[TODO: Translate] File {fileId} is not a downloadable model file.",
|
||||
"mixedSources": "לא ניתן לערבב כתובות URL של CivitAI ו-Hugging Face / ModelScope באותה קבוצה.",
|
||||
"noModelFiles": "לא נמצאו קבצי מודל במאגר זה."
|
||||
},
|
||||
|
||||
@@ -1723,6 +1723,7 @@
|
||||
"invalidUrl": "無効なモデル URL 形式",
|
||||
"noVersions": "このモデルの利用可能なバージョンがありません",
|
||||
"linkedFileNotFound": "[TODO: Translate] File {fileId} was not found in the requested model version.",
|
||||
"linkedFileNotWeightFile": "[TODO: Translate] File {fileId} is not a downloadable model file.",
|
||||
"mixedSources": "同じバッチ内でCivitAIとHugging Face / ModelScopeのURLを混在させることはできません。",
|
||||
"noModelFiles": "このリポジトリにモデルファイルが見つかりませんでした。"
|
||||
},
|
||||
|
||||
@@ -1723,6 +1723,7 @@
|
||||
"invalidUrl": "잘못된 모델 URL 형식",
|
||||
"noVersions": "이 모델에 사용 가능한 버전이 없습니다",
|
||||
"linkedFileNotFound": "[TODO: Translate] File {fileId} was not found in the requested model version.",
|
||||
"linkedFileNotWeightFile": "[TODO: Translate] File {fileId} is not a downloadable model file.",
|
||||
"mixedSources": "동일한 배치에서 CivitAI와 Hugging Face / ModelScope URL을 혼합할 수 없습니다.",
|
||||
"noModelFiles": "이 저장소에서 모델 파일을 찾을 수 없습니다."
|
||||
},
|
||||
|
||||
@@ -1723,6 +1723,7 @@
|
||||
"invalidUrl": "Неверный формат URL модели",
|
||||
"noVersions": "Нет доступных версий для этой модели",
|
||||
"linkedFileNotFound": "[TODO: Translate] File {fileId} was not found in the requested model version.",
|
||||
"linkedFileNotWeightFile": "[TODO: Translate] File {fileId} is not a downloadable model file.",
|
||||
"mixedSources": "Нельзя смешивать URL-адреса CivitAI и Hugging Face / ModelScope в одном пакете.",
|
||||
"noModelFiles": "В этом репозитории не найдено файлов моделей."
|
||||
},
|
||||
|
||||
@@ -1723,6 +1723,7 @@
|
||||
"invalidUrl": "无效的模型 URL 格式",
|
||||
"noVersions": "此模型没有可用版本",
|
||||
"linkedFileNotFound": "[TODO: Translate] File {fileId} was not found in the requested model version.",
|
||||
"linkedFileNotWeightFile": "[TODO: Translate] File {fileId} is not a downloadable model file.",
|
||||
"mixedSources": "无法在同一批次中混合使用 CivitAI 和 Hugging Face / ModelScope URL。",
|
||||
"noModelFiles": "在此仓库中未找到模型文件。"
|
||||
},
|
||||
|
||||
@@ -1723,6 +1723,7 @@
|
||||
"invalidUrl": "模型網址格式無效",
|
||||
"noVersions": "此模型無可用版本",
|
||||
"linkedFileNotFound": "[TODO: Translate] File {fileId} was not found in the requested model version.",
|
||||
"linkedFileNotWeightFile": "[TODO: Translate] File {fileId} is not a downloadable model file.",
|
||||
"mixedSources": "無法在同一批次中混合使用 CivitAI 和 Hugging Face / ModelScope URL。",
|
||||
"noModelFiles": "在此倉庫中未找到模型檔案。"
|
||||
},
|
||||
|
||||
@@ -154,7 +154,6 @@ export const MODEL_SPECIFIC_ENDPOINTS = {
|
||||
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]: {
|
||||
|
||||
@@ -553,11 +553,14 @@ export class DownloadManager {
|
||||
getLinkedFile(version, fileId) {
|
||||
if (!fileId) return null;
|
||||
const file = this._getWeightFiles(version).find(f => String(f.id) === fileId);
|
||||
if (!file) {
|
||||
throw new Error(translate('modals.download.errors.linkedFileNotFound', { fileId },
|
||||
`File ${fileId} was not found in the requested model version.`));
|
||||
if (file) return file;
|
||||
const existsAsNonWeightFile = (version?.files || []).some(f => String(f.id) === fileId);
|
||||
if (existsAsNonWeightFile) {
|
||||
throw new Error(translate('modals.download.errors.linkedFileNotWeightFile', { fileId },
|
||||
`File ${fileId} is not a downloadable model file.`));
|
||||
}
|
||||
return file;
|
||||
throw new Error(translate('modals.download.errors.linkedFileNotFound', { fileId },
|
||||
`File ${fileId} was not found in the requested model version.`));
|
||||
}
|
||||
|
||||
static parseModelUrl(url) {
|
||||
|
||||
@@ -20,9 +20,11 @@ export const OptimizationMode = {
|
||||
|
||||
export const DEFAULT_CIVITAI_PAGE_HOST = 'civitai.com';
|
||||
|
||||
// Mirrors py/utils/civitai_utils.py (_SUPPORTED_CIVITAI_PAGE_HOSTS).
|
||||
const SUPPORTED_CIVITAI_PAGE_HOSTS = new Set([
|
||||
'civitai.com',
|
||||
'civitai.red',
|
||||
'civitai.green',
|
||||
]);
|
||||
|
||||
export function normalizeCivitaiPageHost(hostname) {
|
||||
@@ -218,7 +220,7 @@ export function isSupportedCivitaiPageHost(hostname) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SUPPORTED_CIVITAI_PAGE_HOSTS.has(hostname.toLowerCase());
|
||||
return SUPPORTED_CIVITAI_PAGE_HOSTS.has(hostname.toLowerCase().replace(/^www\./, ''));
|
||||
}
|
||||
|
||||
export function extractCivitaiModelUrlParts(url) {
|
||||
|
||||
@@ -330,4 +330,25 @@ describe('DownloadManager Civitai download URLs', () => {
|
||||
expect(mockApiClient.fetchCivitaiModelByVersion).not.toHaveBeenCalled();
|
||||
expect(mockApiClient.fetchCivitaiVersions).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
'https://www.civitai.com/api/download/models/3409322?fileId=3301284',
|
||||
'https://civitai.red/api/download/models/3409322?fileId=3301284',
|
||||
'https://civitai.green/api/download/models/3409322?fileId=3301284',
|
||||
])('resolves download URLs on mirror and www-prefixed hosts: %s', async (mirrorUrl) => {
|
||||
document.getElementById('modelUrl').value = mirrorUrl;
|
||||
await manager.validateAndFetchVersions();
|
||||
expect(mockApiClient.fetchCivitaiModelByVersion).toHaveBeenCalledWith('3409322');
|
||||
expect(manager.currentVersion.id).toBe(3409322);
|
||||
expect(manager.selectedFile.id).toBe(3301284);
|
||||
});
|
||||
|
||||
it('reports a linked fileId that exists but is not a model weight file', async () => {
|
||||
requestedVersion.files.push({ id: 555, type: 'Training Data', name: 'dataset.zip' });
|
||||
document.getElementById('modelUrl').value = url.replace('3301284', '555');
|
||||
await manager.validateAndFetchVersions();
|
||||
expect(document.getElementById('urlError').textContent).toBe(
|
||||
'File 555 is not a downloadable model file.');
|
||||
expect(manager.selectedFile).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -337,14 +337,20 @@ describe('civitaiUtils', () => {
|
||||
});
|
||||
|
||||
describe('isSupportedCivitaiPageHost', () => {
|
||||
it('accepts civitai.com and civitai.red page hosts', () => {
|
||||
it('accepts civitai.com, civitai.red and civitai.green page hosts', () => {
|
||||
expect(isSupportedCivitaiPageHost('civitai.com')).toBe(true);
|
||||
expect(isSupportedCivitaiPageHost('civitai.red')).toBe(true);
|
||||
expect(isSupportedCivitaiPageHost('civitai.green')).toBe(true);
|
||||
});
|
||||
|
||||
it('accepts www-prefixed page hosts', () => {
|
||||
expect(isSupportedCivitaiPageHost('www.civitai.com')).toBe(true);
|
||||
expect(isSupportedCivitaiPageHost('www.civitai.red')).toBe(true);
|
||||
expect(isSupportedCivitaiPageHost('www.civitai.green')).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects unrelated hosts', () => {
|
||||
expect(isSupportedCivitaiPageHost('www.civitai.com')).toBe(false);
|
||||
expect(isSupportedCivitaiPageHost('www.civitai.red')).toBe(false);
|
||||
expect(isSupportedCivitaiPageHost('civitai.com.evil.test')).toBe(false);
|
||||
expect(isSupportedCivitaiPageHost('example.com')).toBe(false);
|
||||
expect(isSupportedCivitaiPageHost('')).toBe(false);
|
||||
expect(isSupportedCivitaiPageHost(null)).toBe(false);
|
||||
|
||||
Reference in New Issue
Block a user