import os import logging from flask import Flask from flask_session import Session from models import db, Settings, Look app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['UPLOAD_FOLDER'] = 'static/uploads' app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-key-123') app.config['CHARACTERS_DIR'] = 'data/characters' app.config['CLOTHING_DIR'] = 'data/clothing' app.config['ACTIONS_DIR'] = 'data/actions' app.config['STYLES_DIR'] = 'data/styles' app.config['SCENES_DIR'] = 'data/scenes' app.config['DETAILERS_DIR'] = 'data/detailers' app.config['CHECKPOINTS_DIR'] = 'data/checkpoints' app.config['LOOKS_DIR'] = 'data/looks' app.config['PRESETS_DIR'] = 'data/presets' app.config['COMFYUI_URL'] = os.environ.get('COMFYUI_URL', 'http://127.0.0.1:8188') app.config['ILLUSTRIOUS_MODELS_DIR'] = '/ImageModels/Stable-diffusion/Illustrious/' app.config['NOOB_MODELS_DIR'] = '/ImageModels/Stable-diffusion/Noob/' app.config['LORA_DIR'] = '/ImageModels/lora/Illustrious/Looks/' # Server-side session configuration to avoid cookie size limits app.config['SESSION_TYPE'] = 'filesystem' app.config['SESSION_FILE_DIR'] = os.path.join(app.config['UPLOAD_FOLDER'], '../flask_session') app.config['SESSION_PERMANENT'] = False db.init_app(app) Session(app) # --------------------------------------------------------------------------- # Logging # --------------------------------------------------------------------------- log_level_str = os.environ.get('LOG_LEVEL', 'INFO').upper() log_level = getattr(logging, log_level_str, logging.INFO) logging.basicConfig( level=log_level, format='%(asctime)s [%(levelname)s] %(name)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', ) logger = logging.getLogger('gaze') logger.setLevel(log_level) # --------------------------------------------------------------------------- # Register all routes # --------------------------------------------------------------------------- from routes import register_routes register_routes(app) # --------------------------------------------------------------------------- # Startup # --------------------------------------------------------------------------- if __name__ == '__main__': from services.mcp import ensure_mcp_server_running, ensure_character_mcp_server_running from services.job_queue import init_queue_worker from services.sync import ( sync_characters, sync_outfits, sync_actions, sync_styles, sync_detailers, sync_scenes, sync_looks, sync_checkpoints, sync_presets, ) ensure_mcp_server_running() ensure_character_mcp_server_running() init_queue_worker(app) with app.app_context(): os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) db.create_all() # Migration: Add active_outfit column if it doesn't exist try: from sqlalchemy import text db.session.execute(text('ALTER TABLE character ADD COLUMN active_outfit VARCHAR(100) DEFAULT \'default\'')) db.session.commit() print("Added active_outfit column to character table") except Exception as e: if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower(): print("active_outfit column already exists") else: print(f"Migration note: {e}") # Migration: Add default_fields column to action table if it doesn't exist try: from sqlalchemy import text db.session.execute(text('ALTER TABLE action ADD COLUMN default_fields JSON')) db.session.commit() print("Added default_fields column to action table") except Exception as e: if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower(): print("default_fields column already exists in action table") else: print(f"Migration action note: {e}") # Migration: Add new columns to settings table columns_to_add = [ ('llm_provider', "VARCHAR(50) DEFAULT 'openrouter'"), ('local_base_url', "VARCHAR(255)"), ('local_model', "VARCHAR(100)"), ('lora_dir_characters', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Looks'"), ('lora_dir_outfits', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Clothing'"), ('lora_dir_actions', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Poses'"), ('lora_dir_styles', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Styles'"), ('lora_dir_scenes', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Backgrounds'"), ('lora_dir_detailers', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Detailers'"), ('checkpoint_dirs', "VARCHAR(1000) DEFAULT '/ImageModels/Stable-diffusion/Illustrious,/ImageModels/Stable-diffusion/Noob'"), ('default_checkpoint', "VARCHAR(500)"), ('api_key', "VARCHAR(255)"), ] for col_name, col_type in columns_to_add: try: db.session.execute(text(f'ALTER TABLE settings ADD COLUMN {col_name} {col_type}')) db.session.commit() print(f"Added {col_name} column to settings table") except Exception as e: if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower(): pass else: print(f"Migration settings note ({col_name}): {e}") # Migration: Add is_favourite and is_nsfw columns to all resource tables _tag_tables = ['character', 'look', 'outfit', 'action', 'style', 'scene', 'detailer', 'checkpoint'] for _tbl in _tag_tables: for _col, _type in [('is_favourite', 'BOOLEAN DEFAULT 0'), ('is_nsfw', 'BOOLEAN DEFAULT 0')]: try: db.session.execute(text(f'ALTER TABLE {_tbl} ADD COLUMN {_col} {_type}')) db.session.commit() print(f"Added {_col} column to {_tbl} table") except Exception as e: if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower(): pass else: print(f"Migration note ({_tbl}.{_col}): {e}") # Ensure settings exist if not Settings.query.first(): db.session.add(Settings()) db.session.commit() print("Created default settings") # Log the default checkpoint on startup settings = Settings.query.first() if settings and settings.default_checkpoint: logger.info("=" * 80) logger.info("DEFAULT CHECKPOINT loaded from database: %s", settings.default_checkpoint) logger.info("=" * 80) else: logger.info("No default checkpoint set in database") sync_characters() sync_outfits() sync_actions() # Migration: Add data column to checkpoint table try: db.session.execute(text('ALTER TABLE checkpoint ADD COLUMN data JSON')) db.session.commit() print("Added data column to checkpoint table") except Exception as e: if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower(): print("data column already exists in checkpoint table") else: print(f"Migration checkpoint note: {e}") sync_styles() sync_detailers() sync_scenes() sync_looks() sync_checkpoints() sync_presets() # Migration: Convert look.character_id to look.character_ids try: from sqlalchemy import text # First ensure the column exists db.session.execute(text("ALTER TABLE look ADD COLUMN character_ids JSON")) db.session.commit() print("Added character_ids column to look table") except Exception as e: if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower(): pass # Column already exists else: print(f"Migration note (character_ids column): {e}") # Migrate existing character_id to character_ids list try: looks_with_old_field = Look.query.filter(Look.character_id.isnot(None)).all() migrated_count = 0 for look in looks_with_old_field: if not look.character_ids: look.character_ids = [] if look.character_id and look.character_id not in look.character_ids: look.character_ids.append(look.character_id) migrated_count += 1 if migrated_count > 0: db.session.commit() print(f"Migrated {migrated_count} looks from character_id to character_ids") except Exception as e: print(f"Migration note (character_ids data): {e}") app.run(debug=True, host='0.0.0.0', port=5000)