Spaces:
Sleeping
Sleeping
""" | |
Utility functions for the Logo Downloader application | |
""" | |
import os | |
import re | |
import json | |
import time | |
from pathlib import Path | |
from typing import List, Optional | |
from urllib.parse import urlparse | |
import logging | |
from services.appconfig import IMAGE_SIGNATURES, MIN_FILE_SIZE, MAX_FILE_SIZE | |
# Setup logging | |
logging.basicConfig(level=logging.INFO) | |
logger = logging.getLogger(__name__) | |
def create_safe_filename(name: str) -> str: | |
""" | |
Create a safe filename from entity name | |
Args: | |
name (str): Entity name | |
Returns: | |
str: Safe filename | |
""" | |
safe_name = re.sub(r'[^\w\s-]', '', name).strip() | |
safe_name = re.sub(r'[-\s]+', '_', safe_name) | |
return safe_name | |
def get_file_extension(url: str) -> str: | |
""" | |
Extract file extension from URL | |
Args: | |
url (str): Image URL | |
Returns: | |
str: File extension | |
""" | |
parsed_url = urlparse(url) | |
extension = os.path.splitext(parsed_url.path)[1] | |
if not extension or extension.lower() not in ['.png', '.jpg', '.jpeg', '.svg', '.webp']: | |
extension = '.png' | |
return extension | |
def is_valid_image_file(filepath: str) -> bool: | |
""" | |
Validate if file is a proper image | |
Args: | |
filepath (str): Path to image file | |
Returns: | |
bool: True if valid image | |
""" | |
try: | |
# Check file exists and size | |
if not os.path.exists(filepath): | |
return False | |
file_size = os.path.getsize(filepath) | |
if file_size < MIN_FILE_SIZE or file_size > MAX_FILE_SIZE: | |
logger.warning(f"Invalid file size: {file_size}") | |
return False | |
# Check image signature | |
with open(filepath, 'rb') as f: | |
header = f.read(12) | |
for signature in IMAGE_SIGNATURES: | |
if header.startswith(signature): | |
return True | |
return False | |
except Exception as e: | |
logger.error(f"Error validating image: {e}") | |
return False | |
def create_directory(path: Path) -> bool: | |
""" | |
Create directory if it doesn't exist | |
Args: | |
path (Path): Directory path | |
Returns: | |
bool: True if successful | |
""" | |
try: | |
path.mkdir(parents=True, exist_ok=True) | |
return True | |
except Exception as e: | |
logger.error(f"Error creating directory {path}: {e}") | |
return False | |
def clean_up_file(filepath: str) -> None: | |
""" | |
Remove file if it exists | |
Args: | |
filepath (str): Path to file to remove | |
""" | |
try: | |
if os.path.exists(filepath): | |
os.remove(filepath) | |
except Exception as e: | |
logger.error(f"Error removing file {filepath}: {e}") | |
def parse_json_safely(json_string: str) -> Optional[dict]: | |
""" | |
Safely parse JSON string | |
Args: | |
json_string (str): JSON string to parse | |
Returns: | |
dict or None: Parsed JSON or None if failed | |
""" | |
try: | |
return json.loads(json_string) | |
except json.JSONDecodeError: | |
return None | |
def rate_limit_delay(delay: float = 1.0) -> None: | |
""" | |
Add delay between requests to be respectful to servers | |
Args: | |
delay (float): Delay in seconds | |
""" | |
time.sleep(delay) | |
def format_file_size(size_bytes: int) -> str: | |
""" | |
Format file size in human readable format | |
Args: | |
size_bytes (int): Size in bytes | |
Returns: | |
str: Formatted size string | |
""" | |
if size_bytes < 1024: | |
return f"{size_bytes} B" | |
elif size_bytes < 1024 * 1024: | |
return f"{size_bytes / 1024:.1f} KB" | |
else: | |
return f"{size_bytes / (1024 * 1024):.1f} MB" | |
def truncate_text(text: str, max_length: int = 100) -> str: | |
""" | |
Truncate text to specified length | |
Args: | |
text (str): Text to truncate | |
max_length (int): Maximum length | |
Returns: | |
str: Truncated text | |
""" | |
if len(text) <= max_length: | |
return text | |
return text[:max_length - 3] + "..." |