refactor: centralize model-extension handling and search-cache clearing
- Add clear_all_search_caches() in core/sources/__init__.py and replace 4 individual cache-clearing call sites in __init__.py with the new helper. - Add shared parse_civitai_model_path() in core/type_utils.py and use it from both CivitAI and CivArchive URL parsers to remove duplicated regex logic. - Rename parse_huggingface_url() to get_huggingface_repo_and_file_from_url() in core/resolver.py for clarity. - Add stripModelExtension() and hasModelExtension() utilities (with a single MODEL_EXTENSIONS_REGEX) in web/resolver/utils/render_format_methods.js and replace repeated inline regex patterns across search_panel.js, model_info_methods.js, tabs_loaded_methods.js, and queue_methods.js.
This commit is contained in:
+3
-8
@@ -258,6 +258,7 @@ class ModelResolverExtension:
|
||||
search_lora_manager_archive_for_file,
|
||||
clear_search_cache as clear_lora_manager_archive_search_cache,
|
||||
)
|
||||
from .core.sources import clear_all_search_caches
|
||||
|
||||
download_available = True
|
||||
except ImportError as e:
|
||||
@@ -3152,10 +3153,7 @@ class ModelResolverExtension:
|
||||
@json_api_endpoint("Clear search cache")
|
||||
async def clear_search_cache_route(request):
|
||||
"""Clear backend search caches after token/settings changes."""
|
||||
clear_huggingface_search_cache()
|
||||
clear_civitai_search_cache()
|
||||
clear_civarchive_search_cache()
|
||||
clear_lora_manager_archive_search_cache()
|
||||
clear_all_search_caches()
|
||||
reload_popular_databases()
|
||||
reload_model_list()
|
||||
invalidate_model_files_cache()
|
||||
@@ -3236,10 +3234,7 @@ class ModelResolverExtension:
|
||||
async def model_list_update_route(request):
|
||||
"""Download latest ComfyUI-Manager model-list.json."""
|
||||
result = await asyncio.to_thread(update_model_list_from_remote)
|
||||
clear_huggingface_search_cache()
|
||||
clear_civitai_search_cache()
|
||||
clear_civarchive_search_cache()
|
||||
clear_lora_manager_archive_search_cache()
|
||||
clear_all_search_caches()
|
||||
self.search_result_timestamps.clear()
|
||||
return web.json_response(result)
|
||||
|
||||
|
||||
+1
-1
@@ -696,7 +696,7 @@ def extract_workflow_urls(workflow_json: Dict[str, Any]) -> Dict[str, Dict[str,
|
||||
return url_map
|
||||
|
||||
|
||||
def parse_huggingface_url(url: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
def get_huggingface_repo_and_file_from_url(url: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Extract HuggingFace repo and path from URL.
|
||||
|
||||
|
||||
@@ -12,8 +12,14 @@ from .huggingface import (
|
||||
search_huggingface,
|
||||
search_huggingface_for_file,
|
||||
get_huggingface_download_url,
|
||||
clear_search_cache as clear_huggingface_search_cache,
|
||||
)
|
||||
from .civitai import (
|
||||
search_civitai,
|
||||
search_civitai_for_file,
|
||||
get_civitai_download_url,
|
||||
clear_search_cache as clear_civitai_search_cache,
|
||||
)
|
||||
from .civitai import search_civitai, search_civitai_for_file, get_civitai_download_url
|
||||
from .civarchive import (
|
||||
clear_search_cache as clear_civarchive_search_cache,
|
||||
is_civarchive_available,
|
||||
@@ -30,6 +36,13 @@ from .lora_manager_archive import (
|
||||
search_lora_manager_archive_for_file,
|
||||
)
|
||||
|
||||
def clear_all_search_caches() -> None:
|
||||
"""Clear search caches for all external sources."""
|
||||
clear_huggingface_search_cache()
|
||||
clear_civitai_search_cache()
|
||||
clear_civarchive_search_cache()
|
||||
clear_lora_manager_archive_search_cache()
|
||||
|
||||
__all__ = [
|
||||
"search_popular_models",
|
||||
"get_popular_model_url",
|
||||
@@ -52,4 +65,5 @@ __all__ = [
|
||||
"is_lora_manager_archive_available",
|
||||
"search_lora_manager_archive",
|
||||
"search_lora_manager_archive_for_file",
|
||||
"clear_all_search_caches",
|
||||
]
|
||||
|
||||
@@ -34,6 +34,7 @@ from ..type_utils import (
|
||||
parse_size_header as _parse_size_header,
|
||||
parse_content_range_size as _parse_content_range_size,
|
||||
extract_response_file_size as _response_file_size,
|
||||
parse_civitai_model_path,
|
||||
)
|
||||
from ..progress import report_progress, get_progress_reporter
|
||||
from ..log_system.log_funcs import create_module_logger
|
||||
@@ -509,16 +510,7 @@ def parse_civarchive_url(url: str) -> Optional[Dict[str, Any]]:
|
||||
if sha_match:
|
||||
return {"sha256": sha_match.group(1).lower()}
|
||||
|
||||
model_match = re.search(r"/models/(\d+)", parsed.path)
|
||||
if model_match:
|
||||
result: Dict[str, Any] = {"model_id": int(model_match.group(1))}
|
||||
query = parse_qs(parsed.query)
|
||||
version_id = query.get("modelVersionId", [None])[0]
|
||||
if version_id:
|
||||
result["version_id"] = _coerce_int(version_id)
|
||||
return result
|
||||
|
||||
return None
|
||||
return parse_civitai_model_path(parsed.path, parsed.query)
|
||||
|
||||
|
||||
def _extract_sha256(value: str) -> Optional[str]:
|
||||
|
||||
@@ -28,6 +28,7 @@ from ..type_utils import (
|
||||
get_version_sort_key,
|
||||
check_credential_http,
|
||||
DEFAULT_BROWSER_USER_AGENT,
|
||||
parse_civitai_model_path,
|
||||
)
|
||||
from ..progress import report_progress, get_progress_reporter
|
||||
from ..path_utils import calculate_file_sha256, get_filename_from_path
|
||||
@@ -684,15 +685,7 @@ def parse_civitai_url(url: str) -> Optional[Dict[str, Any]]:
|
||||
if match:
|
||||
return {"version_id": int(match.group(1))}
|
||||
|
||||
match = re.search(r"/models/(\d+)", parsed.path)
|
||||
if match:
|
||||
result = {"model_id": int(match.group(1))}
|
||||
query = parse_qs(parsed.query)
|
||||
if "modelVersionId" in query:
|
||||
result["version_id"] = int(query["modelVersionId"][0])
|
||||
return result
|
||||
|
||||
return None
|
||||
return parse_civitai_model_path(parsed.path, parsed.query)
|
||||
|
||||
|
||||
def get_civitai_download_url(version_id: int, api_key: Optional[str] = None) -> str:
|
||||
|
||||
@@ -503,3 +503,26 @@ def extract_response_file_size(response: Any) -> Optional[int]:
|
||||
return size
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def parse_civitai_model_path(path: str, query_string: str) -> Optional[Dict[str, int]]:
|
||||
"""
|
||||
Common helper to parse `/models/{model_id}?modelVersionId={version_id}` URLs
|
||||
for CivitAI and CivArchive.
|
||||
"""
|
||||
import re
|
||||
from urllib.parse import parse_qs
|
||||
|
||||
model_match = re.search(r"/models/(\d+)", path)
|
||||
if model_match:
|
||||
result = {"model_id": int(model_match.group(1))}
|
||||
query = parse_qs(query_string)
|
||||
version_id_list = query.get("modelVersionId")
|
||||
if version_id_list:
|
||||
try:
|
||||
result["version_id"] = int(version_id_list[0])
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
return result
|
||||
return None
|
||||
|
||||
|
||||
@@ -2296,7 +2296,7 @@ export const queueMethods = {
|
||||
text = text.split('/').pop() || text;
|
||||
}
|
||||
if (stem) {
|
||||
text = text.replace(/\.(safetensors|ckpt|pt|pth|bin|gguf|onnx)$/i, '');
|
||||
text = this.stripModelExtension(text);
|
||||
}
|
||||
return text;
|
||||
},
|
||||
|
||||
@@ -150,7 +150,7 @@ export const searchPanelMethods = {
|
||||
.split(/[\\/]+/)
|
||||
.map(part => part.trim())
|
||||
.filter(Boolean);
|
||||
const directoryParts = parts.filter(part => !/\.(safetensors|ckpt|pt|pth|bin|gguf|onnx)$/i.test(part));
|
||||
const directoryParts = parts.filter(part => !this.hasModelExtension(part));
|
||||
|
||||
for (const part of directoryParts) {
|
||||
const exact = this.resolveBaseModelAliasExact(part);
|
||||
@@ -311,7 +311,7 @@ export const searchPanelMethods = {
|
||||
if (/^urn:/i.test(value.trim())) {
|
||||
return;
|
||||
}
|
||||
if (/\.(safetensors|ckpt|pt|pth|bin|gguf|onnx)\b/i.test(value) || /model|checkpoint|unet|diffusion/i.test(key)) {
|
||||
if (this.hasModelExtension(value) || /model|checkpoint|unet|diffusion/i.test(key)) {
|
||||
values.push(value);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -335,5 +335,26 @@ export const renderFormatMethods = {
|
||||
|
||||
normalizePathToBackward(value) {
|
||||
return String(value || '').trim().replace(/\//g, '\\');
|
||||
},
|
||||
|
||||
stripModelExtension(value) {
|
||||
return stripModelExtension(value);
|
||||
},
|
||||
|
||||
hasModelExtension(value) {
|
||||
return hasModelExtension(value);
|
||||
}
|
||||
};
|
||||
|
||||
export const MODEL_EXTENSIONS_REGEX = /\.(safetensors|ckpt|pt2?|bin|pth|pkl|sft|onnx|gguf)(?:$|[?#])/i;
|
||||
|
||||
export function stripModelExtension(value) {
|
||||
if (!value) return '';
|
||||
return String(value).replace(MODEL_EXTENSIONS_REGEX, '');
|
||||
}
|
||||
|
||||
export function hasModelExtension(value) {
|
||||
if (!value) return false;
|
||||
return MODEL_EXTENSIONS_REGEX.test(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -1056,7 +1056,7 @@ export const modelInfoMethods = {
|
||||
}
|
||||
} catch (e) {
|
||||
// Fall back to direct search
|
||||
const searchName = name.replace(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i, '');
|
||||
const searchName = this.stripModelExtension(name);
|
||||
const searchUrl = `https://civitai.com/search?q=${encodeURIComponent(searchName)}`;
|
||||
window.open(searchUrl, '_blank');
|
||||
}
|
||||
@@ -1154,7 +1154,7 @@ export const modelInfoMethods = {
|
||||
},
|
||||
|
||||
createInfoDialog(loraName, modelData) {
|
||||
const loraDisplayName = loraName.replace(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i, '');
|
||||
const loraDisplayName = this.stripModelExtension(loraName);
|
||||
|
||||
const dialog = document.createElement('div');
|
||||
dialog.className = 'mr-info-dialog-backdrop';
|
||||
@@ -1917,7 +1917,7 @@ export const modelInfoMethods = {
|
||||
|
||||
buildLocalInfoDialogData(loraName, modelData = {}) {
|
||||
const filename = loraName || modelData?.filename || modelData?.name || '';
|
||||
const modelName = String(filename || '').replace(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i, '');
|
||||
const modelName = this.stripModelExtension(filename);
|
||||
const resolvedPath = this.getModelInfoResolvedPath(modelData);
|
||||
const categoryTypeMap = {
|
||||
checkpoints: 'checkpoint',
|
||||
|
||||
@@ -168,6 +168,8 @@ export const tabsLoadedMethods = {
|
||||
const activeCount = Object.values(byCategory).reduce((sum, cat) => sum + cat.active.length, 0);
|
||||
const inactiveCount = Object.values(byCategory).reduce((sum, cat) => sum + cat.inactive.length, 0);
|
||||
|
||||
const stripModelExtension = (value) => this.stripModelExtension(value || 'Unknown');
|
||||
|
||||
const buildCategoryStrings = (filter) => {
|
||||
const result = {};
|
||||
for (const [category, modelsObj] of Object.entries(byCategory)) {
|
||||
@@ -175,10 +177,7 @@ export const tabsLoadedMethods = {
|
||||
const models = filter === 'active' ? modelsObj.active : filter === 'inactive' ? modelsObj.inactive : [...modelsObj.active, ...modelsObj.inactive];
|
||||
const parts = models.map(model => {
|
||||
const fullName = model.name || model.original_path?.split(/[\/\\]/).pop() || 'Unknown';
|
||||
let name = fullName;
|
||||
if (fullName.match(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i)) {
|
||||
name = fullName.replace(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i, '');
|
||||
}
|
||||
const name = stripModelExtension(fullName);
|
||||
const strength = model.strength !== null && model.strength !== undefined
|
||||
? model.strength.toFixed(2)
|
||||
: '1.00';
|
||||
@@ -194,7 +193,6 @@ export const tabsLoadedMethods = {
|
||||
const allString = buildCategoryStrings('all');
|
||||
|
||||
const copyIcon = getSvgIcon('copy', 'currentColor', 'mr-copy-btn-icon');
|
||||
const stripModelExtension = (value) => String(value || 'Unknown').replace(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i, '');
|
||||
const modelToken = (model, category) => {
|
||||
const fullName = model.name || model.original_path?.split(/[\/\\]/).pop() || 'Unknown';
|
||||
const name = stripModelExtension(fullName);
|
||||
@@ -253,7 +251,7 @@ export const tabsLoadedMethods = {
|
||||
|
||||
for (const model of modelsObj.active) {
|
||||
const fullName = model.name || model.original_path?.split(/[\/\\]/).pop() || 'Unknown';
|
||||
const name = fullName.replace(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i, '');
|
||||
const name = this.stripModelExtension(fullName);
|
||||
const strength = model.strength !== null && model.strength !== undefined ? model.strength.toFixed(2) : null;
|
||||
html += `<span class="mr-model-chip"${this.getContextMenuAttrs(model)}>${this.escapeHtml(name)}${strength !== null ? `<span class="mr-model-chip-strength">${this.escapeHtml(strength)}</span>` : ''}</span>`;
|
||||
}
|
||||
@@ -272,7 +270,7 @@ export const tabsLoadedMethods = {
|
||||
|
||||
for (const model of modelsObj.inactive) {
|
||||
const fullName = model.name || model.original_path?.split(/[\/\\]/).pop() || 'Unknown';
|
||||
const name = fullName.replace(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i, '');
|
||||
const name = this.stripModelExtension(fullName);
|
||||
const strength = model.strength !== null && model.strength !== undefined ? model.strength.toFixed(2) : null;
|
||||
html += `<span class="mr-model-chip"${this.getContextMenuAttrs(model)}>${this.escapeHtml(name)}${strength !== null ? `<span class="mr-model-chip-strength">${this.escapeHtml(strength)}</span>` : ''}</span>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user