mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 07:05:43 -03:00
feat: enhance A1111MetadataParser to improve metadata extraction and parsing logic. https://github.com/willmiao/ComfyUI-Lora-Manager/issues/148
This commit is contained in:
@@ -452,7 +452,7 @@ class A1111MetadataParser(RecipeMetadataParser):
|
|||||||
|
|
||||||
METADATA_MARKER = r'Lora hashes:'
|
METADATA_MARKER = r'Lora hashes:'
|
||||||
LORA_PATTERN = r'<lora:([^:]+):([^>]+)>'
|
LORA_PATTERN = r'<lora:([^:]+):([^>]+)>'
|
||||||
LORA_HASH_PATTERN = r'([^:]+): ([a-f0-9]+)'
|
LORA_HASH_PATTERN = r'([^:]+):\s*([a-fA-F0-9]+)'
|
||||||
|
|
||||||
def is_metadata_matching(self, user_comment: str) -> bool:
|
def is_metadata_matching(self, user_comment: str) -> bool:
|
||||||
"""Check if the user comment matches the A1111 metadata format"""
|
"""Check if the user comment matches the A1111 metadata format"""
|
||||||
@@ -461,51 +461,103 @@ class A1111MetadataParser(RecipeMetadataParser):
|
|||||||
async def parse_metadata(self, user_comment: str, recipe_scanner=None, civitai_client=None) -> Dict[str, Any]:
|
async def parse_metadata(self, user_comment: str, recipe_scanner=None, civitai_client=None) -> Dict[str, Any]:
|
||||||
"""Parse metadata from images with A1111 metadata format"""
|
"""Parse metadata from images with A1111 metadata format"""
|
||||||
try:
|
try:
|
||||||
# Extract prompt and negative prompt
|
# Initialize metadata with default empty values
|
||||||
parts = user_comment.split('Negative prompt:', 1)
|
metadata = {"prompt": "", "loras": []}
|
||||||
prompt = parts[0].strip()
|
|
||||||
|
|
||||||
# Initialize metadata
|
# Check if the user_comment contains prompt and negative prompt
|
||||||
metadata = {"prompt": prompt, "loras": []}
|
if 'Negative prompt:' in user_comment:
|
||||||
|
# Extract prompt and negative prompt
|
||||||
# Extract negative prompt and parameters
|
parts = user_comment.split('Negative prompt:', 1)
|
||||||
if len(parts) > 1:
|
metadata["prompt"] = parts[0].strip()
|
||||||
negative_and_params = parts[1]
|
|
||||||
|
|
||||||
# Extract negative prompt
|
# Extract negative prompt and parameters
|
||||||
if "Steps:" in negative_and_params:
|
if len(parts) > 1:
|
||||||
neg_prompt = negative_and_params.split("Steps:", 1)[0].strip()
|
negative_and_params = parts[1]
|
||||||
metadata["negative_prompt"] = neg_prompt
|
|
||||||
|
# Extract negative prompt
|
||||||
# Extract key-value parameters (Steps, Sampler, CFG scale, etc.)
|
param_start = re.search(r'([A-Za-z ]+):', negative_and_params)
|
||||||
param_pattern = r'([A-Za-z ]+): ([^,]+)'
|
if param_start:
|
||||||
params = re.findall(param_pattern, negative_and_params)
|
neg_prompt = negative_and_params[:param_start.start()].strip()
|
||||||
for key, value in params:
|
metadata["negative_prompt"] = neg_prompt
|
||||||
clean_key = key.strip().lower().replace(' ', '_')
|
params_section = negative_and_params[param_start.start():]
|
||||||
metadata[clean_key] = value.strip()
|
else:
|
||||||
|
params_section = negative_and_params
|
||||||
|
|
||||||
|
# Extract parameters from this section
|
||||||
|
self._extract_parameters(params_section, metadata)
|
||||||
|
else:
|
||||||
|
# No prompt/negative prompt - extract parameters directly
|
||||||
|
self._extract_parameters(user_comment, metadata)
|
||||||
|
|
||||||
# Extract LoRA information from prompt
|
# Extract LoRA information from prompt if available
|
||||||
lora_weights = {}
|
lora_weights = {}
|
||||||
lora_matches = re.findall(self.LORA_PATTERN, prompt)
|
if metadata["prompt"]:
|
||||||
for lora_name, weights in lora_matches:
|
lora_matches = re.findall(self.LORA_PATTERN, metadata["prompt"])
|
||||||
# Take only the first strength value (before the colon)
|
for lora_name, weights in lora_matches:
|
||||||
weight = weights.split(':')[0]
|
# Take only the first strength value (before the colon)
|
||||||
lora_weights[lora_name.strip()] = float(weight.strip())
|
weight = weights.split(':')[0]
|
||||||
|
lora_weights[lora_name.strip()] = float(weight.strip())
|
||||||
# Remove LoRA patterns from prompt
|
|
||||||
metadata["prompt"] = re.sub(self.LORA_PATTERN, '', prompt).strip()
|
# Remove LoRA patterns from prompt
|
||||||
|
metadata["prompt"] = re.sub(self.LORA_PATTERN, '', metadata["prompt"]).strip()
|
||||||
|
|
||||||
# Extract LoRA hashes
|
# Extract LoRA hashes
|
||||||
lora_hashes = {}
|
lora_hashes = {}
|
||||||
if 'Lora hashes:' in user_comment:
|
if 'Lora hashes:' in user_comment:
|
||||||
|
# Get the LoRA hashes section
|
||||||
lora_hash_section = user_comment.split('Lora hashes:', 1)[1].strip()
|
lora_hash_section = user_comment.split('Lora hashes:', 1)[1].strip()
|
||||||
|
|
||||||
|
# Handle various format possibilities
|
||||||
if lora_hash_section.startswith('"'):
|
if lora_hash_section.startswith('"'):
|
||||||
lora_hash_section = lora_hash_section[1:].split('"', 1)[0]
|
# Extract content within quotes
|
||||||
hash_matches = re.findall(self.LORA_HASH_PATTERN, lora_hash_section)
|
quote_match = re.match(r'"([^"]+)"', lora_hash_section)
|
||||||
for lora_name, hash_value in hash_matches:
|
if quote_match:
|
||||||
# Remove any leading comma and space from lora name
|
lora_hash_section = quote_match.group(1)
|
||||||
clean_name = lora_name.strip().lstrip(',').strip()
|
|
||||||
lora_hashes[clean_name] = hash_value.strip()
|
# Split by commas and parse each LoRA entry
|
||||||
|
lora_entries = []
|
||||||
|
current_entry = ""
|
||||||
|
for part in lora_hash_section.split(','):
|
||||||
|
# Check if this part contains a colon (indicating a complete entry)
|
||||||
|
if ':' in part:
|
||||||
|
if current_entry:
|
||||||
|
lora_entries.append(current_entry.strip())
|
||||||
|
current_entry = part.strip()
|
||||||
|
else:
|
||||||
|
# This is probably a continuation of the previous entry
|
||||||
|
current_entry += ',' + part
|
||||||
|
|
||||||
|
# Add the last entry if it exists
|
||||||
|
if current_entry:
|
||||||
|
lora_entries.append(current_entry.strip())
|
||||||
|
|
||||||
|
# Process each entry
|
||||||
|
for entry in lora_entries:
|
||||||
|
# Split at the colon to get name and hash
|
||||||
|
if ':' in entry:
|
||||||
|
lora_name, hash_value = entry.split(':', 1)
|
||||||
|
# Clean the values
|
||||||
|
lora_name = lora_name.strip()
|
||||||
|
hash_value = hash_value.strip()
|
||||||
|
# Store in our dictionary
|
||||||
|
lora_hashes[lora_name] = hash_value
|
||||||
|
|
||||||
|
# Alternative backup method using regex if the above parsing fails
|
||||||
|
if not lora_hashes:
|
||||||
|
if 'Lora hashes:' in user_comment:
|
||||||
|
lora_hash_section = user_comment.split('Lora hashes:', 1)[1].strip()
|
||||||
|
if lora_hash_section.startswith('"'):
|
||||||
|
# Extract content within quotes if present
|
||||||
|
quote_match = re.match(r'"([^"]+)"', lora_hash_section)
|
||||||
|
if quote_match:
|
||||||
|
lora_hash_section = quote_match.group(1)
|
||||||
|
|
||||||
|
# Use regex to find all name:hash pairs
|
||||||
|
hash_matches = re.findall(self.LORA_HASH_PATTERN, lora_hash_section)
|
||||||
|
for lora_name, hash_value in hash_matches:
|
||||||
|
# Clean up name by removing any leading comma and spaces
|
||||||
|
clean_name = lora_name.strip().lstrip(',').strip()
|
||||||
|
lora_hashes[clean_name] = hash_value.strip()
|
||||||
|
|
||||||
# Process LoRAs and collect base models
|
# Process LoRAs and collect base models
|
||||||
base_model_counts = {}
|
base_model_counts = {}
|
||||||
@@ -523,7 +575,7 @@ class A1111MetadataParser(RecipeMetadataParser):
|
|||||||
'existsLocally': False,
|
'existsLocally': False,
|
||||||
'localPath': None,
|
'localPath': None,
|
||||||
'file_name': lora_name,
|
'file_name': lora_name,
|
||||||
'hash': hash_value,
|
'hash': hash_value.lower(), # Ensure hash is lowercase
|
||||||
'thumbnailUrl': '/loras_static/images/no-preview.png',
|
'thumbnailUrl': '/loras_static/images/no-preview.png',
|
||||||
'baseModel': '',
|
'baseModel': '',
|
||||||
'size': 0,
|
'size': 0,
|
||||||
@@ -573,6 +625,15 @@ class A1111MetadataParser(RecipeMetadataParser):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error parsing A1111 metadata: {e}", exc_info=True)
|
logger.error(f"Error parsing A1111 metadata: {e}", exc_info=True)
|
||||||
return {"error": str(e), "loras": []}
|
return {"error": str(e), "loras": []}
|
||||||
|
|
||||||
|
def _extract_parameters(self, text: str, metadata: Dict[str, Any]) -> None:
|
||||||
|
"""Extract parameters from text section and populate metadata dict"""
|
||||||
|
# Extract key-value parameters (Steps, Sampler, CFG scale, etc.)
|
||||||
|
param_pattern = r'([A-Za-z][A-Za-z0-9 _]+): ([^,]+)(?:,|$)'
|
||||||
|
params = re.findall(param_pattern, text)
|
||||||
|
for key, value in params:
|
||||||
|
clean_key = key.strip().lower().replace(' ', '_')
|
||||||
|
metadata[clean_key] = value.strip()
|
||||||
|
|
||||||
|
|
||||||
class ComfyMetadataParser(RecipeMetadataParser):
|
class ComfyMetadataParser(RecipeMetadataParser):
|
||||||
|
|||||||
Reference in New Issue
Block a user