diff --git a/.gitignore b/.gitignore
index 1e22578..15a5c92 100644
--- a/.gitignore
+++ b/.gitignore
@@ -154,6 +154,6 @@ dmypy.json
# Cython debug symbols
cython_debug/
-settings.local.ini
-*code-workspace
\ No newline at end of file
+*code-workspace
+settings.user.ini
diff --git a/docker-compose.yml b/docker-compose.yml
index c0d58b9..af46cdd 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -8,4 +8,9 @@ services:
- "8080:8080"
volumes:
- ${HOST_OUTPUT_DIR}:/app/output
- restart: unless-stopped
\ No newline at end of file
+ - replicate-flux-lora-settings:/app/settings
+ restart: unless-stopped
+
+
+volumes:
+ replicate-flux-lora-settings:
\ No newline at end of file
diff --git a/settings.ini b/settings.ini
index 201841e..1a82419 100644
--- a/settings.ini
+++ b/settings.ini
@@ -5,7 +5,7 @@ num_outputs = 1
lora_scale = 1
num_inference_steps = 28
guidance_scale = 3.5
-output_format = png
+output_format =
output_quality = 80
disable_safety_checker = true
width = 1024
@@ -14,4 +14,4 @@ seed = -1
output_folder = /path/to/default/output/folder
replicate_model =
models = {"user_added": []}
-
+prompt =
diff --git a/src/config.py b/src/config.py
index e10caac..ea20b8f 100644
--- a/src/config.py
+++ b/src/config.py
@@ -1,29 +1,80 @@
import configparser
import os
+from typing import Any, Type
+
+from loguru import logger
+
+DOCKERIZED = os.environ.get("DOCKER_CONTAINER", "False").lower() == "true"
+CONFIG_DIR = "/app/settings" if DOCKERIZED else "."
+DEFAULT_CONFIG_FILE = os.path.join(CONFIG_DIR, "settings.ini")
+USER_CONFIG_FILE = os.path.join(CONFIG_DIR, "settings.user.ini")
+
+logger.info(
+ f"Configuration files: DEFAULT={DEFAULT_CONFIG_FILE}, USER={USER_CONFIG_FILE}"
+)
config = configparser.ConfigParser()
-config.read(["settings.ini", "settings.local.ini"])
+config.read([DEFAULT_CONFIG_FILE, USER_CONFIG_FILE])
+logger.info("Configuration files loaded")
def get_api_key():
- return os.environ.get("REPLICATE_API_KEY") or config.get(
+ api_key = os.environ.get("REPLICATE_API_KEY") or config.get(
"secrets", "REPLICATE_API_KEY", fallback=None
)
+ if api_key:
+ logger.info("API key retrieved successfully")
+ else:
+ logger.warning("No API key found")
+ return api_key
-def get_setting(section, key, fallback=None):
+def get_setting(
+ section: str, key: str, fallback: Any = None, value_type: Type[Any] = str
+) -> Any:
+ logger.info(
+ f"Attempting to get setting: section={section}, key={key}, fallback={fallback}, value_type={value_type}"
+ )
try:
- return config.get(section, key)
- except (configparser.NoSectionError, configparser.NoOptionError):
+ value = config.get(section, key)
+ logger.debug(f"Raw value retrieved: {value}")
+ if value_type == int:
+ result = int(value)
+ elif value_type == float:
+ result = float(value)
+ elif value_type == bool:
+ result = value.lower() in ("true", "yes", "1", "on")
+ else:
+ result = value
+ logger.info(f"Setting retrieved successfully: {result}")
+ return result
+ except (configparser.NoSectionError, configparser.NoOptionError) as e:
+ logger.warning(f"Setting not found: {str(e)}. Using fallback value: {fallback}")
+ return fallback
+ except ValueError as e:
+ logger.error(
+ f"Error converting setting value: {str(e)}. Using fallback value: {fallback}"
+ )
return fallback
def set_setting(section, key, value):
+ logger.info(f"Setting value: section={section}, key={key}, value={value}")
if not config.has_section(section):
+ logger.info(f"Creating new section: {section}")
config.add_section(section)
config.set(section, key, str(value))
+ logger.info("Value set successfully")
def save_settings():
- with open("settings.local.ini", "w") as configfile:
- config.write(configfile)
+ logger.info(f"Saving settings to {USER_CONFIG_FILE}")
+ try:
+ with open(USER_CONFIG_FILE, "w") as configfile:
+ config.write(configfile)
+ logger.info("Settings saved successfully")
+ except IOError as e:
+ logger.error(f"Error saving settings: {str(e)}")
+
+
+logger.info("Config module initialized")
diff --git a/src/gui.py b/src/gui.py
index f9e67dc..a4bf86b 100644
--- a/src/gui.py
+++ b/src/gui.py
@@ -1,7 +1,6 @@
import asyncio
import json
import os
-import sys
import urllib.parse
import zipfile
from datetime import datetime
@@ -12,23 +11,17 @@
from loguru import logger
from nicegui import ui
-logger.remove()
-logger.add(
- sys.stderr, format="{time} {level} {message}", filter="my_module", level="INFO"
-)
-logger.add("gui.log", rotation="10 MB", format="{time} {level} {message}", level="INFO")
-
DOCKERIZED = os.environ.get("DOCKER_CONTAINER", False)
-SETTINGS_LOCAL_FILE = "settings.local.toml"
-
class Lightbox:
def __init__(self):
+ logger.debug("Initializing Lightbox")
with ui.dialog().props("maximized").classes("bg-black") as self.dialog:
self.dialog.on_key = self._handle_key
self.large_image = ui.image().props("no-spinner fit=scale-down")
self.image_list = []
+ logger.debug("Lightbox initialized")
def add_image(
self,
@@ -36,32 +29,40 @@ def add_image(
orig_url: str,
thumb_classes: str = "w-32 h-32 object-cover",
) -> ui.button:
+ logger.debug(f"Adding image to Lightbox: {orig_url}")
self.image_list.append(orig_url)
button = ui.button(on_click=lambda: self._open(orig_url)).props(
"flat dense square"
)
with button:
ui.image(thumb_url).classes(thumb_classes)
+ logger.debug("Image added to Lightbox")
return button
def _handle_key(self, e) -> None:
+ logger.debug(f"Handling key press in Lightbox: {e.key}")
if not e.action.keydown:
return
if e.key.escape:
+ logger.debug("Closing Lightbox dialog")
self.dialog.close()
image_index = self.image_list.index(self.large_image.source)
if e.key.arrow_left and image_index > 0:
+ logger.debug("Displaying previous image")
self._open(self.image_list[image_index - 1])
if e.key.arrow_right and image_index < len(self.image_list) - 1:
+ logger.debug("Displaying next image")
self._open(self.image_list[image_index + 1])
def _open(self, url: str) -> None:
+ logger.debug(f"Opening image in Lightbox: {url}")
self.large_image.set_source(url)
self.dialog.open()
class ImageGeneratorGUI:
def __init__(self, image_generator):
+ logger.info("Initializing ImageGeneratorGUI")
self.image_generator = image_generator
self.api_key = get_api_key() or os.environ.get("REPLICATE_API_KEY", "")
self.last_generated_images = []
@@ -85,33 +86,43 @@ def __init__(self, image_generator):
]
self.user_added_models = {}
- self.load_settings()
-
- self.flux_model = get_setting("default", "flux_model", "dev")
- self.aspect_ratio = get_setting("default", "aspect_ratio", "1:1")
- self.num_outputs = int(get_setting("default", "num_outputs", "1"))
- self.lora_scale = float(get_setting("default", "lora_scale", "1"))
- self.num_inference_steps = int(
- get_setting("default", "num_inference_steps", "28")
+ self.prompt = get_setting("default", "prompt", "", str)
+
+ self.flux_model = get_setting("default", "flux_model", "dev", str)
+ self.aspect_ratio = get_setting("default", "aspect_ratio", "1:1", str)
+ self.num_outputs = get_setting("default", "num_outputs", "1", int)
+ self.lora_scale = get_setting("default", "lora_scale", "1", float)
+ self.num_inference_steps = get_setting(
+ "default", "num_inference_steps", "28", int
)
- self.guidance_scale = float(get_setting("default", "guidance_scale", "3.5"))
- self.output_format = get_setting("default", "output_format", "webp")
- self.output_quality = int(get_setting("default", "output_quality", "80"))
- self.disable_safety_checker = (
- get_setting("default", "disable_safety_checker", "False").lower() == "true"
+ self.guidance_scale = get_setting("default", "guidance_scale", "3.5", float)
+ self.output_format = get_setting("default", "output_format", "png")
+ self.output_quality = get_setting("default", "output_quality", "80", int)
+ self.disable_safety_checker = get_setting(
+ "default", "disable_safety_checker", True, bool
)
- self.width = int(get_setting("default", "width", "1024"))
- self.height = int(get_setting("default", "height", "1024"))
- self.seed = int(get_setting("default", "seed", "-1"))
- if not self.output_folder:
- self.output_folder = (
- str(Path.home() / "Downloads") if not DOCKERIZED else "/app/output"
- )
+ self.width = get_setting("default", "width", "1024", int)
+ self.height = get_setting("default", "height", "1024", int)
+ self.seed = get_setting("default", "seed", "-1", int)
+
+ self.output_folder = (
+ "/app/output"
+ if DOCKERIZED
+ else get_setting("default", "output_folder", "/Downloads", str)
+ )
+ models_json = get_setting("default", "models", '{"user_added": []}', str)
+ models = json.loads(models_json)
+ self.user_added_models = {
+ model: model for model in models.get("user_added", [])
+ }
+ self.model_options = list(self.user_added_models.keys())
+ self.replicate_model = get_setting("default", "replicate_model", "", str)
logger.info("ImageGeneratorGUI initialized")
def setup_custom_styles(self):
+ logger.debug("Setting up custom styles")
ui.add_head_html("""