mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-06-24 20:11:17 -03:00
feat(ui): show version count in group-by-model cards, add versions_count sort, no-reload VLM
- group_by_model dedup now counts versions per group and attaches version_count; respects update_flag_strategy (same_base) by sub-grouping on base_model - Card footer shows clickable 'x versions' link instead of version name when grouped (hides HIGH/LOW badges); clicking triggers View Local Versions without page reload - Added 'Local Versions' sort option (versions_count), auto-hidden when group_by_model is off - Sort preference is saved/restored separately for normal and grouped modes - VLM flow (triggerVlmView, clearCustomFilter) uses resetAndReload() via API instead of window.location.reload() - Fixed cache mutation bug: version_count is now set on a shallow copy, not the cached dict, preventing stale version_count leaking into VLM responses - i18n: all 9 locale files translated
This commit is contained in:
@@ -115,19 +115,50 @@ class BaseModelService(ABC):
|
||||
# Optionally group by civitai modelId, showing only the latest version per model
|
||||
dedup_lost = 0
|
||||
if kwargs.get("group_by_model") and civitai_model_id is None:
|
||||
dedup_map = {} # modelId -> (item, version_id)
|
||||
# Determine whether to further sub-group by base model
|
||||
# When update_flag_strategy is "same_base", versions with different
|
||||
# base models are effectively different groups — the dedup key
|
||||
# needs to include base_model so the version count and VLM flow
|
||||
# stay consistent (card shows correct count for its base model).
|
||||
ufs = self.settings.get("update_flag_strategy", "same_base")
|
||||
group_by_base = ufs == "same_base"
|
||||
|
||||
dedup_map = {} # (modelId [,base_model]) -> (item, version_id)
|
||||
version_counter = {} # same-key -> count
|
||||
standalone = []
|
||||
for item in sorted_data:
|
||||
mid = self._extract_model_id(item)
|
||||
if mid is None:
|
||||
standalone.append(item)
|
||||
continue
|
||||
key = (mid, item.get("base_model") or "") if group_by_base else mid
|
||||
# Count all versions per key
|
||||
version_counter[key] = version_counter.get(key, 0) + 1
|
||||
vid = self._extract_version_id(item) or 0
|
||||
if mid not in dedup_map or vid > dedup_map[mid][1]:
|
||||
dedup_map[mid] = (item, vid)
|
||||
if key not in dedup_map or vid > dedup_map[key][1]:
|
||||
dedup_map[key] = (item, vid)
|
||||
# Attach version_count to each surviving grouped item (shallow copy
|
||||
# to avoid mutating cached dicts — the cache is shared across requests)
|
||||
for key, (item, vid) in dedup_map.items():
|
||||
item = dict(item)
|
||||
item["version_count"] = version_counter[key]
|
||||
dedup_map[key] = (item, vid)
|
||||
dedup_lost = len(sorted_data) - (len(dedup_map) + len(standalone))
|
||||
sorted_data = [entry[0] for entry in dedup_map.values()] + standalone
|
||||
|
||||
# Re-sort by version_count after dedup (only makes sense in group_by_model mode)
|
||||
is_group_by_active = kwargs.get("group_by_model") and civitai_model_id is None
|
||||
if sort_params.key == "versions_count" and is_group_by_active:
|
||||
reverse = sort_params.order == "desc"
|
||||
sorted_data.sort(
|
||||
key=lambda x: (
|
||||
x.get("version_count", 0),
|
||||
(x.get("model_name") or x.get("file_name") or "").lower(),
|
||||
x.get("file_path", "").lower(),
|
||||
),
|
||||
reverse=reverse,
|
||||
)
|
||||
|
||||
t1 = time.perf_counter()
|
||||
if hash_filters:
|
||||
filtered_data = await self._apply_hash_filters(sorted_data, hash_filters)
|
||||
|
||||
@@ -48,6 +48,7 @@ class CheckpointService(BaseModelService):
|
||||
"skip_metadata_refresh": bool(checkpoint_data.get("skip_metadata_refresh", False)),
|
||||
"civitai": self.filter_civitai_data(checkpoint_data.get("civitai", {}), minimal=True),
|
||||
"auto_tags": checkpoint_data.get("auto_tags") or extract_auto_tags(checkpoint_data),
|
||||
"version_count": checkpoint_data.get("version_count"),
|
||||
}
|
||||
|
||||
def find_duplicate_hashes(self) -> Dict:
|
||||
|
||||
@@ -48,6 +48,7 @@ class EmbeddingService(BaseModelService):
|
||||
"skip_metadata_refresh": bool(embedding_data.get("skip_metadata_refresh", False)),
|
||||
"civitai": self.filter_civitai_data(embedding_data.get("civitai", {}), minimal=True),
|
||||
"auto_tags": embedding_data.get("auto_tags") or extract_auto_tags(embedding_data),
|
||||
"version_count": embedding_data.get("version_count"),
|
||||
}
|
||||
|
||||
def find_duplicate_hashes(self) -> Dict:
|
||||
|
||||
@@ -59,6 +59,7 @@ class LoraService(BaseModelService):
|
||||
lora_data.get("civitai", {}), minimal=True
|
||||
),
|
||||
"auto_tags": lora_data.get("auto_tags") or extract_auto_tags(lora_data),
|
||||
"version_count": lora_data.get("version_count"),
|
||||
}
|
||||
|
||||
async def _apply_specific_filters(self, data: List[Dict], **kwargs) -> List[Dict]:
|
||||
|
||||
@@ -18,6 +18,8 @@ SUPPORTED_SORT_MODES = [
|
||||
('size', 'desc'),
|
||||
('usage', 'asc'),
|
||||
('usage', 'desc'),
|
||||
('versions_count', 'asc'),
|
||||
('versions_count', 'desc'),
|
||||
]
|
||||
# Is this in use?
|
||||
|
||||
@@ -263,6 +265,17 @@ class ModelCache:
|
||||
),
|
||||
reverse=reverse
|
||||
)
|
||||
elif sort_key == 'versions_count':
|
||||
# Pre-dedup sort: fall back to name sort.
|
||||
# Actual re-sort by version_count happens in get_paginated_data after dedup.
|
||||
result = natsorted(
|
||||
data,
|
||||
key=lambda x: (
|
||||
self._get_display_name(x).lower(),
|
||||
x.get('file_path', '').lower()
|
||||
),
|
||||
reverse=reverse
|
||||
)
|
||||
else:
|
||||
# Fallback: no sort
|
||||
result = list(data)
|
||||
|
||||
Reference in New Issue
Block a user