|
|
import gradio as gr |
|
|
from yt_dlp import YoutubeDL |
|
|
import tempfile |
|
|
import os |
|
|
import subprocess |
|
|
|
|
|
def download_snippet(url, duration_sec): |
|
|
"""Download and trim audio snippet""" |
|
|
|
|
|
temp_dir = tempfile.mkdtemp() |
|
|
|
|
|
try: |
|
|
|
|
|
ydl_opts = { |
|
|
'format': 'bestaudio/best', |
|
|
'outtmpl': os.path.join(temp_dir, 'full_audio.%(ext)s'), |
|
|
'quiet': True, |
|
|
'no_warnings': True, |
|
|
'noplaylist': True, |
|
|
} |
|
|
|
|
|
with YoutubeDL(ydl_opts) as ydl: |
|
|
|
|
|
info = ydl.extract_info(url, download=False) |
|
|
title = info.get('title', 'soundcloud_track') |
|
|
safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).rstrip() |
|
|
|
|
|
|
|
|
ydl.download([url]) |
|
|
|
|
|
|
|
|
downloaded_files = [f for f in os.listdir(temp_dir) if f.startswith('full_audio')] |
|
|
if not downloaded_files: |
|
|
raise Exception("No file was downloaded") |
|
|
|
|
|
input_file = os.path.join(temp_dir, downloaded_files[0]) |
|
|
|
|
|
|
|
|
if not os.path.exists(input_file) or os.path.getsize(input_file) == 0: |
|
|
raise Exception("Downloaded file is empty") |
|
|
|
|
|
|
|
|
output_file = os.path.join(temp_dir, f"{safe_title}_{duration_sec}s.mp3") |
|
|
|
|
|
|
|
|
cmd = [ |
|
|
'ffmpeg', |
|
|
'-i', input_file, |
|
|
'-t', str(duration_sec), |
|
|
'-acodec', 'libmp3lame', |
|
|
'-q:a', '2', |
|
|
'-y', |
|
|
output_file |
|
|
] |
|
|
|
|
|
|
|
|
result = subprocess.run(cmd, capture_output=True, text=True) |
|
|
|
|
|
if result.returncode != 0: |
|
|
raise Exception(f"FFmpeg error: {result.stderr}") |
|
|
|
|
|
|
|
|
if not os.path.exists(output_file) or os.path.getsize(output_file) == 0: |
|
|
raise Exception("Trimmed file is empty") |
|
|
|
|
|
return output_file, f"{safe_title}_{duration_sec}s.mp3" |
|
|
|
|
|
except Exception as e: |
|
|
|
|
|
if os.path.exists(temp_dir): |
|
|
import shutil |
|
|
shutil.rmtree(temp_dir, ignore_errors=True) |
|
|
raise e |
|
|
|
|
|
|
|
|
with gr.Blocks(title="SoundCloud Snippet", theme=gr.themes.Soft()) as demo: |
|
|
gr.Markdown(""" |
|
|
# 🎵 SoundCloud Snippet Downloader |
|
|
Download the first N seconds of any public SoundCloud track |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
url = gr.Textbox( |
|
|
label="SoundCloud URL", |
|
|
placeholder="https://soundcloud.com/artist/track-name", |
|
|
value="https://soundcloud.com/emma-eline-pihlstr-m/have-yourself-a-merry-little-christmas", |
|
|
lines=2 |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
duration = gr.Slider( |
|
|
minimum=5, |
|
|
maximum=400, |
|
|
value=30, |
|
|
step=5, |
|
|
label="Duration (seconds)" |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
download_btn = gr.Button("Download Snippet", variant="primary") |
|
|
|
|
|
with gr.Row(): |
|
|
audio_player = gr.Audio(label="Preview", type="filepath") |
|
|
download_file = gr.DownloadButton("Save MP3", visible=False) |
|
|
|
|
|
|
|
|
file_path = gr.State() |
|
|
|
|
|
def process_download(url, duration): |
|
|
if not url or 'soundcloud.com' not in url.lower(): |
|
|
raise gr.Error("Please enter a valid SoundCloud URL") |
|
|
|
|
|
try: |
|
|
filepath, filename = download_snippet(url, duration) |
|
|
return { |
|
|
audio_player: filepath, |
|
|
download_file: gr.DownloadButton(visible=True), |
|
|
file_path: filepath |
|
|
} |
|
|
except Exception as e: |
|
|
raise gr.Error(f"Download failed: {str(e)}") |
|
|
|
|
|
download_btn.click( |
|
|
process_download, |
|
|
inputs=[url, duration], |
|
|
outputs=[audio_player, download_file, file_path] |
|
|
) |
|
|
|
|
|
download_file.click( |
|
|
lambda x: x if x and os.path.exists(x) else None, |
|
|
inputs=[file_path], |
|
|
outputs=None |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |