mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-28 00:18:52 -03:00
- Implemented the base class `RecipeMetadataParser` for parsing recipe metadata from user comments. - Created a factory class `RecipeParserFactory` to instantiate appropriate parser based on user comment content. - Developed multiple parser classes: `ComfyMetadataParser`, `AutomaticMetadataParser`, `MetaFormatParser`, and `RecipeFormatParser` to handle different metadata formats. - Introduced constants for generation parameters and valid LoRA types. - Enhanced error handling and logging throughout the parsing process. - Added functionality to populate LoRA and checkpoint information from Civitai API responses. - Structured the output of parsed metadata to include prompts, LoRAs, generation parameters, and model information.
115 lines
5.6 KiB
Python
115 lines
5.6 KiB
Python
"""Parser for dedicated recipe metadata format."""
|
|
|
|
import re
|
|
import json
|
|
import logging
|
|
from typing import Dict, Any
|
|
from py.config import config
|
|
from ..base import RecipeMetadataParser
|
|
from ..constants import GEN_PARAM_KEYS
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class RecipeFormatParser(RecipeMetadataParser):
|
|
"""Parser for images with dedicated recipe metadata format"""
|
|
|
|
# Regular expression pattern for extracting recipe metadata
|
|
METADATA_MARKER = r'Recipe metadata: (\{.*\})'
|
|
|
|
def is_metadata_matching(self, user_comment: str) -> bool:
|
|
"""Check if the user comment matches the metadata format"""
|
|
return re.search(self.METADATA_MARKER, user_comment, re.IGNORECASE | re.DOTALL) is not None
|
|
|
|
async def parse_metadata(self, user_comment: str, recipe_scanner=None, civitai_client=None) -> Dict[str, Any]:
|
|
"""Parse metadata from images with dedicated recipe metadata format"""
|
|
try:
|
|
# Extract recipe metadata from user comment
|
|
try:
|
|
# Look for recipe metadata section
|
|
recipe_match = re.search(self.METADATA_MARKER, user_comment, re.IGNORECASE | re.DOTALL)
|
|
if not recipe_match:
|
|
recipe_metadata = None
|
|
else:
|
|
recipe_json = recipe_match.group(1)
|
|
recipe_metadata = json.loads(recipe_json)
|
|
except Exception as e:
|
|
logger.error(f"Error extracting recipe metadata: {e}")
|
|
recipe_metadata = None
|
|
if not recipe_metadata:
|
|
return {"error": "No recipe metadata found", "loras": []}
|
|
|
|
# Process the recipe metadata
|
|
loras = []
|
|
for lora in recipe_metadata.get('loras', []):
|
|
# Convert recipe lora format to frontend format
|
|
lora_entry = {
|
|
'id': lora.get('modelVersionId', ''),
|
|
'name': lora.get('modelName', ''),
|
|
'version': lora.get('modelVersionName', ''),
|
|
'type': 'lora',
|
|
'weight': lora.get('strength', 1.0),
|
|
'file_name': lora.get('file_name', ''),
|
|
'hash': lora.get('hash', '')
|
|
}
|
|
|
|
# Check if this LoRA exists locally by SHA256 hash
|
|
if lora.get('hash') and recipe_scanner:
|
|
lora_scanner = recipe_scanner._lora_scanner
|
|
exists_locally = lora_scanner.has_lora_hash(lora['hash'])
|
|
if exists_locally:
|
|
lora_cache = await lora_scanner.get_cached_data()
|
|
lora_item = next((item for item in lora_cache.raw_data if item['sha256'].lower() == lora['hash'].lower()), None)
|
|
if lora_item:
|
|
lora_entry['existsLocally'] = True
|
|
lora_entry['localPath'] = lora_item['file_path']
|
|
lora_entry['file_name'] = lora_item['file_name']
|
|
lora_entry['size'] = lora_item['size']
|
|
lora_entry['thumbnailUrl'] = config.get_preview_static_url(lora_item['preview_url'])
|
|
|
|
else:
|
|
lora_entry['existsLocally'] = False
|
|
lora_entry['localPath'] = None
|
|
|
|
# Try to get additional info from Civitai if we have a model version ID
|
|
if lora.get('modelVersionId') and civitai_client:
|
|
try:
|
|
civitai_info_tuple = await civitai_client.get_model_version_info(lora['modelVersionId'])
|
|
# Populate lora entry with Civitai info
|
|
populated_entry = await self.populate_lora_from_civitai(
|
|
lora_entry,
|
|
civitai_info_tuple,
|
|
recipe_scanner,
|
|
None, # No need to track base model counts
|
|
lora['hash']
|
|
)
|
|
if populated_entry is None:
|
|
continue # Skip invalid LoRA types
|
|
lora_entry = populated_entry
|
|
except Exception as e:
|
|
logger.error(f"Error fetching Civitai info for LoRA: {e}")
|
|
lora_entry['thumbnailUrl'] = '/loras_static/images/no-preview.png'
|
|
|
|
loras.append(lora_entry)
|
|
|
|
logger.info(f"Found {len(loras)} loras in recipe metadata")
|
|
|
|
# Filter gen_params to only include recognized keys
|
|
filtered_gen_params = {}
|
|
if 'gen_params' in recipe_metadata:
|
|
for key, value in recipe_metadata['gen_params'].items():
|
|
if key in GEN_PARAM_KEYS:
|
|
filtered_gen_params[key] = value
|
|
|
|
return {
|
|
'base_model': recipe_metadata.get('base_model', ''),
|
|
'loras': loras,
|
|
'gen_params': filtered_gen_params,
|
|
'tags': recipe_metadata.get('tags', []),
|
|
'title': recipe_metadata.get('title', ''),
|
|
'from_recipe_metadata': True
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error parsing recipe format metadata: {e}", exc_info=True)
|
|
return {"error": str(e), "loras": []}
|