Files
MajoorWaldi-ComfyUI-Majoor-…/__init__.py
T

146 lines
4.8 KiB
Python

"""
ComfyUI-Majoor-AssetsManager
Advanced asset browser for ComfyUI with ratings, tags, and metadata management.
"""
from __future__ import annotations
import importlib
import os
import shutil
import subprocess
import sys
from typing import Optional
from pathlib import Path
# ComfyUI extension metadata
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
WEB_DIRECTORY = "./js"
# ---- Windows safety guard -------------------------------------------------
# ComfyUI loads every entry under `custom_nodes/`. On Windows, reserved device
# filenames like `NUL` can appear (accidentally created by tooling) and cause
# noisy import failures. We best-effort delete them when our extension loads so
# it can't break subsequent restarts.
def _windows_extended_path(path: str) -> str:
if path.startswith("\\\\?\\"):
return path
if path.startswith("\\\\"):
# UNC path: \\server\share\... -> \\?\UNC\server\share\...
return "\\\\?\\UNC\\" + path.lstrip("\\")
return "\\\\?\\" + path
def _try_delete_windows_path(path: str) -> None:
nt_path = _windows_extended_path(path)
try:
os.unlink(nt_path)
return
except IsADirectoryError:
pass
except FileNotFoundError:
return
except OSError:
# Fall through to directory removal / cmd fallback.
pass
try:
shutil.rmtree(nt_path)
return
except FileNotFoundError:
return
except OSError:
pass
# Last-resort fallback via cmd builtins (kept best-effort and silent).
try:
subprocess.run(["cmd", "/c", f'del /f /q "{nt_path}"'], check=False, capture_output=True)
subprocess.run(["cmd", "/c", f'rd /s /q "{nt_path}"'], check=False, capture_output=True)
except Exception:
pass
def _cleanup_windows_reserved_custom_nodes() -> Optional[str]:
try:
if os.name != "nt":
return None
custom_nodes_dir = Path(__file__).resolve().parent.parent
if not custom_nodes_dir.exists():
return None
try:
entries = os.listdir(str(custom_nodes_dir))
except OSError:
return None
reserved = {"CON", "PRN", "AUX", "NUL"}
reserved.update({f"COM{i}" for i in range(1, 10)})
reserved.update({f"LPT{i}" for i in range(1, 10)})
for name in entries:
# Windows device names are reserved even with extensions and trailing dots/spaces.
cleaned = str(name or "").strip().rstrip(". ")
base = cleaned.split(".", 1)[0].strip().upper()
if base not in reserved:
continue
try:
victim = str((custom_nodes_dir / name).resolve())
except Exception:
victim = str(custom_nodes_dir / name)
_try_delete_windows_path(victim)
return victim
except Exception:
return None
return None
try:
removed = _cleanup_windows_reserved_custom_nodes()
if removed:
print(f"Majoor Assets Manager: removed Windows reserved custom node entry: {removed}")
except Exception:
pass
# Register API routes (importing `backend.routes` auto-registers via PromptServer decorators)
try:
# When loaded by ComfyUI as a proper package (preferred).
from .backend import routes # type: ignore # noqa: F401
except Exception:
try:
# When ComfyUI loads custom nodes, it may load this file as a *module* (not a package),
# which breaks relative imports. Ensure our local `backend/` package is importable and
# does not get shadowed by any unrelated `backend` module on sys.path.
root = Path(__file__).resolve().parent
if str(root) not in sys.path:
sys.path.insert(0, str(root))
expected_backend_dir = (root / "backend").resolve()
existing_backend = sys.modules.get("backend")
if existing_backend is not None:
try:
existing_file = Path(getattr(existing_backend, "__file__", "")).resolve()
except Exception:
existing_file = None
if not existing_file or expected_backend_dir not in existing_file.parents:
for name in list(sys.modules.keys()):
if name == "backend" or name.startswith("backend."):
sys.modules.pop(name, None)
routes = importlib.import_module("backend.routes") # type: ignore # noqa: F401
except Exception:
# Keep import side-effect-free when ComfyUI environment isn't available.
try:
import traceback
print("Majoor Assets Manager: failed to import backend routes (API endpoints will be unavailable).")
traceback.print_exc()
except Exception:
pass
routes = None # type: ignore
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]