Spaces:
Sleeping
Sleeping
Chandima Prabhath
Update dependencies in requirements.txt; enhance FFmpegEncoder with availability check and improve logging in worker
a4f374c
| import subprocess | |
| import os | |
| import shutil | |
| from pathlib import Path | |
| from typing import List, Dict | |
| from ..config import EncodingConfig | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| class FFmpegEncoder: | |
| def __init__(self, input_path: str, output_dir: str): | |
| # Verify FFmpeg is available | |
| if not shutil.which("ffmpeg"): | |
| raise RuntimeError("FFmpeg not found in PATH") | |
| self.input_path = input_path | |
| self.output_dir = output_dir | |
| self.base_name = Path(input_path).stem | |
| os.makedirs(output_dir, exist_ok=True) | |
| def generate_commands(self) -> List[List[str]]: | |
| """Generate FFmpeg commands for all resolutions""" | |
| commands = [] | |
| for res in EncodingConfig.RESOLUTIONS: | |
| output_path = os.path.join( | |
| self.output_dir, | |
| f"{self.base_name}_{res['name']}.m3u8" | |
| ) | |
| cmd = [ | |
| "ffmpeg", "-i", self.input_path, | |
| "-vf", f"scale={res['width']}:{res['height']}", | |
| "-c:v", res["codec"], | |
| "-profile:v", res["profile"], | |
| "-preset", res["preset"], | |
| "-b:v", res["video_bitrate"], | |
| "-c:a", "aac", | |
| "-b:a", res["audio_bitrate"], | |
| "-f", "hls", | |
| "-hls_time", "6", | |
| "-hls_playlist_type", "vod", | |
| "-hls_segment_filename", | |
| os.path.join(self.output_dir, f"{self.base_name}_{res['name']}_%03d.ts"), | |
| output_path | |
| ] | |
| commands.append(cmd) | |
| return commands | |
| def encode(self) -> str: | |
| """Execute encoding commands and return master playlist path""" | |
| master_playlist = os.path.join(self.output_dir, f"{self.base_name}_master.m3u8") | |
| with open(master_playlist, "w") as f: | |
| f.write("#EXTM3U\n") | |
| for res in reversed(EncodingConfig.RESOLUTIONS): | |
| f.write(f"#EXT-X-STREAM-INF:BANDWIDTH={res['video_bitrate'].replace('k', '000')}," | |
| f"RESOLUTION={res['width']}x{res['height']}\n") | |
| f.write(f"{self.base_name}_{res['name']}.m3u8\n") | |
| for cmd in self.generate_commands(): | |
| logger.info(f"Executing FFmpeg command: {' '.join(cmd)}") | |
| result = subprocess.run(cmd, capture_output=True) | |
| logger.debug(f"FFmpeg command: {' '.join(cmd)}") | |
| logger.debug(f"FFmpeg output: {result.stdout.decode()}") | |
| logger.debug(f"FFmpeg error: {result.stderr.decode()}") | |
| if result.returncode != 0: | |
| logger.error(f"FFmpeg command failed with return code {result.returncode}") | |
| raise subprocess.CalledProcessError(result.returncode, cmd) | |
| result.check_returncode() | |
| return master_playlist | |