diff --git a/routes/api_routes.py b/routes/api_routes.py index 781b381c..1b22dc2b 100644 --- a/routes/api_routes.py +++ b/routes/api_routes.py @@ -40,6 +40,7 @@ class ApiRoutes: app.router.add_post('/api/download-lora', routes.download_lora) app.router.add_post('/api/settings', routes.update_settings) app.router.add_post('/api/move_model', routes.move_model) + app.router.add_post('/loras/api/save-metadata', routes.save_metadata) async def delete_model(self, request: web.Request) -> web.Response: """Handle model deletion request""" @@ -177,6 +178,8 @@ class ApiRoutes: "file_path": lora["file_path"].replace(os.sep, "/"), "modified": lora["modified"], "from_civitai": lora.get("from_civitai", True), + "usage_tips": lora.get("usage_tips", ""), + "notes": lora.get("notes", ""), "civitai": self._filter_civitai_data(lora.get("civitai", {})) } @@ -541,3 +544,40 @@ class ApiRoutes: """Add cleanup method for application shutdown""" if hasattr(cls, '_instance'): await cls._instance.civitai_client.close() + + async def save_metadata(self, request: web.Request) -> web.Response: + """Handle saving metadata updates""" + try: + data = await request.json() + file_path = data.get('file_path') + if not file_path: + return web.Response(text='File path is required', status=400) + + # Remove file path from data to avoid saving it + metadata_updates = {k: v for k, v in data.items() if k != 'file_path'} + + # Get metadata file path + metadata_path = os.path.splitext(file_path)[0] + '.metadata.json' + + # Load existing metadata + if os.path.exists(metadata_path): + with open(metadata_path, 'r', encoding='utf-8') as f: + metadata = json.load(f) + else: + metadata = {} + + # Update metadata with new values + metadata.update(metadata_updates) + + # Save updated metadata + with open(metadata_path, 'w', encoding='utf-8') as f: + json.dump(metadata, f, indent=2, ensure_ascii=False) + + # Update cache + await self.scanner.update_single_lora_cache(file_path, file_path, metadata) + + return web.json_response({'success': True}) + + except Exception as e: + logger.error(f"Error saving metadata: {e}", exc_info=True) + return web.Response(text=str(e), status=500) diff --git a/static/js/components/LoraCard.js b/static/js/components/LoraCard.js index c3a17738..c4132bbd 100644 --- a/static/js/components/LoraCard.js +++ b/static/js/components/LoraCard.js @@ -13,6 +13,8 @@ export function createLoraCard(lora) { card.dataset.modified = lora.modified; card.dataset.from_civitai = lora.from_civitai; card.dataset.base_model = lora.base_model; + card.dataset.usage_tips = lora.usage_tips; + card.dataset.notes = lora.notes; card.dataset.meta = JSON.stringify(lora.civitai || {}); const version = state.previewVersions.get(lora.file_path); @@ -61,13 +63,15 @@ export function createLoraCard(lora) { card.addEventListener('click', () => { const loraMeta = { sha256: card.dataset.sha256, - file_path: card.dataset.filepath.replace(/[^/]+$/, ''), // Extract directory path + file_path: card.dataset.filepath, model_name: card.dataset.name, file_name: card.dataset.file_name, folder: card.dataset.folder, modified: card.dataset.modified, from_civitai: card.dataset.from_civitai === 'true', base_model: card.dataset.base_model, + usage_tips: card.dataset.usage_tips, + notes: card.dataset.notes, civitai: JSON.parse(card.dataset.meta || '{}') }; showLoraModal(loraMeta); @@ -105,8 +109,8 @@ export function createLoraCard(lora) { } export function showLoraModal(lora) { - const escapedWords = lora.trainedWords?.length ? - lora.trainedWords.map(word => word.replace(/'/g, '\\\'')) : []; + const escapedWords = lora.civitai?.trainedWords?.length ? + lora.civitai.trainedWords.map(word => word.replace(/'/g, '\\\'')) : []; const content = `
- ${lora.file_path || 'N/A'} + ${lora.file_path.replace(/[^/]+$/, '') || 'N/A'}
@@ -213,7 +217,7 @@ window.saveNotes = async function(filePath) { } }; -async function saveModelMetadata(filePath, data) { +async function saveModelMetadata(filePath, data) { const response = await fetch('/loras/api/save-metadata', { method: 'POST', headers: { diff --git a/utils/file_utils.py b/utils/file_utils.py index 72c192fd..61811c3a 100644 --- a/utils/file_utils.py +++ b/utils/file_utils.py @@ -52,6 +52,8 @@ async def get_file_info(file_path: str) -> LoraMetadata: modified=os.path.getmtime(file_path), sha256=await calculate_sha256(file_path), base_model="Unknown", # Will be updated later + usage_tips="", + notes="", from_civitai=True, preview_url=normalize_path(preview_url), ) diff --git a/utils/models.py b/utils/models.py index 0b4d0c67..22af5f5b 100644 --- a/utils/models.py +++ b/utils/models.py @@ -15,6 +15,8 @@ class LoraMetadata: sha256: str # SHA256 hash of the file base_model: str # Base model (SD1.5/SD2.1/SDXL/etc.) preview_url: str # Preview image URL + usage_tips: str = "" # Usage tips for the model + notes: str = "" # Additional notes from_civitai: bool = True # Whether the lora is from Civitai civitai: Optional[Dict] = None # Civitai API data if available