Spaces:
Running
Running
| import gradio as gr | |
| from yt_dlp import YoutubeDL | |
| import os | |
| from pydub import AudioSegment | |
| import tempfile | |
| import json | |
| # Use temp directory for better security | |
| DOWNLOADS_FOLDER = tempfile.mkdtemp(prefix="audio_downloader_") | |
| os.makedirs(DOWNLOADS_FOLDER, exist_ok=True) | |
| def download_youtube_audio(url, file_format, duration_sec): | |
| """Download and process audio from YouTube""" | |
| try: | |
| # Download best audio available | |
| ydl_opts = { | |
| 'format': 'bestaudio/best', | |
| 'outtmpl': os.path.join(DOWNLOADS_FOLDER, '%(title)s.%(ext)s'), | |
| 'quiet': False, | |
| 'no_warnings': False, | |
| 'extract_flat': False, | |
| 'noplaylist': True, | |
| } | |
| with YoutubeDL(ydl_opts) as ydl: | |
| info = ydl.extract_info(url, download=True) | |
| original_file = os.path.join(DOWNLOADS_FOLDER, f"{info['title']}.{info['ext']}") | |
| # Clean filename | |
| safe_title = "".join(c for c in info['title'] if c.isalnum() or c in (' ', '-', '_')).rstrip() | |
| safe_title = safe_title[:100] | |
| original_file_safe = os.path.join(DOWNLOADS_FOLDER, f"{safe_title}.{info['ext']}") | |
| if original_file != original_file_safe: | |
| if os.path.exists(original_file_safe): | |
| os.remove(original_file_safe) | |
| os.rename(original_file, original_file_safe) | |
| original_file = original_file_safe | |
| # Load audio | |
| audio = AudioSegment.from_file(original_file) | |
| # Handle duration | |
| if duration_sec > 0: | |
| duration_ms = min(len(audio), int(duration_sec * 1000)) | |
| trimmed_audio = audio[:duration_ms] | |
| else: | |
| trimmed_audio = audio | |
| # Determine output file | |
| base_name = os.path.splitext(original_file)[0] | |
| if file_format.lower() == "mp3": | |
| output_file = base_name + ".mp3" | |
| trimmed_audio.export(output_file, format="mp3", bitrate="192k") | |
| elif file_format.lower() == "opus": | |
| output_file = base_name + ".opus" | |
| trimmed_audio.export(output_file, format="opus", bitrate="128k") | |
| elif file_format.lower() == "wav": | |
| output_file = base_name + ".wav" | |
| trimmed_audio.export(output_file, format="wav") | |
| elif file_format.lower() == "m4a": | |
| output_file = base_name + ".m4a" | |
| trimmed_audio.export(output_file, format="ipod", codec="aac") | |
| else: | |
| output_file = original_file | |
| # Clean up original if converted | |
| if output_file != original_file and os.path.exists(original_file): | |
| os.remove(original_file) | |
| # For Gradio 6+, return a dictionary for gr.File component | |
| return { | |
| "path": output_file, | |
| "name": os.path.basename(output_file) | |
| }, f"✅ Downloaded: {os.path.basename(output_file)}" | |
| except Exception as e: | |
| return None, f"❌ Error: {str(e)}" | |
| def download_soundcloud_audio(url, file_format, duration_sec): | |
| """Download and process audio from SoundCloud""" | |
| try: | |
| ydl_opts = { | |
| 'format': 'bestaudio/best', | |
| 'outtmpl': os.path.join(DOWNLOADS_FOLDER, '%(title)s.%(ext)s'), | |
| 'quiet': False, | |
| 'no_warnings': False, | |
| 'extract_flat': False, | |
| } | |
| with YoutubeDL(ydl_opts) as ydl: | |
| info = ydl.extract_info(url, download=True) | |
| original_file = os.path.join(DOWNLOADS_FOLDER, f"{info['title']}.{info['ext']}") | |
| # Clean filename | |
| safe_title = "".join(c for c in info['title'] if c.isalnum() or c in (' ', '-', '_')).rstrip() | |
| safe_title = safe_title[:100] | |
| original_file_safe = os.path.join(DOWNLOADS_FOLDER, f"{safe_title}.{info['ext']}") | |
| if original_file != original_file_safe: | |
| if os.path.exists(original_file_safe): | |
| os.remove(original_file_safe) | |
| os.rename(original_file, original_file_safe) | |
| original_file = original_file_safe | |
| # Load and process audio | |
| audio = AudioSegment.from_file(original_file) | |
| if duration_sec > 0: | |
| duration_ms = min(len(audio), int(duration_sec * 1000)) | |
| trimmed_audio = audio[:duration_ms] | |
| else: | |
| trimmed_audio = audio | |
| # Convert to desired format | |
| base_name = os.path.splitext(original_file)[0] | |
| if file_format.lower() == "mp3": | |
| output_file = base_name + ".mp3" | |
| trimmed_audio.export(output_file, format="mp3", bitrate="192k") | |
| elif file_format.lower() == "opus": | |
| output_file = base_name + ".opus" | |
| trimmed_audio.export(output_file, format="opus", bitrate="128k") | |
| elif file_format.lower() == "wav": | |
| output_file = base_name + ".wav" | |
| trimmed_audio.export(output_file, format="wav") | |
| elif file_format.lower() == "m4a": | |
| output_file = base_name + ".m4a" | |
| trimmed_audio.export(output_file, format="ipod", codec="aac") | |
| else: | |
| output_file = original_file | |
| # Clean up | |
| if output_file != original_file and os.path.exists(original_file): | |
| os.remove(original_file) | |
| # For Gradio 6+, return a dictionary for gr.File component | |
| return { | |
| "path": output_file, | |
| "name": os.path.basename(output_file) | |
| }, f"✅ Downloaded: {os.path.basename(output_file)}" | |
| except Exception as e: | |
| return None, f"❌ Error: {str(e)}" | |
| # Create Gradio interface | |
| with gr.Blocks(title="Audio Downloader") as iface: # Removed theme from here | |
| gr.Markdown("# 🎵 Audio Downloader") | |
| gr.Markdown("Download audio from YouTube or SoundCloud") | |
| with gr.Tabs(): | |
| # YouTube Tab | |
| with gr.Tab("YouTube"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| youtube_url = gr.Textbox( | |
| label="YouTube URL", | |
| placeholder="https://www.youtube.com/watch?v=...", | |
| lines=1 | |
| ) | |
| with gr.Row(): | |
| youtube_format = gr.Dropdown( | |
| choices=["mp3", "m4a", "opus", "wav"], | |
| value="mp3", | |
| label="Output Format" | |
| ) | |
| youtube_duration = gr.Slider( | |
| minimum=0, | |
| maximum=1800, | |
| value=60, | |
| step=5, | |
| label="Duration (seconds)", | |
| info="0 = full video" | |
| ) | |
| youtube_btn = gr.Button( | |
| "Download from YouTube", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| youtube_status = gr.Textbox( | |
| label="Status", | |
| interactive=False | |
| ) | |
| youtube_output = gr.File( | |
| label="Downloaded File", | |
| interactive=False, | |
| file_count="single" # Explicitly specify file count | |
| ) | |
| # YouTube examples | |
| with gr.Accordion("YouTube Examples", open=False): | |
| gr.Examples( | |
| examples=[ | |
| ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], | |
| ["https://www.youtube.com/watch?v=JGwWNGJdvx8"], | |
| ["https://www.youtube.com/watch?v=9bZkp7q19f0"], | |
| ["https://youtu.be/kJQP7kiw5Fk"] | |
| ], | |
| inputs=youtube_url, | |
| label="Try these URLs:" | |
| ) | |
| # SoundCloud Tab | |
| with gr.Tab("SoundCloud"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| soundcloud_url = gr.Textbox( | |
| label="SoundCloud URL", | |
| placeholder="https://soundcloud.com/...", | |
| lines=1 | |
| ) | |
| with gr.Row(): | |
| soundcloud_format = gr.Dropdown( | |
| choices=["mp3", "m4a", "opus", "wav"], | |
| value="mp3", | |
| label="Output Format" | |
| ) | |
| soundcloud_duration = gr.Slider( | |
| minimum=0, | |
| maximum=600, | |
| value=60, | |
| step=5, | |
| label="Duration (seconds)", | |
| info="0 = full track" | |
| ) | |
| soundcloud_btn = gr.Button( | |
| "Download from SoundCloud", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| soundcloud_status = gr.Textbox( | |
| label="Status", | |
| interactive=False | |
| ) | |
| soundcloud_output = gr.File( | |
| label="Downloaded File", | |
| interactive=False, | |
| file_count="single" # Explicitly specify file count | |
| ) | |
| # SoundCloud examples | |
| with gr.Accordion("SoundCloud Examples", open=False): | |
| gr.Examples( | |
| examples=[ | |
| ["https://soundcloud.com/antonio-antetomaso/mutiny-on-the-bounty-closing-titles-cover"], | |
| ["https://soundcloud.com/officialnikkig/kill-bill-sza-kill-bill-remix"], | |
| ["https://soundcloud.com/lofi-girl"], | |
| ["https://soundcloud.com/monstercat/pegboard-nerds-disconnected"] | |
| ], | |
| inputs=soundcloud_url, | |
| label="Try these URLs:" | |
| ) | |
| # Instructions | |
| with gr.Accordion("📖 Instructions & Info", open=False): | |
| gr.Markdown(""" | |
| ### How to Use: | |
| 1. **Select the platform tab** (YouTube or SoundCloud) | |
| 2. **Paste a URL** from the selected platform | |
| 3. **Choose output format** (MP3, M4A, Opus, or WAV) | |
| 4. **Set duration** (0 for full track, or specify seconds) | |
| 5. **Click Download** button | |
| ### Supported Formats: | |
| - **MP3**: Most compatible, good quality | |
| - **M4A**: Apple format, smaller file size | |
| - **Opus**: Best quality at low bitrates | |
| - **WAV**: Lossless, large file size | |
| ### Notes: | |
| - Files are saved in a temporary folder | |
| - Some content may have download restrictions | |
| - Long videos may take time to process | |
| """) | |
| # Connect button events | |
| youtube_btn.click( | |
| fn=download_youtube_audio, | |
| inputs=[youtube_url, youtube_format, youtube_duration], | |
| outputs=[youtube_output, youtube_status] | |
| ) | |
| soundcloud_btn.click( | |
| fn=download_soundcloud_audio, | |
| inputs=[soundcloud_url, soundcloud_format, soundcloud_duration], | |
| outputs=[soundcloud_output, soundcloud_status] | |
| ) | |
| # Launch the app | |
| if __name__ == "__main__": | |
| iface.launch( | |
| server_name="127.0.0.1", | |
| server_port=7860, | |
| show_error=True, | |
| share=False, | |
| theme=gr.themes.Soft() # Moved theme to launch() method | |
| ) |