diff --git a/core/download/api.py b/core/download/api.py index 9c7fd90..f82919f 100644 --- a/core/download/api.py +++ b/core/download/api.py @@ -144,7 +144,7 @@ context.invalidate_model_files_cache = invalidate_model_files_cache context.invalidate_local_hash_match_cache = invalidate_local_hash_match_cache context._extract_expected_sha256 = metadata._extract_expected_sha256 -context._normalise_metadata_file_path = metadata._normalise_metadata_file_path +context.normalize_metadata_file_path = metadata.normalize_metadata_file_path context._json_safe_metadata = metadata._json_safe_metadata context._coerce_int_or_value = metadata._coerce_int_or_value context._coerce_size = metadata._coerce_size diff --git a/core/download/metadata.py b/core/download/metadata.py index e671fb5..f328343 100644 --- a/core/download/metadata.py +++ b/core/download/metadata.py @@ -66,7 +66,7 @@ def write_model_resolver_metadata( } preview_path = facade.create_model_preview(dest_path, preview_source) if preview_path: - payload["preview_url"] = facade._normalise_metadata_file_path( + payload["preview_url"] = facade.normalize_metadata_file_path( preview_path ) facade.write_json_atomic(metadata_path, payload, indent=2) @@ -117,9 +117,6 @@ def _coerce_size(value: Any) -> int: return 0 -_normalise_metadata_file_path = normalize_metadata_file_path - - def _resolve_lora_manager_model_type(category: str, source_type: Any = "") -> str: result = normalize_category_to_model_type(category) if result in ("checkpoint", "diffusion_model", "embedding"): @@ -502,7 +499,7 @@ def build_model_resolver_metadata( "file_name": file_name, "filename": basename, "model_name": str(model_name or file_name), - "file_path": _normalise_metadata_file_path(dest_path), + "file_path": normalize_metadata_file_path(dest_path), "size": size, "modified": time.time(), "sha256": sha256, diff --git a/core/metadata_audit.py b/core/metadata_audit.py index 773b659..577285a 100644 --- a/core/metadata_audit.py +++ b/core/metadata_audit.py @@ -10,15 +10,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Any, Dict, Iterable, List, Optional, Tuple from .log_system import create_module_logger -from .metadata_model_utils import ( - dedupe_models as _dedupe_models, -) -from .metadata_model_utils import ( - is_model_file_path as _is_model_file_path, -) -from .metadata_model_utils import ( - model_identity_key as _model_identity_key, # noqa: F401 -) +from .metadata_model_utils import dedupe_models, is_model_file_path from .path_utils import ( find_metadata_sidecar_path, get_filename_from_path, @@ -243,7 +235,7 @@ def _audit_one_model(model: Dict[str, Any]) -> Dict[str, Any]: result["skipped_directories"] += 1 return result - if not _is_model_file_path(model_path): + if not is_model_file_path(model_path): result["skipped_non_model_files"] += 1 return result @@ -362,7 +354,7 @@ def audit_metadata_sizes( models = get_model_files(force_rescan=force_rescan) - audit_models = _dedupe_models(models or []) + audit_models = dedupe_models(models or []) total_models = len(audit_models) resolved_worker_count, cpu_count = _get_worker_count(total_models, worker_count) resolved_batch_size = _get_batch_size(total_models, resolved_worker_count, batch_size) diff --git a/core/metadata_builder.py b/core/metadata_builder.py index 4d5fbbc..8f402e9 100644 --- a/core/metadata_builder.py +++ b/core/metadata_builder.py @@ -13,15 +13,7 @@ from concurrent.futures import CancelledError, ThreadPoolExecutor, as_completed from typing import Any, Callable, Dict, List, Optional, Tuple from .log_system import create_module_logger -from .metadata_model_utils import ( - dedupe_models as _dedupe_models, -) -from .metadata_model_utils import ( - is_model_file_path as _is_model_file_path, -) -from .metadata_model_utils import ( - model_identity_key as _model_identity_key, -) +from .metadata_model_utils import dedupe_models, is_model_file_path, model_identity_key from .path_utils import ( MODEL_RESOLVER_METADATA_SCHEMA, MODEL_RESOLVER_METADATA_SCHEMA_VERSION, @@ -256,9 +248,6 @@ def _merge_unique_strings(*values: Any) -> List[str]: return result -_normalise_metadata_file_path = normalize_metadata_file_path - - def _model_type_for_category(category: str) -> str: model_type = normalize_category_to_model_type(category) return model_type if model_type and model_type != "unknown" else "" @@ -304,7 +293,7 @@ def _build_local_metadata_payload( mark_set("file_name", stem, force=_is_empty_value(payload.get("file_name"))) fill("filename", filename) fill("model_name", header_metadata.get("model_name") or stem) - mark_set("file_path", _normalise_metadata_file_path(model_path), force=True) + mark_set("file_path", normalize_metadata_file_path(model_path), force=True) mark_set("size", file_size, force=True) mark_set("modified", now, force=True) mark_set("last_checked_at", now, force=True) @@ -398,7 +387,7 @@ def _build_local_metadata_payload( mark_set("local_metadata_available", True, force=True) fill("preview_nsfw_level", 0) - mark_set("metadata_path", _normalise_metadata_file_path(metadata_path), force=True) + mark_set("metadata_path", normalize_metadata_file_path(metadata_path), force=True) return payload, sorted(set(changed_fields)) @@ -590,7 +579,7 @@ def _build_missing_local_metadata_parallel( def run_one_model(model: Dict[str, Any]) -> Dict[str, Any]: model_path = str(model.get("path") or "").strip() filename = get_filename_from_path(model_path) - model_key = _model_identity_key(model) or model_path + model_key = model_identity_key(model) or model_path def child_progress(data: Dict[str, Any]) -> None: if not isinstance(data, dict): @@ -643,7 +632,7 @@ def _build_missing_local_metadata_parallel( model = futures[future] model_path = str(model.get("path") or "").strip() filename = get_filename_from_path(model_path) - model_key = _model_identity_key(model) or model_path + model_key = model_identity_key(model) or model_path try: item_result = future.result() except CancelledError: @@ -745,10 +734,10 @@ def build_missing_local_metadata( models = get_model_files(force_rescan=force_rescan) - all_models = _dedupe_models(models or []) + all_models = dedupe_models(models or []) model_items = [ model for model in all_models - if _is_model_file_path(str(model.get("path") or "").strip()) + if is_model_file_path(str(model.get("path") or "").strip()) ] total = len(model_items) resolved_worker_count, cpu_count = _resolve_worker_count(total, worker_count) diff --git a/core/sources/civarchive.py b/core/sources/civarchive.py index 8cb544e..21a3ba2 100644 --- a/core/sources/civarchive.py +++ b/core/sources/civarchive.py @@ -54,8 +54,6 @@ log = create_module_logger(__name__) _coerce_int = to_int -_archive_link_is_dead = is_remote_link_marked_dead - CIVARCHIVE_BASE_URL = "https://civarchive.com" CIVARCHIVE_API_URL = f"{CIVARCHIVE_BASE_URL}/api" CIVITAI_DOWNLOAD_URL_PREFIXES = ( @@ -731,7 +729,7 @@ def _transform_file_entry(file_data: Dict[str, Any]) -> Dict[str, Any]: transformed_mirrors.append(mirror) download_url = None - if not _archive_link_is_dead(file_data): + if not is_remote_link_marked_dead(file_data): download_url = _normalize_download_url( file_data.get("downloadUrl") or file_data.get("download_url") @@ -741,7 +739,7 @@ def _transform_file_entry(file_data: Dict[str, Any]) -> Dict[str, Any]: if not download_url: for mirror in transformed_mirrors: - if not _archive_link_is_dead(mirror) and mirror.get("url"): + if not is_remote_link_marked_dead(mirror) and mirror.get("url"): download_url = mirror["url"] break @@ -869,7 +867,7 @@ def _collect_download_urls_unified( download_url_keys: Tuple[str, ...] = ("downloadUrl",), ) -> List[str]: urls: List[str] = [] - if skip_file_if_dead and _archive_link_is_dead(file_info): + if skip_file_if_dead and is_remote_link_marked_dead(file_info): return urls expected_filename = file_info.get("filename") or file_info.get("name") or "" @@ -881,7 +879,7 @@ def _collect_download_urls_unified( for mirror in mirrors: if not isinstance(mirror, dict): continue - if _archive_link_is_dead(mirror): + if is_remote_link_marked_dead(mirror): dead_url = _normalize_download_url(mirror.get("url")) if dead_url: dead_urls.add(dead_url) @@ -909,7 +907,7 @@ def _collect_download_urls_unified( ): urls.append(url) - if not _archive_link_is_dead(file_info): + if not is_remote_link_marked_dead(file_info): for key in download_url_keys: url = _normalize_download_url(file_info.get(key)) if ( @@ -958,7 +956,7 @@ def _normalize_archive_mirrors(file_info: Dict[str, Any]) -> List[Dict[str, Any] seen_urls.add(url) sha256 = mirror.get("sha256") or mirror.get("hash") deleted_at = mirror.get("deletedAt") or mirror.get("deleted_at") - is_dead = _archive_link_is_dead(mirror) + is_dead = is_remote_link_marked_dead(mirror) normalized.append( { "url": url, @@ -974,7 +972,7 @@ def _normalize_archive_mirrors(file_info: Dict[str, Any]) -> List[Dict[str, Any] ) download_url = None - if not _archive_link_is_dead(file_info): + if not is_remote_link_marked_dead(file_info): download_url = _normalize_download_url(file_info.get("downloadUrl")) if download_url and download_url not in seen_urls: normalized.append( @@ -985,7 +983,7 @@ def _normalize_archive_mirrors(file_info: Dict[str, Any]) -> List[Dict[str, Any] "sha256": file_info.get("sha256"), "hash": file_info.get("sha256"), "deleted_at": file_info.get("deletedAt") or file_info.get("deleted_at"), - "is_dead": _archive_link_is_dead(file_info), + "is_dead": is_remote_link_marked_dead(file_info), "is_gated": file_info.get("is_gated"), "is_paid": file_info.get("is_paid"), } @@ -1460,7 +1458,7 @@ def _select_hash_page_file( for file_info in files: if not isinstance(file_info, dict): continue - if _archive_link_is_dead(file_info): + if is_remote_link_marked_dead(file_info): continue if not _collect_normalized_download_urls(file_info): continue @@ -1535,7 +1533,7 @@ def _prefer_query_matching_mirror( for mirror in mirrors: if not isinstance(mirror, dict): continue - if _archive_link_is_dead(mirror): + if is_remote_link_marked_dead(mirror): continue url = _normalize_download_url(mirror.get("url")) filename = mirror.get("filename") or mirror.get("name") or "" diff --git a/tests/test_downloader_boundaries.py b/tests/test_downloader_boundaries.py index a4d79be..dba9903 100644 --- a/tests/test_downloader_boundaries.py +++ b/tests/test_downloader_boundaries.py @@ -60,7 +60,7 @@ class DownloaderBoundaryTests(unittest.TestCase): self.assertEqual("unknown", downloader._coerce_int_or_value("unknown")) self.assertEqual(12, downloader._coerce_size("12.9")) self.assertEqual(0, downloader._coerce_size("not-a-size")) - self.assertEqual("models/subfolder", downloader._normalise_metadata_file_path("models\\subfolder")) + self.assertEqual("models/subfolder", downloader.normalize_metadata_file_path("models\\subfolder")) self.assertEqual("models/subfolder", normalize_metadata_file_path("models\\subfolder")) def test_metadata_source_and_model_type_helpers_cover_provider_variants(self): diff --git a/tests/test_downloads_tab_workflow_route.mjs b/tests/test_downloads_tab_workflow_route.mjs index ddcf2d7..5fcdab4 100644 --- a/tests/test_downloads_tab_workflow_route.mjs +++ b/tests/test_downloads_tab_workflow_route.mjs @@ -5069,7 +5069,6 @@ test('missing model preview shows accepted GGUF format next to its category', () test('hash match label map numbers distinct matched hashes', () => { const { - normalizeSearchResultSha256, getLocalMatchHash, collectHashLabelMapHashes, getHashMatchLabelMap, @@ -5077,7 +5076,6 @@ test('hash match label map numbers distinct matched hashes', () => { const firstHash = 'a'.repeat(64); const secondHash = 'b'.repeat(64); const dialog = { - normalizeSearchResultSha256, getLocalMatchHash, collectHashLabelMapHashes, }; @@ -5100,7 +5098,6 @@ test('hash match label map numbers distinct matched hashes', () => { test('search result hash labels use linked local hash identity', () => { const { - normalizeSearchResultSha256, getSearchResultSha256, getHashMatchLabelForSearchResult, } = searchHashMethods; @@ -5108,7 +5105,6 @@ test('search result hash labels use linked local hash identity', () => { const sha256 = 'd'.repeat(64); const hashLabelMap = new Map([[sha256, 'Hash 2']]); const dialog = { - normalizeSearchResultSha256, getSearchResultSha256, }; const result = { @@ -5132,7 +5128,6 @@ test('search result hash labels use linked local hash identity', () => { test('local hash identities link search results across sources by sha', () => { const { - normalizeSearchResultSha256, getLocalMatchHash, getSearchResultSha256, normalizeHashLookupSourceKey, @@ -5140,7 +5135,6 @@ test('local hash identities link search results across sources by sha', () => { } = searchHashMethods; const sha256 = 'e'.repeat(64); const dialog = { - normalizeSearchResultSha256, getLocalMatchHash, getSearchResultSha256, normalizeHashLookupSourceKey, @@ -5162,7 +5156,7 @@ test('local hash identities link search results across sources by sha', () => { }); test('remote hash sync fetches local matches by selected result sha', async () => { - const { normalizeSearchResultSha256, getSearchResultSha256 } = searchHashMethods; + const { getSearchResultSha256 } = searchHashMethods; const getExistingLocalHashMatchesForSha = eval(`(${extractMethod(resolveDownloadMethodsSource, 'getExistingLocalHashMatchesForSha')})`); const syncRemoteHashMatchesForResult = eval(`(${extractMethod(resolveDownloadMethodsSource, 'syncRemoteHashMatchesForResult')})`); const sha256 = 'f'.repeat(64); @@ -5175,7 +5169,6 @@ test('remote hash sync fetches local matches by selected result sha', async () = }; const state = { results: { local_hash_matches: [] } }; const dialog = { - normalizeSearchResultSha256, getSearchResultSha256, getExistingLocalHashMatchesForSha, getWorkflowScopedQueueKey() { diff --git a/tests/test_metadata_model_helpers.py b/tests/test_metadata_model_helpers.py index 53d5f54..9589167 100644 --- a/tests/test_metadata_model_helpers.py +++ b/tests/test_metadata_model_helpers.py @@ -2,16 +2,7 @@ import os import tempfile import unittest -from core.metadata_audit import ( - _dedupe_models as audit_dedupe_models, - _is_model_file_path as audit_is_model_file_path, - _model_identity_key as audit_model_identity_key, -) -from core.metadata_builder import ( - _dedupe_models as builder_dedupe_models, - _is_model_file_path as builder_is_model_file_path, - _model_identity_key as builder_model_identity_key, -) +from core.metadata_model_utils import dedupe_models, is_model_file_path, model_identity_key class MetadataModelHelperTests(unittest.TestCase): @@ -35,8 +26,7 @@ class MetadataModelHelperTests(unittest.TestCase): } for path, is_model in expected.items(): - self.assertEqual(is_model, builder_is_model_file_path(path)) - self.assertEqual(is_model, audit_is_model_file_path(path)) + self.assertEqual(is_model, is_model_file_path(path)) def test_model_identity_and_dedupe_match_between_metadata_flows(self): with tempfile.TemporaryDirectory() as tmpdir: @@ -51,23 +41,9 @@ class MetadataModelHelperTests(unittest.TestCase): empty = {"path": ""} models = [first, duplicate, empty, "not a model"] - self.assertEqual( - builder_model_identity_key(first), - audit_model_identity_key(first), - ) - self.assertEqual( - builder_model_identity_key(first), - builder_model_identity_key(duplicate), - ) - self.assertEqual( - audit_model_identity_key(first), - audit_model_identity_key(duplicate), - ) - self.assertEqual("", builder_model_identity_key(empty)) - self.assertEqual("", audit_model_identity_key(empty)) - - self.assertEqual([first], builder_dedupe_models(models)) - self.assertEqual([first], audit_dedupe_models(models)) + self.assertEqual(model_identity_key(first), model_identity_key(duplicate)) + self.assertEqual("", model_identity_key(empty)) + self.assertEqual([first], dedupe_models(models)) if __name__ == "__main__": diff --git a/tests/test_model_hash_compare_methods.mjs b/tests/test_model_hash_compare_methods.mjs index 31525de..81d36e1 100644 --- a/tests/test_model_hash_compare_methods.mjs +++ b/tests/test_model_hash_compare_methods.mjs @@ -1,6 +1,7 @@ import test from 'node:test'; import assert from 'node:assert/strict'; import { modelHashCompareMethods } from '../web/resolver/views/model_hash_compare_methods.js'; +import { normalizeSha256 } from '../web/resolver/utils/hash_utils.js'; const methods = modelHashCompareMethods; @@ -18,8 +19,8 @@ test('hash comparison normalizes and formats SHA-256 values', () => { const dialog = createDialog(); const hash = 'A'.repeat(64); - assert.equal(dialog.normalizeSha256ForCompare(`sha256:${hash}`), hash.toLowerCase()); - assert.equal(dialog.normalizeSha256ForCompare('not-a-hash'), ''); + assert.equal(normalizeSha256(`sha256:${hash}`), hash.toLowerCase()); + assert.equal(normalizeSha256('not-a-hash'), ''); assert.equal(dialog.formatSha256Short(hash), 'aaaaaaaa...aaaaaaaa'); }); diff --git a/tests/test_search_hash_methods.mjs b/tests/test_search_hash_methods.mjs index eb0a3d4..106ba06 100644 --- a/tests/test_search_hash_methods.mjs +++ b/tests/test_search_hash_methods.mjs @@ -21,11 +21,11 @@ test('shared SHA-256 normalization preserves the accepted input contract', () => test('search hash normalization accepts SHA-256 prefixes and rejects invalid values', () => { const dialog = createDialog(); - assert.equal(dialog.normalizeSearchResultSha256(`sha256:${hashA.toUpperCase()}`), hashA); - assert.equal(dialog.normalizeSearchResultSha256(`SHA256=${hashB}`), hashB); - assert.equal(dialog.normalizeSearchResultSha256(hashA), hashA); - assert.equal(dialog.normalizeSearchResultSha256('short'), ''); - assert.equal(dialog.normalizeSearchResultSha256(`${hashA}x`), ''); + assert.equal(normalizeSha256(`sha256:${hashA.toUpperCase()}`), hashA); + assert.equal(normalizeSha256(`SHA256=${hashB}`), hashB); + assert.equal(normalizeSha256(hashA), hashA); + assert.equal(normalizeSha256('short'), ''); + assert.equal(normalizeSha256(`${hashA}x`), ''); assert.equal(dialog.getSearchResultSha256(null), ''); assert.equal(dialog.getSearchResultSha256({}), ''); }); diff --git a/tests/test_source_internals.py b/tests/test_source_internals.py index 05bb7c2..18ccd2b 100644 --- a/tests/test_source_internals.py +++ b/tests/test_source_internals.py @@ -22,7 +22,6 @@ import requests # civarchive internals # --------------------------------------------------------------------------- from core.sources.civarchive import ( - _archive_link_is_dead, _collect_download_urls, _collect_download_urls_unified, _collect_normalized_download_urls, @@ -380,41 +379,41 @@ class NormalizeDownloadUrlTests(unittest.TestCase): # =========================================================================== -# civarchive - _archive_link_is_dead +# shared remote link dead marker # =========================================================================== class ArchiveLinkIsDeadTests(unittest.TestCase): def test_not_dead_when_no_flags(self): - self.assertFalse(_archive_link_is_dead({"url": "https://example.com/m.safetensors"})) + self.assertFalse(is_remote_link_marked_dead({"url": "https://example.com/m.safetensors"})) def test_dead_when_deletedAt_set(self): - self.assertTrue(_archive_link_is_dead({"deletedAt": "2024-01-01"})) + self.assertTrue(is_remote_link_marked_dead({"deletedAt": "2024-01-01"})) def test_dead_when_deleted_at_set(self): - self.assertTrue(_archive_link_is_dead({"deleted_at": "2024-01-01"})) + self.assertTrue(is_remote_link_marked_dead({"deleted_at": "2024-01-01"})) def test_dead_when_isDead_true(self): - self.assertTrue(_archive_link_is_dead({"isDead": True})) + self.assertTrue(is_remote_link_marked_dead({"isDead": True})) def test_dead_when_is_dead_true(self): - self.assertTrue(_archive_link_is_dead({"is_dead": True})) + self.assertTrue(is_remote_link_marked_dead({"is_dead": True})) def test_dead_when_likelyDead_true(self): - self.assertTrue(_archive_link_is_dead({"likelyDead": True})) + self.assertTrue(is_remote_link_marked_dead({"likelyDead": True})) def test_dead_when_status_dead(self): - self.assertTrue(_archive_link_is_dead({"status": "dead"})) + self.assertTrue(is_remote_link_marked_dead({"status": "dead"})) def test_dead_when_status_deleted_case_insensitive(self): - self.assertTrue(_archive_link_is_dead({"status": "Deleted"})) + self.assertTrue(is_remote_link_marked_dead({"status": "Deleted"})) def test_dead_when_status_unavailable(self): - self.assertTrue(_archive_link_is_dead({"status": "unavailable"})) + self.assertTrue(is_remote_link_marked_dead({"status": "unavailable"})) def test_not_dead_for_non_dict(self): - self.assertFalse(_archive_link_is_dead("dead")) - self.assertFalse(_archive_link_is_dead(None)) - self.assertFalse(_archive_link_is_dead([])) + self.assertFalse(is_remote_link_marked_dead("dead")) + self.assertFalse(is_remote_link_marked_dead(None)) + self.assertFalse(is_remote_link_marked_dead([])) class RemoteLinkIsDeadTests(unittest.TestCase): diff --git a/web/resolver/actions/resolve_download_methods.js b/web/resolver/actions/resolve_download_methods.js index 4096f63..32fb8ae 100644 --- a/web/resolver/actions/resolve_download_methods.js +++ b/web/resolver/actions/resolve_download_methods.js @@ -2,6 +2,7 @@ import { createModuleLogger } from "../../log_system/log_funcs.js"; import { getSvgIcon } from "../../utils/icon_utils.js"; import { getModelCardUrl, parseHuggingFaceFileUrl } from "../utils/url_utils.js"; import { getCivitaiModelUrl } from "../globals.js"; +import { normalizeSha256 } from "../utils/hash_utils.js"; import { normalizePathIdentity } from "../utils/html_utils.js"; const log = createModuleLogger('resolve_download_methods'); @@ -664,7 +665,7 @@ export const resolveDownloadMethods = { }, getExistingLocalHashMatchesForSha(missing = {}, sha256 = '', state = null) { - const hash = this.normalizeSearchResultSha256?.(sha256) + const hash = normalizeSha256(sha256) || String(sha256 || '').trim().toLowerCase(); if (!hash) return []; @@ -678,7 +679,7 @@ export const resolveDownloadMethods = { matches.forEach(match => { if (!match || typeof match !== 'object') return; if (!(match.hash_match || match.match_type === 'hash' || match.hash_lookup_source)) return; - const matchHash = this.normalizeSearchResultSha256?.(this.getLocalMatchHash?.(match) || match.sha256 || match.hash) + const matchHash = normalizeSha256(this.getLocalMatchHash?.(match) || match.sha256 || match.hash) || String(this.getLocalMatchHash?.(match) || match.sha256 || match.hash || '').trim().toLowerCase(); if (matchHash !== hash) return; const identity = this.getLocalMatchIdentity?.(match) || JSON.stringify(match); diff --git a/web/resolver/search/search_hash_methods.js b/web/resolver/search/search_hash_methods.js index bc4db1f..d2963dd 100644 --- a/web/resolver/search/search_hash_methods.js +++ b/web/resolver/search/search_hash_methods.js @@ -1,10 +1,6 @@ import { normalizeSha256 } from '../utils/hash_utils.js'; export const searchHashMethods = { - normalizeSearchResultSha256(value = '') { - return normalizeSha256(value); - }, - getSearchResultSha256(result = {}) { if (!result || typeof result !== 'object') return ''; @@ -24,7 +20,7 @@ export const searchHashMethods = { ]; for (const candidate of candidates) { - const hash = this.normalizeSearchResultSha256(candidate); + const hash = normalizeSha256(candidate); if (hash) return hash; } return ''; @@ -34,8 +30,7 @@ export const searchHashMethods = { const hashes = []; const seen = new Set(); const addHash = (value) => { - const hash = this.normalizeSearchResultSha256?.(value) - || String(value || '').trim().toLowerCase(); + const hash = normalizeSha256(value); if (!hash || !/^[a-f0-9]{64}$/.test(hash) || seen.has(hash)) return; seen.add(hash); hashes.push(hash); @@ -133,7 +128,7 @@ export const searchHashMethods = { if (!match || typeof match !== 'object') return; const matchSource = this.normalizeHashLookupSourceKey(match.hash_lookup_source || ''); - const matchHash = this.normalizeSearchResultSha256(this.getLocalMatchHash(match)); + const matchHash = normalizeSha256(this.getLocalMatchHash(match)); if (sourceHash) { if (matchHash !== sourceHash) return; } else if (!matchSource || matchSource !== normalizedSource) { diff --git a/web/resolver/views/model_hash_compare_methods.js b/web/resolver/views/model_hash_compare_methods.js index a51e102..8ea0fcc 100644 --- a/web/resolver/views/model_hash_compare_methods.js +++ b/web/resolver/views/model_hash_compare_methods.js @@ -1,12 +1,8 @@ import { normalizeSha256 } from '../utils/hash_utils.js'; export const modelHashCompareMethods = { - normalizeSha256ForCompare(value = '') { - return normalizeSha256(value); - }, - formatSha256Short(value = '') { - const hash = this.normalizeSha256ForCompare(value) || String(value || '').trim(); + const hash = normalizeSha256(value) || String(value || '').trim(); if (hash.length <= 18) return hash; return `${hash.slice(0, 8)}...${hash.slice(-8)}`; }, @@ -16,7 +12,7 @@ export const modelHashCompareMethods = { if (value === undefined || value === null || depth > 5) return candidates; const addHash = (rawValue, hashSource = source) => { - const hash = this.normalizeSha256ForCompare(rawValue); + const hash = normalizeSha256(rawValue); if (!hash || seen.has(hash)) return; seen.add(hash); candidates.push({ hash, source: hashSource || source || 'metadata' }); @@ -98,7 +94,7 @@ export const modelHashCompareMethods = { }, updateHashCompareModelMetadata(model = {}, result = {}) { - const sha256 = this.normalizeSha256ForCompare(result?.sha256 || result?.hash || ''); + const sha256 = normalizeSha256(result?.sha256 || result?.hash || ''); if (!sha256 || !model || typeof model !== 'object') return; model.sha256 = sha256; diff --git a/web/resolver/views/model_info_methods.js b/web/resolver/views/model_info_methods.js index f42c011..fcbf8e7 100644 --- a/web/resolver/views/model_info_methods.js +++ b/web/resolver/views/model_info_methods.js @@ -3,6 +3,7 @@ import { getSvgIcon } from "../../utils/icon_utils.js"; import { escapeHtml, escapeJsString, getFilenameFromPath, sanitizeDescriptionHtml, pollBackgroundTask, safeStorage, copyTextWithFeedback } from "../utils/html_utils.js"; import { getModelCardUrl } from "../utils/url_utils.js"; import { extractComfyWorkflow } from "../utils/workflow_metadata.js"; +import { normalizeSha256 } from "../utils/hash_utils.js"; import { getCivitaiModelUrl } from "../globals.js"; export const modelInfoMethods = { escapeHtml, @@ -701,7 +702,7 @@ export const modelInfoMethods = { if (!result) return null; - const sha256 = this.normalizeSha256ForCompare(result?.sha256 || result?.hash || ''); + const sha256 = normalizeSha256(result?.sha256 || result?.hash || ''); if (!sha256) { throw new Error('No hash returned'); } @@ -758,7 +759,7 @@ export const modelInfoMethods = { }, markSearchResultHashBadgesForMissing(missing = {}, sha256 = '', options = {}) { - const hash = this.normalizeSha256ForCompare(sha256); + const hash = normalizeSha256(sha256); if (!missing || !hash) return []; const localMatchIdentity = String(options.localMatchIdentity || '').trim();