imran-decoder/filecrackhead1
0
1"""2Temp file management and cleanup utilities.3"""4 5import os6import shutil7import tempfile8import zipfile9from contextlib import contextmanager10from pathlib import Path11from typing import Generator12 13from app.utils.logging_utils import get_logger14 15logger = get_logger(__name__)16 17 18@contextmanager19def temp_conversion_dir() -> Generator[Path, None, None]:20 """21 Context manager that creates a temporary directory for a conversion job22 and guarantees deletion (including all contents) on exit.23 """24 tmp_dir = Path(tempfile.mkdtemp(prefix="fc_"))25 try:26 yield tmp_dir27 finally:28 try:29 shutil.rmtree(tmp_dir, ignore_errors=True)30 except Exception as exc:31 logger.warning("Failed to clean up temp dir %s: %s", tmp_dir, exc)32 33 34def ensure_dir_exists(path: Path) -> None:35 path.mkdir(parents=True, exist_ok=True)36 37 38def zip_directory(source_dir: Path, output_zip: Path) -> Path:39 """40 Zip all files in source_dir into output_zip.41 Used when a conversion produces multiple files (e.g. PDF pages → images).42 """43 with zipfile.ZipFile(output_zip, "w", zipfile.ZIP_DEFLATED) as zf:44 for file_path in sorted(source_dir.iterdir()):45 if file_path.is_file() and file_path.resolve() != output_zip.resolve():46 zf.write(file_path, file_path.name)47 return output_zip48 49 50def get_file_size(path: Path) -> int:51 """Return file size in bytes."""52 return path.stat().st_size53 54 55def safe_output_path(work_dir: Path, stem: str, extension: str) -> Path:56 """Build a safe output path inside the working directory."""57 clean_ext = extension.lstrip(".")58 return work_dir / f"{stem}.{clean_ext}"59 60 61async def save_upload_to_temp(62 upload_file, work_dir: Path, safe_name: str, chunk_size: int = 256 * 102463) -> tuple[Path, int]:64 """65 Stream a FastAPI UploadFile to disk in chunks, avoiding loading the66 entire file into memory at once.67 68 Returns (file_path, total_bytes_written).69 """70 dest = work_dir / safe_name71 total = 072 with open(dest, "wb") as f:73 while True:74 chunk = await upload_file.read(chunk_size)75 if not chunk:76 break77 f.write(chunk)78 total += len(chunk)79 return dest, total80 