| import tempfile | |
| import os | |
| import subprocess | |
| import json | |
| import shutil | |
| from typing import Optional | |
| import yt_dlp | |
| class VideoLoader: | |
| def __init__(self): | |
| self.temp_dir = tempfile.mkdtemp() | |
| def load_youtube(self, url: str) -> Optional[str]: | |
| """ | |
| Download YouTube video. | |
| Args: | |
| url: YouTube URL | |
| Returns: | |
| Path to downloaded video file, or None if failed | |
| """ | |
| output_path = os.path.join(self.temp_dir, "video.mp4") | |
| ydl_opts = { | |
| 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio/best', | |
| 'outtmpl': output_path, | |
| 'merge_output_format': 'mp4', | |
| 'quiet': True, | |
| 'no_warnings': True, | |
| 'postprocessors': [{ | |
| 'key': 'FFmpegVideoConvertor', | |
| 'preferedformat': 'mp4', | |
| }], | |
| } | |
| try: | |
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
| ydl.download([url]) | |
| return output_path | |
| except Exception as e: | |
| print(f"Error downloading YouTube video: {e}") | |
| return None | |
| def load_local(self, uploaded_file) -> Optional[str]: | |
| """ | |
| Save uploaded file to temp directory. | |
| Args: | |
| uploaded_file: Streamlit UploadedFile object | |
| Returns: | |
| Path to saved file | |
| """ | |
| output_path = os.path.join(self.temp_dir, uploaded_file.name) | |
| with open(output_path, "wb") as f: | |
| f.write(uploaded_file.read()) | |
| return output_path | |
| def get_video_duration(self, video_path: str) -> float: | |
| """Get video duration in seconds using ffprobe.""" | |
| cmd = [ | |
| 'ffprobe', '-v', 'quiet', '-print_format', 'json', | |
| '-show_format', video_path | |
| ] | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| data = json.loads(result.stdout) | |
| return float(data['format']['duration']) | |
| def cleanup(self): | |
| """Remove temp files.""" | |
| if os.path.exists(self.temp_dir): | |
| shutil.rmtree(self.temp_dir) | |