From 1ea468cfc4b43fae260f1175422ceb7768a2d895 Mon Sep 17 00:00:00 2001 From: Will Miao <13051207myq@gmail.com> Date: Tue, 9 Sep 2025 15:24:28 +0800 Subject: [PATCH] feat(metadata): enhance metadata archive management with download progress and status updates --- locales/en.json | 6 +- py/routes/misc_routes.py | 14 +- py/services/metadata_archive_manager.py | 2 +- static/css/components/modal/_base.css | 82 +++++++++- static/js/managers/SettingsManager.js | 149 +++++++++++++++--- .../components/modals/settings_modal.html | 48 +++--- 6 files changed, 241 insertions(+), 60 deletions(-) diff --git a/locales/en.json b/locales/en.json index 52bc8580..343b813a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -300,7 +300,11 @@ "downloadError": "Failed to download metadata archive database", "removeSuccess": "Metadata archive database removed successfully", "removeError": "Failed to remove metadata archive database", - "removeConfirm": "Are you sure you want to remove the metadata archive database? This will delete the local database file and you'll need to download it again to use this feature." + "removeConfirm": "Are you sure you want to remove the metadata archive database? This will delete the local database file and you'll need to download it again to use this feature.", + "preparing": "Preparing download...", + "connecting": "Connecting to download server...", + "completed": "Completed", + "downloadComplete": "Download completed successfully" } }, "loras": { diff --git a/py/routes/misc_routes.py b/py/routes/misc_routes.py index 9a29a24d..118afea6 100644 --- a/py/routes/misc_routes.py +++ b/py/routes/misc_routes.py @@ -711,13 +711,23 @@ class MiscRoutes: try: archive_manager = await get_metadata_archive_manager() + # Get the download_id from query parameters if provided + download_id = request.query.get('download_id') + # Progress callback to send updates via WebSocket def progress_callback(stage, message): - asyncio.create_task(ws_manager.broadcast({ + data = { 'stage': stage, 'message': message, 'type': 'metadata_archive_download' - })) + } + + if download_id: + # Send to specific download WebSocket if download_id is provided + asyncio.create_task(ws_manager.broadcast_download_progress(download_id, data)) + else: + # Fallback to general broadcast + asyncio.create_task(ws_manager.broadcast(data)) # Download and extract in background success = await archive_manager.download_and_extract_database(progress_callback) diff --git a/py/services/metadata_archive_manager.py b/py/services/metadata_archive_manager.py index a1ba9b74..49b22c01 100644 --- a/py/services/metadata_archive_manager.py +++ b/py/services/metadata_archive_manager.py @@ -79,7 +79,7 @@ class MetadataArchiveManager: # Custom progress callback to report download progress async def download_progress(progress): if progress_callback: - progress_callback("download", f"Downloaded {progress:.1f}%") + progress_callback("download", f"Downloading archive... {progress:.1f}%") success, result = await downloader.download_file( url=url, diff --git a/static/css/components/modal/_base.css b/static/css/components/modal/_base.css index 2b1d542d..57d01ac9 100644 --- a/static/css/components/modal/_base.css +++ b/static/css/components/modal/_base.css @@ -208,6 +208,14 @@ body.modal-open { pointer-events: none; } +button:disabled, +.primary-btn:disabled, +.danger-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + pointer-events: none; +} + .restart-required-icon { color: var(--lora-warning); margin-left: 5px; @@ -228,14 +236,76 @@ body.modal-open { background-color: oklch(35% 0.02 256 / 0.98); } -.primary-btn.disabled { - opacity: 0.5; - cursor: not-allowed; +/* Danger button styles */ +.danger-btn { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + background-color: var(--lora-error); + color: white; + border: none; + border-radius: var(--border-radius-sm); + cursor: pointer; + transition: background-color 0.2s; + font-size: 0.95em; } -.primary-btn.disabled { - opacity: 0.5; - cursor: not-allowed; +.danger-btn:hover { + background-color: oklch(from var(--lora-error) l c h / 85%); + color: white; +} + +/* Metadata archive status styles */ +.metadata-archive-status { + background: rgba(0, 0, 0, 0.03); + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: var(--border-radius-sm); + padding: var(--space-2); + margin-bottom: var(--space-2); +} + +[data-theme="dark"] .metadata-archive-status { + background: rgba(255, 255, 255, 0.03); + border: 1px solid var(--lora-border); +} + +.archive-status-item { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + font-size: 0.95em; +} + +.archive-status-item:last-child { + margin-bottom: 0; +} + +.archive-status-label { + font-weight: 500; + color: var(--text-color); + opacity: 0.8; +} + +.archive-status-value { + color: var(--text-color); +} + +.archive-status-value.status-available { + color: var(--lora-success, #10b981); +} + +.archive-status-value.status-unavailable { + color: var(--lora-warning, #f59e0b); +} + +.archive-status-value.status-enabled { + color: var(--lora-success, #10b981); +} + +.archive-status-value.status-disabled { + color: var(--lora-error, #ef4444); } /* Add styles for delete preview image */ diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index 1a15bdb4..ac795903 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -789,6 +789,8 @@ export class SettingsManager { state.global.settings.compactMode = value; } else if (settingKey === 'include_trigger_words') { state.global.settings.includeTriggerWords = value; + } else if (settingKey === 'enable_metadata_archive_db') { + state.global.settings.enable_metadata_archive_db = value; } else { // For any other settings that might be added in the future state.global.settings[settingKey] = value; @@ -799,7 +801,7 @@ export class SettingsManager { try { // For backend settings, make API call - if (['show_only_sfw'].includes(settingKey)) { + if (['show_only_sfw', 'enable_metadata_archive_db'].includes(settingKey)) { const payload = {}; payload[settingKey] = value; @@ -814,6 +816,11 @@ export class SettingsManager { if (!response.ok) { throw new Error('Failed to save setting'); } + + // Refresh metadata archive status when enable setting changes + if (settingKey === 'enable_metadata_archive_db') { + await this.updateMetadataArchiveStatus(); + } } showToast('toast.settings.settingsUpdated', { setting: settingKey.replace(/_/g, ' ') }, 'success'); @@ -872,6 +879,8 @@ export class SettingsManager { state.global.settings.compactMode = (value !== 'default'); } else if (settingKey === 'card_info_display') { state.global.settings.cardInfoDisplay = value; + } else if (settingKey === 'metadata_provider_priority') { + state.global.settings.metadata_provider_priority = value; } else { // For any other settings that might be added in the future state.global.settings[settingKey] = value; @@ -882,7 +891,7 @@ export class SettingsManager { try { // For backend settings, make API call - if (settingKey === 'default_lora_root' || settingKey === 'default_checkpoint_root' || settingKey === 'default_embedding_root' || settingKey === 'download_path_templates') { + if (settingKey === 'default_lora_root' || settingKey === 'default_checkpoint_root' || settingKey === 'default_embedding_root' || settingKey === 'download_path_templates' || settingKey === 'metadata_provider_priority') { const payload = {}; if (settingKey === 'download_path_templates') { payload[settingKey] = state.global.settings.download_path_templates; @@ -903,6 +912,11 @@ export class SettingsManager { } showToast('toast.settings.settingsUpdated', { setting: settingKey.replace(/_/g, ' ') }, 'success'); + + // Refresh metadata archive status when provider priority changes + if (settingKey === 'metadata_provider_priority') { + await this.updateMetadataArchiveStatus(); + } } // Apply frontend settings immediately @@ -960,24 +974,24 @@ export class SettingsManager { const sizeText = status.databaseSize > 0 ? ` (${this.formatFileSize(status.databaseSize)})` : ''; statusContainer.innerHTML = ` -