""" TranscodeHandler — executes transcode/trim jobs using FFmpeg. Extracted from the old tasks.py Celery task logic. """ import logging import os import tempfile from pathlib import Path from typing import Any, Callable, Dict, Optional from core.ffmpeg.transcode import TranscodeConfig, transcode from core.storage import BUCKET_IN, BUCKET_OUT, download_to_temp, upload_file from .base import Handler logger = logging.getLogger(__name__) class TranscodeHandler(Handler): """Handle transcode and trim jobs via FFmpeg.""" def process( self, job_id: str, payload: Dict[str, Any], progress_callback: Optional[Callable[[int, Dict[str, Any]], None]] = None, ) -> Dict[str, Any]: source_key = payload["source_key"] output_key = payload["output_key"] preset = payload.get("preset") trim_start = payload.get("trim_start") trim_end = payload.get("trim_end") duration = payload.get("duration") logger.info(f"TranscodeHandler: {source_key} -> {output_key}") # Download source tmp_source = download_to_temp(BUCKET_IN, source_key) ext = Path(output_key).suffix or ".mp4" fd, tmp_output = tempfile.mkstemp(suffix=ext) os.close(fd) try: if preset: config = TranscodeConfig( input_path=tmp_source, output_path=tmp_output, video_codec=preset.get("video_codec", "libx264"), video_bitrate=preset.get("video_bitrate"), video_crf=preset.get("video_crf"), video_preset=preset.get("video_preset"), resolution=preset.get("resolution"), framerate=preset.get("framerate"), audio_codec=preset.get("audio_codec", "aac"), audio_bitrate=preset.get("audio_bitrate"), audio_channels=preset.get("audio_channels"), audio_samplerate=preset.get("audio_samplerate"), container=preset.get("container", "mp4"), extra_args=preset.get("extra_args", []), trim_start=trim_start, trim_end=trim_end, ) else: config = TranscodeConfig( input_path=tmp_source, output_path=tmp_output, video_codec="copy", audio_codec="copy", trim_start=trim_start, trim_end=trim_end, ) def wrapped_callback(percent: float, details: Dict[str, Any]) -> None: if progress_callback: progress_callback(int(percent), details) success = transcode( config, duration=duration, progress_callback=wrapped_callback if progress_callback else None, ) if not success: raise RuntimeError("Transcode returned False") # Upload result logger.info(f"Uploading {output_key} to {BUCKET_OUT}") upload_file(tmp_output, BUCKET_OUT, output_key) return { "status": "completed", "job_id": job_id, "output_key": output_key, } finally: for f in [tmp_source, tmp_output]: try: os.unlink(f) except OSError: pass