File size: 8,884 Bytes
06925e6 5eb395b 66a97e7 5eb395b 66a97e7 67ae8ed 5eb395b 14966b4 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 14966b4 5eb395b 5d2d0c3 76ffd7d 5eb395b 14966b4 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 5eb395b 67ae8ed 06925e6 5eb395b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
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()
|