mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-10-04 00:55:32 -03:00
feat(metadata): add OpenModelDB metadata provider and model source for upscalers
Add OpenModelDB (openmodeldb.info) as a metadata and download source for
the existing upscaler model type.
Metadata:
- New OpenModelDBClient: fetches the site's bulk JSON dumps, caches them
on disk (24h TTL + ETag revalidation), and builds a local sha256 index
- New OpenModelDBModelMetadataProvider adapts catalogue entries to the
CivitAI-shaped version dict contract; registered in the fallback chain
behind the enable_openmodeldb_api setting (default on), gated to the
upscaler sub-type so other model types never trigger the dump download
- Persisted provenance uses metadata_source "openmodeldb" plus a nested
openmodeldb block (page URL, architecture, scale, license)
Images: paired-image LR/SR URLs are ephemeral imgdiff.net sessions, so
displayable images come from the site-hosted auto-generated thumbnails
(model-level cover leads images[], per-image thumbs for the rest); the
original comparison URL is kept in meta.comparisonUrl.
Downloads:
- New OpenModelDBSource (flat model ids, omdb: group prefix) with
resource filename derivation that recovers names hidden mid-path
(mediafire) or synthesizes {id}.{type} for folder links
- HTML-gateway mirrors (mediafire/mega/drive) are rejected with a clear
manual-download hint instead of silently saving an HTML page as .pth
- ModelSource base gains is_valid_source_id / default_subdir_parts /
resolve_download_url hooks so flat-id sources need no platform branches
UI: "View on OpenModelDB" link in the model modal (downloaded and
hash-enriched models), settings toggle next to the CivArchive one.
This commit is contained in:
@@ -1807,6 +1807,7 @@ class SettingsHandler:
|
||||
if key in (
|
||||
"enable_metadata_archive_db",
|
||||
"enable_civarchive_api",
|
||||
"enable_openmodeldb_api",
|
||||
"metadata_provider_order",
|
||||
):
|
||||
await self._metadata_provider_updater()
|
||||
|
||||
@@ -31,7 +31,6 @@ from ...services.model_sources import (
|
||||
detect_source,
|
||||
get_download_source,
|
||||
hydrate_from_source,
|
||||
is_valid_source_id,
|
||||
list_sources,
|
||||
normalize_metadata_source,
|
||||
)
|
||||
@@ -277,9 +276,7 @@ class ModelSourceHandler:
|
||||
"supports_enrichment": source.supports_enrichment,
|
||||
"supports_download": source.supports_download,
|
||||
"default_revision": source.default_revision,
|
||||
"example_url": source.canonical_url(
|
||||
"user/repo" if source.platform != "tensorart" else "827823520299086029"
|
||||
),
|
||||
"example_url": source.canonical_url(source.example_source_id),
|
||||
}
|
||||
for source in list_sources()
|
||||
])
|
||||
@@ -326,9 +323,7 @@ class ModelSourceHandler:
|
||||
"error": (
|
||||
"Unsupported model URL. Supported formats: "
|
||||
+ ", ".join(
|
||||
f"{s.label} ({s.canonical_url('user/repo')})"
|
||||
if s.platform != "tensorart"
|
||||
else f"{s.label} (https://tensor.art/models/<id>)"
|
||||
f"{s.label} ({s.canonical_url(s.example_source_id)})"
|
||||
for s in list_sources()
|
||||
)
|
||||
),
|
||||
@@ -424,9 +419,9 @@ class ModelSourceHandler:
|
||||
source = get_download_source(platform)
|
||||
if source is None:
|
||||
return _unsupported_platform_error(platform)
|
||||
if not is_valid_source_id(repo):
|
||||
if not source.is_valid_source_id(repo):
|
||||
return web.json_response(
|
||||
{"error": "Missing or invalid 'repo' parameter (expected owner/name)"},
|
||||
{"error": "Missing or invalid 'repo' parameter"},
|
||||
status=400,
|
||||
)
|
||||
|
||||
@@ -492,10 +487,11 @@ class ModelSourceHandler:
|
||||
{"error": "Missing required fields: 'repo' and 'filename'"}, status=400
|
||||
)
|
||||
|
||||
# `owner/name` only; the components become path segments below.
|
||||
if not is_valid_source_id(repo):
|
||||
# The id becomes a path segment below; each site defines what a safe
|
||||
# id looks like (`owner/name` for repository sites, a flat token for
|
||||
# OpenModelDB).
|
||||
if not source.is_valid_source_id(repo):
|
||||
return web.json_response({"error": f"Invalid repo format: {repo}"}, status=400)
|
||||
owner, repo_name = repo.split("/", 1)
|
||||
|
||||
# Validate filename — must not contain path traversal
|
||||
if ".." in filename:
|
||||
@@ -521,7 +517,7 @@ class ModelSourceHandler:
|
||||
base_dir = os.path.normpath(os.path.join(os.getcwd(), "models", model_root))
|
||||
|
||||
if use_default_paths:
|
||||
target_dir = os.path.join(base_dir, source.default_subdir, owner, repo_name)
|
||||
target_dir = os.path.join(base_dir, *source.default_subdir_parts(repo))
|
||||
elif relative_path:
|
||||
target_dir = os.path.join(base_dir, relative_path)
|
||||
else:
|
||||
@@ -536,7 +532,10 @@ class ModelSourceHandler:
|
||||
|
||||
# Built per request: sites that redirect to a CDN hand out a
|
||||
# time-limited token in the redirect, so the URL must never be cached.
|
||||
resolve_url = source.file_download_url(repo, filename, revision)
|
||||
try:
|
||||
resolve_url = await source.resolve_download_url(repo, filename, revision)
|
||||
except ModelSourceError as exc:
|
||||
return web.json_response({"error": str(exc)}, status=exc.status)
|
||||
ref = SourceRef(
|
||||
platform=source.platform, source_id=repo, url=source.canonical_url(repo)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user