Presets are saved generation recipes that combine all resource types (character, outfit, action, style, scene, detailer, look, checkpoint) with per-field on/off/random toggles. At generation time, entities marked "random" are picked from the DB and fields marked "random" are randomly included or excluded. - Preset model + sync_presets() following existing category pattern - _resolve_preset_entity() / _resolve_preset_fields() helpers - Full route set: index, detail, generate, edit, upload, clone, save_json, create (LLM), rescan - 4 templates: index (gallery), detail (summary + generate), edit (3-way toggle UI), create (LLM form) - example_01.json reference preset + preset_system.txt LLM prompt - Presets nav link in layout.html Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
160 lines
7.1 KiB
Python
160 lines
7.1 KiB
Python
from flask_sqlalchemy import SQLAlchemy
|
|
|
|
db = SQLAlchemy()
|
|
|
|
class Character(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
character_id = db.Column(db.String(100), unique=True, nullable=False)
|
|
slug = db.Column(db.String(100), unique=True, nullable=False)
|
|
filename = db.Column(db.String(255), nullable=True)
|
|
name = db.Column(db.String(100), nullable=False)
|
|
data = db.Column(db.JSON, nullable=False)
|
|
default_fields = db.Column(db.JSON, nullable=True)
|
|
image_path = db.Column(db.String(255), nullable=True)
|
|
active_outfit = db.Column(db.String(100), default='default')
|
|
|
|
def get_active_wardrobe(self):
|
|
"""Get the currently active wardrobe outfit."""
|
|
wardrobe = self.data.get('wardrobe', {})
|
|
# Check if wardrobe is nested (new format) or flat (legacy)
|
|
if 'default' in wardrobe and isinstance(wardrobe.get('default'), dict):
|
|
# New nested format - return active outfit
|
|
return wardrobe.get(self.active_outfit or 'default', wardrobe.get('default', {}))
|
|
else:
|
|
# Legacy flat format - return as-is
|
|
return wardrobe
|
|
|
|
def get_available_outfits(self):
|
|
"""Get list of available outfit names."""
|
|
wardrobe = self.data.get('wardrobe', {})
|
|
if 'default' in wardrobe and isinstance(wardrobe.get('default'), dict):
|
|
return list(wardrobe.keys())
|
|
return ['default']
|
|
|
|
def __repr__(self):
|
|
return f'<Character {self.character_id}>'
|
|
|
|
class Look(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
look_id = db.Column(db.String(100), unique=True, nullable=False)
|
|
slug = db.Column(db.String(100), unique=True, nullable=False)
|
|
filename = db.Column(db.String(255), nullable=True)
|
|
name = db.Column(db.String(100), nullable=False)
|
|
character_id = db.Column(db.String(100), nullable=True) # linked character
|
|
data = db.Column(db.JSON, nullable=False)
|
|
default_fields = db.Column(db.JSON, nullable=True)
|
|
image_path = db.Column(db.String(255), nullable=True)
|
|
|
|
def __repr__(self):
|
|
return f'<Look {self.look_id}>'
|
|
|
|
class Outfit(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
outfit_id = db.Column(db.String(100), unique=True, nullable=False)
|
|
slug = db.Column(db.String(100), unique=True, nullable=False)
|
|
filename = db.Column(db.String(255), nullable=True)
|
|
name = db.Column(db.String(100), nullable=False)
|
|
data = db.Column(db.JSON, nullable=False)
|
|
default_fields = db.Column(db.JSON, nullable=True)
|
|
image_path = db.Column(db.String(255), nullable=True)
|
|
|
|
def __repr__(self):
|
|
return f'<Outfit {self.outfit_id}>'
|
|
|
|
class Action(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
action_id = db.Column(db.String(100), unique=True, nullable=False)
|
|
slug = db.Column(db.String(100), unique=True, nullable=False)
|
|
filename = db.Column(db.String(255), nullable=True)
|
|
name = db.Column(db.String(100), nullable=False)
|
|
data = db.Column(db.JSON, nullable=False)
|
|
default_fields = db.Column(db.JSON, nullable=True)
|
|
image_path = db.Column(db.String(255), nullable=True)
|
|
|
|
def __repr__(self):
|
|
return f'<Action {self.action_id}>'
|
|
|
|
class Style(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
style_id = db.Column(db.String(100), unique=True, nullable=False)
|
|
slug = db.Column(db.String(100), unique=True, nullable=False)
|
|
filename = db.Column(db.String(255), nullable=True)
|
|
name = db.Column(db.String(100), nullable=False)
|
|
data = db.Column(db.JSON, nullable=False)
|
|
default_fields = db.Column(db.JSON, nullable=True)
|
|
image_path = db.Column(db.String(255), nullable=True)
|
|
|
|
def __repr__(self):
|
|
return f'<Style {self.style_id}>'
|
|
|
|
class Scene(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
scene_id = db.Column(db.String(100), unique=True, nullable=False)
|
|
slug = db.Column(db.String(100), unique=True, nullable=False)
|
|
filename = db.Column(db.String(255), nullable=True)
|
|
name = db.Column(db.String(100), nullable=False)
|
|
data = db.Column(db.JSON, nullable=False)
|
|
default_fields = db.Column(db.JSON, nullable=True)
|
|
image_path = db.Column(db.String(255), nullable=True)
|
|
|
|
def __repr__(self):
|
|
return f'<Scene {self.scene_id}>'
|
|
|
|
class Detailer(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
detailer_id = db.Column(db.String(100), unique=True, nullable=False)
|
|
slug = db.Column(db.String(100), unique=True, nullable=False)
|
|
filename = db.Column(db.String(255), nullable=True)
|
|
name = db.Column(db.String(100), nullable=False)
|
|
data = db.Column(db.JSON, nullable=False)
|
|
default_fields = db.Column(db.JSON, nullable=True)
|
|
image_path = db.Column(db.String(255), nullable=True)
|
|
|
|
def __repr__(self):
|
|
return f'<Detailer {self.detailer_id}>'
|
|
|
|
class Checkpoint(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
checkpoint_id = db.Column(db.String(255), unique=True, nullable=False)
|
|
slug = db.Column(db.String(255), unique=True, nullable=False)
|
|
name = db.Column(db.String(255), nullable=False)
|
|
checkpoint_path = db.Column(db.String(255), nullable=False) # e.g. "Illustrious/model.safetensors"
|
|
data = db.Column(db.JSON, nullable=True)
|
|
image_path = db.Column(db.String(255), nullable=True)
|
|
|
|
def __repr__(self):
|
|
return f'<Checkpoint {self.checkpoint_id}>'
|
|
|
|
class Preset(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
preset_id = db.Column(db.String(100), unique=True, nullable=False)
|
|
slug = db.Column(db.String(100), unique=True, nullable=False)
|
|
filename = db.Column(db.String(255), nullable=True)
|
|
name = db.Column(db.String(100), nullable=False)
|
|
data = db.Column(db.JSON, nullable=False)
|
|
image_path = db.Column(db.String(255), nullable=True)
|
|
|
|
def __repr__(self):
|
|
return f'<Preset {self.preset_id}>'
|
|
|
|
|
|
class Settings(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
llm_provider = db.Column(db.String(50), default='openrouter') # 'openrouter', 'ollama', 'lmstudio'
|
|
openrouter_api_key = db.Column(db.String(255), nullable=True)
|
|
openrouter_model = db.Column(db.String(100), default='google/gemini-2.0-flash-001')
|
|
local_base_url = db.Column(db.String(255), nullable=True)
|
|
local_model = db.Column(db.String(100), nullable=True)
|
|
# LoRA directories (absolute paths on disk)
|
|
lora_dir_characters = db.Column(db.String(500), default='/ImageModels/lora/Illustrious/Looks')
|
|
lora_dir_outfits = db.Column(db.String(500), default='/ImageModels/lora/Illustrious/Clothing')
|
|
lora_dir_actions = db.Column(db.String(500), default='/ImageModels/lora/Illustrious/Poses')
|
|
lora_dir_styles = db.Column(db.String(500), default='/ImageModels/lora/Illustrious/Styles')
|
|
lora_dir_scenes = db.Column(db.String(500), default='/ImageModels/lora/Illustrious/Backgrounds')
|
|
lora_dir_detailers = db.Column(db.String(500), default='/ImageModels/lora/Illustrious/Detailers')
|
|
# Checkpoint scan directories (comma-separated list of absolute paths)
|
|
checkpoint_dirs = db.Column(db.String(1000), default='/ImageModels/Stable-diffusion/Illustrious,/ImageModels/Stable-diffusion/Noob')
|
|
|
|
def __repr__(self):
|
|
return '<Settings>'
|