- Migrate 11 character JSONs from old wardrobe keys to _BODY_GROUP_KEYS format - Add is_favourite/is_nsfw columns to Preset model - Add HTTP response validation and timeouts to ComfyUI client - Add path traversal protection on replace cover route - Deduplicate services/mcp.py (4 functions → 2 generic + 2 wrappers) - Extract apply_library_filters() and clean_html_text() shared helpers - Add named constants for 17 ComfyUI workflow node IDs - Fix bare except clauses in services/llm.py - Fix tags schema in ensure_default_outfit() (list → dict) - Convert f-string logging to lazy % formatting - Add 5-minute polling timeout to frontend waitForJob() - Improve migration error handling (non-duplicate errors log at WARNING) - Update CLAUDE.md to reflect all changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
77 lines
2.5 KiB
Python
77 lines
2.5 KiB
Python
import random
|
|
|
|
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'}
|
|
|
|
_LORA_DEFAULTS = {
|
|
'characters': '/ImageModels/lora/Illustrious/Looks',
|
|
'outfits': '/ImageModels/lora/Illustrious/Clothing',
|
|
'actions': '/ImageModels/lora/Illustrious/Poses',
|
|
'styles': '/ImageModels/lora/Illustrious/Styles',
|
|
'scenes': '/ImageModels/lora/Illustrious/Backgrounds',
|
|
'detailers': '/ImageModels/lora/Illustrious/Detailers',
|
|
}
|
|
|
|
_BODY_GROUP_KEYS = ['base', 'head', 'upper_body', 'lower_body', 'hands', 'feet', 'additional']
|
|
|
|
|
|
def clean_html_text(html_raw):
|
|
"""Strip HTML tags, scripts, styles, and images from raw HTML, returning plain text."""
|
|
import re
|
|
text = re.sub(r'<script[^>]*>.*?</script>', '', html_raw, flags=re.DOTALL)
|
|
text = re.sub(r'<style[^>]*>.*?</style>', '', text, flags=re.DOTALL)
|
|
text = re.sub(r'<img[^>]*>', '', text)
|
|
text = re.sub(r'<[^>]+>', ' ', text)
|
|
return ' '.join(text.split())
|
|
|
|
|
|
def allowed_file(filename):
|
|
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
|
|
|
|
|
def parse_orientation(orientation_str):
|
|
if not orientation_str: return []
|
|
|
|
m_count = orientation_str.upper().count('M')
|
|
f_count = orientation_str.upper().count('F')
|
|
total = m_count + f_count
|
|
|
|
tags = []
|
|
|
|
# Gender counts
|
|
if m_count == 1: tags.append("1boy")
|
|
elif m_count > 1: tags.append(f"{m_count}boys")
|
|
|
|
if f_count == 1: tags.append("1girl")
|
|
elif f_count > 1: tags.append(f"{f_count}girls")
|
|
|
|
# Relationships/Group type
|
|
if total == 1:
|
|
tags.append("solo")
|
|
elif total > 1:
|
|
if m_count > 0 and f_count > 0:
|
|
tags.append("hetero")
|
|
elif f_count > 1 and m_count == 0:
|
|
tags.append("yuri")
|
|
elif m_count > 1 and f_count == 0:
|
|
tags.append("yaoi")
|
|
|
|
return tags
|
|
|
|
|
|
def _resolve_lora_weight(lora_data, override=None):
|
|
"""Return effective LoRA weight, randomising between min/max when they differ.
|
|
|
|
If *override* is provided it takes absolute precedence (used by the Strengths
|
|
Gallery to pin a specific value for each step).
|
|
"""
|
|
if override is not None:
|
|
return float(override)
|
|
weight = float(lora_data.get('lora_weight', 1.0))
|
|
min_w = lora_data.get('lora_weight_min')
|
|
max_w = lora_data.get('lora_weight_max')
|
|
if min_w is not None and max_w is not None:
|
|
min_w, max_w = float(min_w), float(max_w)
|
|
if min_w != max_w:
|
|
weight = random.uniform(min(min_w, max_w), max(min_w, max_w))
|
|
return weight
|