diff --git a/py/routes/api_routes.py b/py/routes/api_routes.py index 3846eabb..1d255141 100644 --- a/py/routes/api_routes.py +++ b/py/routes/api_routes.py @@ -51,6 +51,7 @@ class ApiRoutes: app.router.add_post('/api/move_models_bulk', routes.move_models_bulk) app.router.add_get('/api/loras/top-tags', routes.get_top_tags) # Add new route for top tags app.router.add_get('/api/loras/base-models', routes.get_base_models) # Add new route for base models + app.router.add_get('/api/lora-civitai-url', routes.get_lora_civitai_url) # Add new route for Civitai URL # Add update check routes UpdateRoutes.setup_routes(app) @@ -717,6 +718,48 @@ class ApiRoutes: logger.error(f"Error getting lora preview URL: {e}", exc_info=True) return web.Response(text=str(e), status=500) + async def get_lora_civitai_url(self, request: web.Request) -> web.Response: + """Get the Civitai URL for a LoRA file""" + try: + # Get lora file name from query parameters + lora_name = request.query.get('name') + if not lora_name: + return web.Response(text='Lora file name is required', status=400) + + # Get cache data + cache = await self.scanner.get_cached_data() + + # Search for the lora in cache data + for lora in cache.raw_data: + file_name = lora['file_name'] + if file_name == lora_name: + civitai_data = lora.get('civitai', {}) + model_id = civitai_data.get('modelId') + version_id = civitai_data.get('id') + + if model_id: + civitai_url = f"https://civitai.com/models/{model_id}" + if version_id: + civitai_url += f"?modelVersionId={version_id}" + + return web.json_response({ + 'success': True, + 'civitai_url': civitai_url, + 'model_id': model_id, + 'version_id': version_id + }) + break + + # If no Civitai data found + return web.json_response({ + 'success': False, + 'error': 'No Civitai data found for the specified lora' + }, status=404) + + except Exception as e: + logger.error(f"Error getting lora Civitai URL: {e}", exc_info=True) + return web.Response(text=str(e), status=500) + async def move_models_bulk(self, request: web.Request) -> web.Response: """Handle bulk model move request""" try: diff --git a/web/comfyui/loras_widget.js b/web/comfyui/loras_widget.js index 2540826f..d8ea4d29 100644 --- a/web/comfyui/loras_widget.js +++ b/web/comfyui/loras_widget.js @@ -360,6 +360,58 @@ export function addLorasWidget(node, name, opts, callback) { minWidth: '180px', }); + // View on Civitai option with globe icon + const viewOnCivitaiOption = createMenuItem( + 'View on Civitai', + '', + async () => { + menu.remove(); + document.removeEventListener('click', closeMenu); + + try { + // Get Civitai URL from API + const response = await api.fetchApi(`/lora-civitai-url?name=${encodeURIComponent(loraName)}`, { + method: 'GET' + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(errorText || 'Failed to get Civitai URL'); + } + + const data = await response.json(); + if (data.success && data.civitai_url) { + // Open the URL in a new tab + window.open(data.civitai_url, '_blank'); + } else { + // Show error message if no Civitai URL + if (app && app.extensionManager && app.extensionManager.toast) { + app.extensionManager.toast.add({ + severity: 'warning', + summary: 'Not Found', + detail: 'This LoRA has no associated Civitai URL', + life: 3000 + }); + } else { + alert('This LoRA has no associated Civitai URL'); + } + } + } catch (error) { + console.error('Error getting Civitai URL:', error); + if (app && app.extensionManager && app.extensionManager.toast) { + app.extensionManager.toast.add({ + severity: 'error', + summary: 'Error', + detail: error.message || 'Failed to get Civitai URL', + life: 5000 + }); + } else { + alert('Error: ' + (error.message || 'Failed to get Civitai URL')); + } + } + } + ); + // Delete option with trash icon const deleteOption = createMenuItem( 'Delete', @@ -395,6 +447,7 @@ export function addLorasWidget(node, name, opts, callback) { borderTop: '1px solid rgba(255, 255, 255, 0.1)', }); + menu.appendChild(viewOnCivitaiOption); // Add the new menu option menu.appendChild(deleteOption); menu.appendChild(separator); menu.appendChild(saveOption);