diff --git a/.gitignore b/.gitignore index 9bc6d5a24..3bb999538 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ tunableop_results*.csv /*.txt /*.mp3 /*.lnk +/*.swp !webui.bat !webui.sh !package.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d61b70fb8..9f5808a1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log for SD.Next -## Update for 2025-10-19 +## Update for 2025-10-23 - **Features** - **offline mode**: enable in *settings -> hugginface* @@ -13,9 +13,18 @@ - improved SDNQ SVD and low-bit matmul performance - **Other** - **scheduler** add base and max shift parameters for flow-matching samplers + - enhance `--optional` flag to pre-install optional packages + - add `[lora]` to recognized filename patterns - **Fixes** - startup error with `--profile` enabled if using `--skip` - restore orig init image for each batch sequence + - fix modernui hints layout + - fix `wan-2.2-a14b` stage selection + - fix `wan-2.2-5b` vae decode + - disabling live preview should not disable progress updates + - video tab create `params.txt` with metadata + - fix full-screen image-viewer toolbar actions with control tab + - improve filename sanitization ## Update for 2025-10-18 diff --git a/extensions-builtin/sdnext-modernui b/extensions-builtin/sdnext-modernui index 8f6427aa0..80d1030c6 160000 --- a/extensions-builtin/sdnext-modernui +++ b/extensions-builtin/sdnext-modernui @@ -1 +1 @@ -Subproject commit 8f6427aa037b654ae664a0197c794e48fdbbc648 +Subproject commit 80d1030c6bf161c301bed7ec14ba43eabe8c69bb diff --git a/installer.py b/installer.py index f9a2954b2..6a8948ff7 100644 --- a/installer.py +++ b/installer.py @@ -386,6 +386,9 @@ def pip(arg: str, ignore: bool = False, quiet: bool = True, uv = True): t_start = time.time() originalArg = arg arg = arg.replace('>=', '==') + if opts.get('offline_mode', False): + log.warning('Offline mode enabled') + return package = arg.replace("install", "").replace("--upgrade", "").replace("--no-deps", "").replace("--force-reinstall", "").replace(" ", " ").strip() uv = uv and args.uv and not package.startswith('git+') pipCmd = "uv pip" if uv else "pip" @@ -608,7 +611,7 @@ def check_diffusers(): if args.skip_git: install('diffusers') return - sha = '23ebbb4bc81a17ebea17cb7cb94f301199e49a7f' # diffusers commit hash + sha = 'b3e56e71fb7c73601851bb83e7583f113f563d26' # diffusers commit hash # if args.use_rocm or args.use_zluda or args.use_directml: # sha = '043ab2520f6a19fce78e6e060a68dbc947edb9f9' # lock diffusers versions for now pkg = pkg_resources.working_set.by_key.get('diffusers', None) @@ -1270,18 +1273,23 @@ def install_insightface(): def install_optional(): t_start = time.time() log.info('Installing optional requirements...') - install('--no-build-isolation git+https://github.com/Disty0/BasicSR@23c1fb6f5c559ef5ce7ad657f2fa56e41b121754', 'basicsr') - install('--no-build-isolation git+https://github.com/Disty0/GFPGAN@ae0f7e44fafe0ef4716f3c10067f8f379b74c21c', 'gfpgan') - install('clean-fid', quiet=True) - install('pillow-jxl-plugin==1.3.4', ignore=True, quiet=True) - install('optimum-quanto==0.2.7', ignore=True, quiet=True) - install('torchao==0.10.0', ignore=True, quiet=True) - install('bitsandbytes==0.47.0', ignore=True, quiet=True) - install('nvidia-ml-py', ignore=True, quiet=True) - install('ultralytics==8.3.40', ignore=True, quiet=True) - install('Cython', ignore=True, quiet=True) + install('--no-build-isolation git+https://github.com/Disty0/BasicSR@23c1fb6f5c559ef5ce7ad657f2fa56e41b121754', 'basicsr', ignore=True, quiet=True) + install('--no-build-isolation git+https://github.com/Disty0/GFPGAN@ae0f7e44fafe0ef4716f3c10067f8f379b74c21c', 'gfpgan', ignore=True, quiet=True) install('av', ignore=True, quiet=True) - install('gguf', ignore=True) + install('beautifulsoup4', ignore=True, quiet=True) + install('bitsandbytes==0.47.0', ignore=True, quiet=True) + install('clean-fid', ignore=True, quiet=True) + install('clip_interrogator==0.6.0', ignore=True, quiet=True) + install('Cython', ignore=True, quiet=True) + install('gguf', ignore=True, quiet=True) + install('git+https://github.com/tencent-ailab/IP-Adapter.git', 'ip_adapter', ignore=True, quiet=True) + install('hf_transfer', ignore=True, quiet=True) + install('hf_xet', ignore=True, quiet=True) + install('nvidia-ml-py', ignore=True, quiet=True) + install('optimum-quanto==0.2.7', ignore=True, quiet=True) + install('pillow-jxl-plugin==1.3.4', ignore=True, quiet=True) + install('torchao==0.10.0', ignore=True, quiet=True) + install('ultralytics==8.3.40', ignore=True, quiet=True) try: import gguf scripts_dir = os.path.join(os.path.dirname(gguf.__file__), '..', 'scripts') diff --git a/javascript/imageViewer.js b/javascript/imageViewer.js index e7eaca7a8..8788ca689 100644 --- a/javascript/imageViewer.js +++ b/javascript/imageViewer.js @@ -52,12 +52,15 @@ function modalImageSwitch(offset) { } function modalSaveImage(event) { - if (gradioApp().getElementById('tab_txt2img').style.display !== 'none') gradioApp().getElementById('save_txt2img').click(); - else if (gradioApp().getElementById('tab_img2img').style.display !== 'none') gradioApp().getElementById('save_img2img').click(); - else if (gradioApp().getElementById('tab_process').style.display !== 'none') gradioApp().getElementById('save_extras').click(); + const tabName = getENActiveTab(); + const saveBtn = gradioApp().getElementById(`save_${tabName}`); + log('modalSaveImage', tabName, saveBtn); + if (saveBtn) saveBtn.click(); + modalImageSwitch(0); } function modalKeyHandler(event) { + log('modalKeyHandler', event.key); switch (event.key) { case 's': modalSaveImage(); @@ -158,6 +161,7 @@ function modalZoomToggle(event) { const modalImage = gradioApp().getElementById('modalImage'); modalZoomSet(modalImage, !modalImage.classList.contains('modalImageFullscreen')); event.stopPropagation(); + modalImageSwitch(0); } function modalTileToggle(event) { @@ -172,12 +176,15 @@ function modalTileToggle(event) { modal.style.setProperty('background-image', `url(${modalImage.src})`); } event.stopPropagation(); + modalImageSwitch(0); } function modalResetInstance(event) { const modalImage = document.getElementById('modalImage'); previewInstance.dispose(); previewInstance = panzoom(modalImage, { zoomSpeed: 0.05, minZoom: 0.1, maxZoom: 5.0, filterKey: (/* e, dx, dy, dz */) => true }); + event.stopPropagation(); + modalImageSwitch(0); } function modalToggleParams(event) { @@ -188,6 +195,7 @@ function modalToggleParams(event) { modalExif.style.display = 'none'; } event.stopPropagation(); + modalImageSwitch(0); } function galleryClickEventHandler(event) { diff --git a/javascript/progressBar.js b/javascript/progressBar.js index 5c148836b..5062517e0 100644 --- a/javascript/progressBar.js +++ b/javascript/progressBar.js @@ -132,7 +132,7 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres }; const start = (id_task, id_live_preview) => { // eslint-disable-line no-shadow - if (!opts.live_previews_enable || opts.live_preview_refresh_period === 0 || opts.show_progress_every_n_steps === 0) return; + if (opts.live_preview_refresh_period === 0) return; const request_id = document.hidden ? -1 : id_live_preview; const onProgressHandler = (res) => { diff --git a/modules/civitai/search_civitai.py b/modules/civitai/search_civitai.py index 0465411b9..488a05cdd 100644 --- a/modules/civitai/search_civitai.py +++ b/modules/civitai/search_civitai.py @@ -108,7 +108,7 @@ def search_civitai( global models # pylint: disable=global-statement import requests from urllib.parse import urlencode - install('bs4') # Ensure BeautifulSoup is installed + install('beautifulsoup4') if len(query) == 0: log.error('CivitAI: empty query') diff --git a/modules/control/proc/depth_anything/__init__.py b/modules/control/proc/depth_anything/__init__.py index cf7ee92e2..4709727e4 100644 --- a/modules/control/proc/depth_anything/__init__.py +++ b/modules/control/proc/depth_anything/__init__.py @@ -27,7 +27,7 @@ class DepthAnythingDetector: PrepareForNet()]) @classmethod - def from_pretrained(cls, pretrained_model_or_path: str, cache_dir: str) -> str: + def from_pretrained(cls, pretrained_model_or_path: str, cache_dir: str, local_files_only=False) -> str: from modules.control.proc.depth_anything.dpt import DPT_DINOv2 import huggingface_hub as hf model = ( @@ -40,7 +40,7 @@ class DepthAnythingDetector: .to(devices.device) .eval() ) - model_path = hf.hf_hub_download(repo_id=pretrained_model_or_path, filename="pytorch_model.bin", cache_dir=cache_dir) + model_path = hf.hf_hub_download(repo_id=pretrained_model_or_path, filename="pytorch_model.bin", cache_dir=cache_dir, local_files_only=local_files_only) model_dict = torch.load(model_path) model.load_state_dict(model_dict) return cls(model) diff --git a/modules/control/proc/dwpose/__init__.py b/modules/control/proc/dwpose/__init__.py index e97ade3e6..9e8466a69 100644 --- a/modules/control/proc/dwpose/__init__.py +++ b/modules/control/proc/dwpose/__init__.py @@ -51,7 +51,7 @@ def check_dependencies(): status = [installed(p, reload=False, quiet=True) for p in packages] debug(f'DWPose required={packages} status={status}') if not all(status): - log.info(f'Installing DWPose dependencies: {packages}') + log.info(f'Installing dependencies: for=dwpose packages={packages}') cmd = 'install --upgrade --no-deps --force-reinstall ' pkgs = ' '.join(packages) pip(cmd + pkgs, ignore=False, quiet=True, uv=False) diff --git a/modules/control/proc/hed.py b/modules/control/proc/hed.py index e0144c41f..06610921e 100644 --- a/modules/control/proc/hed.py +++ b/modules/control/proc/hed.py @@ -60,12 +60,12 @@ class HEDdetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None, local_files_only=False): filename = filename or "ControlNetHED.pth" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) model = ControlNetHED_Apache2() model.load_state_dict(torch.load(model_path, map_location='cpu')) model.float().eval() diff --git a/modules/control/proc/leres/__init__.py b/modules/control/proc/leres/__init__.py index 63040bc49..e2f358c7d 100644 --- a/modules/control/proc/leres/__init__.py +++ b/modules/control/proc/leres/__init__.py @@ -20,13 +20,13 @@ class LeresDetector: self.pix2pixmodel = pix2pixmodel @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, pix2pix_filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, pix2pix_filename=None, cache_dir=None, local_files_only=False): filename = filename or "res101.pth" pix2pix_filename = pix2pix_filename or "latest_net_G.pth" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) checkpoint = torch.load(model_path, map_location=torch.device('cpu')) model = RelDepthModel(backbone='resnext101') model.load_state_dict(strip_prefix_if_present(checkpoint['depth_model'], "module."), strict=True) @@ -34,7 +34,7 @@ class LeresDetector: if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, pix2pix_filename) else: - model_path = hf_hub_download(pretrained_model_or_path, pix2pix_filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, pix2pix_filename, cache_dir=cache_dir, local_files_only=local_files_only) opt = TestOptions().parse() if not torch.cuda.is_available(): opt.gpu_ids = [] # cpu mode diff --git a/modules/control/proc/lineart.py b/modules/control/proc/lineart.py index afd55d9a7..61d3ce8b4 100644 --- a/modules/control/proc/lineart.py +++ b/modules/control/proc/lineart.py @@ -95,7 +95,7 @@ class LineartDetector: self.model_coarse = coarse_model @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, coarse_filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, coarse_filename=None, cache_dir=None, local_files_only=False): filename = filename or "sk_model.pth" coarse_filename = coarse_filename or "sk_model2.pth" @@ -103,8 +103,8 @@ class LineartDetector: model_path = os.path.join(pretrained_model_or_path, filename) coarse_model_path = os.path.join(pretrained_model_or_path, coarse_filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) - coarse_model_path = hf_hub_download(pretrained_model_or_path, coarse_filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) + coarse_model_path = hf_hub_download(pretrained_model_or_path, coarse_filename, cache_dir=cache_dir, local_files_only=local_files_only) model = Generator(3, 1, 3) model.load_state_dict(torch.load(model_path, map_location=torch.device('cpu'))) diff --git a/modules/control/proc/lineart_anime.py b/modules/control/proc/lineart_anime.py index 541fcad61..c70da3784 100644 --- a/modules/control/proc/lineart_anime.py +++ b/modules/control/proc/lineart_anime.py @@ -117,12 +117,12 @@ class LineartAnimeDetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None, local_files_only=False): filename = filename or "netG.pth" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) norm_layer = functools.partial(nn.InstanceNorm2d, affine=False, track_running_stats=False) net = UnetGenerator(3, 1, 8, 64, norm_layer=norm_layer, use_dropout=False) ckpt = torch.load(model_path) diff --git a/modules/control/proc/midas/__init__.py b/modules/control/proc/midas/__init__.py index ba6d1b0e5..0ca4b4fee 100644 --- a/modules/control/proc/midas/__init__.py +++ b/modules/control/proc/midas/__init__.py @@ -17,7 +17,7 @@ class MidasDetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, model_type="dpt_hybrid", filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, model_type="dpt_hybrid", filename=None, cache_dir=None, local_files_only=False): if pretrained_model_or_path == "lllyasviel/ControlNet": filename = filename or "annotator/ckpts/dpt_hybrid-midas-501f0c75.pt" else: @@ -25,7 +25,7 @@ class MidasDetector: if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) model = MiDaSInference(model_type=model_type, model_path=model_path) return cls(model) diff --git a/modules/control/proc/mlsd/__init__.py b/modules/control/proc/mlsd/__init__.py index ea26c5b0d..e46ac865c 100644 --- a/modules/control/proc/mlsd/__init__.py +++ b/modules/control/proc/mlsd/__init__.py @@ -16,7 +16,7 @@ class MLSDdetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None, local_files_only=False): if pretrained_model_or_path == "lllyasviel/ControlNet": filename = filename or "annotator/ckpts/mlsd_large_512_fp32.pth" else: @@ -24,7 +24,7 @@ class MLSDdetector: if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) model = MobileV2_MLSD_Large() model.load_state_dict(torch.load(model_path), strict=True) model.eval() diff --git a/modules/control/proc/normalbae/__init__.py b/modules/control/proc/normalbae/__init__.py index ba10570c6..16e613dc9 100644 --- a/modules/control/proc/normalbae/__init__.py +++ b/modules/control/proc/normalbae/__init__.py @@ -33,12 +33,12 @@ class NormalBaeDetector: self.norm = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None, local_files_only=False): filename = filename or "scannet.pt" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) args = types.SimpleNamespace() args.mode = 'client' args.architecture = 'BN' diff --git a/modules/control/proc/openpose/__init__.py b/modules/control/proc/openpose/__init__.py index 80649e213..746351718 100644 --- a/modules/control/proc/openpose/__init__.py +++ b/modules/control/proc/openpose/__init__.py @@ -76,7 +76,7 @@ class OpenposeDetector: self.face_estimation = face_estimation @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, hand_filename=None, face_filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, hand_filename=None, face_filename=None, cache_dir=None, local_files_only=False): if pretrained_model_or_path == "lllyasviel/ControlNet": filename = filename or "annotator/ckpts/body_pose_model.pth" @@ -96,9 +96,9 @@ class OpenposeDetector: hand_model_path = os.path.join(pretrained_model_or_path, hand_filename) face_model_path = os.path.join(face_pretrained_model_or_path, face_filename) else: - body_model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) - hand_model_path = hf_hub_download(pretrained_model_or_path, hand_filename, cache_dir=cache_dir) - face_model_path = hf_hub_download(face_pretrained_model_or_path, face_filename, cache_dir=cache_dir) + body_model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) + hand_model_path = hf_hub_download(pretrained_model_or_path, hand_filename, cache_dir=cache_dir, local_files_only=local_files_only) + face_model_path = hf_hub_download(face_pretrained_model_or_path, face_filename, cache_dir=cache_dir, local_files_only=local_files_only) body_estimation = Body(body_model_path) hand_estimation = Hand(hand_model_path) diff --git a/modules/control/proc/pidi.py b/modules/control/proc/pidi.py index 078525f2b..2b18eedf9 100644 --- a/modules/control/proc/pidi.py +++ b/modules/control/proc/pidi.py @@ -16,12 +16,12 @@ class PidiNetDetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None, local_files_only=False): filename = filename or "table5_pidinet.pth" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) model = pidinet() model.load_state_dict({k.replace('module.', ''): v for k, v in torch.load(model_path)['state_dict'].items()}) model.eval() diff --git a/modules/control/proc/segment_anything/__init__.py b/modules/control/proc/segment_anything/__init__.py index d698b46fc..121421c18 100644 --- a/modules/control/proc/segment_anything/__init__.py +++ b/modules/control/proc/segment_anything/__init__.py @@ -23,12 +23,12 @@ class SamDetector: self.model = mask_generator @classmethod - def from_pretrained(cls, model_path, filename, model_type, cache_dir=None): + def from_pretrained(cls, model_path, filename, model_type, cache_dir=None, local_files_only=False): """ Possible model_type : vit_h, vit_l, vit_b, vit_t download weights from https://github.com/facebookresearch/segment-anything """ - model_path = hf_hub_download(model_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(model_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) sam = sam_model_registry[model_type](checkpoint=model_path) sam.to(devices.device) mask_generator = SamAutomaticMaskGenerator(sam) diff --git a/modules/control/proc/zoe/__init__.py b/modules/control/proc/zoe/__init__.py index bb18201e0..a6550d605 100644 --- a/modules/control/proc/zoe/__init__.py +++ b/modules/control/proc/zoe/__init__.py @@ -20,12 +20,12 @@ class ZoeDetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, model_type="zoedepth", filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, model_type="zoedepth", filename=None, cache_dir=None, local_files_only=False): filename = filename or "ZoeD_M12_N.pt" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) if model_type == "zoedepth": model_cls = ZoeDepth elif model_type == "zoedepth_nk": diff --git a/modules/control/processors.py b/modules/control/processors.py index edb02bd09..4d66bfac6 100644 --- a/modules/control/processors.py +++ b/modules/control/processors.py @@ -181,9 +181,15 @@ class Processor(): self.model = None self.processor_id = processor_id devices.torch_gc(force=True, reason='processor') - # self.override = None - # devices.torch_gc() self.load_config = { 'cache_dir': cache_dir } + from modules.shared import opts + if opts.offline_mode: + self.load_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') + def config(self, processor_id = None): if processor_id is not None: diff --git a/modules/control/units/controlnet.py b/modules/control/units/controlnet.py index 990ca2e4a..7fd9cab0f 100644 --- a/modules/control/units/controlnet.py +++ b/modules/control/units/controlnet.py @@ -205,6 +205,12 @@ class ControlNet(): self.load_config = { 'cache_dir': cache_dir } if load_config is not None: self.load_config.update(load_config) + if opts.offline_mode: + self.load_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') if model_id is not None: self.load() diff --git a/modules/control/units/lite.py b/modules/control/units/lite.py index fbabe2280..107ebb0a0 100644 --- a/modules/control/units/lite.py +++ b/modules/control/units/lite.py @@ -108,8 +108,15 @@ class ControlLLLite(): self.model = ControlNetLLLite(model_path) else: import huggingface_hub as hf + offline_config = {} + if opts.offline_mode: + offline_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') folder, filename = os.path.split(model_path) - model_path = hf.hf_hub_download(repo_id=folder, filename=f'{filename}.safetensors', cache_dir=cache_dir) + model_path = hf.hf_hub_download(repo_id=folder, filename=f'{filename}.safetensors', cache_dir=cache_dir, **offline_config) self.model = ControlNetLLLite(model_path) if self.device is not None: self.model.to(self.device) diff --git a/modules/control/units/t2iadapter.py b/modules/control/units/t2iadapter.py index 35ba8ab43..b9e049779 100644 --- a/modules/control/units/t2iadapter.py +++ b/modules/control/units/t2iadapter.py @@ -2,7 +2,7 @@ import os import time from typing import Union import threading -from diffusers import pipelines, StableDiffusionPipeline, StableDiffusionXLPipeline, T2IAdapter, MultiAdapter, StableDiffusionAdapterPipeline, StableDiffusionXLAdapterPipeline # pylint: disable=unused-import +from diffusers import StableDiffusionPipeline, StableDiffusionXLPipeline, T2IAdapter, MultiAdapter, StableDiffusionAdapterPipeline, StableDiffusionXLAdapterPipeline # pylint: disable=unused-import from installer import log from modules import errors, sd_models from modules.control.units import detect @@ -104,6 +104,13 @@ class Adapter(): return model_path, model_args = all_models[model_id] self.load_config.update(model_args) + from modules.shared import opts + if opts.offline_mode: + self.load_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') if model_path is None: log.error(f'Control {what} model load failed: id="{model_id}" error=unknown model id') return @@ -168,6 +175,7 @@ class AdapterPipeline(): adapter=adapter, ) sd_models.move_model(self.pipeline, pipeline.device) + sd_models.apply_balanced_offload(self.pipeline, force=True) elif detect.is_sd15(pipeline): self.pipeline = StableDiffusionAdapterPipeline( vae=pipeline.vae, @@ -181,6 +189,7 @@ class AdapterPipeline(): adapter=adapter, ) sd_models.move_model(self.pipeline, pipeline.device) + sd_models.apply_balanced_offload(self.pipeline, force=True) else: log.error(f'Control {what} pipeline: class={pipeline.__class__.__name__} unsupported model type') return diff --git a/modules/control/units/xs.py b/modules/control/units/xs.py index f727a0111..2d56fd7ff 100644 --- a/modules/control/units/xs.py +++ b/modules/control/units/xs.py @@ -100,6 +100,12 @@ class ControlNetXS(): # log.debug(f'Control {what} model: id="{model_id}" path="{model_path}" already loaded') return self.load_config['time_embedding_mix'] = time_embedding_mix + if opts.offline_mode: + self.load_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') log.debug(f'Control {what} model loading: id="{model_id}" path="{model_path}" {self.load_config}') if model_path.endswith('.safetensors'): self.model = ControlNetXSModel.from_single_file(model_path, **self.load_config) @@ -140,6 +146,7 @@ class ControlNetXSPipeline(): controlnet=controlnet, # can be a list ) sd_models.move_model(self.pipeline, pipeline.device) + sd_models.apply_balanced_offload(self.pipeline, force=True) elif detect.is_sd15(pipeline): self.pipeline = StableDiffusionControlNetXSPipeline( vae=pipeline.vae, @@ -153,6 +160,7 @@ class ControlNetXSPipeline(): controlnet=controlnet, # can be a list ) sd_models.move_model(self.pipeline, pipeline.device) + sd_models.apply_balanced_offload(self.pipeline, force=True) else: log.error(f'Control {what} pipeline: class={pipeline.__class__.__name__} unsupported model type') return diff --git a/modules/framepack/framepack_load.py b/modules/framepack/framepack_load.py index ed9fdde43..4b99126ef 100644 --- a/modules/framepack/framepack_load.py +++ b/modules/framepack/framepack_load.py @@ -1,3 +1,4 @@ +import os import time from modules import shared, devices, errors, sd_models, sd_checkpoint, model_quant @@ -128,23 +129,31 @@ def load_model(variant:str=None, pipeline:str=None, text_encoder:str=None, text_ sd_models.hf_auth_check(model["text_encoder"]["repo"]) sd_models.hf_auth_check(model["text_encoder_2"]["repo"]) + offline_config = {} + if shared.opts.offline_mode: + offline_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') + shared.log.debug(f'FramePack load: module=llm {model["text_encoder"]}') load_args, quant_args = model_quant.get_dit_args({}, module='TE', device_map=True) - text_encoder = LlamaModel.from_pretrained(model["text_encoder"]["repo"], subfolder=model["text_encoder"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **load_args, **quant_args) - tokenizer = LlamaTokenizerFast.from_pretrained(model["tokenizer"]["repo"], subfolder=model["tokenizer"]["subfolder"], cache_dir=shared.opts.hfcache_dir) + text_encoder = LlamaModel.from_pretrained(model["text_encoder"]["repo"], subfolder=model["text_encoder"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **load_args, **quant_args, **offline_config) + tokenizer = LlamaTokenizerFast.from_pretrained(model["tokenizer"]["repo"], subfolder=model["tokenizer"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **offline_config) text_encoder.requires_grad_(False) text_encoder.eval() sd_models.move_model(text_encoder, devices.cpu) shared.log.debug(f'FramePack load: module=te {model["text_encoder_2"]}') - text_encoder_2 = CLIPTextModel.from_pretrained(model["text_encoder_2"]["repo"], subfolder=model["text_encoder_2"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir) - tokenizer_2 = CLIPTokenizer.from_pretrained(model["pipeline"]["repo"], subfolder='tokenizer_2', cache_dir=shared.opts.hfcache_dir) + text_encoder_2 = CLIPTextModel.from_pretrained(model["text_encoder_2"]["repo"], subfolder=model["text_encoder_2"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir, **offline_config) + tokenizer_2 = CLIPTokenizer.from_pretrained(model["pipeline"]["repo"], subfolder='tokenizer_2', cache_dir=shared.opts.hfcache_dir, **offline_config) text_encoder_2.requires_grad_(False) text_encoder_2.eval() sd_models.move_model(text_encoder_2, devices.cpu) shared.log.debug(f'FramePack load: module=vae {model["vae"]}') - vae = AutoencoderKLHunyuanVideo.from_pretrained(model["vae"]["repo"], subfolder=model["vae"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir) + vae = AutoencoderKLHunyuanVideo.from_pretrained(model["vae"]["repo"], subfolder=model["vae"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir, **offline_config) vae.requires_grad_(False) vae.eval() vae.enable_slicing() @@ -152,8 +161,8 @@ def load_model(variant:str=None, pipeline:str=None, text_encoder:str=None, text_ sd_models.move_model(vae, devices.cpu) shared.log.debug(f'FramePack load: module=encoder {model["feature_extractor"]} model={model["image_encoder"]}') - feature_extractor = SiglipImageProcessor.from_pretrained(model["feature_extractor"]["repo"], subfolder=model["feature_extractor"]["subfolder"], cache_dir=shared.opts.hfcache_dir) - image_encoder = SiglipVisionModel.from_pretrained(model["image_encoder"]["repo"], subfolder=model["image_encoder"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir) + feature_extractor = SiglipImageProcessor.from_pretrained(model["feature_extractor"]["repo"], subfolder=model["feature_extractor"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **offline_config) + image_encoder = SiglipVisionModel.from_pretrained(model["image_encoder"]["repo"], subfolder=model["image_encoder"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir, **offline_config) image_encoder.requires_grad_(False) image_encoder.eval() sd_models.move_model(image_encoder, devices.cpu) @@ -161,7 +170,7 @@ def load_model(variant:str=None, pipeline:str=None, text_encoder:str=None, text_ shared.log.debug(f'FramePack load: module=transformer {model["transformer"]}') dit_repo = model["transformer"]["repo"] load_args, quant_args = model_quant.get_dit_args({}, module='Model', device_map=True) - transformer = HunyuanVideoTransformer3DModelPacked.from_pretrained(dit_repo, subfolder=model["transformer"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **load_args, **quant_args) + transformer = HunyuanVideoTransformer3DModelPacked.from_pretrained(dit_repo, subfolder=model["transformer"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **load_args, **quant_args, **offline_config) transformer.high_quality_fp32_output_for_inference = False transformer.requires_grad_(False) transformer.eval() diff --git a/modules/framepack/framepack_worker.py b/modules/framepack/framepack_worker.py index cda5677f8..6558c0765 100644 --- a/modules/framepack/framepack_worker.py +++ b/modules/framepack/framepack_worker.py @@ -308,12 +308,26 @@ def worker( if is_last_section: break - total_generated_frames, _video_filename = save_video(history_pixels, mp4_fps, mp4_codec, mp4_opt, mp4_ext, mp4_sf, mp4_video, mp4_frames, mp4_interpolate, pbar=pbar, stream=stream, metadata=metadata) + total_generated_frames, _video_filename = save_video( + None, + history_pixels, + mp4_fps, + mp4_codec, + mp4_opt, + mp4_ext, + mp4_sf, + mp4_video, + mp4_frames, + mp4_interpolate, + pbar=pbar, + stream=stream, + metadata=metadata, + ) except AssertionError: shared.log.info('FramePack: interrupted') if shared.opts.keep_incomplete: - save_video(history_pixels, mp4_fps, mp4_codec, mp4_opt, mp4_ext, mp4_sf, mp4_video, mp4_frames, mp4_interpolate=0, stream=stream, metadata=metadata) + save_video(None, history_pixels, mp4_fps, mp4_codec, mp4_opt, mp4_ext, mp4_sf, mp4_video, mp4_frames, mp4_interpolate=0, stream=stream, metadata=metadata) except Exception as e: shared.log.error(f'FramePack: {e}') errors.display(e, 'FramePack') diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 1427e53cc..df0ea1643 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -3,7 +3,6 @@ import io import os from PIL import Image import gradio as gr -from modules.paths import params_path from modules import shared, gr_tempdir, script_callbacks, images from modules.infotext import parse, mapping, quote, unquote # pylint: disable=unused-import @@ -204,6 +203,7 @@ def create_override_settings_dict(text_pairs): def connect_paste(button, local_paste_fields, input_comp, override_settings_component, tabname): def paste_func(prompt): + from modules.paths import params_path if prompt is None or len(prompt.strip()) == 0: if os.path.exists(params_path): with open(params_path, "r", encoding="utf8") as file: diff --git a/modules/images_namegen.py b/modules/images_namegen.py index 849450d5b..b89adbb7a 100644 --- a/modules/images_namegen.py +++ b/modules/images_namegen.py @@ -1,6 +1,7 @@ import re import os import time +import unicodedata import uuid import string import hashlib @@ -23,8 +24,8 @@ NOTHING = object() class FilenameGenerator: replacements = { - 'width': lambda self: self.image.width, - 'height': lambda self: self.image.height, + 'width': lambda self: self.width, + 'height': lambda self: self.height, 'batch_number': lambda self: self.batch_number, 'iter_number': lambda self: self.iter_number, 'num': lambda self: NOTHING if self.p.n_iter == 1 and self.p.batch_size == 1 else self.p.iteration * self.p.batch_size + self.p.batch_index + 1, @@ -32,8 +33,8 @@ class FilenameGenerator: 'date': lambda self: datetime.datetime.now().strftime('%Y-%m-%d'), 'datetime': lambda self, *args: self.datetime(*args), # accepts formats: [datetime], [datetime], [datetime