mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-07-13 20:21:16 -03:00
feat(ui): add cancel button to download progress modal
This commit is contained in:
@@ -1207,7 +1207,9 @@
|
|||||||
"preparing": "Download wird vorbereitet...",
|
"preparing": "Download wird vorbereitet...",
|
||||||
"downloadedPreview": "Vorschaubild heruntergeladen",
|
"downloadedPreview": "Vorschaubild heruntergeladen",
|
||||||
"downloadingFile": "{type}-Datei wird heruntergeladen",
|
"downloadingFile": "{type}-Datei wird heruntergeladen",
|
||||||
"finalizing": "Download wird abgeschlossen..."
|
"finalizing": "Download wird abgeschlossen...",
|
||||||
|
"cancelling": "Download wird abgebrochen...",
|
||||||
|
"cancelled": "Download abgebrochen"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"currentFile": "Aktuelle Datei:",
|
"currentFile": "Aktuelle Datei:",
|
||||||
|
|||||||
@@ -1207,7 +1207,9 @@
|
|||||||
"preparing": "Preparing download...",
|
"preparing": "Preparing download...",
|
||||||
"downloadedPreview": "Downloaded preview image",
|
"downloadedPreview": "Downloaded preview image",
|
||||||
"downloadingFile": "Downloading {type} file",
|
"downloadingFile": "Downloading {type} file",
|
||||||
"finalizing": "Finalizing download..."
|
"finalizing": "Finalizing download...",
|
||||||
|
"cancelling": "Cancelling download...",
|
||||||
|
"cancelled": "Download cancelled"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"currentFile": "Current file:",
|
"currentFile": "Current file:",
|
||||||
|
|||||||
@@ -1207,7 +1207,9 @@
|
|||||||
"preparing": "Preparando descarga...",
|
"preparing": "Preparando descarga...",
|
||||||
"downloadedPreview": "Imagen de vista previa descargada",
|
"downloadedPreview": "Imagen de vista previa descargada",
|
||||||
"downloadingFile": "Descargando archivo de {type}",
|
"downloadingFile": "Descargando archivo de {type}",
|
||||||
"finalizing": "Finalizando descarga..."
|
"finalizing": "Finalizando descarga...",
|
||||||
|
"cancelling": "Cancelando descarga...",
|
||||||
|
"cancelled": "Descarga cancelada"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"currentFile": "Archivo actual:",
|
"currentFile": "Archivo actual:",
|
||||||
|
|||||||
@@ -1207,7 +1207,9 @@
|
|||||||
"preparing": "Préparation du téléchargement...",
|
"preparing": "Préparation du téléchargement...",
|
||||||
"downloadedPreview": "Image d'aperçu téléchargée",
|
"downloadedPreview": "Image d'aperçu téléchargée",
|
||||||
"downloadingFile": "Téléchargement du fichier {type}",
|
"downloadingFile": "Téléchargement du fichier {type}",
|
||||||
"finalizing": "Finalisation du téléchargement..."
|
"finalizing": "Finalisation du téléchargement...",
|
||||||
|
"cancelling": "Annulation du téléchargement...",
|
||||||
|
"cancelled": "Téléchargement annulé"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"currentFile": "Fichier actuel :",
|
"currentFile": "Fichier actuel :",
|
||||||
|
|||||||
@@ -1207,7 +1207,9 @@
|
|||||||
"preparing": "מכין הורדה...",
|
"preparing": "מכין הורדה...",
|
||||||
"downloadedPreview": "תמונת תצוגה מקדימה הורדה",
|
"downloadedPreview": "תמונת תצוגה מקדימה הורדה",
|
||||||
"downloadingFile": "מוריד קובץ {type}",
|
"downloadingFile": "מוריד קובץ {type}",
|
||||||
"finalizing": "מסיים הורדה..."
|
"finalizing": "מסיים הורדה...",
|
||||||
|
"cancelling": "מבטל הורדה...",
|
||||||
|
"cancelled": "ההורדה בוטלה"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"currentFile": "הקובץ הנוכחי:",
|
"currentFile": "הקובץ הנוכחי:",
|
||||||
|
|||||||
@@ -1207,7 +1207,9 @@
|
|||||||
"preparing": "ダウンロードを準備中...",
|
"preparing": "ダウンロードを準備中...",
|
||||||
"downloadedPreview": "プレビュー画像をダウンロードしました",
|
"downloadedPreview": "プレビュー画像をダウンロードしました",
|
||||||
"downloadingFile": "{type}ファイルをダウンロード中",
|
"downloadingFile": "{type}ファイルをダウンロード中",
|
||||||
"finalizing": "ダウンロードを完了中..."
|
"finalizing": "ダウンロードを完了中...",
|
||||||
|
"cancelling": "ダウンロードをキャンセル中...",
|
||||||
|
"cancelled": "ダウンロードをキャンセルしました"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"currentFile": "現在のファイル:",
|
"currentFile": "現在のファイル:",
|
||||||
|
|||||||
@@ -1207,7 +1207,9 @@
|
|||||||
"preparing": "다운로드 준비 중...",
|
"preparing": "다운로드 준비 중...",
|
||||||
"downloadedPreview": "미리보기 이미지 다운로드됨",
|
"downloadedPreview": "미리보기 이미지 다운로드됨",
|
||||||
"downloadingFile": "{type} 파일 다운로드 중",
|
"downloadingFile": "{type} 파일 다운로드 중",
|
||||||
"finalizing": "다운로드 완료 중..."
|
"finalizing": "다운로드 완료 중...",
|
||||||
|
"cancelling": "다운로드 취소 중...",
|
||||||
|
"cancelled": "다운로드가 취소되었습니다"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"currentFile": "현재 파일:",
|
"currentFile": "현재 파일:",
|
||||||
|
|||||||
@@ -1207,7 +1207,9 @@
|
|||||||
"preparing": "Подготовка загрузки...",
|
"preparing": "Подготовка загрузки...",
|
||||||
"downloadedPreview": "Превью изображение загружено",
|
"downloadedPreview": "Превью изображение загружено",
|
||||||
"downloadingFile": "Загрузка файла {type}",
|
"downloadingFile": "Загрузка файла {type}",
|
||||||
"finalizing": "Завершение загрузки..."
|
"finalizing": "Завершение загрузки...",
|
||||||
|
"cancelling": "Отмена загрузки...",
|
||||||
|
"cancelled": "Загрузка отменена"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"currentFile": "Текущий файл:",
|
"currentFile": "Текущий файл:",
|
||||||
|
|||||||
@@ -1207,7 +1207,9 @@
|
|||||||
"preparing": "正在准备下载...",
|
"preparing": "正在准备下载...",
|
||||||
"downloadedPreview": "预览图片已下载",
|
"downloadedPreview": "预览图片已下载",
|
||||||
"downloadingFile": "正在下载 {type} 文件",
|
"downloadingFile": "正在下载 {type} 文件",
|
||||||
"finalizing": "正在完成下载..."
|
"finalizing": "正在完成下载...",
|
||||||
|
"cancelling": "取消下载中...",
|
||||||
|
"cancelled": "下载已取消"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"currentFile": "当前文件:",
|
"currentFile": "当前文件:",
|
||||||
|
|||||||
@@ -1207,7 +1207,9 @@
|
|||||||
"preparing": "準備下載中...",
|
"preparing": "準備下載中...",
|
||||||
"downloadedPreview": "已下載預覽圖片",
|
"downloadedPreview": "已下載預覽圖片",
|
||||||
"downloadingFile": "正在下載 {type} 檔案",
|
"downloadingFile": "正在下載 {type} 檔案",
|
||||||
"finalizing": "完成下載中..."
|
"finalizing": "完成下載中...",
|
||||||
|
"cancelling": "取消下載中...",
|
||||||
|
"cancelled": "下載已取消"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"currentFile": "目前檔案:",
|
"currentFile": "目前檔案:",
|
||||||
|
|||||||
@@ -112,6 +112,18 @@ export class BaseModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async cancelDownload(downloadId) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`${DOWNLOAD_ENDPOINTS.cancelGet}?download_id=${encodeURIComponent(downloadId)}`
|
||||||
|
);
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error cancelling download:', error);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async loadMoreWithVirtualScroll(resetPage = false, updateFolders = false) {
|
async loadMoreWithVirtualScroll(resetPage = false, updateFolders = false) {
|
||||||
const pageState = this.getPageState();
|
const pageState = this.getPageState();
|
||||||
|
|
||||||
|
|||||||
@@ -196,6 +196,17 @@ export class BulkMissingLoraDownloadManager {
|
|||||||
let completedDownloads = 0;
|
let completedDownloads = 0;
|
||||||
let failedDownloads = 0;
|
let failedDownloads = 0;
|
||||||
let currentLoraProgress = 0;
|
let currentLoraProgress = 0;
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
loadingManager.showCancelButton(async () => {
|
||||||
|
if (cancelled) return;
|
||||||
|
cancelled = true;
|
||||||
|
try {
|
||||||
|
await this.loraApiClient.cancelDownload(batchDownloadId);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Cancel request failed:', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Set up WebSocket message handler
|
// Set up WebSocket message handler
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
@@ -207,6 +218,11 @@ export class BulkMissingLoraDownloadManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.status === 'cancelled') {
|
||||||
|
cancelled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Process progress updates
|
// Process progress updates
|
||||||
if (data.status === 'progress' && data.download_id && data.download_id.startsWith(batchDownloadId)) {
|
if (data.status === 'progress' && data.download_id && data.download_id.startsWith(batchDownloadId)) {
|
||||||
currentLoraProgress = data.progress;
|
currentLoraProgress = data.progress;
|
||||||
@@ -249,6 +265,8 @@ export class BulkMissingLoraDownloadManager {
|
|||||||
|
|
||||||
// Download each LoRA sequentially
|
// Download each LoRA sequentially
|
||||||
for (let i = 0; i < lorasToDownload.length; i++) {
|
for (let i = 0; i < lorasToDownload.length; i++) {
|
||||||
|
if (cancelled) break;
|
||||||
|
|
||||||
const lora = lorasToDownload[i];
|
const lora = lorasToDownload[i];
|
||||||
|
|
||||||
currentLoraProgress = 0;
|
currentLoraProgress = 0;
|
||||||
@@ -275,11 +293,13 @@ export class BulkMissingLoraDownloadManager {
|
|||||||
modelId,
|
modelId,
|
||||||
versionId,
|
versionId,
|
||||||
loraRoot,
|
loraRoot,
|
||||||
'', // Empty relative path, use default paths
|
'',
|
||||||
useDefaultPaths,
|
useDefaultPaths,
|
||||||
batchDownloadId
|
batchDownloadId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (cancelled) break;
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
console.error(`Failed to download LoRA ${lora.name || lora.file_name}: ${response.error}`);
|
console.error(`Failed to download LoRA ${lora.name || lora.file_name}: ${response.error}`);
|
||||||
failedDownloads++;
|
failedDownloads++;
|
||||||
@@ -288,8 +308,10 @@ export class BulkMissingLoraDownloadManager {
|
|||||||
updateProgress(100, completedDownloads, '');
|
updateProgress(100, completedDownloads, '');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error downloading LoRA ${lora.name || lora.file_name}:`, error);
|
if (!cancelled) {
|
||||||
failedDownloads++;
|
console.error(`Error downloading LoRA ${lora.name || lora.file_name}:`, error);
|
||||||
|
failedDownloads++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +322,10 @@ export class BulkMissingLoraDownloadManager {
|
|||||||
loadingManager.hide();
|
loadingManager.hide();
|
||||||
|
|
||||||
// Show completion message
|
// Show completion message
|
||||||
if (failedDownloads === 0) {
|
if (cancelled) {
|
||||||
|
showToast('toast.downloads.downloadStopped', {}, 'info',
|
||||||
|
`Download cancelled. ${completedDownloads} item(s) completed.`);
|
||||||
|
} else if (failedDownloads === 0) {
|
||||||
showToast('toast.loras.allDownloadSuccessful', { count: completedDownloads }, 'success');
|
showToast('toast.loras.allDownloadSuccessful', { count: completedDownloads }, 'success');
|
||||||
} else {
|
} else {
|
||||||
showToast('toast.loras.downloadPartialSuccess', {
|
showToast('toast.loras.downloadPartialSuccess', {
|
||||||
|
|||||||
@@ -872,6 +872,7 @@ export class DownloadManager {
|
|||||||
const displayName = versionName || `#${versionId}`;
|
const displayName = versionName || `#${versionId}`;
|
||||||
let ws = null;
|
let ws = null;
|
||||||
let updateProgress = () => { };
|
let updateProgress = () => { };
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.loadingManager.restoreProgressBar();
|
this.loadingManager.restoreProgressBar();
|
||||||
@@ -882,6 +883,16 @@ export class DownloadManager {
|
|||||||
const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
|
const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||||
ws = new WebSocket(`${wsProtocol}${window.location.host}/ws/download-progress?id=${downloadId}`);
|
ws = new WebSocket(`${wsProtocol}${window.location.host}/ws/download-progress?id=${downloadId}`);
|
||||||
|
|
||||||
|
this.loadingManager.showCancelButton(async () => {
|
||||||
|
if (cancelled) return;
|
||||||
|
cancelled = true;
|
||||||
|
try {
|
||||||
|
await this.apiClient.cancelDownload(downloadId);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Cancel request failed:', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ws.onmessage = event => {
|
ws.onmessage = event => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
|
|
||||||
@@ -890,6 +901,12 @@ export class DownloadManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.status === 'cancelled') {
|
||||||
|
cancelled = true;
|
||||||
|
this.loadingManager.setStatus(translate('modals.download.status.cancelled', {}, 'Download cancelled'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (data.status === 'progress' && data.download_id === downloadId) {
|
if (data.status === 'progress' && data.download_id === downloadId) {
|
||||||
const metrics = {
|
const metrics = {
|
||||||
bytesDownloaded: data.bytes_downloaded,
|
bytesDownloaded: data.bytes_downloaded,
|
||||||
@@ -928,6 +945,10 @@ export class DownloadManager {
|
|||||||
fileParams
|
fileParams
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (cancelled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (response?.skipped) {
|
if (response?.skipped) {
|
||||||
this.loadingManager.setStatus(translate('modals.download.status.finalizing'));
|
this.loadingManager.setStatus(translate('modals.download.status.finalizing'));
|
||||||
updateProgress(100, 0, displayName);
|
updateProgress(100, 0, displayName);
|
||||||
@@ -968,8 +989,12 @@ export class DownloadManager {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to download model version:', error);
|
if (cancelled) {
|
||||||
showToast('toast.downloads.downloadError', { message: error?.message }, 'error');
|
console.log('Download cancelled by user:', downloadId);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to download model version:', error);
|
||||||
|
showToast('toast.downloads.downloadError', { message: error?.message }, 'error');
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
@@ -989,16 +1014,33 @@ export class DownloadManager {
|
|||||||
const totalFiles = this.hfSelectedFiles.length;
|
const totalFiles = this.hfSelectedFiles.length;
|
||||||
const updateProgress = this.loadingManager.showDownloadProgress(totalFiles);
|
const updateProgress = this.loadingManager.showDownloadProgress(totalFiles);
|
||||||
|
|
||||||
|
let cancelled = false;
|
||||||
|
let currentDownloadId = null;
|
||||||
|
|
||||||
|
this.loadingManager.showCancelButton(async () => {
|
||||||
|
if (cancelled) return;
|
||||||
|
cancelled = true;
|
||||||
|
if (currentDownloadId) {
|
||||||
|
try {
|
||||||
|
await this.apiClient.cancelDownload(currentDownloadId);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Cancel request failed:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let completedDownloads = 0;
|
let completedDownloads = 0;
|
||||||
for (let i = 0; i < totalFiles; i++) {
|
for (let i = 0; i < totalFiles; i++) {
|
||||||
|
if (cancelled) break;
|
||||||
|
|
||||||
const filename = this.hfSelectedFiles[i];
|
const filename = this.hfSelectedFiles[i];
|
||||||
updateProgress(0, completedDownloads, filename);
|
updateProgress(0, completedDownloads, filename);
|
||||||
this.loadingManager.setStatus(`Downloading ${filename}...`);
|
this.loadingManager.setStatus(`Downloading ${filename}...`);
|
||||||
|
|
||||||
const downloadId = Date.now().toString() + '_' + i;
|
currentDownloadId = Date.now().toString() + '_' + i;
|
||||||
const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
|
const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||||
const ws = new WebSocket(`${wsProtocol}${window.location.host}/ws/download-progress?id=${downloadId}`);
|
const ws = new WebSocket(`${wsProtocol}${window.location.host}/ws/download-progress?id=${currentDownloadId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
@@ -1006,12 +1048,13 @@ export class DownloadManager {
|
|||||||
ws.onerror = reject;
|
ws.onerror = reject;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Capture completed count at WS creation time so progress
|
|
||||||
// updates arriving after completedDownloads increments still
|
|
||||||
// show the correct "N / total" position.
|
|
||||||
const snapshotCompleted = completedDownloads;
|
const snapshotCompleted = completedDownloads;
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
|
if (data.status === 'cancelled') {
|
||||||
|
cancelled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (data.status === 'progress') {
|
if (data.status === 'progress') {
|
||||||
const metrics = {
|
const metrics = {
|
||||||
bytesDownloaded: data.bytes_downloaded,
|
bytesDownloaded: data.bytes_downloaded,
|
||||||
@@ -1029,9 +1072,11 @@ export class DownloadManager {
|
|||||||
modelRoot,
|
modelRoot,
|
||||||
relativePath: targetFolder,
|
relativePath: targetFolder,
|
||||||
useDefaultPaths,
|
useDefaultPaths,
|
||||||
download_id: downloadId,
|
download_id: currentDownloadId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (cancelled) break;
|
||||||
|
|
||||||
if (response?.success) {
|
if (response?.success) {
|
||||||
completedDownloads++;
|
completedDownloads++;
|
||||||
updateProgress(100, completedDownloads, filename);
|
updateProgress(100, completedDownloads, filename);
|
||||||
@@ -1041,13 +1086,19 @@ export class DownloadManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showToast('toast.loras.downloadCompleted', {}, 'success');
|
if (cancelled) {
|
||||||
// Reload page data — model is already in scanner cache via backend
|
showToast('toast.downloads.downloadStopped', {}, 'info',
|
||||||
|
`Download cancelled. ${completedDownloads} item(s) completed.`);
|
||||||
|
} else {
|
||||||
|
showToast('toast.loras.downloadCompleted', {}, 'success');
|
||||||
|
}
|
||||||
await resetAndReload(true);
|
await resetAndReload(true);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to download HF model:', error);
|
if (!cancelled) {
|
||||||
showToast('toast.downloads.downloadError', { message: error?.message }, 'error');
|
console.error('Failed to download HF model:', error);
|
||||||
|
showToast('toast.downloads.downloadError', { message: error?.message }, 'error');
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
this.loadingManager.hide();
|
this.loadingManager.hide();
|
||||||
@@ -1470,11 +1521,27 @@ export class DownloadManager {
|
|||||||
|
|
||||||
let completedDownloads = 0;
|
let completedDownloads = 0;
|
||||||
let failedDownloads = 0;
|
let failedDownloads = 0;
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
loadingManager.showCancelButton(async () => {
|
||||||
|
if (cancelled) return;
|
||||||
|
cancelled = true;
|
||||||
|
try {
|
||||||
|
await this.apiClient.cancelDownload(batchDownloadId);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Cancel request failed:', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
if (data.type === 'download_id') return;
|
if (data.type === 'download_id') return;
|
||||||
|
|
||||||
|
if (data.status === 'cancelled') {
|
||||||
|
cancelled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (data.status === 'progress' && data.download_id?.startsWith(batchDownloadId)) {
|
if (data.status === 'progress' && data.download_id?.startsWith(batchDownloadId)) {
|
||||||
const current = downloadItems[completedDownloads + failedDownloads];
|
const current = downloadItems[completedDownloads + failedDownloads];
|
||||||
const name = current?.selectedVersion?.name || current?.displayName || current?.filename || `#${completedDownloads + failedDownloads + 1}`;
|
const name = current?.selectedVersion?.name || current?.displayName || current?.filename || `#${completedDownloads + failedDownloads + 1}`;
|
||||||
@@ -1493,6 +1560,8 @@ export class DownloadManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (let i = 0; i < downloadItems.length; i++) {
|
for (let i = 0; i < downloadItems.length; i++) {
|
||||||
|
if (cancelled) break;
|
||||||
|
|
||||||
const item = downloadItems[i];
|
const item = downloadItems[i];
|
||||||
const name = item.displayName || item.filename || (item.selectedVersion?.name || `Model #${item.modelId}`);
|
const name = item.displayName || item.filename || (item.selectedVersion?.name || `Model #${item.modelId}`);
|
||||||
const isHf = item.source === 'huggingface';
|
const isHf = item.source === 'huggingface';
|
||||||
@@ -1503,7 +1572,6 @@ export class DownloadManager {
|
|||||||
try {
|
try {
|
||||||
let response;
|
let response;
|
||||||
if (isHf) {
|
if (isHf) {
|
||||||
// Per-file WebSocket for real-time progress
|
|
||||||
const downloadId = Date.now().toString() + '_hf_' + i;
|
const downloadId = Date.now().toString() + '_hf_' + i;
|
||||||
const wsHf = new WebSocket(`${wsProtocol}${window.location.host}/ws/download-progress?id=${downloadId}`);
|
const wsHf = new WebSocket(`${wsProtocol}${window.location.host}/ws/download-progress?id=${downloadId}`);
|
||||||
try {
|
try {
|
||||||
@@ -1548,6 +1616,8 @@ export class DownloadManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cancelled) break;
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
failedDownloads++;
|
failedDownloads++;
|
||||||
} else {
|
} else {
|
||||||
@@ -1555,15 +1625,20 @@ export class DownloadManager {
|
|||||||
updateProgress(100, completedDownloads, '');
|
updateProgress(100, completedDownloads, '');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Failed to download ${name}:`, err);
|
if (!cancelled) {
|
||||||
failedDownloads++;
|
console.error(`Failed to download ${name}:`, err);
|
||||||
|
failedDownloads++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.close();
|
ws.close();
|
||||||
loadingManager.hide();
|
loadingManager.hide();
|
||||||
|
|
||||||
if (failedDownloads === 0) {
|
if (cancelled) {
|
||||||
|
showToast('toast.downloads.downloadStopped', {}, 'info',
|
||||||
|
`Download cancelled. ${completedDownloads} item(s) completed.`);
|
||||||
|
} else if (failedDownloads === 0) {
|
||||||
showToast('toast.loras.allDownloadSuccessful', { count: completedDownloads }, 'success');
|
showToast('toast.loras.allDownloadSuccessful', { count: completedDownloads }, 'success');
|
||||||
} else {
|
} else {
|
||||||
showToast('toast.loras.downloadPartialSuccess', {
|
showToast('toast.loras.downloadPartialSuccess', {
|
||||||
|
|||||||
@@ -281,6 +281,10 @@ export class LoadingManager {
|
|||||||
// Initialize transfer stats with empty data
|
// Initialize transfer stats with empty data
|
||||||
updateTransferStats();
|
updateTransferStats();
|
||||||
|
|
||||||
|
if (this.cancelButton) {
|
||||||
|
this.loadingContent.appendChild(this.cancelButton);
|
||||||
|
}
|
||||||
|
|
||||||
// Return update function
|
// Return update function
|
||||||
return (currentProgress, currentIndex = 0, currentName = '', metrics = {}) => {
|
return (currentProgress, currentIndex = 0, currentName = '', metrics = {}) => {
|
||||||
// Update current item progress
|
// Update current item progress
|
||||||
|
|||||||
@@ -168,6 +168,18 @@ export class DownloadManager {
|
|||||||
let failedDownloads = 0;
|
let failedDownloads = 0;
|
||||||
let accessFailures = 0;
|
let accessFailures = 0;
|
||||||
let currentLoraProgress = 0;
|
let currentLoraProgress = 0;
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
this.importManager.loadingManager.showCancelButton(async () => {
|
||||||
|
if (cancelled) return;
|
||||||
|
cancelled = true;
|
||||||
|
try {
|
||||||
|
const loraClient = getModelApiClient(MODEL_TYPES.LORA);
|
||||||
|
await loraClient.cancelDownload(batchDownloadId);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Cancel request failed:', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Set up progress tracking for current download
|
// Set up progress tracking for current download
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
@@ -179,6 +191,11 @@ export class DownloadManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.status === 'cancelled') {
|
||||||
|
cancelled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Process progress updates for our current active download
|
// Process progress updates for our current active download
|
||||||
if (data.status === 'progress' && data.download_id && data.download_id.startsWith(batchDownloadId)) {
|
if (data.status === 'progress' && data.download_id && data.download_id.startsWith(batchDownloadId)) {
|
||||||
// Update current LoRA progress
|
// Update current LoRA progress
|
||||||
@@ -221,6 +238,8 @@ export class DownloadManager {
|
|||||||
const useDefaultPaths = getStorageItem('use_default_path_loras', false);
|
const useDefaultPaths = getStorageItem('use_default_path_loras', false);
|
||||||
|
|
||||||
for (let i = 0; i < this.importManager.downloadableLoRAs.length; i++) {
|
for (let i = 0; i < this.importManager.downloadableLoRAs.length; i++) {
|
||||||
|
if (cancelled) break;
|
||||||
|
|
||||||
const lora = this.importManager.downloadableLoRAs[i];
|
const lora = this.importManager.downloadableLoRAs[i];
|
||||||
|
|
||||||
// Reset current LoRA progress for new download
|
// Reset current LoRA progress for new download
|
||||||
@@ -241,15 +260,13 @@ export class DownloadManager {
|
|||||||
batchDownloadId
|
batchDownloadId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (cancelled) break;
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
console.error(`Failed to download LoRA ${lora.name}: ${response.error}`);
|
console.error(`Failed to download LoRA ${lora.name}: ${response.error}`);
|
||||||
|
|
||||||
failedDownloads++;
|
failedDownloads++;
|
||||||
// Continue with next download
|
|
||||||
} else {
|
} else {
|
||||||
completedDownloads++;
|
completedDownloads++;
|
||||||
|
|
||||||
// Update progress to show completion of current LoRA
|
|
||||||
updateProgress(100, completedDownloads, '');
|
updateProgress(100, completedDownloads, '');
|
||||||
|
|
||||||
if (completedDownloads + failedDownloads < this.importManager.downloadableLoRAs.length) {
|
if (completedDownloads + failedDownloads < this.importManager.downloadableLoRAs.length) {
|
||||||
@@ -259,9 +276,10 @@ export class DownloadManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (downloadError) {
|
} catch (downloadError) {
|
||||||
console.error(`Error downloading LoRA ${lora.name}:`, downloadError);
|
if (!cancelled) {
|
||||||
failedDownloads++;
|
console.error(`Error downloading LoRA ${lora.name}:`, downloadError);
|
||||||
// Continue with next download
|
failedDownloads++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +287,10 @@ export class DownloadManager {
|
|||||||
ws.close();
|
ws.close();
|
||||||
|
|
||||||
// Show appropriate completion message based on results
|
// Show appropriate completion message based on results
|
||||||
if (failedDownloads === 0) {
|
if (cancelled) {
|
||||||
|
showToast('toast.downloads.downloadStopped', {}, 'info',
|
||||||
|
`Download cancelled. ${completedDownloads} item(s) completed.`);
|
||||||
|
} else if (failedDownloads === 0) {
|
||||||
showToast('toast.loras.allDownloadSuccessful', { count: completedDownloads }, 'success');
|
showToast('toast.loras.allDownloadSuccessful', { count: completedDownloads }, 'success');
|
||||||
} else {
|
} else {
|
||||||
if (accessFailures > 0) {
|
if (accessFailures > 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user