| 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] |
| |
| |
| 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 |
| |
| |
| images = [] |
| for path in imgs: |
| with Image.open(path) as img: |
| copy = img.copy() |
| copy.thumbnail((120, 120)) |
| images.append(copy) |
| |
| |
| 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) |