MySafeCode's picture
Update app.py
ba95a67 verified
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
)