import gradio as gr from yt_dlp import YoutubeDL import tempfile import os import subprocess import traceback import shutil # Constants MAX_DURATION = 300 # 5 minutes max DEFAULT_URL = "https://soundcloud.com/emma-eline-pihlstr-m/have-yourself-a-merry-little-christmas" DEFAULT_DURATION = 30 def download_and_trim(url, duration_sec): """Two-step process: download full audio then trim""" # Create temporary directory temp_dir = tempfile.mkdtemp() try: # Step 1: Download with yt-dlp print(f"Downloading from: {url}") # First get info for title with YoutubeDL({'quiet': True, 'no_warnings': True}) as ydl: info = ydl.extract_info(url, download=False) if not info: raise Exception("Could not fetch track information") title = info.get('title', 'soundcloud_track') # Create safe filename safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).rstrip() safe_title = safe_title[:100] # Limit length # Download options download_path = os.path.join(temp_dir, "original") ydl_opts = { 'format': 'bestaudio/best', 'outtmpl': download_path + '.%(ext)s', 'quiet': True, 'no_warnings': True, 'noplaylist': True, 'extractaudio': True, 'audioformat': 'mp3', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }], 'socket_timeout': 30, 'retries': 3, } # Download with YoutubeDL(ydl_opts) as ydl: ydl.download([url]) # Find the downloaded file downloaded_files = [f for f in os.listdir(temp_dir) if f.startswith('original') and f.endswith('.mp3')] if not downloaded_files: # Try to find any audio file downloaded_files = [f for f in os.listdir(temp_dir) if f.endswith(('.mp3', '.webm', '.m4a'))] if not downloaded_files: raise Exception("No audio file was downloaded") input_file = os.path.join(temp_dir, downloaded_files[0]) # Verify file if not os.path.exists(input_file): raise Exception("Downloaded file not found") file_size = os.path.getsize(input_file) if file_size == 0: raise Exception("Downloaded file is empty") print(f"Downloaded: {input_file} ({file_size} bytes)") # Step 2: Trim with ffmpeg output_filename = f"{safe_title}_{duration_sec}s.mp3" output_file = os.path.join(temp_dir, output_filename) # Use ffmpeg to trim cmd = [ 'ffmpeg', '-i', input_file, # Input file '-t', str(duration_sec), # Duration to keep '-acodec', 'libmp3lame', # MP3 codec '-q:a', '2', # Good quality (0-9, 2=good) '-metadata', f'title={safe_title} [{duration_sec}s snippet]', '-metadata', f'comment=Snippet from {url}', '-y', # Overwrite output output_file ] print(f"Trimming with command: {' '.join(cmd)}") result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: print(f"FFmpeg stderr: {result.stderr}") raise Exception(f"Trimming failed: {result.stderr[:200]}") # Verify trimmed file if not os.path.exists(output_file): raise Exception("Trimmed file was not created") trimmed_size = os.path.getsize(output_file) if trimmed_size == 0: raise Exception("Trimmed file is empty") print(f"Created: {output_file} ({trimmed_size} bytes)") # Clean up original file try: os.unlink(input_file) except: pass return output_file, output_filename except Exception as e: # Clean up on error if os.path.exists(temp_dir): shutil.rmtree(temp_dir, ignore_errors=True) print(f"Error: {str(e)}") traceback.print_exc() raise e def validate_url(url): """Validate SoundCloud URL""" if not url or not url.strip(): return False, "Please enter a URL" url_lower = url.lower() if 'soundcloud.com' not in url_lower: return False, "Please enter a SoundCloud URL" # Check if it's a track, not a playlist or user page if '/sets/' in url_lower: return False, "Playlists not supported. Please use a track URL." # Count slashes to guess if it's a track parts = url_lower.replace('https://', '').replace('http://', '').split('/') if len(parts) < 3 or not parts[2]: return False, "Please enter a specific track URL" return True, "" # Create the Gradio app with gr.Blocks( title="SoundCloud Snippet Generator", theme=gr.themes.Soft(), css=""" .gradio-container { max-width: 1000px !important; margin: auto; } .header { text-align: center; margin-bottom: 20px; } .success { color: #28a745; font-weight: bold; } .error { color: #dc3545; font-weight: bold; } .warning { background-color: #fff3cd; padding: 10px; border-radius: 5px; } .example-url { background: #f8f9fa; padding: 5px 10px; border-radius: 5px; margin: 5px 0; cursor: pointer; } .example-url:hover { background: #e9ecef; } """ ) as demo: # Header gr.Markdown("""
Download the first N seconds of any public SoundCloud track
Built with ❤️ using Gradio & yt-dlp
Respect artists' rights. For personal use only.