2025-12-09
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
import bpy # type: ignore
|
||||
|
||||
RAINYS_EXTENSIONS_REPO_NAME = "Rainy's Extensions"
|
||||
RAINYS_EXTENSIONS_REPO_URL = (
|
||||
"https://raw.githubusercontent.com/RaincloudTheDragon/rainys-blender-extensions/refs/heads/main/index.json"
|
||||
)
|
||||
|
||||
_BOOTSTRAP_DONE = False
|
||||
|
||||
|
||||
def _log(message: str) -> None:
|
||||
print(f"RainysExtensionsCheck: {message}")
|
||||
|
||||
|
||||
def ensure_rainys_extensions_repo(_deferred: bool = False) -> None:
|
||||
"""
|
||||
Ensure the Rainy's Extensions repository is registered in Blender.
|
||||
|
||||
Safe to import and call from multiple add-ons; the helper guards against doing the
|
||||
work more than once per Blender session.
|
||||
"""
|
||||
global _BOOTSTRAP_DONE
|
||||
|
||||
if _BOOTSTRAP_DONE:
|
||||
return
|
||||
|
||||
_log("starting repository verification")
|
||||
|
||||
context_class_name = type(bpy.context).__name__
|
||||
if context_class_name == "_RestrictContext":
|
||||
if _deferred:
|
||||
_log("context still restricted after deferral; aborting repo check")
|
||||
return
|
||||
|
||||
_log("context restricted; scheduling repo check retry")
|
||||
|
||||
def _retry():
|
||||
ensure_rainys_extensions_repo(_deferred=True)
|
||||
return None
|
||||
|
||||
bpy.app.timers.register(_retry, first_interval=0.5)
|
||||
return
|
||||
|
||||
prefs = getattr(bpy.context, "preferences", None)
|
||||
if prefs is None:
|
||||
_log("no preferences available on context; skipping")
|
||||
return
|
||||
|
||||
preferences_changed = False
|
||||
addon_prefs = None
|
||||
addon_entry = None
|
||||
if hasattr(getattr(prefs, "addons", None), "get"):
|
||||
addon_entry = prefs.addons.get(__name__)
|
||||
elif hasattr(prefs, "addons"):
|
||||
try:
|
||||
addon_entry = prefs.addons[__name__]
|
||||
except Exception:
|
||||
addon_entry = None
|
||||
if addon_entry:
|
||||
addon_prefs = getattr(addon_entry, "preferences", None)
|
||||
addon_repo_initialized = bool(
|
||||
addon_prefs and getattr(addon_prefs, "repo_initialized", False)
|
||||
)
|
||||
|
||||
experimental = getattr(prefs, "experimental", None)
|
||||
if experimental and hasattr(experimental, "use_extension_platform"):
|
||||
if not experimental.use_extension_platform:
|
||||
experimental.use_extension_platform = True
|
||||
preferences_changed = True
|
||||
_log("enabled experimental extension platform")
|
||||
|
||||
repositories = None
|
||||
extensions_obj = getattr(prefs, "extensions", None)
|
||||
if extensions_obj:
|
||||
if hasattr(extensions_obj, "repos"):
|
||||
repositories = extensions_obj.repos
|
||||
elif hasattr(extensions_obj, "repositories"):
|
||||
repositories = extensions_obj.repositories
|
||||
|
||||
if repositories is None:
|
||||
filepaths = getattr(prefs, "filepaths", None)
|
||||
repositories = getattr(filepaths, "extension_repos", None) if filepaths else None
|
||||
|
||||
if repositories is None:
|
||||
_log("extension repositories collection missing; skipping")
|
||||
return
|
||||
|
||||
def _repo_matches(repo) -> bool:
|
||||
return getattr(repo, "remote_url", "") == RAINYS_EXTENSIONS_REPO_URL or getattr(
|
||||
repo, "url", ""
|
||||
) == RAINYS_EXTENSIONS_REPO_URL
|
||||
|
||||
matching_indices = [idx for idx, repo in enumerate(repositories) if _repo_matches(repo)]
|
||||
|
||||
target_repo = None
|
||||
if matching_indices:
|
||||
target_repo = repositories[matching_indices[0]]
|
||||
if len(matching_indices) > 1 and hasattr(repositories, "remove"):
|
||||
for dup_idx in reversed(matching_indices[1:]):
|
||||
try:
|
||||
repositories.remove(dup_idx)
|
||||
_log(f"removed duplicate repository entry at index {dup_idx}")
|
||||
except Exception as exc:
|
||||
_log(f"could not remove duplicate repository at index {dup_idx}: {exc}")
|
||||
else:
|
||||
target_repo = next(
|
||||
(
|
||||
repo
|
||||
for repo in repositories
|
||||
if getattr(repo, "name", "") == RAINYS_EXTENSIONS_REPO_NAME
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if target_repo is None:
|
||||
_log("repo missing; creating new entry")
|
||||
if hasattr(repositories, "new"):
|
||||
target_repo = repositories.new()
|
||||
elif hasattr(repositories, "add"):
|
||||
target_repo = repositories.add()
|
||||
else:
|
||||
_log("repository collection does not support creation; aborting")
|
||||
return
|
||||
else:
|
||||
_log("repo entry already present; validating fields")
|
||||
|
||||
changed = preferences_changed
|
||||
|
||||
def _ensure_attr(obj, attr, value):
|
||||
if hasattr(obj, attr) and getattr(obj, attr) != value:
|
||||
setattr(obj, attr, value)
|
||||
return True
|
||||
if not hasattr(obj, attr):
|
||||
_log(f"repository entry missing attribute '{attr}', skipping field")
|
||||
return False
|
||||
|
||||
changed |= _ensure_attr(target_repo, "name", RAINYS_EXTENSIONS_REPO_NAME)
|
||||
changed |= _ensure_attr(target_repo, "module", "rainys_extensions")
|
||||
changed |= _ensure_attr(target_repo, "use_remote_url", True)
|
||||
changed |= _ensure_attr(target_repo, "remote_url", RAINYS_EXTENSIONS_REPO_URL)
|
||||
changed |= _ensure_attr(target_repo, "use_sync_on_startup", True)
|
||||
changed |= _ensure_attr(target_repo, "use_cache", True)
|
||||
changed |= _ensure_attr(target_repo, "use_access_token", False)
|
||||
|
||||
if addon_prefs and hasattr(addon_prefs, "repo_initialized") and not addon_prefs.repo_initialized:
|
||||
addon_prefs.repo_initialized = True
|
||||
changed = True
|
||||
|
||||
if not changed:
|
||||
_log("repository already configured; skipping preference save")
|
||||
_BOOTSTRAP_DONE = True
|
||||
return
|
||||
|
||||
if hasattr(bpy.ops, "wm") and hasattr(bpy.ops.wm, "save_userpref"):
|
||||
try:
|
||||
bpy.ops.wm.save_userpref()
|
||||
_log("preferences updated and saved")
|
||||
except Exception as exc: # pragma: no cover
|
||||
print(f"RainysExtensionsCheck: could not save preferences after repo update -> {exc}")
|
||||
else:
|
||||
_log("preferences API unavailable; changes not persisted")
|
||||
|
||||
_BOOTSTRAP_DONE = True
|
||||
|
||||
Reference in New Issue
Block a user