fix metadata save and temp file handler

Signed-off-by: vladmandic <mandic00@live.com>
This commit is contained in:
vladmandic
2026-01-28 19:28:49 +01:00
parent ccff480d24
commit 58351b1f53
7 changed files with 58 additions and 31 deletions
+2
View File
@@ -36,6 +36,7 @@
existing data files are auto-migrated on startup
- further work on type consistency and type checking, thanks @awsr
- log captured exceptions
- improve temp folder handling and cleanup
- add ui placeholders for future agent-scheduler work, thanks @ryanmeador
- implement abort system on repeated errors, thanks @awsr
currently used by lora and textual-inversion loaders
@@ -46,6 +47,7 @@
- ui css fixes for modernui
- support lora inside prompt selector
- framepack video save
- metadata save for manual saves
## Update for 2026-01-22
+2 -1
View File
@@ -78,7 +78,8 @@ def image_from_url_text(filedata):
filedata = filedata[len("data:image/jxl;base64,"):]
filebytes = base64.decodebytes(filedata.encode('utf-8'))
image = Image.open(io.BytesIO(filebytes))
images.read_info_from_image(image)
image.load()
# images.read_info_from_image(image)
return image
+3
View File
@@ -104,6 +104,9 @@ def on_tmpdir_changed():
def cleanup_tmpdr():
temp_dir = shared.opts.temp_dir
if temp_dir == "" or not os.path.isdir(temp_dir):
temp_dir = os.path.join(paths.temp_dir, "gradio")
shared.log.debug(f'Temp folder: path="{temp_dir}"')
if not os.path.isdir(temp_dir):
return
for root, _dirs, files in os.walk(temp_dir, topdown=False):
for name in files:
+30 -16
View File
@@ -10,7 +10,8 @@ from pathlib import Path
from modules import shared, errors
debug = errors.log.trace if os.environ.get('SD_NAMEGEN_DEBUG', None) is not None else lambda *args, **kwargs: None
debug= os.environ.get('SD_NAMEGEN_DEBUG', None) is not None
debug_log = errors.log.trace if debug else lambda *args, **kwargs: None
re_nonletters = re.compile(r'[\s' + string.punctuation + ']+')
re_pattern = re.compile(r"(.*?)(?:\[([^\[\]]+)\]|$)")
re_pattern_arg = re.compile(r"(.*)<([^>]*)>$")
@@ -66,9 +67,9 @@ class FilenameGenerator:
def __init__(self, p, seed, prompt, image=None, grid=False, width=None, height=None):
if p is None:
debug('Filename generator init skip')
debug_log('Filename generator init skip')
else:
debug(f'Filename generator init: seed={seed} prompt="{prompt}"')
debug_log(f'Filename generator init: seed={seed} prompt="{prompt}"')
self.p = p
if seed is not None and int(seed) > 0:
self.seed = seed
@@ -163,7 +164,7 @@ class FilenameGenerator:
def prompt_sanitize(self, prompt):
invalid_chars = '#<>:\'"\\|?*\n\t\r'
sanitized = prompt.translate({ ord(x): '_' for x in invalid_chars }).strip()
debug(f'Prompt sanitize: input="{prompt}" output={sanitized}')
debug_log(f'Prompt sanitize: input="{prompt}" output="{sanitized}"')
return sanitized
def sanitize(self, filename):
@@ -200,7 +201,7 @@ class FilenameGenerator:
while len(os.path.abspath(fn)) > max_length:
fn = fn[:-1]
fn += ext
debug(f'Filename sanitize: input="{filename}" parts={parts} output="{fn}" ext={ext} max={max_length} len={len(fn)}')
debug_log(f'Filename sanitize: input="{filename}" parts={parts} output="{fn}" ext={ext} max={max_length} len={len(fn)}')
return fn
def safe_int(self, s):
@@ -234,25 +235,38 @@ class FilenameGenerator:
def apply(self, x):
res = ''
if debug:
for k in self.replacements.keys():
try:
fn = self.replacements.get(k, None)
debug_log(f'Namegen: key={k} value={fn(self)}')
except Exception as e:
shared.log.error(f'Namegen: key={k} {e}')
errors.display(e, 'namegen')
for m in re_pattern.finditer(x):
text, pattern = m.groups()
if pattern is None:
res += text
continue
pattern_args = []
while True:
m = re_pattern_arg.match(pattern)
if m is None:
break
pattern, arg = m.groups()
pattern_args.insert(0, arg)
debug_log(f'Filename apply: text="{text}" pattern="{pattern}"')
if isinstance(pattern, list):
pattern = ' '.join(pattern)
if pattern is None or not isinstance(pattern, str) or pattern.strip() == '':
debug_log(f'Filename skip: pattern="{pattern}"')
res += text
continue
_pattern = pattern
pattern_args = []
while True:
m = re_pattern_arg.match(_pattern)
if m is None:
break
_pattern, arg = m.groups()
pattern_args.insert(0, arg)
fun = self.replacements.get(pattern.lower(), None)
if fun is not None:
try:
debug(f'Filename apply: pattern={pattern.lower()} args={pattern_args}')
replacement = fun(self, *pattern_args)
debug_log(f'Filename apply: pattern="{pattern}" args={pattern_args} replacement="{replacement}"')
except Exception as e:
replacement = None
errors.display(e, 'namegen')
+5
View File
@@ -4,6 +4,7 @@ import sys
import json
import shlex
import argparse
import tempfile
from installer import log
@@ -18,12 +19,16 @@ cli = parser.parse_known_args(argv)[0]
parser.add_argument("--config", type=str, default=os.environ.get("SD_CONFIG", os.path.join(cli.data_dir, 'config.json')), help="Use specific server configuration file, default: %(default)s") # twice because we want data_dir
cli = parser.parse_known_args(argv)[0]
config_path = cli.config if os.path.isabs(cli.config) else os.path.join(cli.data_dir, cli.config)
try:
with open(config_path, 'r', encoding='utf8') as f:
config = json.load(f)
except Exception:
config = {}
temp_dir = config.get('temp_dir', '')
if len(temp_dir) == 0:
temp_dir = tempfile.gettempdir()
reference_path = os.path.join('models', 'Reference')
modules_path = os.path.dirname(os.path.realpath(__file__))
script_path = os.path.dirname(modules_path)
+15 -13
View File
@@ -5,8 +5,7 @@ import shutil
import platform
import subprocess
import gradio as gr
from modules import call_queue, shared, errors, ui_sections, ui_symbols, ui_components, generation_parameters_copypaste, images, scripts_manager, script_callbacks, infotext, processing
from modules.paths import resolve_output_path
from modules import paths, call_queue, shared, errors, ui_sections, ui_symbols, ui_components, generation_parameters_copypaste, images, scripts_manager, script_callbacks, infotext, processing
folder_symbol = ui_symbols.folder
@@ -106,7 +105,7 @@ def delete_files(js_data, files, all_files, index):
def save_files(js_data, files, html_info, index):
os.makedirs(resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save), exist_ok=True)
os.makedirs(paths.resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save), exist_ok=True)
class PObject: # pylint: disable=too-few-public-methods
def __init__(self, d=None):
@@ -116,6 +115,7 @@ def save_files(js_data, files, html_info, index):
self.prompt = getattr(self, 'prompt', None) or getattr(self, 'Prompt', None) or ''
self.negative_prompt = getattr(self, 'negative_prompt', None) or getattr(self, 'Negative_prompt', None) or ''
self.sampler = getattr(self, 'sampler', None) or getattr(self, 'Sampler', None) or ''
self.sampler_name = self.sampler
self.seed = getattr(self, 'seed', None) or getattr(self, 'Seed', None) or 0
self.steps = getattr(self, 'steps', None) or getattr(self, 'Steps', None) or 0
self.width = getattr(self, 'width', None) or getattr(self, 'Width', None) or getattr(self, 'Size-1', None) or 0
@@ -128,13 +128,16 @@ def save_files(js_data, files, html_info, index):
self.styles = getattr(self, 'styles', None) or getattr(self, 'Styles', None) or []
self.styles = [s.strip() for s in self.styles.split(',')] if isinstance(self.styles, str) else self.styles
self.outpath_grids = resolve_output_path(shared.opts.outdir_grids, shared.opts.outdir_txt2img_grids)
self.outpath_grids = paths.resolve_output_path(shared.opts.outdir_grids, shared.opts.outdir_txt2img_grids)
self.infotexts = getattr(self, 'infotexts', [html_info])
self.infotext = self.infotexts[0] if len(self.infotexts) > 0 else html_info
self.all_negative_prompt = getattr(self, 'all_negative_prompts', [self.negative_prompt])
self.all_prompts = getattr(self, 'all_prompts', [self.prompt])
self.all_seeds = getattr(self, 'all_seeds', [self.seed])
self.all_subseeds = getattr(self, 'all_subseeds', [self.subseed])
self.n_iter = 1
self.batch_size = 1
try:
data = json.loads(js_data)
except Exception:
@@ -159,17 +162,17 @@ def save_files(js_data, files, html_info, index):
p.all_prompts.append(p.prompt)
while len(p.infotexts) <= i:
p.infotexts.append(p.infotext)
if 'name' in filedata and ('tmp' not in filedata['name']) and os.path.isfile(filedata['name']):
if 'name' in filedata and (paths.temp_dir not in filedata['name']) and os.path.isfile(filedata['name']):
fullfn = filedata['name']
fullfns.append(fullfn)
destination = resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save)
destination = paths.resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save)
namegen = images.FilenameGenerator(p, seed=p.all_seeds[i], prompt=p.all_prompts[i], image=None) # pylint: disable=no-member
dirname = namegen.apply(shared.opts.directories_filename_pattern or "[prompt_words]").lstrip(' ').rstrip('\\ /')
destination = os.path.join(destination, dirname)
destination = namegen.sanitize(destination)
os.makedirs(destination, exist_ok = True)
tgt_filename = os.path.join(destination, os.path.basename(fullfn))
relfn = os.path.relpath(tgt_filename, resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save))
relfn = os.path.relpath(tgt_filename, paths.resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save))
filenames.append(relfn)
if not os.path.exists(tgt_filename):
try:
@@ -195,21 +198,20 @@ def save_files(js_data, files, html_info, index):
if len(info) == 0:
info = None
if (js_data is None or len(js_data) == 0) and image is not None and image.info is not None:
info = image.info.pop('parameters', None) or image.info.pop('UserComment', None)
geninfo, _ = images.read_info_from_image(image)
items = infotext.parse(geninfo)
info, _items = images.read_info_from_image(image)
items = infotext.parse(info)
p = PObject(items)
try:
seed = p.all_seeds[i] if i < len(p.all_seeds) else p.seed
prompt = p.all_prompts[i] if i < len(p.all_prompts) else p.prompt
fullfn, txt_fullfn, _exif = images.save_image(image, resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save), "", seed=seed, prompt=prompt, info=info, extension=shared.opts.samples_format, grid=is_grid, p=p)
fullfn, txt_fullfn, _exif = images.save_image(image, paths.resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save), "", seed=seed, prompt=prompt, info=info, extension=shared.opts.samples_format, grid=is_grid, p=p)
except Exception as e:
fullfn, txt_fullfn = None, None
shared.log.error(f'Save: image={image} i={i} seeds={p.all_seeds} prompts={p.all_prompts}')
errors.display(e, 'save')
if fullfn is None:
continue
filename = os.path.relpath(fullfn, resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save))
filename = os.path.relpath(fullfn, paths.resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save))
filenames.append(filename)
fullfns.append(fullfn)
if txt_fullfn:
@@ -217,7 +219,7 @@ def save_files(js_data, files, html_info, index):
# fullfns.append(txt_fullfn)
script_callbacks.image_save_btn_callback(filename)
if shared.opts.samples_save_zip and len(fullfns) > 1:
zip_filepath = os.path.join(resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save), "images.zip")
zip_filepath = os.path.join(paths.resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_save), "images.zip")
from zipfile import ZipFile
with ZipFile(zip_filepath, "w") as zip_file:
for i in range(len(fullfns)):
+1 -1
Submodule wiki updated: 440b883838...a678731c5d