diff --git a/py/routes/handlers/model_handlers.py b/py/routes/handlers/model_handlers.py
index 268657e8..8f91b5de 100644
--- a/py/routes/handlers/model_handlers.py
+++ b/py/routes/handlers/model_handlers.py
@@ -1532,13 +1532,20 @@ class ModelCivitaiHandler:
cache = await self._service.scanner.get_cached_data()
version_index = cache.version_index
- history_service = await ServiceRegistry.get_downloaded_version_history_service()
- downloaded_version_ids = set(
- await history_service.get_downloaded_version_ids(
- self._service.model_type,
- model_id,
+ downloaded_version_ids: set[int] = set()
+ try:
+ history_service = await ServiceRegistry.get_downloaded_version_history_service()
+ downloaded_version_ids = set(
+ await history_service.get_downloaded_version_ids(
+ self._service.model_type,
+ model_id,
+ )
+ )
+ except Exception as exc: # pragma: no cover - defensive logging
+ self._logger.debug(
+ "Failed to load download history for CivitAI versions: %s",
+ exc,
)
- )
for version in versions:
version_id = None
diff --git a/static/css/components/shared.css b/static/css/components/shared.css
index 0f6f69fa..5baf996f 100644
--- a/static/css/components/shared.css
+++ b/static/css/components/shared.css
@@ -33,7 +33,6 @@
font-weight: 500;
white-space: nowrap;
flex-shrink: 0;
- position: relative;
transform: translateZ(0);
will-change: transform;
}
@@ -43,34 +42,6 @@
font-size: 0.9em;
}
-.downloaded-info {
- display: none;
- position: absolute;
- top: 100%;
- right: 0;
- background: var(--card-bg);
- border: 1px solid color-mix(in oklch, var(--badge-update-bg, #4a90e2) 50%, transparent);
- border-radius: var(--border-radius-xs);
- padding: var(--space-1);
- margin-top: 4px;
- font-size: 0.9em;
- color: var(--text-color);
- white-space: normal;
- word-break: break-word;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- z-index: 100;
- min-width: 240px;
- max-width: 320px;
- transform: translateZ(0);
- position: fixed;
- pointer-events: none;
-}
-
-.downloaded-badge:hover .downloaded-info {
- display: block;
- pointer-events: auto;
-}
-
/* Early Access Badge */
.early-access-badge {
display: inline-flex;
diff --git a/static/js/managers/DownloadManager.js b/static/js/managers/DownloadManager.js
index 9d442d71..2d52c95d 100644
--- a/static/js/managers/DownloadManager.js
+++ b/static/js/managers/DownloadManager.js
@@ -270,13 +270,13 @@ export class DownloadManager {
+ const downloadedTooltip = translate(
+ 'modals.download.downloadedTooltip',
+ {},
+ 'Previously downloaded, but it is not currently in your library.'
+ );
+ localStatus = `
${translate('modals.download.downloaded', {}, 'Downloaded')}
-
${translate(
- 'modals.download.downloadedTooltip',
- {},
- 'Previously downloaded, but it is not currently in your library.'
- )}
`;
}
diff --git a/tests/frontend/managers/downloadManager.history.test.js b/tests/frontend/managers/downloadManager.history.test.js
index a0329dab..f34e03ae 100644
--- a/tests/frontend/managers/downloadManager.history.test.js
+++ b/tests/frontend/managers/downloadManager.history.test.js
@@ -127,7 +127,7 @@ describe('DownloadManager version history badges', () => {
expect(items).toHaveLength(2);
expect(items[0].querySelector('.downloaded-badge')?.textContent).toContain('Downloaded');
- expect(items[0].querySelector('.downloaded-info')?.textContent).toContain(
+ expect(items[0].querySelector('.downloaded-badge')?.getAttribute('title')).toContain(
'Previously downloaded, but it is not currently in your library.'
);
expect(items[0].querySelector('.local-badge')).toBeNull();
diff --git a/tests/routes/test_model_update_handler.py b/tests/routes/test_model_update_handler.py
index 4dce7279..b347a14c 100644
--- a/tests/routes/test_model_update_handler.py
+++ b/tests/routes/test_model_update_handler.py
@@ -6,7 +6,7 @@ from types import SimpleNamespace
import pytest
from py.config import config
-from py.routes.handlers.model_handlers import ModelUpdateHandler
+from py.routes.handlers.model_handlers import ModelCivitaiHandler, ModelUpdateHandler
from py.services.service_registry import ServiceRegistry
from py.utils.metadata_manager import MetadataManager
from py.services.model_update_service import ModelUpdateRecord, ModelVersionRecord
@@ -163,6 +163,62 @@ async def test_build_version_context_includes_download_history(monkeypatch):
assert overrides[124]["has_been_downloaded"] is False
+@pytest.mark.asyncio
+async def test_get_civitai_versions_degrades_when_download_history_unavailable(monkeypatch):
+ cache = SimpleNamespace(version_index={})
+ service = DummyService(cache)
+
+ class DummyProvider:
+ async def get_model_versions(self, model_id):
+ assert model_id == "42"
+ return {
+ "type": "lora",
+ "modelVersions": [
+ {
+ "id": 7,
+ "name": "Version 7",
+ "files": [],
+ }
+ ],
+ }
+
+ async def fake_history_service_factory():
+ raise RuntimeError("download history unavailable")
+
+ monkeypatch.setattr(
+ ServiceRegistry,
+ "get_downloaded_version_history_service",
+ staticmethod(fake_history_service_factory),
+ )
+
+ async def metadata_provider_factory():
+ return DummyProvider()
+
+ handler = ModelCivitaiHandler(
+ service=service,
+ settings_service=SimpleNamespace(get=lambda *_: False),
+ ws_manager=SimpleNamespace(),
+ logger=logging.getLogger(__name__),
+ metadata_provider_factory=metadata_provider_factory,
+ validate_model_type=lambda *_: True,
+ expected_model_types=lambda: "LoRA",
+ find_model_file=lambda *_: None,
+ metadata_sync=SimpleNamespace(),
+ metadata_refresh_use_case=SimpleNamespace(),
+ metadata_progress_callback=lambda *_args, **_kwargs: None,
+ )
+
+ response = await handler.get_civitai_versions(
+ SimpleNamespace(match_info={"model_id": "42"})
+ )
+ payload = json.loads(response.text)
+
+ assert response.status == 200
+ assert payload[0]["id"] == 7
+ assert payload[0]["existsLocally"] is False
+ assert payload[0]["hasBeenDownloaded"] is False
+
+
@pytest.mark.asyncio
async def test_refresh_model_updates_filters_records_without_updates():
cache = SimpleNamespace(version_index={})