Files
ComfyUI-Lora-Manager/py/utils/file_utils.py
2025-11-10 10:15:01 +08:00

109 lines
4.1 KiB
Python

import hashlib
import logging
import os
from .constants import (
CARD_PREVIEW_WIDTH,
DEFAULT_HASH_CHUNK_SIZE_MB,
PREVIEW_EXTENSIONS,
)
from .exif_utils import ExifUtils
from ..services.settings_manager import get_settings_manager
logger = logging.getLogger(__name__)
def _get_hash_chunk_size_bytes() -> int:
"""Return the chunk size used for hashing, in bytes."""
settings_manager = get_settings_manager()
chunk_size_mb = settings_manager.get("hash_chunk_size_mb", DEFAULT_HASH_CHUNK_SIZE_MB)
try:
chunk_size_value = float(chunk_size_mb)
except (TypeError, ValueError):
chunk_size_value = float(DEFAULT_HASH_CHUNK_SIZE_MB)
if chunk_size_value <= 0:
chunk_size_value = float(DEFAULT_HASH_CHUNK_SIZE_MB)
return max(1, int(chunk_size_value * 1024 * 1024))
async def calculate_sha256(file_path: str) -> str:
"""Calculate SHA256 hash of a file"""
sha256_hash = hashlib.sha256()
chunk_size = _get_hash_chunk_size_bytes()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(chunk_size), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
def find_preview_file(base_name: str, dir_path: str) -> str:
"""Find preview file for given base name in directory"""
temp_extensions = PREVIEW_EXTENSIONS.copy()
# Add example extension for compatibility
# https://github.com/willmiao/ComfyUI-Lora-Manager/issues/225
# The preview image will be optimized to lora-name.webp, so it won't affect other logic
temp_extensions.append(".example.0.jpeg")
for ext in temp_extensions:
full_pattern = os.path.join(dir_path, f"{base_name}{ext}")
if os.path.exists(full_pattern):
# Check if this is an image and not already webp
# TODO: disable the optimization for now, maybe add a config option later
# if ext.lower().endswith(('.jpg', '.jpeg', '.png')) and not ext.lower().endswith('.webp'):
# try:
# # Optimize the image to webp format
# webp_path = os.path.join(dir_path, f"{base_name}.webp")
# # Use ExifUtils to optimize the image
# with open(full_pattern, 'rb') as f:
# image_data = f.read()
# optimized_data, _ = ExifUtils.optimize_image(
# image_data=image_data,
# target_width=CARD_PREVIEW_WIDTH,
# format='webp',
# quality=85,
# preserve_metadata=False
# )
# # Save the optimized webp file
# with open(webp_path, 'wb') as f:
# f.write(optimized_data)
# logger.debug(f"Optimized preview image from {full_pattern} to {webp_path}")
# return webp_path.replace(os.sep, "/")
# except Exception as e:
# logger.error(f"Error optimizing preview image {full_pattern}: {e}")
# # Fall back to original file if optimization fails
# return full_pattern.replace(os.sep, "/")
# Return the original path for webp images or non-image files
return full_pattern.replace(os.sep, "/")
return ""
def get_preview_extension(preview_path: str) -> str:
"""Get the complete preview extension from a preview file path
Args:
preview_path: Path to the preview file
Returns:
str: The complete extension (e.g., '.preview.png', '.png', '.webp')
"""
preview_path_lower = preview_path.lower()
# Check for compound extensions first (longer matches first)
for ext in sorted(PREVIEW_EXTENSIONS, key=len, reverse=True):
if preview_path_lower.endswith(ext.lower()):
return ext
return os.path.splitext(preview_path)[1]
def normalize_path(path: str) -> str:
"""Normalize file path to use forward slashes"""
return path.replace(os.sep, "/") if path else path