mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat(example-images): add force parameter to retry failed downloads
When force=true is passed via API, models in failed_models set are re-downloaded instead of being skipped. On successful download, model is removed from failed_models set. This provides a manual batch repair mechanism for users when CivitAI media server is temporarily down and causes empty folders. Changes: - Backend: Add force parameter to start_download(), _download_all_example_images(), _process_model() - Backend: Skip failed_models check when force=true - Backend: Remove model from failed_models on successful force retry - Frontend: GlobalContextMenu now calls API with force=true directly - Tests: Update mock to accept force parameter
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -75,13 +75,6 @@ export class GlobalContextMenu extends BaseContextMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async downloadExampleImages(menuItem) {
|
async downloadExampleImages(menuItem) {
|
||||||
const exampleImagesManager = window.exampleImagesManager;
|
|
||||||
|
|
||||||
if (!exampleImagesManager) {
|
|
||||||
showToast('globalContextMenu.downloadExampleImages.unavailable', {}, 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const downloadPath = state?.global?.settings?.example_images_path;
|
const downloadPath = state?.global?.settings?.example_images_path;
|
||||||
if (!downloadPath) {
|
if (!downloadPath) {
|
||||||
showToast('globalContextMenu.downloadExampleImages.missingPath', {}, 'warning');
|
showToast('globalContextMenu.downloadExampleImages.missingPath', {}, 'warning');
|
||||||
@@ -91,7 +84,48 @@ export class GlobalContextMenu extends BaseContextMenu {
|
|||||||
menuItem?.classList.add('disabled');
|
menuItem?.classList.add('disabled');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await exampleImagesManager.handleDownloadButton();
|
const optimize = state.global.settings.optimize_example_images;
|
||||||
|
|
||||||
|
const response = await fetch('/api/lm/download-example-images', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
force: true,
|
||||||
|
optimize,
|
||||||
|
model_types: ['lora', 'checkpoint', 'embedding']
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
showToast('toast.exampleImages.downloadStarted', {}, 'success');
|
||||||
|
|
||||||
|
const exampleImagesManager = window.exampleImagesManager;
|
||||||
|
if (exampleImagesManager) {
|
||||||
|
exampleImagesManager.isDownloading = true;
|
||||||
|
exampleImagesManager.isPaused = false;
|
||||||
|
exampleImagesManager.isStopping = false;
|
||||||
|
exampleImagesManager.hasShownCompletionToast = false;
|
||||||
|
exampleImagesManager.startTime = new Date();
|
||||||
|
exampleImagesManager.updateUI(data.status);
|
||||||
|
exampleImagesManager.showProgressPanel();
|
||||||
|
exampleImagesManager.startProgressUpdates();
|
||||||
|
exampleImagesManager.updateDownloadButtonText();
|
||||||
|
|
||||||
|
const stopButton = document.getElementById('stopExampleDownloadBtn');
|
||||||
|
if (stopButton) {
|
||||||
|
stopButton.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast('toast.exampleImages.downloadStartFailed', { error: data.error }, 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to trigger example images download:', error);
|
||||||
|
showToast('toast.exampleImages.downloadStartFailed', {}, 'error');
|
||||||
} finally {
|
} finally {
|
||||||
menuItem?.classList.remove('disabled');
|
menuItem?.classList.remove('disabled');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,12 +29,14 @@ def restore_settings() -> None:
|
|||||||
manager.settings.update(original)
|
manager.settings.update(original)
|
||||||
|
|
||||||
|
|
||||||
async def test_start_download_requires_configured_path(monkeypatch: pytest.MonkeyPatch) -> None:
|
async def test_start_download_requires_configured_path(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
manager = download_module.DownloadManager(ws_manager=RecordingWebSocketManager())
|
manager = download_module.DownloadManager(ws_manager=RecordingWebSocketManager())
|
||||||
|
|
||||||
# Ensure example_images_path is not configured
|
# Ensure example_images_path is not configured
|
||||||
settings_manager = get_settings_manager()
|
settings_manager = get_settings_manager()
|
||||||
settings_manager.settings.pop('example_images_path', None)
|
settings_manager.settings.pop("example_images_path", None)
|
||||||
|
|
||||||
with pytest.raises(download_module.DownloadConfigurationError) as exc_info:
|
with pytest.raises(download_module.DownloadConfigurationError) as exc_info:
|
||||||
await manager.start_download({})
|
await manager.start_download({})
|
||||||
@@ -46,7 +48,9 @@ async def test_start_download_requires_configured_path(monkeypatch: pytest.Monke
|
|||||||
assert "skipping auto download" in result["message"]
|
assert "skipping auto download" in result["message"]
|
||||||
|
|
||||||
|
|
||||||
async def test_start_download_bootstraps_progress_and_task(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None:
|
async def test_start_download_bootstraps_progress_and_task(
|
||||||
|
monkeypatch: pytest.MonkeyPatch, tmp_path
|
||||||
|
) -> None:
|
||||||
settings_manager = get_settings_manager()
|
settings_manager = get_settings_manager()
|
||||||
settings_manager.settings["example_images_path"] = str(tmp_path)
|
settings_manager.settings["example_images_path"] = str(tmp_path)
|
||||||
settings_manager.settings["libraries"] = {"default": {}}
|
settings_manager.settings["libraries"] = {"default": {}}
|
||||||
@@ -58,7 +62,9 @@ async def test_start_download_bootstraps_progress_and_task(monkeypatch: pytest.M
|
|||||||
started = asyncio.Event()
|
started = asyncio.Event()
|
||||||
release = asyncio.Event()
|
release = asyncio.Event()
|
||||||
|
|
||||||
async def fake_download(self, output_dir, optimize, model_types, delay, library_name):
|
async def fake_download(
|
||||||
|
self, output_dir, optimize, model_types, delay, library_name, force=False
|
||||||
|
):
|
||||||
started.set()
|
started.set()
|
||||||
await release.wait()
|
await release.wait()
|
||||||
async with self._state_lock:
|
async with self._state_lock:
|
||||||
@@ -129,7 +135,9 @@ async def test_pause_and_resume_flow(monkeypatch: pytest.MonkeyPatch, tmp_path)
|
|||||||
await asyncio.wait_for(task, timeout=1)
|
await asyncio.wait_for(task, timeout=1)
|
||||||
|
|
||||||
|
|
||||||
async def test_stop_download_transitions_to_stopped(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None:
|
async def test_stop_download_transitions_to_stopped(
|
||||||
|
monkeypatch: pytest.MonkeyPatch, tmp_path
|
||||||
|
) -> None:
|
||||||
settings_manager = get_settings_manager()
|
settings_manager = get_settings_manager()
|
||||||
settings_manager.settings["example_images_path"] = str(tmp_path)
|
settings_manager.settings["example_images_path"] = str(tmp_path)
|
||||||
settings_manager.settings["libraries"] = {"default": {}}
|
settings_manager.settings["libraries"] = {"default": {}}
|
||||||
@@ -145,13 +153,13 @@ async def test_stop_download_transitions_to_stopped(monkeypatch: pytest.MonkeyPa
|
|||||||
started.set()
|
started.set()
|
||||||
await release.wait()
|
await release.wait()
|
||||||
async with self._state_lock:
|
async with self._state_lock:
|
||||||
if self._stop_requested and self._progress['status'] == 'stopping':
|
if self._stop_requested and self._progress["status"] == "stopping":
|
||||||
self._progress['status'] = 'stopped'
|
self._progress["status"] = "stopped"
|
||||||
else:
|
else:
|
||||||
self._progress['status'] = 'completed'
|
self._progress["status"] = "completed"
|
||||||
self._progress['end_time'] = time.time()
|
self._progress["end_time"] = time.time()
|
||||||
self._stop_requested = False
|
self._stop_requested = False
|
||||||
await self._broadcast_progress(status=self._progress['status'])
|
await self._broadcast_progress(status=self._progress["status"])
|
||||||
async with self._state_lock:
|
async with self._state_lock:
|
||||||
self._is_downloading = False
|
self._is_downloading = False
|
||||||
self._download_task = None
|
self._download_task = None
|
||||||
@@ -182,7 +190,9 @@ async def test_stop_download_transitions_to_stopped(monkeypatch: pytest.MonkeyPa
|
|||||||
assert "stopped" in statuses
|
assert "stopped" in statuses
|
||||||
|
|
||||||
|
|
||||||
async def test_pause_or_resume_without_running_download(monkeypatch: pytest.MonkeyPatch) -> None:
|
async def test_pause_or_resume_without_running_download(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
manager = download_module.DownloadManager(ws_manager=RecordingWebSocketManager())
|
manager = download_module.DownloadManager(ws_manager=RecordingWebSocketManager())
|
||||||
|
|
||||||
with pytest.raises(download_module.DownloadNotRunningError):
|
with pytest.raises(download_module.DownloadNotRunningError):
|
||||||
|
|||||||
Reference in New Issue
Block a user