refactor: centralize filename and size utilities across sources
- Introduce get_filename_from_path in path_utils and replace repeated os.path.basename calls across core/downloader.py, core/resolver.py, core/sources/civarchive.py, core/sources/civitai.py, and core/sources/huggingface.py - Move duplicated inline HTTP response size helpers (_response_file_size, _parse_size_header, _parse_content_range_size) into core/type_utils.py and update civarchive.py and huggingface.py to import them - Remove inline normalize_sha256_value from __init__.py and reuse the existing normalize_sha256 helper - Refactor options_methods.js localStorage read/write logic into a SETTINGS_MAP-driven loop to reduce repetition
This commit is contained in:
+2
-13
@@ -1320,28 +1320,17 @@ class ModelResolverExtension:
|
||||
"metadata_checked": bool(civitai_checked),
|
||||
"civitai_checked": bool(civitai_checked),
|
||||
}
|
||||
|
||||
def normalize_sha256_value(value):
|
||||
text = str(value or "").strip().lower()
|
||||
if text.startswith("sha256:"):
|
||||
text = text.split(":", 1)[1].strip()
|
||||
if len(text) == 64 and all(
|
||||
ch in "0123456789abcdef" for ch in text
|
||||
):
|
||||
return text
|
||||
return ""
|
||||
|
||||
def extract_result_sha256(result):
|
||||
if not isinstance(result, dict):
|
||||
return ""
|
||||
for key in ("sha256", "hash"):
|
||||
normalized = normalize_sha256_value(result.get(key))
|
||||
normalized = normalize_sha256(result.get(key))
|
||||
if normalized:
|
||||
return normalized
|
||||
hashes = result.get("hashes")
|
||||
if isinstance(hashes, dict):
|
||||
for key in ("SHA256", "sha256", "Sha256"):
|
||||
normalized = normalize_sha256_value(hashes.get(key))
|
||||
normalized = normalize_sha256(hashes.get(key))
|
||||
if normalized:
|
||||
return normalized
|
||||
return ""
|
||||
|
||||
+9
-9
@@ -23,7 +23,7 @@ from .log_system.log_funcs import create_module_logger
|
||||
log = create_module_logger(__name__)
|
||||
|
||||
from .resolver import normalize_sha256
|
||||
from .path_utils import is_path_within, get_path_identity, write_json_atomic, read_json_safe, get_comfy_root_path, calculate_file_sha256 as _calculate_file_sha256
|
||||
from .path_utils import is_path_within, get_path_identity, write_json_atomic, read_json_safe, get_comfy_root_path, calculate_file_sha256 as _calculate_file_sha256, get_filename_from_path
|
||||
from .type_utils import as_dict, as_list, first_non_empty, format_size_bytes as format_bytes
|
||||
|
||||
try:
|
||||
@@ -310,7 +310,7 @@ def _find_metadata_file_info(
|
||||
if isinstance(value, dict):
|
||||
return value
|
||||
|
||||
filename_lower = os.path.basename(str(filename or "")).lower()
|
||||
filename_lower = get_filename_from_path(str(filename or "")).lower()
|
||||
file_lists = [
|
||||
source.get("files"),
|
||||
selected_version.get("files"),
|
||||
@@ -404,7 +404,7 @@ def build_lora_manager_metadata(
|
||||
source.get("selected_version") or details.get("selected_version")
|
||||
)
|
||||
|
||||
basename = os.path.basename(dest_path)
|
||||
basename = get_filename_from_path(dest_path)
|
||||
file_name = os.path.splitext(basename)[0]
|
||||
filename = _first_present(
|
||||
source.get("filename"),
|
||||
@@ -679,7 +679,7 @@ def get_download_directory(category: str, preferred_base_directory: str = "") ->
|
||||
comfy_root = get_comfy_root_path(folder_paths)
|
||||
|
||||
def _basename(path_value: str) -> str:
|
||||
return os.path.basename(os.path.normpath(path_value)).lower()
|
||||
return get_filename_from_path(os.path.normpath(path_value)).lower()
|
||||
|
||||
def _prefer_redirected(candidate_paths: List[str]) -> Optional[str]:
|
||||
if not candidate_paths:
|
||||
@@ -1170,7 +1170,7 @@ def download_file_with_aria2(
|
||||
"size": 0,
|
||||
}
|
||||
start_time = time.time()
|
||||
filename = os.path.basename(dest_path)
|
||||
filename = get_filename_from_path(dest_path)
|
||||
|
||||
with download_lock:
|
||||
download_progress[download_id] = {
|
||||
@@ -1431,7 +1431,7 @@ def download_file(
|
||||
"progress": 0,
|
||||
"total_size": 0,
|
||||
"downloaded": 0,
|
||||
"filename": os.path.basename(dest_path),
|
||||
"filename": get_filename_from_path(dest_path),
|
||||
"path": dest_path,
|
||||
"directory": os.path.dirname(dest_path),
|
||||
"url": url,
|
||||
@@ -1446,7 +1446,7 @@ def download_file(
|
||||
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
||||
|
||||
# Verbose logging - what model and from where
|
||||
filename = os.path.basename(dest_path)
|
||||
filename = get_filename_from_path(dest_path)
|
||||
source = (
|
||||
"HuggingFace"
|
||||
if "huggingface.co" in url
|
||||
@@ -1627,7 +1627,7 @@ def download_file(
|
||||
result["error"] = error_msg
|
||||
|
||||
# CLI error log
|
||||
log.error(f"✗ Download failed: {os.path.basename(dest_path)}")
|
||||
log.error(f"✗ Download failed: {get_filename_from_path(dest_path)}")
|
||||
log.error(f"Error: {error_msg}")
|
||||
|
||||
# Clean up partial file
|
||||
@@ -1645,7 +1645,7 @@ def download_file(
|
||||
result["error"] = error_msg
|
||||
|
||||
# CLI error log
|
||||
log.error(f"✗ Download failed: {os.path.basename(dest_path)}")
|
||||
log.error(f"✗ Download failed: {get_filename_from_path(dest_path)}")
|
||||
log.error(f"Error: {error_msg}")
|
||||
log.error(f"Download error: {e}", exc_info=True)
|
||||
|
||||
|
||||
+10
-9
@@ -42,6 +42,7 @@ from .path_utils import (
|
||||
is_path_within,
|
||||
prefer_local_base_directory,
|
||||
dedupe_local_base_directories,
|
||||
get_filename_from_path,
|
||||
)
|
||||
|
||||
|
||||
@@ -54,13 +55,13 @@ def get_workflow_url_info_for_filename(
|
||||
if filename in workflow_urls:
|
||||
return workflow_urls[filename]
|
||||
|
||||
filename_stem = strip_known_model_extension(os.path.basename(filename)).lower()
|
||||
filename_stem = strip_known_model_extension(get_filename_from_path(filename)).lower()
|
||||
if not filename_stem:
|
||||
return None
|
||||
|
||||
for workflow_filename, url_info in workflow_urls.items():
|
||||
workflow_stem = strip_known_model_extension(
|
||||
os.path.basename(workflow_filename)
|
||||
get_filename_from_path(workflow_filename)
|
||||
).lower()
|
||||
if workflow_stem == filename_stem:
|
||||
return url_info
|
||||
@@ -294,7 +295,7 @@ def _metadata_sidecar_candidates(model_path: str) -> List[str]:
|
||||
return []
|
||||
|
||||
directory = os.path.dirname(model_path)
|
||||
filename = os.path.basename(model_path)
|
||||
filename = get_filename_from_path(model_path)
|
||||
stem, _ = os.path.splitext(filename)
|
||||
|
||||
candidates = [
|
||||
@@ -340,9 +341,9 @@ def _collect_hashes_from_container(value: Any) -> List[str]:
|
||||
|
||||
|
||||
def _metadata_file_matches_model(file_info: Dict[str, Any], model: Dict[str, Any]) -> bool:
|
||||
model_filename = str(model.get("filename") or os.path.basename(model.get("path", ""))).lower()
|
||||
model_filename = str(model.get("filename") or get_filename_from_path(model.get("path", ""))).lower()
|
||||
model_relative = str(model.get("relative_path") or "").replace("\\", "/").lower()
|
||||
model_stem = strip_known_model_extension(os.path.basename(model_filename)).lower()
|
||||
model_stem = strip_known_model_extension(get_filename_from_path(model_filename)).lower()
|
||||
|
||||
candidates = [
|
||||
file_info.get("name"),
|
||||
@@ -352,7 +353,7 @@ def _metadata_file_matches_model(file_info: Dict[str, Any], model: Dict[str, Any
|
||||
]
|
||||
for candidate in candidates:
|
||||
text = str(candidate or "").replace("\\", "/").lower()
|
||||
basename = os.path.basename(text)
|
||||
basename = get_filename_from_path(text)
|
||||
stem = strip_known_model_extension(basename).lower()
|
||||
if text and model_relative and text == model_relative:
|
||||
return True
|
||||
@@ -463,7 +464,7 @@ def search_local_matches_by_hash(
|
||||
matches.append(
|
||||
{
|
||||
"model": model_with_metadata,
|
||||
"filename": model.get("filename") or os.path.basename(model_path),
|
||||
"filename": model.get("filename") or get_filename_from_path(model_path),
|
||||
"similarity": 1.0,
|
||||
"confidence": 100.0,
|
||||
"match_type": "hash",
|
||||
@@ -499,7 +500,7 @@ def get_local_model_hash_metadata(
|
||||
**(model if isinstance(model, dict) else {}),
|
||||
"path": normalized_path,
|
||||
}
|
||||
model_info.setdefault("filename", os.path.basename(normalized_path))
|
||||
model_info.setdefault("filename", get_filename_from_path(normalized_path))
|
||||
model_info.setdefault("relative_path", model_info.get("filename", ""))
|
||||
|
||||
first_metadata_path = ""
|
||||
@@ -846,7 +847,7 @@ def analyze_and_find_matches(
|
||||
# Enrich missing models with workflow URLs
|
||||
for missing in missing_models:
|
||||
original_path = missing.get("original_path", "")
|
||||
filename = os.path.basename(original_path)
|
||||
filename = get_filename_from_path(original_path)
|
||||
|
||||
url_info = get_workflow_url_info_for_filename(workflow_urls, filename)
|
||||
if url_info:
|
||||
|
||||
@@ -23,6 +23,7 @@ from ..matcher import (
|
||||
base_model_score as _base_model_score,
|
||||
calculate_candidate_rank,
|
||||
)
|
||||
from ..path_utils import get_filename_from_path
|
||||
from ..type_utils import (
|
||||
to_int,
|
||||
CIVARCHIVE_API_TYPE_MAP,
|
||||
@@ -30,6 +31,9 @@ from ..type_utils import (
|
||||
parse_size_to_bytes,
|
||||
get_version_sort_key,
|
||||
DEFAULT_BROWSER_USER_AGENT,
|
||||
parse_size_header as _parse_size_header,
|
||||
parse_content_range_size as _parse_content_range_size,
|
||||
extract_response_file_size as _response_file_size,
|
||||
)
|
||||
from ..progress import report_progress, get_progress_reporter
|
||||
from ..log_system.log_funcs import create_module_logger
|
||||
@@ -582,8 +586,8 @@ def _download_url_looks_like_model_file(
|
||||
if normalized.startswith(CIVITAI_DOWNLOAD_URL_PREFIXES):
|
||||
return True
|
||||
|
||||
basename = os.path.basename(path).lower()
|
||||
expected = os.path.basename(str(expected_filename or "")).lower()
|
||||
basename = get_filename_from_path(path).lower()
|
||||
expected = get_filename_from_path(str(expected_filename or "")).lower()
|
||||
if expected and basename == expected:
|
||||
return True
|
||||
return _has_known_model_extension(basename)
|
||||
@@ -628,25 +632,6 @@ def _prepare_size_probe_url(url: Any) -> Optional[str]:
|
||||
return normalized
|
||||
|
||||
|
||||
def _response_file_size(response: requests.Response) -> Optional[int]:
|
||||
size = _parse_content_range_size(response.headers.get("Content-Range"))
|
||||
if size:
|
||||
return size
|
||||
|
||||
for key in (
|
||||
"x-linked-size",
|
||||
"x-file-size",
|
||||
"x-goog-stored-content-length",
|
||||
"content-length",
|
||||
):
|
||||
size = _parse_size_header(response.headers.get(key))
|
||||
if size:
|
||||
content_type = (response.headers.get("content-type") or "").lower()
|
||||
if key == "content-length" and "text/html" in content_type:
|
||||
continue
|
||||
return size
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _fetch_remote_file_size_bytes(url: Any, timeout: int = 15) -> Optional[int]:
|
||||
@@ -1436,24 +1421,6 @@ def _find_model_title_match_in_model_details(
|
||||
return None
|
||||
|
||||
|
||||
def _parse_size_header(value: Any) -> Optional[int]:
|
||||
if value is None or value == "":
|
||||
return None
|
||||
try:
|
||||
size = int(str(value).strip())
|
||||
return size if size > 0 else None
|
||||
except (TypeError, ValueError):
|
||||
size = parse_size_to_bytes(value)
|
||||
return size if size and size > 0 else None
|
||||
|
||||
|
||||
def _parse_content_range_size(value: Any) -> Optional[int]:
|
||||
if not value:
|
||||
return None
|
||||
match = re.search(r"/(\d+)\s*$", str(value))
|
||||
if not match:
|
||||
return None
|
||||
return _parse_size_header(match.group(1))
|
||||
|
||||
|
||||
def _build_result_from_model_details(
|
||||
@@ -1941,7 +1908,7 @@ def _build_result_from_search_candidate(
|
||||
parsed: Optional[Dict[str, Any]] = None,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
candidate_name = _candidate_display_name(candidate)
|
||||
query_basename = os.path.basename(query or "").strip()
|
||||
query_basename = get_filename_from_path(query or "").strip()
|
||||
filename = (
|
||||
candidate.get("filename")
|
||||
or candidate.get("fileName")
|
||||
@@ -2187,7 +2154,7 @@ def _is_hash_verified_exact_match(result: Optional[Dict[str, Any]], confidence:
|
||||
|
||||
|
||||
def _build_search_queries(filename: str) -> List[str]:
|
||||
basename = os.path.basename(filename or "").strip()
|
||||
basename = get_filename_from_path(filename or "").strip()
|
||||
stem = os.path.splitext(basename)[0].strip()
|
||||
simplified = re.sub(
|
||||
r"[-_]?(fp16|fp32|fp8|fp4|bf16|e4m3fn|scaled|pruned|emaonly|mixed|q4|q8)$",
|
||||
@@ -2318,7 +2285,7 @@ def search_civarchive_for_file(
|
||||
best_rank = -9999.0
|
||||
seen = set()
|
||||
allow_model_title_match = not exact_only and not _has_known_model_extension(
|
||||
os.path.basename(normalized_filename)
|
||||
get_filename_from_path(normalized_filename)
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
@@ -30,7 +30,7 @@ from ..type_utils import (
|
||||
DEFAULT_BROWSER_USER_AGENT,
|
||||
)
|
||||
from ..progress import report_progress, get_progress_reporter
|
||||
from ..path_utils import calculate_file_sha256
|
||||
from ..path_utils import calculate_file_sha256, get_filename_from_path
|
||||
from ..log_system.log_funcs import create_module_logger
|
||||
log = create_module_logger(__name__)
|
||||
|
||||
@@ -469,7 +469,7 @@ def _find_civitai_file_in_model(
|
||||
filename_lower = filename.lower()
|
||||
filename_base = os.path.splitext(filename_lower)[0]
|
||||
allow_model_title_match = not exact_only and not _has_known_model_extension(
|
||||
os.path.basename(filename)
|
||||
get_filename_from_path(filename)
|
||||
)
|
||||
|
||||
def build_result_from_resolved_version(
|
||||
@@ -1564,7 +1564,7 @@ def _get_metadata_file_path(model_path: str) -> str:
|
||||
|
||||
# Get directory and filename
|
||||
directory = os.path.dirname(model_path)
|
||||
filename = os.path.basename(model_path)
|
||||
filename = get_filename_from_path(model_path)
|
||||
|
||||
# Try different variations of the metadata file name
|
||||
base_name = filename.rsplit(".", 1)[0] if "." in filename else filename
|
||||
@@ -1948,7 +1948,7 @@ def get_model_info_for_file(
|
||||
log.info(f"Files in {directory}: {metadata_files}")
|
||||
|
||||
if local_only:
|
||||
filename = os.path.basename(file_path)
|
||||
filename = get_filename_from_path(file_path)
|
||||
stem = os.path.splitext(filename)[0]
|
||||
result = {
|
||||
"source": "local",
|
||||
|
||||
@@ -18,7 +18,7 @@ from ..log_system.log_funcs import create_module_logger
|
||||
log = create_module_logger(__name__)
|
||||
|
||||
from ..path_utils import write_json_atomic, METADATA_DIR
|
||||
from ..type_utils import check_credential_http
|
||||
from ..type_utils import check_credential_http, parse_size_header as _parse_size_header, parse_content_range_size as _parse_content_range_size, extract_response_file_size as _response_file_size
|
||||
|
||||
HF_API_URL = "https://huggingface.co/api"
|
||||
HF_AUTHOR_FALLBACKS = ["Comfy-Org"]
|
||||
@@ -334,23 +334,6 @@ def get_huggingface_download_url(repo: str, filename: str, branch: str = "main")
|
||||
return f"https://huggingface.co/{repo}/resolve/{branch}/{quote(filename)}"
|
||||
|
||||
|
||||
def _parse_size_header(value: Any) -> Optional[int]:
|
||||
if value is None or value == "":
|
||||
return None
|
||||
try:
|
||||
size = int(str(value).strip())
|
||||
return size if size > 0 else None
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def _parse_content_range_size(value: Any) -> Optional[int]:
|
||||
if not value:
|
||||
return None
|
||||
match = re.search(r"/(\d+)\s*$", str(value))
|
||||
if not match:
|
||||
return None
|
||||
return _parse_size_header(match.group(1))
|
||||
|
||||
|
||||
def _extract_file_info_size(file_info: Dict[str, Any]) -> Optional[int]:
|
||||
@@ -383,25 +366,6 @@ def _normalize_huggingface_size_probe_url(url: str) -> Optional[str]:
|
||||
return value
|
||||
|
||||
|
||||
def _response_file_size(response: requests.Response) -> Optional[int]:
|
||||
size = _parse_content_range_size(response.headers.get("Content-Range"))
|
||||
if size:
|
||||
return size
|
||||
|
||||
for key in (
|
||||
"x-linked-size",
|
||||
"x-file-size",
|
||||
"x-goog-stored-content-length",
|
||||
"content-length",
|
||||
):
|
||||
size = _parse_size_header(response.headers.get(key))
|
||||
if size:
|
||||
content_type = (response.headers.get("content-type") or "").lower()
|
||||
if key == "content-length" and "text/html" in content_type:
|
||||
continue
|
||||
return size
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _fetch_remote_file_size_bytes(
|
||||
|
||||
@@ -456,7 +456,50 @@ DEFAULT_BROWSER_USER_AGENT = (
|
||||
)
|
||||
|
||||
|
||||
def parse_size_header(value: Any) -> Optional[int]:
|
||||
"""Safely parse a size value (like a header) to bytes with fallback."""
|
||||
if value is None or value == "":
|
||||
return None
|
||||
try:
|
||||
size = int(str(value).strip())
|
||||
return size if size > 0 else None
|
||||
except (TypeError, ValueError):
|
||||
size = parse_size_to_bytes(value)
|
||||
return size if size and size > 0 else None
|
||||
|
||||
|
||||
def parse_content_range_size(value: Any) -> Optional[int]:
|
||||
"""Extract total size from a Content-Range header value."""
|
||||
if not value:
|
||||
return None
|
||||
match = re.search(r"/(\d+)\s*$", str(value))
|
||||
if not match:
|
||||
return None
|
||||
return parse_size_header(match.group(1))
|
||||
|
||||
|
||||
def extract_response_file_size(response: Any) -> Optional[int]:
|
||||
"""Extract file size from various HTTP response headers."""
|
||||
if response is None:
|
||||
return None
|
||||
|
||||
# Check Content-Range first
|
||||
size = parse_content_range_size(response.headers.get("Content-Range"))
|
||||
if size:
|
||||
return size
|
||||
|
||||
# Try other common size headers
|
||||
for key in (
|
||||
"x-linked-size",
|
||||
"x-file-size",
|
||||
"x-goog-stored-content-length",
|
||||
"content-length",
|
||||
):
|
||||
size = parse_size_header(response.headers.get(key))
|
||||
if size:
|
||||
content_type = (response.headers.get("content-type") or "").lower()
|
||||
if key == "content-length" and "text/html" in content_type:
|
||||
continue
|
||||
return size
|
||||
|
||||
return None
|
||||
|
||||
@@ -4,6 +4,32 @@ import { $el } from "../../../../../scripts/ui.js";
|
||||
import { getSvgIcon } from "../../utils/icon_utils.js";
|
||||
import { LOG_LEVEL as DEFAULT_FRONTEND_LOG_LEVEL } from "../../log_system/config.js";
|
||||
import { logger as frontendLogger } from "../../log_system/logger.js";
|
||||
const SETTINGS_MAP = [
|
||||
{ serverKey: 'civitai_key', localKey: 'ModelResolver.civitaiApiKey', type: 'string', default: '' },
|
||||
{ serverKey: 'civitai_session_token', localKey: 'ModelResolver.civitaiSessionToken', type: 'string', default: '' },
|
||||
{ serverKey: 'civitai_use_trpc_search', localKey: 'ModelResolver.civitaiUseTrpcSearch', type: 'boolean', default: true },
|
||||
{ serverKey: 'civitai_use_html_fallback', localKey: 'ModelResolver.civitaiUseHtmlFallback', type: 'boolean', default: true },
|
||||
{ serverKey: 'hf_token', localKey: 'ModelResolver.huggingFaceToken', type: 'string', default: '' },
|
||||
{ serverKey: 'brave_search_api_key', localKey: 'ModelResolver.braveSearchApiKey', type: 'string', default: '' },
|
||||
{ serverKey: 'hf_use_api_search', localKey: 'ModelResolver.hfUseApiSearch', type: 'boolean', default: true },
|
||||
{ serverKey: 'hf_use_comfy_org_fallback', localKey: 'ModelResolver.hfUseComfyOrgFallback', type: 'boolean', default: true },
|
||||
{ serverKey: 'hf_use_brave_fallback', localKey: 'ModelResolver.hfUseBraveFallback', type: 'boolean', default: true },
|
||||
{ serverKey: 'auto_fill_base_model', localKey: 'ModelResolver.autoFillBaseModel', type: 'boolean', default: true },
|
||||
{ serverKey: 'auto_fill_subfolder', localKey: 'ModelResolver.autoFillSubfolder', type: 'boolean', default: true },
|
||||
{ serverKey: 'auto_refresh_comfy_models_after_apply', localKey: 'ModelResolver.autoRefreshComfyModelsAfterApply', type: 'boolean', default: true },
|
||||
{ serverKey: 'download_backend', localKey: 'ModelResolver.downloadBackend', type: 'backend', default: 'python' },
|
||||
{ serverKey: 'aria2c_path', localKey: 'ModelResolver.aria2cPath', type: 'string', default: '' },
|
||||
{ serverKey: 'aria2_auto_stop_daemon', localKey: 'ModelResolver.aria2AutoStopDaemon', type: 'boolean', default: true },
|
||||
{ serverKey: 'download_path_mode', localKey: 'ModelResolver.downloadPathMode', type: 'pathMode', default: 'suggested' },
|
||||
{ serverKey: 'download_path_templates', localKey: 'ModelResolver.downloadPathTemplates', type: 'json', default: {} },
|
||||
{ serverKey: 'base_model_path_mappings', localKey: 'ModelResolver.baseModelPathMappings', type: 'json', default: {} },
|
||||
{ serverKey: 'frontend_logs_enabled', localKey: 'ModelResolver.frontendLogsEnabled', type: 'frontendLogsEnabled', default: true },
|
||||
{ serverKey: 'backend_logs_enabled', localKey: 'ModelResolver.backendLogsEnabled', type: 'backendLogsEnabled', default: true },
|
||||
{ serverKey: 'frontend_log_level', localKey: 'ModelResolver.frontendLogLevel', type: 'string', default: 'DEBUG' },
|
||||
{ serverKey: 'backend_log_level', localKey: 'ModelResolver.backendLogLevel', type: 'string', default: 'DEBUG' },
|
||||
{ serverKey: 'civitai_candidate_limit', localKey: 'ModelResolver.civitaiCandidateLimit', type: 'candidateLimit', default: 5 },
|
||||
];
|
||||
|
||||
export const optionsMethods = {
|
||||
displayOptions() {
|
||||
if (!this.contentElement) return;
|
||||
@@ -2106,35 +2132,22 @@ export const optionsMethods = {
|
||||
};
|
||||
|
||||
// 1. Write to localStorage (fast local cache)
|
||||
localStorage.setItem('ModelResolver.civitaiApiKey', newSettings.civitai_key);
|
||||
localStorage.setItem('ModelResolver.civitaiSessionToken', newSettings.civitai_session_token);
|
||||
localStorage.setItem('ModelResolver.civitaiUseTrpcSearch', newSettings.civitai_use_trpc_search ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.civitaiUseHtmlFallback', newSettings.civitai_use_html_fallback ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.huggingFaceToken', newSettings.hf_token);
|
||||
localStorage.setItem('ModelResolver.braveSearchApiKey', newSettings.brave_search_api_key);
|
||||
localStorage.setItem('ModelResolver.hfUseApiSearch', newSettings.hf_use_api_search ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.hfUseComfyOrgFallback', newSettings.hf_use_comfy_org_fallback ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.hfUseBraveFallback', newSettings.hf_use_brave_fallback ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.autoFillBaseModel', newSettings.auto_fill_base_model ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.autoFillSubfolder', newSettings.auto_fill_subfolder ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.autoRefreshComfyModelsAfterApply', newSettings.auto_refresh_comfy_models_after_apply ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.downloadBackend', newSettings.download_backend);
|
||||
localStorage.setItem('ModelResolver.aria2cPath', newSettings.aria2c_path);
|
||||
localStorage.setItem('ModelResolver.aria2AutoStopDaemon', newSettings.aria2_auto_stop_daemon ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.downloadPathMode', newSettings.download_path_mode);
|
||||
localStorage.setItem('ModelResolver.downloadPathTemplates', JSON.stringify(newSettings.download_path_templates));
|
||||
localStorage.setItem('ModelResolver.baseModelPathMappings', JSON.stringify(newSettings.base_model_path_mappings));
|
||||
for (const opt of SETTINGS_MAP) {
|
||||
const val = newSettings[opt.serverKey];
|
||||
if (opt.type === 'boolean' || opt.type === 'frontendLogsEnabled' || opt.type === 'backendLogsEnabled') {
|
||||
localStorage.setItem(opt.localKey, val ? 'true' : 'false');
|
||||
} else if (opt.type === 'json') {
|
||||
localStorage.setItem(opt.localKey, JSON.stringify(val));
|
||||
} else {
|
||||
localStorage.setItem(opt.localKey, String(val ?? ''));
|
||||
}
|
||||
}
|
||||
defaultRootSelectInputs.forEach((select) => {
|
||||
const storageKey = select.dataset.storageKey;
|
||||
if (storageKey) {
|
||||
localStorage.setItem(storageKey, select.value || '');
|
||||
}
|
||||
});
|
||||
localStorage.setItem('ModelResolver.frontendLogsEnabled', newSettings.frontend_logs_enabled ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.backendLogsEnabled', newSettings.backend_logs_enabled ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.frontendLogLevel', newSettings.frontend_log_level);
|
||||
localStorage.setItem('ModelResolver.backendLogLevel', newSettings.backend_log_level);
|
||||
localStorage.setItem('ModelResolver.civitaiCandidateLimit', `${civitaiCandidateLimit}`);
|
||||
Object.entries(sourceEnabled).forEach(([key, val]) => {
|
||||
localStorage.setItem(key, val ? 'true' : 'false');
|
||||
});
|
||||
@@ -2170,45 +2183,35 @@ export const optionsMethods = {
|
||||
},
|
||||
|
||||
getStoredTokens() {
|
||||
const civitaiCandidateLimitRaw = parseInt(localStorage.getItem('ModelResolver.civitaiCandidateLimit') || '5', 10);
|
||||
const civitai_candidate_limit = Number.isFinite(civitaiCandidateLimitRaw)
|
||||
? Math.min(20, Math.max(1, civitaiCandidateLimitRaw))
|
||||
: 5;
|
||||
const search_source_enabled = this.getSearchSourceEnabledMap();
|
||||
const storedFrontendLogsEnabled = localStorage.getItem('ModelResolver.frontendLogsEnabled');
|
||||
const storedBackendLogsEnabled = localStorage.getItem('ModelResolver.backendLogsEnabled');
|
||||
const storedFrontendLogLevel = localStorage.getItem('ModelResolver.frontendLogLevel');
|
||||
const storedBackendLogLevel = localStorage.getItem('ModelResolver.backendLogLevel');
|
||||
|
||||
const tokens = {};
|
||||
for (const opt of SETTINGS_MAP) {
|
||||
const raw = localStorage.getItem(opt.localKey);
|
||||
if (opt.type === 'boolean') {
|
||||
tokens[opt.serverKey] = raw === null ? opt.default : raw !== 'false';
|
||||
} else if (opt.type === 'json') {
|
||||
try {
|
||||
tokens[opt.serverKey] = raw ? JSON.parse(raw) : opt.default;
|
||||
} catch (e) {
|
||||
tokens[opt.serverKey] = opt.default;
|
||||
}
|
||||
} else if (opt.type === 'backend') {
|
||||
tokens[opt.serverKey] = this.normalizeDownloadBackend(raw || opt.default);
|
||||
} else if (opt.type === 'pathMode') {
|
||||
tokens[opt.serverKey] = this.normalizeDownloadPathMode(raw || opt.default);
|
||||
} else if (opt.type === 'frontendLogsEnabled') {
|
||||
tokens[opt.serverKey] = raw === null ? (frontendLogger.enabled !== false) : raw !== 'false';
|
||||
} else if (opt.type === 'backendLogsEnabled') {
|
||||
tokens[opt.serverKey] = raw === null ? true : raw !== 'false';
|
||||
} else if (opt.type === 'candidateLimit') {
|
||||
const limit = parseInt(raw || String(opt.default), 10);
|
||||
tokens[opt.serverKey] = Number.isFinite(limit) ? Math.min(20, Math.max(1, limit)) : opt.default;
|
||||
} else {
|
||||
tokens[opt.serverKey] = raw || opt.default;
|
||||
}
|
||||
}
|
||||
tokens.search_source_enabled = this.getSearchSourceEnabledMap();
|
||||
return {
|
||||
civitai_key: localStorage.getItem('ModelResolver.civitaiApiKey') || '',
|
||||
civitai_session_token: localStorage.getItem('ModelResolver.civitaiSessionToken') || '',
|
||||
hf_token: localStorage.getItem('ModelResolver.huggingFaceToken') || '',
|
||||
brave_search_api_key: localStorage.getItem('ModelResolver.braveSearchApiKey') || '',
|
||||
civitai_use_trpc_search: localStorage.getItem('ModelResolver.civitaiUseTrpcSearch') !== 'false',
|
||||
civitai_use_html_fallback: localStorage.getItem('ModelResolver.civitaiUseHtmlFallback') !== 'false',
|
||||
hf_use_api_search: localStorage.getItem('ModelResolver.hfUseApiSearch') !== 'false',
|
||||
hf_use_comfy_org_fallback: localStorage.getItem('ModelResolver.hfUseComfyOrgFallback') !== 'false',
|
||||
hf_use_brave_fallback: localStorage.getItem('ModelResolver.hfUseBraveFallback') !== 'false',
|
||||
auto_fill_base_model: localStorage.getItem('ModelResolver.autoFillBaseModel') !== 'false',
|
||||
auto_fill_subfolder: localStorage.getItem('ModelResolver.autoFillSubfolder') !== 'false',
|
||||
auto_refresh_comfy_models_after_apply: localStorage.getItem('ModelResolver.autoRefreshComfyModelsAfterApply') !== 'false',
|
||||
frontend_logs_enabled: storedFrontendLogsEnabled === null
|
||||
? frontendLogger.enabled !== false
|
||||
: storedFrontendLogsEnabled !== 'false',
|
||||
backend_logs_enabled: storedBackendLogsEnabled === null
|
||||
? true
|
||||
: storedBackendLogsEnabled !== 'false',
|
||||
frontend_log_level: storedFrontendLogLevel || DEFAULT_FRONTEND_LOG_LEVEL,
|
||||
backend_log_level: storedBackendLogLevel || 'DEBUG',
|
||||
civitai_candidate_limit,
|
||||
search_source_enabled,
|
||||
download_backend: this.normalizeDownloadBackend(localStorage.getItem('ModelResolver.downloadBackend') || 'python'),
|
||||
aria2c_path: localStorage.getItem('ModelResolver.aria2cPath') || '',
|
||||
aria2_auto_stop_daemon: localStorage.getItem('ModelResolver.aria2AutoStopDaemon') !== 'false',
|
||||
download_path_mode: this.getDownloadPathMode(),
|
||||
download_path_templates: this.getDownloadPathTemplates(),
|
||||
base_model_path_mappings: this.getBaseModelPathMappings(),
|
||||
...tokens,
|
||||
...this.getDefaultRootSettings()
|
||||
};
|
||||
},
|
||||
@@ -2232,62 +2235,29 @@ export const optionsMethods = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Helper: write to localStorage only when the value from server is
|
||||
// non-empty, so we don't overwrite a key the user already has locally.
|
||||
const sync = (localKey, serverValue) => {
|
||||
if (serverValue !== undefined && serverValue !== null && serverValue !== '') {
|
||||
localStorage.setItem(localKey, serverValue);
|
||||
for (const opt of SETTINGS_MAP) {
|
||||
const serverValue = data[opt.serverKey];
|
||||
if (serverValue === undefined) continue;
|
||||
|
||||
if (opt.type === 'boolean' || opt.type === 'frontendLogsEnabled' || opt.type === 'backendLogsEnabled') {
|
||||
localStorage.setItem(opt.localKey, serverValue ? 'true' : 'false');
|
||||
} else if (opt.type === 'json') {
|
||||
localStorage.setItem(opt.localKey, JSON.stringify(serverValue || {}));
|
||||
} else if (opt.type === 'backend') {
|
||||
localStorage.setItem(opt.localKey, this.normalizeDownloadBackend(serverValue));
|
||||
} else if (opt.type === 'pathMode') {
|
||||
localStorage.setItem(opt.localKey, this.normalizeDownloadPathMode(serverValue));
|
||||
} else {
|
||||
if (serverValue !== null && serverValue !== '') {
|
||||
localStorage.setItem(opt.localKey, String(serverValue));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sync('ModelResolver.civitaiApiKey', data.civitai_key);
|
||||
sync('ModelResolver.civitaiSessionToken', data.civitai_session_token);
|
||||
sync('ModelResolver.huggingFaceToken', data.hf_token);
|
||||
sync('ModelResolver.braveSearchApiKey', data.brave_search_api_key);
|
||||
|
||||
if (data.civitai_use_trpc_search !== undefined)
|
||||
localStorage.setItem('ModelResolver.civitaiUseTrpcSearch', data.civitai_use_trpc_search ? 'true' : 'false');
|
||||
if (data.civitai_use_html_fallback !== undefined)
|
||||
localStorage.setItem('ModelResolver.civitaiUseHtmlFallback', data.civitai_use_html_fallback ? 'true' : 'false');
|
||||
if (data.hf_use_api_search !== undefined)
|
||||
localStorage.setItem('ModelResolver.hfUseApiSearch', data.hf_use_api_search ? 'true' : 'false');
|
||||
if (data.hf_use_comfy_org_fallback !== undefined)
|
||||
localStorage.setItem('ModelResolver.hfUseComfyOrgFallback', data.hf_use_comfy_org_fallback ? 'true' : 'false');
|
||||
if (data.hf_use_brave_fallback !== undefined)
|
||||
localStorage.setItem('ModelResolver.hfUseBraveFallback', data.hf_use_brave_fallback ? 'true' : 'false');
|
||||
if (data.auto_fill_base_model !== undefined)
|
||||
localStorage.setItem('ModelResolver.autoFillBaseModel', data.auto_fill_base_model ? 'true' : 'false');
|
||||
if (data.auto_fill_subfolder !== undefined)
|
||||
localStorage.setItem('ModelResolver.autoFillSubfolder', data.auto_fill_subfolder ? 'true' : 'false');
|
||||
if (data.auto_refresh_comfy_models_after_apply !== undefined)
|
||||
localStorage.setItem('ModelResolver.autoRefreshComfyModelsAfterApply', data.auto_refresh_comfy_models_after_apply ? 'true' : 'false');
|
||||
if (data.download_backend !== undefined)
|
||||
localStorage.setItem('ModelResolver.downloadBackend', this.normalizeDownloadBackend(data.download_backend));
|
||||
if (data.aria2c_path !== undefined)
|
||||
localStorage.setItem('ModelResolver.aria2cPath', String(data.aria2c_path || ''));
|
||||
if (data.aria2_auto_stop_daemon !== undefined)
|
||||
localStorage.setItem('ModelResolver.aria2AutoStopDaemon', data.aria2_auto_stop_daemon ? 'true' : 'false');
|
||||
if (data.download_path_mode !== undefined)
|
||||
localStorage.setItem('ModelResolver.downloadPathMode', this.normalizeDownloadPathMode(data.download_path_mode));
|
||||
if (data.download_path_templates !== undefined)
|
||||
localStorage.setItem('ModelResolver.downloadPathTemplates', JSON.stringify(data.download_path_templates || {}));
|
||||
if (data.base_model_path_mappings !== undefined)
|
||||
localStorage.setItem('ModelResolver.baseModelPathMappings', JSON.stringify(data.base_model_path_mappings || {}));
|
||||
}
|
||||
this.getDefaultRootCategoryDefinitions().forEach((item) => {
|
||||
if (data[item.settingKey] !== undefined) {
|
||||
localStorage.setItem(item.storageKey, String(data[item.settingKey] || ''));
|
||||
}
|
||||
});
|
||||
if (data.civitai_candidate_limit !== undefined)
|
||||
localStorage.setItem('ModelResolver.civitaiCandidateLimit', `${data.civitai_candidate_limit}`);
|
||||
if (data.frontend_logs_enabled !== undefined)
|
||||
localStorage.setItem('ModelResolver.frontendLogsEnabled', data.frontend_logs_enabled ? 'true' : 'false');
|
||||
if (data.backend_logs_enabled !== undefined)
|
||||
localStorage.setItem('ModelResolver.backendLogsEnabled', data.backend_logs_enabled ? 'true' : 'false');
|
||||
if (data.frontend_log_level !== undefined)
|
||||
localStorage.setItem('ModelResolver.frontendLogLevel', String(data.frontend_log_level || 'DEBUG').toUpperCase());
|
||||
if (data.backend_log_level !== undefined)
|
||||
localStorage.setItem('ModelResolver.backendLogLevel', String(data.backend_log_level || 'DEBUG').toUpperCase());
|
||||
|
||||
// Source-enabled flags stored as a nested object
|
||||
if (data.search_source_enabled && typeof data.search_source_enabled === 'object') {
|
||||
|
||||
Reference in New Issue
Block a user