import yt_dlp import os import time def download_audio(url, output_folder, max_length_seconds=600): """ Downloads audio from a YouTube URL. Returns the path to the downloaded file or raises an Exception. Enforces max_length_seconds (default 10 mins). """ timestamp = int(time.time()) output_template = os.path.join(output_folder, f'yt_{timestamp}_%(id)s.%(ext)s') ydl_opts = { 'format': 'bestaudio/best', 'outtmpl': output_template, 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }], 'quiet': True, 'no_warnings': True, 'noplaylist': True, 'match_filter': yt_dlp.utils.match_filter_func("duration <= " + str(max_length_seconds)) } try: with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=True) # The file path might differ slightly because of the postprocessor (mp3 conversion) # yt-dlp usually returns the final filename in 'requested_downloads' or similar, # but constructing it from info is safer if we know the template. # However, extract_info returns the info dict. # Since we force mp3, the file will end in .mp3 # We used %(id)s in template, so we can reconstruct or find it. # Let's find the file in the folder that matches the timestamp prefix # This is safer than guessing what yt-dlp named it exactly valid_files = [f for f in os.listdir(output_folder) if f.startswith(f'yt_{timestamp}_') and f.endswith('.mp3')] if not valid_files: raise Exception("Download failed: Audio file not found after processing.") return os.path.join(output_folder, valid_files[0]) except yt_dlp.utils.DownloadError as e: if "video is too long" in str(e).lower() or "duration" in str(e).lower(): raise Exception(f"Video is too long. Maximum allowed duration is {max_length_seconds} seconds.") raise Exception(f"Failed to download video: {str(e)}")