feat: implement local-only model info fetching and refactor model info dialog
- Add local_only endpoint mode: returns basic file info (filename, size, path,
location, sha256) without querying CivitAI or computing file hashes
- Validate provided sha256/hash inputs (must be 64-char hex) and fall back to
empty string when invalid
- Refactor monolithic info response builder into build_info_response() helper
used across all result paths (local, CivitAI success, CivitAI fallback,
not-found), reducing duplication
- Add get_model_info_for_file(local_only=True) early return in CivitAI source
that builds a minimal local-only result without hash lookup
- Split frontend fetchModelInfoForDialog into:
- fetchLocalModelInfoForDialog: opens dialog with local data immediately,
marks civitai_checked=false
- fetchCivitaiModelInfoForDialog: async CivitAI lookup triggered by user
- Add 'Fetch with CivitAI' button in info dialog when local file exists but
CivitAI has not been checked yet
- Infer model_type from category in both backend and frontend to reduce
reliance on upstream data shape
- Fix sidecar metadata condition (was skipping save when path already existed)
- Add cursor/disabled styles for info dialog CivitAI link button
This commit is contained in:
+137
-39
@@ -1112,6 +1112,19 @@ class ModelResolverExtension:
|
||||
filename = data.get("filename", "")
|
||||
category = data.get("category", "")
|
||||
resolved_path = data.get("resolved_path", "")
|
||||
local_only = to_bool(data.get("local_only"), False)
|
||||
provided_hash = (
|
||||
data.get("sha256")
|
||||
or data.get("hash")
|
||||
or data.get("file_hash")
|
||||
or ""
|
||||
)
|
||||
provided_hash = str(provided_hash or "").strip().lower()
|
||||
if not (
|
||||
len(provided_hash) == 64
|
||||
and all(ch in "0123456789abcdef" for ch in provided_hash)
|
||||
):
|
||||
provided_hash = ""
|
||||
|
||||
if not filename:
|
||||
return web.json_response(
|
||||
@@ -1155,17 +1168,125 @@ class ModelResolverExtension:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Search CivitAI for the model using hash
|
||||
if download_available and file_path and _os.path.exists(file_path):
|
||||
if file_path and _os.path.exists(file_path):
|
||||
file_location = _os.path.dirname(file_path).replace("\\", "/")
|
||||
if file_location and not file_location.endswith("/"):
|
||||
file_location += "/"
|
||||
|
||||
def infer_model_type_from_category(value):
|
||||
if not str(value or "").strip():
|
||||
return ""
|
||||
normalized = normalize_download_category(value or "")
|
||||
type_map = {
|
||||
"checkpoints": "checkpoint",
|
||||
"loras": "lora",
|
||||
"vae": "vae",
|
||||
"text_encoders": "text_encoder",
|
||||
"clip": "clip",
|
||||
"clip_vision": "clip_vision",
|
||||
"controlnet": "controlnet",
|
||||
"upscale_models": "upscale",
|
||||
"diffusion_models": "diffusion_model",
|
||||
}
|
||||
return type_map.get(normalized, normalized.rstrip("s"))
|
||||
|
||||
def build_info_response(
|
||||
result=None,
|
||||
*,
|
||||
metadata_path="",
|
||||
metadata_saved=False,
|
||||
civitai_checked=False,
|
||||
local_payload=False,
|
||||
):
|
||||
result = result or {}
|
||||
size_value = result.get("size")
|
||||
if not size_value and file_path and _os.path.exists(file_path):
|
||||
try:
|
||||
size_value = _os.path.getsize(file_path)
|
||||
except Exception:
|
||||
size_value = None
|
||||
|
||||
model_type = (
|
||||
result.get("model_type")
|
||||
or result.get("type")
|
||||
or infer_model_type_from_category(category)
|
||||
)
|
||||
|
||||
return {
|
||||
"filename": result.get("filename") or filename,
|
||||
"category": category,
|
||||
"file_path": result.get("file_path") or file_path or "",
|
||||
"resolved_path": result.get("resolved_path") or file_path or "",
|
||||
"metadata_path": metadata_path
|
||||
or result.get("metadata_path")
|
||||
or "",
|
||||
"metadata_saved": bool(metadata_saved),
|
||||
"location": result.get("location") or file_location,
|
||||
"url": result.get("url"),
|
||||
"version_url": result.get("version_url"),
|
||||
"model_id": result.get("model_id"),
|
||||
"model_name": result.get("model_name") or clean_name,
|
||||
"model_type": model_type,
|
||||
"version_id": result.get("version_id"),
|
||||
"version_name": result.get("version_name", ""),
|
||||
"sha256": result.get("sha256") or provided_hash,
|
||||
"size": size_value,
|
||||
"base_model": result.get("base_model"),
|
||||
"tags": result.get("tags", []),
|
||||
"trained_words": result.get("trained_words", []),
|
||||
"images": result.get("images", []),
|
||||
"clip_skip": result.get("clip_skip"),
|
||||
"description": result.get("description", ""),
|
||||
"model_description": result.get("model_description", ""),
|
||||
"from_metadata": bool(result.get("from_metadata")),
|
||||
"local_only": bool(local_payload),
|
||||
"civitai_checked": bool(civitai_checked),
|
||||
}
|
||||
|
||||
if local_only:
|
||||
result = None
|
||||
if download_available and file_path and _os.path.exists(file_path):
|
||||
try:
|
||||
from .core.sources.civitai import (
|
||||
get_model_info_for_file,
|
||||
)
|
||||
|
||||
result = get_model_info_for_file(
|
||||
file_path,
|
||||
local_only=True,
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.warning(f"Local model info error: {e}")
|
||||
return web.json_response(
|
||||
build_info_response(
|
||||
result,
|
||||
local_payload=True,
|
||||
civitai_checked=False,
|
||||
)
|
||||
)
|
||||
|
||||
# Search CivitAI for the model using hash
|
||||
if download_available and file_path and _os.path.exists(file_path):
|
||||
try:
|
||||
from .core.sources.civitai import (
|
||||
get_model_info_by_hash,
|
||||
get_model_info_for_file,
|
||||
)
|
||||
|
||||
result = get_model_info_for_file(file_path)
|
||||
if provided_hash:
|
||||
result = get_model_info_by_hash(provided_hash)
|
||||
if result:
|
||||
result["file_path"] = file_path
|
||||
result["resolved_path"] = file_path
|
||||
result["location"] = file_location
|
||||
if not result.get("size"):
|
||||
try:
|
||||
result["size"] = _os.path.getsize(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
result = get_model_info_for_file(file_path)
|
||||
|
||||
if result and (
|
||||
result.get("url")
|
||||
or result.get("version_url")
|
||||
@@ -1177,7 +1298,7 @@ class ModelResolverExtension:
|
||||
if not result.get("from_metadata"):
|
||||
try:
|
||||
sidecar_path = get_metadata_sidecar_path(file_path)
|
||||
if sidecar_path and not _os.path.exists(sidecar_path):
|
||||
if sidecar_path:
|
||||
metadata_payload = {
|
||||
"source": "civitai",
|
||||
"details_source": "civitai",
|
||||
@@ -1190,7 +1311,7 @@ class ModelResolverExtension:
|
||||
"model_id": result.get("model_id"),
|
||||
"version_id": result.get("version_id"),
|
||||
"version_name": result.get("version_name", ""),
|
||||
"sha256": result.get("sha256"),
|
||||
"sha256": result.get("sha256") or provided_hash,
|
||||
"size": result.get("size"),
|
||||
"base_model": result.get("base_model"),
|
||||
"tags": result.get("tags", []),
|
||||
@@ -1219,45 +1340,18 @@ class ModelResolverExtension:
|
||||
or "",
|
||||
) or ""
|
||||
metadata_saved = bool(metadata_path)
|
||||
elif sidecar_path:
|
||||
metadata_path = sidecar_path
|
||||
except Exception as metadata_error:
|
||||
self.logger.warning(
|
||||
f"CivitAI metadata sidecar save failed: {metadata_error}"
|
||||
)
|
||||
|
||||
return web.json_response(
|
||||
{
|
||||
"filename": filename,
|
||||
"file_path": file_path,
|
||||
"resolved_path": file_path,
|
||||
"metadata_path": metadata_path,
|
||||
"metadata_saved": metadata_saved,
|
||||
"location": result.get("location")
|
||||
or file_location,
|
||||
"url": result.get("url"),
|
||||
"version_url": result.get("version_url"),
|
||||
"model_id": result.get("model_id"),
|
||||
"model_name": result.get(
|
||||
"model_name", clean_name
|
||||
),
|
||||
"model_type": result.get("model_type", ""),
|
||||
"version_id": result.get("version_id"),
|
||||
"version_name": result.get("version_name", ""),
|
||||
"sha256": result.get("sha256"),
|
||||
"size": result.get("size"),
|
||||
"base_model": result.get("base_model"),
|
||||
"tags": result.get("tags", []),
|
||||
"trained_words": result.get(
|
||||
"trained_words", []
|
||||
),
|
||||
"images": result.get("images", []),
|
||||
"clip_skip": result.get("clip_skip"),
|
||||
"description": result.get("description", ""),
|
||||
"model_description": result.get(
|
||||
"model_description", ""
|
||||
),
|
||||
}
|
||||
build_info_response(
|
||||
result,
|
||||
metadata_path=metadata_path,
|
||||
metadata_saved=metadata_saved,
|
||||
civitai_checked=True,
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.warning(f"CivitAI search error: {e}")
|
||||
@@ -1275,6 +1369,7 @@ class ModelResolverExtension:
|
||||
if result and result.get("url"):
|
||||
return web.json_response(
|
||||
{
|
||||
"filename": filename,
|
||||
"url": result["url"],
|
||||
"file_path": file_path,
|
||||
"resolved_path": file_path,
|
||||
@@ -1283,13 +1378,16 @@ class ModelResolverExtension:
|
||||
"version_id": result.get("version_id"),
|
||||
"size": result.get("size"),
|
||||
"tags": result.get("tags", []),
|
||||
"civitai_checked": True,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.warning(f"CivitAI fallback search error: {e}")
|
||||
|
||||
# No result found
|
||||
return web.json_response({"url": None})
|
||||
response = build_info_response(None, civitai_checked=True)
|
||||
response["url"] = None
|
||||
return web.json_response(response)
|
||||
|
||||
@routes.post("/model_resolver/model-details")
|
||||
@json_api_endpoint("model-details")
|
||||
|
||||
+23
-1
@@ -1783,7 +1783,7 @@ def _metadata_to_model_info(metadata: Dict[str, Any]) -> Dict[str, Any]:
|
||||
|
||||
|
||||
def get_model_info_for_file(
|
||||
file_path: str, api_key: Optional[str] = None
|
||||
file_path: str, api_key: Optional[str] = None, local_only: bool = False
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Get model info from CivitAI by computing file hash and looking up by hash.
|
||||
@@ -1792,6 +1792,8 @@ def get_model_info_for_file(
|
||||
Args:
|
||||
file_path: Full path to the model file
|
||||
api_key: Optional CivitAI API key
|
||||
local_only: If true, never calculate a hash or query CivitAI when
|
||||
metadata is missing. Return only local file information.
|
||||
|
||||
Returns:
|
||||
Dict with model info from CivitAI, or None if not found
|
||||
@@ -1826,6 +1828,26 @@ def get_model_info_for_file(
|
||||
]
|
||||
log_info(f"Files in {directory}: {metadata_files}")
|
||||
|
||||
if local_only:
|
||||
filename = os.path.basename(file_path)
|
||||
stem = os.path.splitext(filename)[0]
|
||||
result = {
|
||||
"source": "local",
|
||||
"filename": filename,
|
||||
"file_path": file_path,
|
||||
"resolved_path": file_path,
|
||||
"model_name": stem,
|
||||
"model_type": "",
|
||||
"location": _format_model_location(file_path),
|
||||
"from_metadata": False,
|
||||
"local_only": True,
|
||||
}
|
||||
try:
|
||||
result["size"] = os.path.getsize(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
return result
|
||||
|
||||
# If no metadata file, compute hash and look up on CivitAI
|
||||
file_hash = _get_sha256_hash(file_path)
|
||||
if not file_hash:
|
||||
|
||||
@@ -3834,6 +3834,7 @@
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.08) inset;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mr-info-link:hover {
|
||||
@@ -3842,6 +3843,15 @@
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
button.mr-info-link {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
button.mr-info-link:disabled {
|
||||
cursor: wait;
|
||||
opacity: 0.72;
|
||||
}
|
||||
|
||||
.mr-info-external-link-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
||||
@@ -264,8 +264,8 @@ export const modelInfoMethods = {
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch CivitAI info
|
||||
this.fetchModelInfoForDialog(loraName, modelData, dialog);
|
||||
dialog._infoDialogLookup = { loraName, modelData };
|
||||
this.fetchLocalModelInfoForDialog(loraName, modelData, dialog);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -793,14 +793,14 @@ export const modelInfoMethods = {
|
||||
async calculateInfoDialogHash(dialog, button) {
|
||||
if (button?.dataset.hashCalculating === 'true') {
|
||||
await this.cancelInfoDialogHashCalculation(dialog, button);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = dialog?._infoDialogData || {};
|
||||
const filePath = this.getInfoDialogModelFilePath(data);
|
||||
if (!filePath) {
|
||||
this.showNotification?.('No local file path available', 'error');
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
dialog._hashCalculation = { progressId: '', cancelRequested: false };
|
||||
@@ -842,7 +842,7 @@ export const modelInfoMethods = {
|
||||
await new Promise(resolve => setTimeout(resolve, 250));
|
||||
if (!dialog.isConnected) {
|
||||
await this.cancelInfoDialogHashCalculation(dialog, button);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
const progress = await this.fetchJson(
|
||||
`/model_resolver/calculate-file-hash/progress/${encodeURIComponent(progressId)}`,
|
||||
@@ -869,7 +869,7 @@ export const modelInfoMethods = {
|
||||
}
|
||||
dialog._hashCalculation = null;
|
||||
this.showNotification?.('Hash calculation stopped.', 'info');
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
if (progress?.status === 'error') {
|
||||
throw new Error(progress.error || progress.message || 'Hash calculation failed');
|
||||
@@ -895,6 +895,7 @@ export const modelInfoMethods = {
|
||||
result.metadata_updated ? 'Hash calculated and saved to metadata.' : 'Hash calculated.',
|
||||
'success'
|
||||
);
|
||||
return updatedData;
|
||||
} catch (error) {
|
||||
dialog._hashCalculation = null;
|
||||
if (button) {
|
||||
@@ -905,6 +906,7 @@ export const modelInfoMethods = {
|
||||
}
|
||||
this.hideInfoDialogHashProgress(dialog);
|
||||
this.showNotification?.(`Hash calculation failed: ${error?.message || error}`, 'error');
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -913,6 +915,12 @@ export const modelInfoMethods = {
|
||||
dialog.dataset.mlInfoBound = 'true';
|
||||
|
||||
dialog.addEventListener('click', async (event) => {
|
||||
const fetchCivitaiButton = event.target.closest('.mr-info-fetch-civitai');
|
||||
if (fetchCivitaiButton && dialog.contains(fetchCivitaiButton)) {
|
||||
await this.fetchCivitaiModelInfoForDialog(dialog, fetchCivitaiButton);
|
||||
return;
|
||||
}
|
||||
|
||||
const hashButton = event.target.closest('.mr-info-hash-calculate');
|
||||
if (hashButton && dialog.contains(hashButton)) {
|
||||
await this.calculateInfoDialogHash(dialog, hashButton);
|
||||
@@ -1047,28 +1055,167 @@ export const modelInfoMethods = {
|
||||
dialog._infoDialogResizeSaveTimer = resizeSaveTimer;
|
||||
},
|
||||
|
||||
getModelInfoResolvedPath(modelData = {}) {
|
||||
return modelData?.resolved_path
|
||||
|| modelData?.path
|
||||
|| modelData?.full_path
|
||||
|| modelData?.folder_path
|
||||
|| modelData?.download_directory
|
||||
|| '';
|
||||
},
|
||||
|
||||
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 resolvedPath = this.getModelInfoResolvedPath(modelData);
|
||||
const categoryTypeMap = {
|
||||
checkpoints: 'checkpoint',
|
||||
loras: 'lora',
|
||||
vae: 'vae',
|
||||
text_encoders: 'text_encoder',
|
||||
clip: 'clip',
|
||||
clip_vision: 'clip_vision',
|
||||
controlnet: 'controlnet',
|
||||
upscale_models: 'upscale',
|
||||
diffusion_models: 'diffusion_model'
|
||||
};
|
||||
const rawType = modelData?.model_type || modelData?.modelType || modelData?.type || modelData?.category || '';
|
||||
const normalizedType = String(rawType || '').trim().toLowerCase().replace(/[\s-]+/g, '_');
|
||||
return {
|
||||
filename,
|
||||
category: modelData?.category || '',
|
||||
file_path: resolvedPath,
|
||||
resolved_path: resolvedPath,
|
||||
location: resolvedPath ? this.formatInfoDialogLocation({ file_path: resolvedPath }) : '',
|
||||
model_name: modelData?.model_name || modelData?.modelName || modelName,
|
||||
model_type: categoryTypeMap[normalizedType] || rawType,
|
||||
sha256: modelData?.sha256 || modelData?.hash || modelData?.hashes?.SHA256 || '',
|
||||
size: modelData?.size || modelData?.file_size || modelData?.fileSize || '',
|
||||
base_model: modelData?.base_model || modelData?.baseModel || '',
|
||||
tags: modelData?.tags || [],
|
||||
trained_words: modelData?.trained_words || modelData?.trainedWords || [],
|
||||
local_only: true,
|
||||
civitai_checked: false
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch model info and update the dialog
|
||||
* Fetch local model info and update the dialog without querying CivitAI.
|
||||
*/
|
||||
async fetchModelInfoForDialog(loraName, modelData, dialog) {
|
||||
async fetchLocalModelInfoForDialog(loraName, modelData, dialog) {
|
||||
const requestId = `${Date.now()}_${Math.random()}`;
|
||||
dialog._localInfoRequestId = requestId;
|
||||
const fallbackData = this.buildLocalInfoDialogData(loraName, modelData);
|
||||
this.updateInfoDialogWithData(dialog, fallbackData);
|
||||
|
||||
try {
|
||||
const resolvedPath = modelData?.resolved_path
|
||||
|| modelData?.path
|
||||
|| modelData?.full_path
|
||||
|| modelData?.folder_path
|
||||
|| modelData?.download_directory
|
||||
|| '';
|
||||
const data = await this.fetchJson('/model_resolver/civitai-search', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
filename: loraName,
|
||||
category: modelData?.category || '',
|
||||
resolved_path: resolvedPath
|
||||
resolved_path: this.getModelInfoResolvedPath(modelData),
|
||||
local_only: true
|
||||
})
|
||||
}, 'Fetch local model info');
|
||||
if (
|
||||
dialog._localInfoRequestId !== requestId
|
||||
|| dialog._infoDialogCivitaiFetchStarted
|
||||
|| dialog._infoDialogData?.civitai_checked
|
||||
|| dialog._infoDialogData?.version_url
|
||||
|| dialog._infoDialogData?.url
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.updateInfoDialogWithData(dialog, { ...fallbackData, ...data, civitai_checked: false });
|
||||
} catch (e) {
|
||||
if (dialog._localInfoRequestId !== requestId || dialog._infoDialogCivitaiFetchStarted) {
|
||||
return;
|
||||
}
|
||||
this.updateInfoDialogWithData(dialog, fallbackData);
|
||||
}
|
||||
},
|
||||
|
||||
async fetchCivitaiModelInfoForDialog(dialog, button = null) {
|
||||
if (!dialog) return;
|
||||
if (button?.dataset.fetchingCivitai === 'true') return;
|
||||
|
||||
const lookup = dialog._infoDialogLookup || {};
|
||||
let data = dialog._infoDialogData || {};
|
||||
const filename = data.filename || lookup.loraName || '';
|
||||
const category = data.category || lookup.modelData?.category || '';
|
||||
const resolvedPath = this.getInfoDialogModelFilePath(data)
|
||||
|| this.getModelInfoResolvedPath(lookup.modelData || {});
|
||||
|
||||
if (!filename) {
|
||||
this.showNotification?.('No model filename available', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
dialog._infoDialogCivitaiFetchStarted = true;
|
||||
const setFetchingState = (isFetching, label = 'Fetch with CivitAI') => {
|
||||
if (!button) return;
|
||||
button.dataset.fetchingCivitai = isFetching ? 'true' : 'false';
|
||||
button.disabled = isFetching;
|
||||
button.innerHTML = isFetching
|
||||
? `${getSvgIcon('civitai', 'currentColor', 'mr-info-external-link-icon')} Fetching...`
|
||||
: `${getSvgIcon('civitai', 'currentColor', 'mr-info-external-link-icon')} ${this.escapeHtml(label)}`;
|
||||
};
|
||||
|
||||
setFetchingState(true);
|
||||
|
||||
try {
|
||||
let hash = this.getInfoDialogHash(data);
|
||||
if (!hash) {
|
||||
const hashButton = dialog.querySelector('.mr-info-hash-calculate');
|
||||
const hashResult = await this.calculateInfoDialogHash(dialog, hashButton);
|
||||
if (!hashResult) {
|
||||
setFetchingState(false);
|
||||
return;
|
||||
}
|
||||
data = dialog._infoDialogData || data;
|
||||
hash = this.getInfoDialogHash(data);
|
||||
}
|
||||
|
||||
if (!hash) {
|
||||
throw new Error('No SHA256 hash available');
|
||||
}
|
||||
|
||||
const result = await this.fetchJson('/model_resolver/civitai-search', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
filename,
|
||||
category,
|
||||
resolved_path: resolvedPath,
|
||||
sha256: hash
|
||||
})
|
||||
}, 'Fetch CivitAI model info');
|
||||
this.updateInfoDialogWithData(dialog, data);
|
||||
} catch (e) {
|
||||
this.updateInfoDialogError(dialog, 'Error fetching info from CivitAI');
|
||||
|
||||
const merged = {
|
||||
...data,
|
||||
...result,
|
||||
filename,
|
||||
category,
|
||||
file_path: result.file_path || data.file_path || resolvedPath,
|
||||
resolved_path: result.resolved_path || data.resolved_path || resolvedPath,
|
||||
sha256: result.sha256 || hash,
|
||||
civitai_checked: true
|
||||
};
|
||||
dialog._infoDialogData = merged;
|
||||
this.updateInfoDialogWithData(dialog, merged);
|
||||
if (merged.url || merged.version_url) {
|
||||
this.showNotification?.('CivitAI info loaded.', 'success');
|
||||
} else {
|
||||
this.showNotification?.('No exact CivitAI match found.', 'info');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification?.(`CivitAI fetch failed: ${error?.message || error}`, 'error');
|
||||
this.updateInfoDialogWithData(dialog, {
|
||||
...(dialog._infoDialogData || data),
|
||||
civitai_checked: true
|
||||
});
|
||||
} finally {
|
||||
setFetchingState(false);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1092,7 +1239,7 @@ export const modelInfoMethods = {
|
||||
// Update title
|
||||
const titleEl = dialog.querySelector('.mr-info-dialog-title');
|
||||
if (titleEl) {
|
||||
const modelName = data.model_name || data.modelName || 'Unknown Model';
|
||||
const modelName = data.model_name || data.modelName || data.name || data.filename || 'Unknown Model';
|
||||
const versionName = data.version_name || data.versionName || '';
|
||||
titleEl.innerHTML = this.renderVersionedModelNameHtml(modelName, versionName) || this.escapeHtml(modelName);
|
||||
}
|
||||
@@ -1178,7 +1325,7 @@ export const modelInfoMethods = {
|
||||
${getSvgIcon('externalLink', 'currentColor', 'mr-info-external-link-icon')}
|
||||
</a>
|
||||
`;
|
||||
} else {
|
||||
} else if (data.civitai_checked || data.civitaiChecked) {
|
||||
const searchName = data.model_name || data.modelName || 'Unknown';
|
||||
civitaiLinkEl.innerHTML = `
|
||||
<span class="mr-info-not-found">Model not found</span>
|
||||
@@ -1187,6 +1334,15 @@ export const modelInfoMethods = {
|
||||
${getSvgIcon('externalLink', 'currentColor', 'mr-info-external-link-icon')}
|
||||
</a>
|
||||
`;
|
||||
} else if (this.getInfoDialogModelFilePath(data)) {
|
||||
civitaiLinkEl.innerHTML = `
|
||||
<button type="button" class="mr-info-link mr-info-fetch-civitai">
|
||||
${getSvgIcon('civitai', 'currentColor', 'mr-info-external-link-icon')}
|
||||
Fetch with CivitAI
|
||||
</button>
|
||||
`;
|
||||
} else {
|
||||
civitaiLinkEl.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user