diff --git a/CHANGELOG.md b/CHANGELOG.md index d810b7ed2..5ac1bd607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,17 @@ ## Update for 2025-02-20 -### Highlight for 2025-02-20 +Quick release refresh: +- remove ui splash screen on auth fail +- add `--extensions-dir` cli arg and `SD_EXTENSIONSDIR` env variable to specify extensions directory +- log full path when reading/saving `config.json` +- log full path to `sdnext.log` +- log system hostname in `sdnext.log` +- log extensions path in `sdnext.log` + +## Update for 2025-02-18 + +### Highlight for 2025-02-18 We're back with another update with nearly 100 commits! - Starting with massive UI update with full [localization](https://vladmandic.github.io/sdnext-docs/Locale/) for 8 languages diff --git a/installer.py b/installer.py index 0deacf675..531f85205 100644 --- a/installer.py +++ b/installer.py @@ -5,6 +5,7 @@ import json import time import shutil import locale +import socket import logging import platform import subprocess @@ -26,6 +27,7 @@ console = None debug = log.debug if os.environ.get('SD_INSTALL_DEBUG', None) is not None else lambda *args, **kwargs: None pip_log = '--log pip.log ' if os.environ.get('SD_PIP_DEBUG', None) is not None else '' log_file = os.path.join(os.path.dirname(__file__), 'sdnext.log') +hostname = socket.gethostname() log_rolled = False first_call = True quick_allowed = True @@ -176,7 +178,7 @@ def setup_logging(): fh.doRollover() log_rolled = True - fh.formatter = logging.Formatter('%(asctime)s | %(name)s | %(levelname)s | %(module)s | %(message)s') + fh.formatter = logging.Formatter(f'%(asctime)s | {hostname} | %(name)s | %(levelname)s | %(module)s | %(message)s') fh.setLevel(logging.DEBUG) log.addHandler(fh) @@ -203,7 +205,7 @@ def setup_logging(): def get_logfile(): log_size = os.path.getsize(log_file) if os.path.exists(log_file) else 0 - log.info(f'Logger: file="{log_file}" level={logging.getLevelName(logging.DEBUG if args.debug else logging.INFO)} size={log_size} mode={"append" if not log_rolled else "create"}') + log.info(f'Logger: file="{os.path.abspath(log_file)}" level={logging.getLevelName(logging.DEBUG if args.debug else logging.INFO)} host="{hostname}" size={log_size} mode={"append" if not log_rolled else "create"}') return log_file @@ -979,14 +981,13 @@ def run_extension_installer(folder): # get list of all enabled extensions def list_extensions_folder(folder, quiet=False): - name = os.path.basename(folder) disabled_extensions_all = opts.get('disable_all_extensions', 'none') if disabled_extensions_all != 'none': return [] disabled_extensions = opts.get('disabled_extensions', []) enabled_extensions = [x for x in os.listdir(folder) if os.path.isdir(os.path.join(folder, x)) and x not in disabled_extensions and not x.startswith('.')] if not quiet: - log.info(f'Extensions: enabled={enabled_extensions} {name}') + log.info(f'Extensions: path="{folder}" enabled={enabled_extensions}') return enabled_extensions diff --git a/javascript/loader.js b/javascript/loader.js index 35b54ad9b..588cf3247 100644 --- a/javascript/loader.js +++ b/javascript/loader.js @@ -19,11 +19,22 @@ async function preloadImages() { } try { await Promise.all(imagePromises); + return true; } catch (error) { error(`preloadImages: ${error}`); + return false; } } +async function removeSplash() { + const splash = document.getElementById('splash'); + if (splash) splash.remove(); + log('removeSplash'); + const t = Math.round(performance.now() - appStartTime) / 1000; + log('startupTime', t); + xhrPost(`${window.api}/log`, { message: `ready time=${t}` }); +} + async function createSplash() { const dark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; log('createSplash', { theme: dark ? 'dark' : 'light' }); @@ -34,7 +45,11 @@ async function createSplash() {
`; document.body.insertAdjacentHTML('beforeend', splash); - await preloadImages(); + const ok = await preloadImages(); + if (!ok) { + removeSplash(); + return; + } const imgEl = ``; document.getElementById('splash').insertAdjacentHTML('afterbegin', imgEl); fetch(`${window.api}/motd`) @@ -46,13 +61,4 @@ async function createSplash() { .catch((err) => error(`getMOTD: ${err}`)); } -async function removeSplash() { - const splash = document.getElementById('splash'); - if (splash) splash.remove(); - log('removeSplash'); - const t = Math.round(performance.now() - appStartTime) / 1000; - log('startupTime', t); - xhrPost(`${window.api}/log`, { message: `ready time=${t}` }); -} - window.onload = createSplash; diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 4ea45946a..5e5e21054 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -31,6 +31,7 @@ def main_args(): group_paths.add_argument("--ckpt", type=str, default=os.environ.get("SD_MODEL", None), help="Path to model checkpoint to load immediately, default: %(default)s") group_paths.add_argument("--data-dir", type=str, default=os.environ.get("SD_DATADIR", ''), help="Base path where all user data is stored, default: %(default)s") group_paths.add_argument("--models-dir", type=str, default=os.environ.get("SD_MODELSDIR", 'models'), help="Base path where all models are stored, default: %(default)s",) + group_paths.add_argument("--extensions-dir", type=str, default=os.environ.get("SD_EXTENSIONSDIR", None), help="Base path where all extensions are stored, default: %(default)s",) group_diag = parser.add_argument_group('Diagnostics') group_diag.add_argument("--no-hashing", default=os.environ.get("SD_NOHASHING", False), action='store_true', help="Disable hashing of checkpoints, default: %(default)s") diff --git a/modules/paths.py b/modules/paths.py index 9261990db..06cf34bff 100644 --- a/modules/paths.py +++ b/modules/paths.py @@ -13,6 +13,7 @@ parser = argparse.ArgumentParser(add_help=False) parser.add_argument("--ckpt", type=str, default=os.environ.get("SD_MODEL", None), help="Path to model checkpoint to load immediately, default: %(default)s") parser.add_argument("--data-dir", type=str, default=os.environ.get("SD_DATADIR", ''), help="Base path where all user data is stored, default: %(default)s") parser.add_argument("--models-dir", type=str, default=os.environ.get("SD_MODELSDIR", None), help="Base path where all models are stored, default: %(default)s",) +parser.add_argument("--extensions-dir", type=str, default=os.environ.get("SD_EXTENSIONSDIR", None), help="Base path where all extensions are stored, default: %(default)s",) 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] @@ -28,7 +29,7 @@ script_path = os.path.dirname(modules_path) data_path = cli.data_dir models_config = cli.models_dir or config.get('models_dir') or 'models' models_path = models_config if os.path.isabs(models_config) else os.path.join(data_path, models_config) -extensions_dir = os.path.join(data_path, "extensions") +extensions_dir = cli.extensions_dir or os.path.join(data_path, "extensions") extensions_builtin_dir = "extensions-builtin" sd_configs_path = os.path.join(script_path, "configs") sd_default_config = os.path.join(sd_configs_path, "v1-inference.yaml") diff --git a/modules/shared.py b/modules/shared.py index d1aa82ba2..d99ae3482 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -1129,6 +1129,7 @@ class Options: self.filename = config_filename if filename is None: filename = self.filename + filename = os.path.abspath(filename) if cmd_opts.freeze: log.warning(f'Setting: fn="{filename}" save disabled') return @@ -1173,6 +1174,7 @@ class Options: def load(self, filename=None): if filename is None: filename = self.filename + filename = os.path.abspath(filename) if not os.path.isfile(filename): log.debug(f'Settings: fn="{filename}" created') self.save(filename)