fps-gaon / ui /workspace.py
rahul7star's picture
Migrated from GitHub
1030ba2 verified
# ui/workspace.py
# Contains functions for saving, loading, and managing workspace settings from files.
import gradio as gr
import json
import os
import traceback
import tkinter as tk
from tkinter import filedialog
from PIL import Image
# Import shared state and other managers
from . import shared_state
from . import metadata as metadata_manager
# --- Constants ---
# Define filenames and the default output folder at the module level.
# These were moved from demo_gradio_svc.py
outputs_folder = './outputs_svc/'
SETTINGS_FILENAME = "goan_settings.json"
UNLOAD_SAVE_FILENAME = "goan_unload_save.json"
REFRESH_IMAGE_FILENAME = "goan_refresh_image.png"
# --- Core Save/Load Logic ---
def get_default_values_map():
"""Returns a dictionary with the default values for all UI settings."""
return {
'prompt': '', 'n_prompt': '', 'total_second_length': 5.0, 'seed': -1,
'use_teacache': True, 'preview_frequency_ui': 5, 'segments_to_decode_csv': '',
'gs_ui': 10.0, 'gs_schedule_shape_ui': 'Off', 'gs_final_ui': 10.0, 'steps': 25,
'cfg': 1.0, 'latent_window_size': 9, 'gpu_memory_preservation': 6.0,
'use_fp32_transformer_output_ui': False, 'rs': 0.0, 'mp4_crf': 18,
'output_folder_ui': outputs_folder,
}
def save_settings_to_file(filepath, *ui_values_tuple):
"""Saves a tuple of UI values to a specified JSON file."""
settings_to_save = dict(zip(shared_state.ALL_TASK_UI_KEYS, ui_values_tuple))
try:
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(settings_to_save, f, indent=4)
gr.Info(f"Workspace saved to {filepath}")
except Exception as e:
gr.Warning(f"Error saving workspace: {e}")
traceback.print_exc()
def load_settings_from_file(filepath, return_updates=True):
"""Loads settings from a JSON file and returns Gradio updates or raw values."""
default_values = get_default_values_map()
try:
with open(filepath, 'r', encoding='utf-8') as f:
loaded_settings = json.load(f)
gr.Info(f"Loaded workspace from {filepath}")
except Exception as e:
gr.Warning(f"Could not load workspace from {filepath}: {e}")
loaded_settings = {}
final_settings = {**default_values, **loaded_settings}
output_values = [final_settings.get(key, default_values.get(key)) for key in shared_state.ALL_TASK_UI_KEYS]
# Type correction to prevent errors when loading from JSON
for i, key in enumerate(shared_state.ALL_TASK_UI_KEYS):
try:
if key in ['seed', 'latent_window_size', 'steps', 'mp4_crf', 'preview_frequency_ui']:
output_values[i] = int(output_values[i])
elif key in ['total_second_length', 'cfg', 'gs_ui', 'rs', 'gpu_memory_preservation', 'gs_final_ui']:
output_values[i] = float(output_values[i])
elif key in ['use_teacache', 'use_fp32_transformer_output_ui']:
output_values[i] = bool(output_values[i])
except (ValueError, TypeError):
output_values[i] = default_values.get(key)
return [gr.update(value=v) for v in output_values] if return_updates else output_values
# --- NEW FUNCTION FOR INITIAL OUTPUT FOLDER LOADING ---
def get_initial_output_folder_from_settings():
"""
Attempts to load the 'output_folder_ui' value from UNLOAD_SAVE_FILENAME or SETTINGS_FILENAME.
If not found or an error occurs, returns the module's default outputs_folder.
Expands user home directory paths (e.g., '~') if present.
"""
# Use the module-level outputs_folder as the fallback default
default_output_folder_path = outputs_folder
# Prioritize the temporary unload save file, then the default settings file
filename_to_check = None
if os.path.exists(UNLOAD_SAVE_FILENAME):
filename_to_check = UNLOAD_SAVE_FILENAME
elif os.path.exists(SETTINGS_FILENAME):
filename_to_check = SETTINGS_FILENAME
if filename_to_check:
try:
with open(filename_to_check, 'r', encoding='utf-8') as f:
settings = json.load(f)
# If 'output_folder_ui' exists in the loaded settings, use it
if 'output_folder_ui' in settings:
# Expand user path (e.g., '~/.goan/outputs/') to its absolute form
return os.path.expanduser(settings['output_folder_ui'])
except Exception as e:
# Log a warning if loading fails, but proceed with default
print(f"Warning: Could not load 'output_folder_ui' from {filename_to_check} for initial path setup: {e}")
traceback.print_exc() # Print full traceback for deeper debugging if needed
# If no file found, or loading failed, or key not present, return the module's default
return default_output_folder_path
# --- UI Handler Functions ---
def save_workspace(*ui_values_tuple):
"""Opens a file dialog to save the full workspace settings."""
root = tk.Tk()
root.withdraw()
file_path = filedialog.asksaveasfilename(
defaultextension=".json",
initialfile="goan_workspace.json",
filetypes=[("JSON files", "*.json")]
)
root.destroy()
if file_path:
save_settings_to_file(file_path, *ui_values_tuple)
else:
gr.Warning("Save cancelled.")
def save_as_default_workspace(*ui_values_tuple):
"""Saves the current UI settings as the default startup configuration."""
gr.Info(f"Saving current settings as default to {SETTINGS_FILENAME}")
save_settings_to_file(SETTINGS_FILENAME, *ui_values_tuple)
def save_ui_and_image_for_refresh(*args_from_ui_controls_tuple):
"""Saves UI state and the current image to temporary files for session recovery."""
gallery_list = args_from_ui_controls_tuple[0]
all_ui_values_tuple = args_from_ui_controls_tuple[1:]
full_params_map = dict(zip(shared_state.ALL_TASK_UI_KEYS, all_ui_values_tuple))
settings_to_save = full_params_map.copy()
if gallery_list and isinstance(gallery_list[0], (tuple, Image.Image)):
pil_image = gallery_list[0][0] if isinstance(gallery_list[0], tuple) else gallery_list[0]
try:
creative_params = {k: full_params_map.get(k) for k in shared_state.CREATIVE_PARAM_KEYS}
pil_image = metadata_manager.write_image_metadata(pil_image, creative_params)
# Use the output folder path from the UI settings
output_folder_path = full_params_map.get('output_folder_ui', outputs_folder)
refresh_image_path = os.path.join(output_folder_path, REFRESH_IMAGE_FILENAME)
pil_image.save(refresh_image_path)
settings_to_save["refresh_image_path"] = refresh_image_path
except Exception as e:
gr.Warning(f"Could not save refresh image: {e}")
# Save all settings to the unload file
save_settings_to_file(UNLOAD_SAVE_FILENAME, *settings_to_save.values())
def load_workspace():
"""Opens a file dialog to load a workspace from a JSON file."""
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
root.destroy()
return load_settings_from_file(file_path) if file_path else [gr.update()] * len(shared_state.ALL_TASK_UI_KEYS)
def load_workspace_on_start():
"""
Loads settings on app startup, prioritizing a temporary session file,
then a default file, and finally falling back to hardcoded defaults.
"""
image_path_to_load = None
settings_file = None
if os.path.exists(UNLOAD_SAVE_FILENAME):
settings_file = UNLOAD_SAVE_FILENAME
try:
with open(settings_file, 'r') as f:
settings = json.load(f)
# Check if the saved image path exists
if "refresh_image_path" in settings and os.path.exists(settings["refresh_image_path"]):
image_path_to_load = settings["refresh_image_path"]
except Exception:
pass # Ignore errors reading the temp file
elif os.path.exists(SETTINGS_FILENAME):
settings_file = SETTINGS_FILENAME
if settings_file:
print(f"Loading workspace from {settings_file}")
ui_updates = load_settings_from_file(settings_file)
if image_path_to_load:
gr.Info("Restoring UI state and image from previous session.")
if settings_file == UNLOAD_SAVE_FILENAME:
os.remove(UNLOAD_SAVE_FILENAME) # Clean up temp file
return [image_path_to_load] + ui_updates
print("No workspace file found. Using default values.")
default_vals = get_default_values_map()
return [None] + [default_vals[key] for key in shared_state.ALL_TASK_UI_KEYS]
def load_image_from_path(image_path):
"""Loads an image from a given path and deletes the temporary file."""
if image_path and os.path.exists(image_path):
try:
# The gallery component expects a list of (image, name) tuples.
return gr.update(value=[(Image.open(image_path), "refresh_image")])
finally:
os.remove(image_path) # Clean up the temp image
return gr.update(value=None)