Enhance LoraScanner and file_utils for improved metadata handling

- Updated LoraScanner to first attempt to create metadata from .civitai.info files, improving metadata extraction from existing files.
- Added error handling for reading .civitai.info files and fallback to generating metadata using get_file_info if necessary.
- Refactored file_utils to expose find_preview_file function and added logic to utilize SHA256 from existing .json files to avoid recalculation.
- Improved overall robustness of metadata loading and preview file retrieval processes.
This commit is contained in:
Will Miao
2025-03-28 16:27:59 +08:00
parent 5a8a402fdc
commit c31c9c16cf
2 changed files with 50 additions and 10 deletions

View File

@@ -5,10 +5,11 @@ import asyncio
import shutil import shutil
import time import time
from typing import List, Dict, Optional from typing import List, Dict, Optional
from dataclasses import dataclass
from operator import itemgetter from ..utils.models import LoraMetadata
from ..config import config from ..config import config
from ..utils.file_utils import load_metadata, get_file_info from ..utils.file_utils import load_metadata, get_file_info, normalize_path, find_preview_file, save_metadata
from ..utils.lora_metadata import extract_lora_metadata
from .lora_cache import LoraCache from .lora_cache import LoraCache
from .lora_hash_index import LoraHashIndex from .lora_hash_index import LoraHashIndex
from .settings_manager import settings from .settings_manager import settings
@@ -332,8 +333,30 @@ class LoraScanner:
metadata = await load_metadata(file_path) metadata = await load_metadata(file_path)
if metadata is None: if metadata is None:
# Create new metadata if none exists # Try to find and use .civitai.info file first
metadata = await get_file_info(file_path) civitai_info_path = f"{os.path.splitext(file_path)[0]}.civitai.info"
if os.path.exists(civitai_info_path):
try:
with open(civitai_info_path, 'r', encoding='utf-8') as f:
version_info = json.load(f)
file_info = next((f for f in version_info.get('files', []) if f.get('primary')), None)
if file_info:
# Create a minimal file_info with the required fields
file_name = os.path.splitext(os.path.basename(file_path))[0]
file_info['name'] = file_name
# Use from_civitai_info to create metadata
metadata = LoraMetadata.from_civitai_info(version_info, file_info, file_path)
metadata.preview_url = find_preview_file(file_name, os.path.dirname(file_path))
await save_metadata(file_path, metadata)
logger.debug(f"Created metadata from .civitai.info for {file_path}")
except Exception as e:
logger.error(f"Error creating metadata from .civitai.info for {file_path}: {e}")
# If still no metadata, create new metadata using get_file_info
if metadata is None:
metadata = await get_file_info(file_path)
# Convert to dict and add folder info # Convert to dict and add folder info
lora_data = metadata.to_dict() lora_data = metadata.to_dict()
@@ -344,7 +367,7 @@ class LoraScanner:
lora_data['folder'] = folder.replace(os.path.sep, '/') lora_data['folder'] = folder.replace(os.path.sep, '/')
return lora_data return lora_data
async def _fetch_missing_metadata(self, file_path: str, lora_data: Dict) -> None: async def _fetch_missing_metadata(self, file_path: str, lora_data: Dict) -> None:
"""Fetch missing description and tags from Civitai if needed """Fetch missing description and tags from Civitai if needed

View File

@@ -19,7 +19,7 @@ async def calculate_sha256(file_path: str) -> str:
sha256_hash.update(byte_block) sha256_hash.update(byte_block)
return sha256_hash.hexdigest() return sha256_hash.hexdigest()
def _find_preview_file(base_name: str, dir_path: str) -> str: def find_preview_file(base_name: str, dir_path: str) -> str:
"""Find preview file for given base name in directory""" """Find preview file for given base name in directory"""
preview_patterns = [ preview_patterns = [
f"{base_name}.preview.png", f"{base_name}.preview.png",
@@ -56,16 +56,33 @@ async def get_file_info(file_path: str) -> Optional[LoraMetadata]:
base_name = os.path.splitext(os.path.basename(file_path))[0] base_name = os.path.splitext(os.path.basename(file_path))[0]
dir_path = os.path.dirname(file_path) dir_path = os.path.dirname(file_path)
preview_url = _find_preview_file(base_name, dir_path) preview_url = find_preview_file(base_name, dir_path)
# Check if a .json file exists with SHA256 hash to avoid recalculation
json_path = f"{os.path.splitext(file_path)[0]}.json"
sha256 = None
if os.path.exists(json_path):
try:
with open(json_path, 'r', encoding='utf-8') as f:
json_data = json.load(f)
if 'sha256' in json_data:
sha256 = json_data['sha256'].lower()
logger.debug(f"Using SHA256 from .json file for {file_path}")
except Exception as e:
logger.error(f"Error reading .json file for {file_path}: {e}")
try: try:
# If we didn't get SHA256 from the .json file, calculate it
if not sha256:
sha256 = await calculate_sha256(real_path)
metadata = LoraMetadata( metadata = LoraMetadata(
file_name=base_name, file_name=base_name,
model_name=base_name, model_name=base_name,
file_path=normalize_path(file_path), file_path=normalize_path(file_path),
size=os.path.getsize(real_path), size=os.path.getsize(real_path),
modified=os.path.getmtime(real_path), modified=os.path.getmtime(real_path),
sha256=await calculate_sha256(real_path), sha256=sha256,
base_model="Unknown", # Will be updated later base_model="Unknown", # Will be updated later
usage_tips="", usage_tips="",
notes="", notes="",
@@ -125,7 +142,7 @@ async def load_metadata(file_path: str) -> Optional[LoraMetadata]:
if not preview_url or not os.path.exists(preview_url): if not preview_url or not os.path.exists(preview_url):
base_name = os.path.splitext(os.path.basename(file_path))[0] base_name = os.path.splitext(os.path.basename(file_path))[0]
dir_path = os.path.dirname(file_path) dir_path = os.path.dirname(file_path)
new_preview_url = normalize_path(_find_preview_file(base_name, dir_path)) new_preview_url = normalize_path(find_preview_file(base_name, dir_path))
if new_preview_url != preview_url: if new_preview_url != preview_url:
data['preview_url'] = new_preview_url data['preview_url'] = new_preview_url
needs_update = True needs_update = True