import os from pathlib import Path from PIL import Image from typing import List, Optional, Union, Any, Dict, Tuple from .utils import IMG_EXTS, VID_EXTS, is_media, size_mb, unique_name class FileHandler: def __init__(self) -> None: self.temp_dir = self._setup_temp() def _setup_temp(self) -> str: temp = os.path.join(os.getcwd(), 'temp_processing') os.makedirs(temp, exist_ok=True) return temp def cleanup(self) -> None: if os.path.exists(self.temp_dir): import shutil shutil.rmtree(self.temp_dir, ignore_errors=True) def info(self, path: str) -> Dict[str, Any]: return { 'path': path, 'name': os.path.basename(path), 'size_mb': size_mb(path), 'type': self._type(path), 'dims': self._dims(path) } def _type(self, path: str) -> str: ext = Path(path).suffix.lower() if ext in IMG_EXTS: return 'image' elif ext in VID_EXTS: return 'video' return 'unknown' def _dims(self, path: str) -> Optional[str]: if not os.path.exists(path): return None if Path(path).suffix.lower() in IMG_EXTS: with Image.open(path) as img: return f"{img.width}x{img.height}" return None def get_files(self, input: Union[str, List[Any], None]) -> List[str]: if not input: return [] if not isinstance(input, list): input = [input] # Handle folder if (len(input) == 1 and isinstance(input[0], str) and os.path.isdir(input[0])): return self._from_folder(input[0]) return self._extract_paths(input) def _from_folder(self, folder: str) -> List[str]: return [str(p) for p in Path(folder).rglob('*') if p.is_file() and is_media(str(p))] def _extract_paths(self, files: List[Any]) -> List[str]: result = [] for f in files: if f is None: continue path = None if hasattr(f, 'name') and f.name: path = f.name elif hasattr(f, 'path') and f.path: path = f.path elif isinstance(f, str): path = f if path and os.path.exists(path) and is_media(path): result.append(path) return result def preview(self, files: List[str], max_items: int = 6) -> Optional[str]: if not files: return None imgs = [f for f in files[:max_items] if os.path.exists(f) and Path(f).suffix.lower() in IMG_EXTS] if not imgs: return None # Load and resize images = [] for path in imgs: with Image.open(path) as img: copy = img.copy() copy.thumbnail((120, 120)) images.append(copy) # Create grid cols = min(3, len(images)) rows = (len(images) + cols - 1) // cols grid = Image.new('RGB', (cols * 120, rows * 120), 'white') for i, img in enumerate(images): x, y = (i % cols) * 120, (i // cols) * 120 grid.paste(img, (x, y)) path = os.path.join(self.temp_dir, unique_name('preview', '.jpg')) grid.save(path, 'JPEG', quality=85) return path def files_info(self, input: Union[str, List[Any], None]) -> Tuple[List[str], Dict[str, Any]]: files = self.get_files(input) info = { 'total': len(files), 'size_mb': 0, 'images': 0, 'videos': 0, 'details': [] } for path in files: detail = self.info(path) info['details'].append(detail) info['size_mb'] += detail['size_mb'] if detail['type'] == 'image': info['images'] += 1 elif detail['type'] == 'video': info['videos'] += 1 info['size_mb'] = round(info['size_mb'], 2) return files, info def temp_path(self, name: str) -> str: return os.path.join(self.temp_dir, name)