mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-20 18:51:26 -03:00
38d4c59b4c
ModelScope became a linkable source, but downloading from it was impossible:
the URL picker only recognised huggingface.co, the file listing hit a
huggingface-only endpoint, the resolve URL was hardcoded, and the default
path template always wrote into a `huggingface/` directory.
Move the download knowledge into the providers so the handlers stay generic:
- `ModelSource` gains `list_files()`, `file_download_url()`,
`default_revision` and `default_subdir`. `HuggingFaceSource` keeps the Hub
tree API (`/api/models/{id}/tree/{rev}`, LFS-aware sizes, `main`).
`ModelScopeSource` uses `/api/v1/models/{id}/repo/files?Revision=master`
— which reports real byte sizes for LFS files, so no HEAD probe is needed,
and which only accepts `master` (an HF-imported repo still 404s on `main`)
— and downloads through `/models/{id}/resolve/{rev}/{path}`. That URL
redirects to a CDN target carrying a time-limited `auth_key`, so it is
rebuilt on every request and never cached, which is also what keeps
resumable Range requests working.
- `hf_handlers.py`/`HfHandler` become `model_source_handlers.py`/
`ModelSourceHandler` with `list_model_source_files` and
`download_model_source`. New routes `/api/lm/model-source-files` and
`/api/lm/download-model-source`; the old `/api/lm/hf-repo-files` and
`/api/lm/download-hf-model` paths stay as aliases, and a payload without
`platform` still means Hugging Face, so existing callers are unaffected.
- A downloaded sidecar now records `source_platform` + `source_url` (with the
`hf_url` alias only for Hugging Face) instead of always writing `hf_url`,
and `use_default_paths` files ModelScope downloads under
`modelscope/<owner>/<repo>`. The now-unused shared HF aiohttp session and
its shutdown hook are gone; providers open short-lived sessions.
- Frontend: `detectUrlType` returns the platform-neutral
`model-source-repo` / `model-source-file` plus an explicit `platform`, the
DownloadManager's `hf*` state and methods are renamed to `source*`, every
`source === 'huggingface'` check becomes `isExternalModelSource()`, and
batch groups are keyed by `platform:repo` so the same `owner/name` on two
sites renders as two groups. A bare `owner/name` still means Hugging Face.
- `is_valid_source_id()` centralises repo-id validation (exactly
`owner/name`, no traversal, no leading dot). This also fixes the old HF
download check that rejected any dot in the name, i.e. legitimate repos
such as `black-forest-labs/FLUX.1-dev`.
Verified against the live APIs: the example repo lists 8 weight files with
correct sizes, and a ranged GET of the built resolve URL returns 206 after
following the redirect to the CDN. Backend 2853 passed; frontend 1143 JS +
91 Vue passed. The nine locales carry the refreshed download copy in the
next commit.
171 lines
6.4 KiB
Python
171 lines
6.4 KiB
Python
"""Route controller for miscellaneous endpoints."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
from typing import Awaitable, Callable, Mapping
|
|
|
|
from aiohttp import web
|
|
from server import PromptServer # pyright: ignore[reportMissingImports]
|
|
|
|
from ..services.metadata_service import (
|
|
get_metadata_archive_manager,
|
|
get_metadata_provider,
|
|
update_metadata_providers,
|
|
)
|
|
from ..services.settings_manager import get_settings_manager
|
|
from ..services.downloader import get_downloader
|
|
from ..utils.usage_stats import UsageStats
|
|
from .handlers.misc_handlers import (
|
|
CustomWordsHandler,
|
|
DoctorHandler,
|
|
ExampleWorkflowsHandler,
|
|
FileSystemHandler,
|
|
HealthCheckHandler,
|
|
LoraCodeHandler,
|
|
BackupHandler,
|
|
MetadataArchiveHandler,
|
|
MiscHandlerSet,
|
|
ModelExampleFilesHandler,
|
|
ModelLibraryHandler,
|
|
NodeRegistry,
|
|
NodeRegistryHandler,
|
|
SettingsHandler,
|
|
SupportersHandler,
|
|
TrainedWordsHandler,
|
|
UsageStatsHandler,
|
|
WildcardsHandler,
|
|
build_service_registry_adapter,
|
|
)
|
|
from .handlers.base_model_handlers import BaseModelHandlerSet
|
|
from .handlers.model_source_handlers import ModelSourceHandler
|
|
from .handlers.agent_handlers import AgentHandler
|
|
from .handlers.download_routing_handlers import DownloadRoutingHandler
|
|
from .misc_route_registrar import MiscRouteRegistrar
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
standalone_mode = (
|
|
os.environ.get("LORA_MANAGER_STANDALONE", "0") == "1"
|
|
or os.environ.get("HF_HUB_DISABLE_TELEMETRY", "0") == "0"
|
|
)
|
|
|
|
|
|
class MiscRoutes:
|
|
"""Route controller that mirrors the model route architecture."""
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
settings_service=None,
|
|
usage_stats_factory: Callable[[], UsageStats] = UsageStats,
|
|
prompt_server: type[PromptServer] = PromptServer,
|
|
service_registry_adapter=build_service_registry_adapter(),
|
|
metadata_provider_factory=get_metadata_provider,
|
|
metadata_archive_manager_factory=get_metadata_archive_manager,
|
|
metadata_provider_updater=update_metadata_providers,
|
|
downloader_factory=get_downloader,
|
|
registrar_factory=MiscRouteRegistrar,
|
|
handler_set_factory=MiscHandlerSet,
|
|
node_registry: NodeRegistry | None = None,
|
|
standalone_mode_flag: bool = standalone_mode,
|
|
) -> None:
|
|
self._settings = settings_service or get_settings_manager()
|
|
self._usage_stats_factory = usage_stats_factory
|
|
self._prompt_server = prompt_server
|
|
self._service_registry_adapter = service_registry_adapter
|
|
self._metadata_provider_factory = metadata_provider_factory
|
|
self._metadata_archive_manager_factory = metadata_archive_manager_factory
|
|
self._metadata_provider_updater = metadata_provider_updater
|
|
self._downloader_factory = downloader_factory
|
|
self._registrar_factory = registrar_factory
|
|
self._handler_set_factory = handler_set_factory
|
|
self._node_registry = node_registry or NodeRegistry()
|
|
self._standalone_mode = standalone_mode_flag
|
|
|
|
self._handler_mapping: (
|
|
Mapping[str, Callable[[web.Request], Awaitable[web.StreamResponse]]] | None
|
|
) = None
|
|
|
|
@staticmethod
|
|
def setup_routes(app: web.Application) -> None:
|
|
"""Entry point used by the application bootstrap."""
|
|
controller = MiscRoutes()
|
|
controller.bind(app)
|
|
|
|
def bind(self, app: web.Application) -> None:
|
|
registrar = self._registrar_factory(app)
|
|
registrar.register_routes(self._ensure_handler_mapping())
|
|
|
|
def _ensure_handler_mapping(
|
|
self,
|
|
) -> Mapping[str, Callable[[web.Request], Awaitable[web.StreamResponse]]]:
|
|
if self._handler_mapping is None:
|
|
handler_set = self._create_handler_set()
|
|
self._handler_mapping = handler_set.to_route_mapping()
|
|
return self._handler_mapping
|
|
|
|
def _create_handler_set(self) -> MiscHandlerSet:
|
|
health = HealthCheckHandler()
|
|
settings_handler = SettingsHandler(
|
|
settings_service=self._settings,
|
|
metadata_provider_updater=self._metadata_provider_updater,
|
|
downloader_factory=self._downloader_factory,
|
|
)
|
|
usage_stats = UsageStatsHandler(usage_stats_factory=self._usage_stats_factory)
|
|
lora_code = LoraCodeHandler(prompt_server=self._prompt_server)
|
|
trained_words = TrainedWordsHandler()
|
|
model_examples = ModelExampleFilesHandler()
|
|
metadata_archive = MetadataArchiveHandler(
|
|
metadata_archive_manager_factory=self._metadata_archive_manager_factory,
|
|
settings_service=self._settings,
|
|
metadata_provider_updater=self._metadata_provider_updater,
|
|
)
|
|
backup = BackupHandler()
|
|
filesystem = FileSystemHandler(settings_service=self._settings)
|
|
node_registry_handler = NodeRegistryHandler(
|
|
node_registry=self._node_registry,
|
|
prompt_server=self._prompt_server,
|
|
standalone_mode=self._standalone_mode,
|
|
)
|
|
model_library = ModelLibraryHandler(
|
|
service_registry=self._service_registry_adapter,
|
|
metadata_provider_factory=self._metadata_provider_factory,
|
|
)
|
|
custom_words = CustomWordsHandler()
|
|
wildcards = WildcardsHandler()
|
|
supporters = SupportersHandler()
|
|
doctor = DoctorHandler(settings_service=self._settings)
|
|
example_workflows = ExampleWorkflowsHandler()
|
|
base_model = BaseModelHandlerSet()
|
|
model_source_handler = ModelSourceHandler()
|
|
agent_handler = AgentHandler()
|
|
download_routing = DownloadRoutingHandler()
|
|
|
|
return self._handler_set_factory(
|
|
health=health,
|
|
settings=settings_handler,
|
|
usage_stats=usage_stats,
|
|
lora_code=lora_code,
|
|
trained_words=trained_words,
|
|
model_examples=model_examples,
|
|
node_registry=node_registry_handler,
|
|
model_library=model_library,
|
|
metadata_archive=metadata_archive,
|
|
backup=backup,
|
|
filesystem=filesystem,
|
|
custom_words=custom_words,
|
|
wildcards=wildcards,
|
|
supporters=supporters,
|
|
doctor=doctor,
|
|
example_workflows=example_workflows,
|
|
base_model=base_model,
|
|
model_source_handler=model_source_handler,
|
|
agent_handler=agent_handler,
|
|
download_routing=download_routing,
|
|
)
|
|
|
|
|
|
__all__ = ["MiscRoutes"]
|