feat(backend): gate Other Models behind opt-in management toggles

Other Models management is now opt-in: enable_other_models (default false)
plus the enabled_other_sub_types allow-list replace the unreleased additive
enabled_other_folders key.

- config._get_enabled_other_folder_keys() is the single scan gate; a new
  refresh_other_roots() rebuilds roots and preview roots on toggle.
- ModelScanner gains a _should_keep_cached_entry() hydration hook and
  on_library_changed(reconcile=...) so switching a sub_type off drops its
  entries (and hash/autov3 rows) at load time and switching it on rescans.
- OtherScanner filters location-derived entries accordingly.
- Other routes reject every other type while off (or a disabled sub_type) and
  expose an "other_disabled" page flag; download routing returns a disabled
  marker instead of guessing; the download manager refuses other-type
  downloads and default-path routing for switched-off sub_types.
- Doctor / init-status / refresh-all skip the other scanner while off; the
  scanner stays registered so staged pending-deletes still merge.
- Tests updated with explicit opt-in fixtures plus new gating coverage.
This commit is contained in:
Will Miao
2026-09-13 07:59:28 +08:00
parent f88fe2665c
commit 28fbb86dce
19 changed files with 583 additions and 84 deletions
+24 -18
View File
@@ -18,7 +18,6 @@ import time
from .utils.cache_paths import CacheType, get_cache_file_path, get_legacy_cache_paths
from .utils.constants import (
DEFAULT_OTHER_MODEL_FOLDERS,
OTHER_MODEL_FOLDER_SUBTYPES,
)
from .utils.settings_paths import (
@@ -1150,28 +1149,25 @@ class Config:
def _get_enabled_other_folder_keys(self) -> List[str]:
"""Return the OTHER_MODEL_FOLDER_SUBTYPES keys that are enabled.
Default-enabled categories come from DEFAULT_OTHER_MODEL_FOLDERS;
opt-in categories (e.g. controlnet) are added via the
``enabled_other_folders`` setting (a list of folder_paths keys).
Other Models management is opt-in: while ``enable_other_models`` is
off (the default) no other-model folder is scanned at all. When it is
on, only the folder keys of the enabled sub_types are scanned
(text_encoder merges ``text_encoders`` with the legacy ``clip`` key).
"""
keys = list(DEFAULT_OTHER_MODEL_FOLDERS)
try:
from .services.settings_manager import get_settings_manager
extra = get_settings_manager().get("enabled_other_folders", [])
enabled_sub_types = get_settings_manager().get_enabled_other_sub_types()
except Exception:
extra = []
if isinstance(extra, str):
extra = [extra]
if isinstance(extra, Iterable):
for key in extra:
if (
isinstance(key, str)
and key in OTHER_MODEL_FOLDER_SUBTYPES
and key not in keys
):
keys.append(key)
return keys
enabled_sub_types = []
if not enabled_sub_types:
return []
allowed = set(enabled_sub_types)
return [
key
for key, sub_type in OTHER_MODEL_FOLDER_SUBTYPES.items()
if sub_type in allowed
]
def _prepare_other_paths(
self, folder_path_map: Mapping[str, Iterable[str]]
@@ -1427,6 +1423,16 @@ class Config:
logger.warning(f"Error initializing other model paths: {e}")
return []
def refresh_other_roots(self) -> None:
"""Rebuild other-model roots after the management toggles changed.
Called when ``enable_other_models`` / ``enabled_other_sub_types`` are
updated so the scanner immediately reflects the new folder set without
a full application restart.
"""
self.other_roots = self._init_other_paths()
self._rebuild_preview_roots()
def get_preview_static_url(self, preview_path: str) -> str:
if not preview_path:
return ""