Download app.py from MySafeCode/SoundCloudDurationDownload: direct link, hf CLI and curl.
- Browser
- Download file 8.88 kB
-
https://huggingface.co/spaces/MySafeCode/SoundCloudDurationDownload/resolve/main/app.py
- Command line
-
hf download hf://spaces/MySafeCode/SoundCloudDurationDownload/app.py
-
curl -L -o app.py https://huggingface.co/spaces/MySafeCode/SoundCloudDurationDownload/resolve/main/app.py
8.88 kB
| import gradio as gr | |
| from yt_dlp import YoutubeDL | |
| import tempfile | |
| import os | |
| import subprocess | |
| def download_snippet(url, start_sec, end_sec): | |
| """Download and trim audio snippet with custom start/end times""" | |
| # Create temp directory | |
| temp_dir = tempfile.mkdtemp() | |
| try: | |
| # Validate times | |
| if start_sec >= end_sec: | |
| raise Exception("Start time must be before end time") | |
| duration = end_sec - start_sec | |
| if duration > 600: # Limit to 10 minutes max | |
| raise Exception("Maximum duration is 600 seconds (10 minutes)") | |
| # First, download the full track (or best we can get) | |
| 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: | |
| # Get info for filename | |
| info = ydl.extract_info(url, download=False) | |
| title = info.get('title', 'soundcloud_track') | |
| duration_full = info.get('duration', 0) | |
| # Validate against full duration | |
| if duration_full and end_sec > duration_full: | |
| raise Exception(f"End time ({end_sec}s) exceeds track duration ({duration_full}s)") | |
| safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).rstrip() | |
| # Download | |
| ydl.download([url]) | |
| # Find the downloaded file | |
| 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]) | |
| # Check if file exists and has content | |
| if not os.path.exists(input_file) or os.path.getsize(input_file) == 0: | |
| raise Exception("Downloaded file is empty") | |
| # Create output filename | |
| output_file = os.path.join(temp_dir, f"{safe_title}_{start_sec}-{end_sec}s.mp3") | |
| # Use ffmpeg to trim with start and end times | |
| cmd = [ | |
| 'ffmpeg', | |
| '-i', input_file, # Input file | |
| '-ss', str(start_sec), # Start time | |
| '-to', str(end_sec), # End time | |
| '-acodec', 'libmp3lame', # MP3 codec | |
| '-q:a', '2', # Good quality | |
| '-y', # Overwrite output | |
| output_file | |
| ] | |
| # Run ffmpeg | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| if result.returncode != 0: | |
| raise Exception(f"FFmpeg error: {result.stderr}") | |
| # Check if output was created | |
| 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}_{start_sec}-{end_sec}s.mp3", duration_full | |
| except Exception as e: | |
| # Clean up on error | |
| if os.path.exists(temp_dir): | |
| import shutil | |
| shutil.rmtree(temp_dir, ignore_errors=True) | |
| raise e | |
| # Simple Gradio interface | |
| with gr.Blocks(title="SoundCloud Snippet", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(""" | |
| # 🎵 SoundCloud Snippet Downloader | |
| Download any segment of a 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(): | |
| with gr.Column(scale=1): | |
| start_slider = gr.Slider( | |
| minimum=0, | |
| maximum=600, | |
| value=0, | |
| step=1, | |
| label="Start Time (seconds)" | |
| ) | |
| start_number = gr.Number( | |
| value=0, | |
| label="Start (seconds)", | |
| precision=0, | |
| minimum=0, | |
| maximum=600 | |
| ) | |
| with gr.Column(scale=1): | |
| end_slider = gr.Slider( | |
| minimum=1, | |
| maximum=600, | |
| value=30, | |
| step=1, | |
| label="End Time (seconds)" | |
| ) | |
| end_number = gr.Number( | |
| value=30, | |
| label="End (seconds)", | |
| precision=0, | |
| minimum=1, | |
| maximum=600 | |
| ) | |
| with gr.Column(scale=1): | |
| duration_display = gr.Textbox( | |
| label="Segment Duration", | |
| value="30 seconds", | |
| interactive=False | |
| ) | |
| max_duration = gr.Textbox( | |
| label="Track Duration", | |
| value="Unknown", | |
| interactive=False, | |
| visible=False | |
| ) | |
| 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) | |
| # Store file path and track duration | |
| file_path = gr.State() | |
| track_duration = gr.State(0) | |
| # Sync sliders and number inputs | |
| def sync_start(start_val): | |
| return start_val, start_val | |
| def sync_end(end_val): | |
| return end_val, end_val | |
| start_slider.change( | |
| sync_start, | |
| inputs=[start_slider], | |
| outputs=[start_number, start_slider] | |
| ) | |
| start_number.change( | |
| sync_start, | |
| inputs=[start_number], | |
| outputs=[start_slider, start_number] | |
| ) | |
| end_slider.change( | |
| sync_end, | |
| inputs=[end_slider], | |
| outputs=[end_number, end_slider] | |
| ) | |
| end_number.change( | |
| sync_end, | |
| inputs=[end_number], | |
| outputs=[end_slider, end_number] | |
| ) | |
| # Update duration display | |
| def update_duration(start, end): | |
| duration = end - start | |
| if duration <= 0: | |
| return "Invalid (start must be before end)", gr.update(visible=False) | |
| return f"{duration} seconds", gr.update(visible=True) | |
| start_slider.change( | |
| update_duration, | |
| inputs=[start_slider, end_slider], | |
| outputs=[duration_display, download_btn] | |
| ) | |
| end_slider.change( | |
| update_duration, | |
| inputs=[start_slider, end_slider], | |
| outputs=[duration_display, download_btn] | |
| ) | |
| def process_download(url, start, end): | |
| if not url or 'soundcloud.com' not in url.lower(): | |
| raise gr.Error("Please enter a valid SoundCloud URL") | |
| if start >= end: | |
| raise gr.Error("Start time must be before end time") | |
| try: | |
| filepath, filename, full_duration = download_snippet(url, start, end) | |
| # Update max duration display if we have it | |
| max_dur_update = gr.update( | |
| value=f"{full_duration} seconds" if full_duration > 0 else "Unknown", | |
| visible=True | |
| ) | |
| return { | |
| audio_player: filepath, | |
| download_file: gr.DownloadButton(visible=True), | |
| file_path: filepath, | |
| max_duration: max_dur_update, | |
| track_duration: full_duration | |
| } | |
| except Exception as e: | |
| raise gr.Error(f"Download failed: {str(e)}") | |
| download_btn.click( | |
| process_download, | |
| inputs=[url, start_slider, end_slider], | |
| outputs=[audio_player, download_file, file_path, max_duration, track_duration] | |
| ) | |
| download_file.click( | |
| lambda x: x if x and os.path.exists(x) else None, | |
| inputs=[file_path], | |
| outputs=None | |
| ) | |
| # Auto-adjust end slider when track duration is known | |
| def adjust_sliders(full_duration): | |
| if full_duration and full_duration > 0: | |
| return ( | |
| gr.update(maximum=min(600, full_duration)), | |
| gr.update(maximum=min(600, full_duration), value=min(30, full_duration)), | |
| gr.update(maximum=min(600, full_duration)), | |
| gr.update(maximum=min(600, full_duration), value=min(30, full_duration)) | |
| ) | |
| return ( | |
| gr.update(maximum=600), | |
| gr.update(maximum=600), | |
| gr.update(maximum=600), | |
| gr.update(maximum=600) | |
| ) | |
| # This would need to be triggered after we get track duration | |
| # For simplicity, we'll update when the download completes | |
| if __name__ == "__main__": | |
| demo.launch() | |