Final changes and update
This commit is contained in:
@@ -21,6 +21,14 @@ def set_log_file(filepath):
|
||||
def set_log_callback(callback):
|
||||
_log_context.callback = callback
|
||||
|
||||
def set_progress_callback(callback):
|
||||
_log_context.progress_callback = callback
|
||||
|
||||
def update_progress(percent):
|
||||
if getattr(_log_context, 'progress_callback', None):
|
||||
try: _log_context.progress_callback(percent)
|
||||
except: pass
|
||||
|
||||
def clean_json(text):
|
||||
text = text.replace("```json", "").replace("```", "").strip()
|
||||
# Robust extraction: find first { or [ and last } or ]
|
||||
@@ -32,6 +40,32 @@ def clean_json(text):
|
||||
else:
|
||||
return text[start_arr:text.rfind(']')+1]
|
||||
|
||||
def sanitize_filename(name):
|
||||
"""Sanitizes a string to be safe for filenames."""
|
||||
if not name: return "Untitled"
|
||||
safe = "".join([c for c in name if c.isalnum() or c=='_']).replace(" ", "_")
|
||||
return safe if safe else "Untitled"
|
||||
|
||||
def chapter_sort_key(ch):
|
||||
"""Sort key for chapters handling integers, strings, Prologue, and Epilogue."""
|
||||
num = ch.get('num', 0)
|
||||
if isinstance(num, int): return num
|
||||
if isinstance(num, str) and num.isdigit(): return int(num)
|
||||
s = str(num).lower().strip()
|
||||
if 'prologue' in s: return -1
|
||||
if 'epilogue' in s: return 9999
|
||||
return 999
|
||||
|
||||
def get_sorted_book_folders(run_dir):
|
||||
"""Returns a list of book folder names in a run directory, sorted numerically."""
|
||||
if not os.path.exists(run_dir): return []
|
||||
subdirs = [d for d in os.listdir(run_dir) if os.path.isdir(os.path.join(run_dir, d)) and d.startswith("Book_")]
|
||||
def sort_key(d):
|
||||
parts = d.split('_')
|
||||
if len(parts) > 1 and parts[1].isdigit(): return int(parts[1])
|
||||
return 0
|
||||
return sorted(subdirs, key=sort_key)
|
||||
|
||||
# --- SHARED UTILS ---
|
||||
def log(phase, msg):
|
||||
timestamp = datetime.datetime.now().strftime('%H:%M:%S')
|
||||
@@ -158,45 +192,4 @@ def log_usage(folder, model_label, usage_metadata=None, image_count=0):
|
||||
"est_cost_usd": round(cost, 4)
|
||||
}
|
||||
|
||||
with open(log_path, 'w') as f: json.dump(data, f, indent=2)
|
||||
|
||||
def normalize_settings(bp):
|
||||
"""
|
||||
CRITICAL: Enforces defaults.
|
||||
1. If series_metadata is missing, force it to SINGLE mode.
|
||||
2. If length_settings is missing, force explicit numbers.
|
||||
"""
|
||||
# Force Series Default (1 Book)
|
||||
if 'series_metadata' not in bp:
|
||||
bp['series_metadata'] = {
|
||||
"is_series": False,
|
||||
"mode": "single",
|
||||
"series_title": "Standalone",
|
||||
"total_books_to_generate": 1
|
||||
}
|
||||
|
||||
# Check for empty series count just in case
|
||||
if bp['series_metadata'].get('total_books_to_generate') is None:
|
||||
bp['series_metadata']['total_books_to_generate'] = 1
|
||||
|
||||
# Force Length Defaults
|
||||
settings = bp.get('length_settings', {})
|
||||
label = settings.get('label', 'Novella') # Default to Novella if nothing provided
|
||||
|
||||
# Get defaults based on label (or Novella if unknown)
|
||||
presets = get_length_presets()
|
||||
defaults = presets.get(label, presets['Novella'])
|
||||
|
||||
if 'chapters' not in settings: settings['chapters'] = defaults['chapters']
|
||||
if 'words' not in settings: settings['words'] = defaults['words']
|
||||
|
||||
# Smart Depth Calculation (if not manually set)
|
||||
if 'depth' not in settings:
|
||||
c = int(settings['chapters'])
|
||||
if c <= 5: settings['depth'] = 1
|
||||
elif c <= 20: settings['depth'] = 2
|
||||
elif c <= 40: settings['depth'] = 3
|
||||
else: settings['depth'] = 4
|
||||
|
||||
bp['length_settings'] = settings
|
||||
return bp
|
||||
with open(log_path, 'w') as f: json.dump(data, f, indent=2)
|
||||
Reference in New Issue
Block a user