mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat(recipe_scanner): prioritize local sibling images and persist repairs
Updated image path resolution logic to prioritize local sibling images in the same directory as recipes. When a stored image path differs from a local sibling, the system now automatically updates the recipe file to use the local path and persists this repair. This improves reliability when recipe assets are moved or reorganized, ensuring images remain accessible even if original paths become invalid.
This commit is contained in:
@@ -437,14 +437,25 @@ class RecipeScanner:
|
||||
logger.warning(f"Missing required fields in {recipe_path}")
|
||||
continue
|
||||
|
||||
# Ensure the image file exists
|
||||
# Ensure the image file exists and prioritize local siblings
|
||||
image_path = recipe_data.get('file_path')
|
||||
if not os.path.exists(image_path):
|
||||
if image_path:
|
||||
recipe_dir = os.path.dirname(recipe_path)
|
||||
image_filename = os.path.basename(image_path)
|
||||
alternative_path = os.path.join(recipe_dir, image_filename)
|
||||
if os.path.exists(alternative_path):
|
||||
recipe_data['file_path'] = alternative_path
|
||||
local_sibling_path = os.path.normpath(os.path.join(recipe_dir, image_filename))
|
||||
|
||||
# If local sibling exists and stored path is different, prefer local
|
||||
if os.path.exists(local_sibling_path) and os.path.normpath(image_path) != local_sibling_path:
|
||||
recipe_data['file_path'] = local_sibling_path
|
||||
# Persist the repair
|
||||
try:
|
||||
with open(recipe_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(recipe_data, f, indent=4, ensure_ascii=False)
|
||||
logger.info(f"Updated recipe image path to local sibling: {local_sibling_path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to persist repair for {recipe_path}: {e}")
|
||||
elif not os.path.exists(image_path):
|
||||
logger.warning(f"Recipe image not found and no local sibling: {image_path}")
|
||||
|
||||
# Ensure loras array exists
|
||||
if 'loras' not in recipe_data:
|
||||
@@ -823,31 +834,22 @@ class RecipeScanner:
|
||||
logger.warning(f"Missing required field '{field}' in {recipe_path}")
|
||||
return None
|
||||
|
||||
# Ensure the image file exists
|
||||
# Ensure the image file exists and prioritize local siblings
|
||||
image_path = recipe_data.get('file_path')
|
||||
normalized_image_path = os.path.normpath(image_path) if image_path else image_path
|
||||
path_updated = False
|
||||
if image_path and normalized_image_path != image_path:
|
||||
recipe_data['file_path'] = normalized_image_path
|
||||
image_path = normalized_image_path
|
||||
path_updated = True
|
||||
|
||||
if image_path and not os.path.exists(image_path):
|
||||
logger.warning(f"Recipe image not found: {image_path}")
|
||||
# Try to find the image in the same directory as the recipe
|
||||
if image_path:
|
||||
recipe_dir = os.path.dirname(recipe_path)
|
||||
image_filename = os.path.basename(image_path)
|
||||
alternative_path = os.path.join(recipe_dir, image_filename)
|
||||
if os.path.exists(alternative_path):
|
||||
normalized_alternative = os.path.normpath(alternative_path)
|
||||
recipe_data['file_path'] = normalized_alternative
|
||||
image_path = normalized_alternative
|
||||
local_sibling_path = os.path.normpath(os.path.join(recipe_dir, image_filename))
|
||||
|
||||
# If local sibling exists and stored path is different, prefer local
|
||||
if os.path.exists(local_sibling_path) and os.path.normpath(image_path) != local_sibling_path:
|
||||
recipe_data['file_path'] = local_sibling_path
|
||||
image_path = local_sibling_path
|
||||
path_updated = True
|
||||
logger.info(
|
||||
"Updated recipe image path to %s after relocating asset", normalized_alternative
|
||||
)
|
||||
else:
|
||||
logger.warning(f"Could not find alternative image path for {image_path}")
|
||||
logger.info("Updated recipe image path to local sibling: %s", local_sibling_path)
|
||||
elif not os.path.exists(image_path):
|
||||
logger.warning(f"Recipe image not found and no local sibling: {image_path}")
|
||||
|
||||
if path_updated:
|
||||
self._write_recipe_file(recipe_path, recipe_data)
|
||||
@@ -1446,8 +1448,17 @@ class RecipeScanner:
|
||||
# Get paginated items
|
||||
paginated_items = filtered_data[start_idx:end_idx]
|
||||
|
||||
# Add inLibrary information for each lora
|
||||
# Add inLibrary information and URLs for each recipe
|
||||
for item in paginated_items:
|
||||
# Format file path to URL
|
||||
if 'file_path' in item:
|
||||
item['file_url'] = self._format_file_url(item['file_path'])
|
||||
|
||||
# Format dates for display
|
||||
for date_field in ['created_date', 'modified']:
|
||||
if date_field in item:
|
||||
item[f"{date_field}_formatted"] = self._format_timestamp(item[date_field])
|
||||
|
||||
if 'loras' in item:
|
||||
item['loras'] = [self._enrich_lora_entry(dict(lora)) for lora in item['loras']]
|
||||
if item.get('checkpoint'):
|
||||
|
||||
@@ -40,10 +40,16 @@ class RecipeCard {
|
||||
const missingLorasCount = loras.filter(lora => !lora.inLibrary && !lora.isDeleted).length;
|
||||
const allLorasAvailable = missingLorasCount === 0 && lorasCount > 0;
|
||||
|
||||
// Ensure file_url exists, fallback to file_path if needed
|
||||
const previewUrl = this.recipe.file_url ||
|
||||
(this.recipe.file_path ? `/loras_static/root1/preview/${this.recipe.file_path.split('/').pop()}` :
|
||||
'/loras_static/images/no-preview.png');
|
||||
// Ensure file_url exists, fallback to API URL if needed
|
||||
let previewUrl = this.recipe.file_url;
|
||||
if (!previewUrl) {
|
||||
if (this.recipe.file_path) {
|
||||
const encodedPath = encodeURIComponent(this.recipe.file_path.replace(/\\/g, '/'));
|
||||
previewUrl = `/api/lm/previews?path=${encodedPath}`;
|
||||
} else {
|
||||
previewUrl = '/loras_static/images/no-preview.png';
|
||||
}
|
||||
}
|
||||
|
||||
const isDuplicatesMode = getCurrentPageState().duplicatesMode;
|
||||
const autoplayOnHover = state?.global?.settings?.autoplay_on_hover === true;
|
||||
|
||||
Reference in New Issue
Block a user