MySafeCode commited on
Commit
67ae8ed
·
verified ·
1 Parent(s): 76ffd7d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +154 -20
app.py CHANGED
@@ -4,12 +4,20 @@ import tempfile
4
  import os
5
  import subprocess
6
 
7
- def download_snippet(url, duration_sec):
8
- """Download and trim audio snippet"""
9
  # Create temp directory
10
  temp_dir = tempfile.mkdtemp()
11
 
12
  try:
 
 
 
 
 
 
 
 
13
  # First, download the full track (or best we can get)
14
  ydl_opts = {
15
  'format': 'bestaudio/best',
@@ -23,6 +31,12 @@ def download_snippet(url, duration_sec):
23
  # Get info for filename
24
  info = ydl.extract_info(url, download=False)
25
  title = info.get('title', 'soundcloud_track')
 
 
 
 
 
 
26
  safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).rstrip()
27
 
28
  # Download
@@ -40,13 +54,14 @@ def download_snippet(url, duration_sec):
40
  raise Exception("Downloaded file is empty")
41
 
42
  # Create output filename
43
- output_file = os.path.join(temp_dir, f"{safe_title}_{duration_sec}s.mp3")
44
 
45
- # Use ffmpeg to trim (this is the key fix!)
46
  cmd = [
47
  'ffmpeg',
48
  '-i', input_file, # Input file
49
- '-t', str(duration_sec), # Duration to keep
 
50
  '-acodec', 'libmp3lame', # MP3 codec
51
  '-q:a', '2', # Good quality
52
  '-y', # Overwrite output
@@ -63,7 +78,7 @@ def download_snippet(url, duration_sec):
63
  if not os.path.exists(output_file) or os.path.getsize(output_file) == 0:
64
  raise Exception("Trimmed file is empty")
65
 
66
- return output_file, f"{safe_title}_{duration_sec}s.mp3"
67
 
68
  except Exception as e:
69
  # Clean up on error
@@ -76,7 +91,7 @@ def download_snippet(url, duration_sec):
76
  with gr.Blocks(title="SoundCloud Snippet", theme=gr.themes.Soft()) as demo:
77
  gr.Markdown("""
78
  # 🎵 SoundCloud Snippet Downloader
79
- Download the first N seconds of any public SoundCloud track
80
  """)
81
 
82
  with gr.Row():
@@ -88,13 +103,50 @@ with gr.Blocks(title="SoundCloud Snippet", theme=gr.themes.Soft()) as demo:
88
  )
89
 
90
  with gr.Row():
91
- duration = gr.Slider(
92
- minimum=5,
93
- maximum=400,
94
- value=30,
95
- step=5,
96
- label="Duration (seconds)"
97
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
  with gr.Row():
100
  download_btn = gr.Button("Download Snippet", variant="primary")
@@ -103,27 +155,90 @@ with gr.Blocks(title="SoundCloud Snippet", theme=gr.themes.Soft()) as demo:
103
  audio_player = gr.Audio(label="Preview", type="filepath")
104
  download_file = gr.DownloadButton("Save MP3", visible=False)
105
 
106
- # Store file path
107
  file_path = gr.State()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
- def process_download(url, duration):
110
  if not url or 'soundcloud.com' not in url.lower():
111
  raise gr.Error("Please enter a valid SoundCloud URL")
112
 
 
 
 
113
  try:
114
- filepath, filename = download_snippet(url, duration)
 
 
 
 
 
 
 
115
  return {
116
  audio_player: filepath,
117
  download_file: gr.DownloadButton(visible=True),
118
- file_path: filepath
 
 
119
  }
120
  except Exception as e:
121
  raise gr.Error(f"Download failed: {str(e)}")
122
 
123
  download_btn.click(
124
  process_download,
125
- inputs=[url, duration],
126
- outputs=[audio_player, download_file, file_path]
127
  )
128
 
129
  download_file.click(
@@ -131,6 +246,25 @@ with gr.Blocks(title="SoundCloud Snippet", theme=gr.themes.Soft()) as demo:
131
  inputs=[file_path],
132
  outputs=None
133
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
  if __name__ == "__main__":
136
  demo.launch()
 
4
  import os
5
  import subprocess
6
 
7
+ def download_snippet(url, start_sec, end_sec):
8
+ """Download and trim audio snippet with custom start/end times"""
9
  # Create temp directory
10
  temp_dir = tempfile.mkdtemp()
11
 
12
  try:
13
+ # Validate times
14
+ if start_sec >= end_sec:
15
+ raise Exception("Start time must be before end time")
16
+
17
+ duration = end_sec - start_sec
18
+ if duration > 600: # Limit to 10 minutes max
19
+ raise Exception("Maximum duration is 600 seconds (10 minutes)")
20
+
21
  # First, download the full track (or best we can get)
22
  ydl_opts = {
23
  'format': 'bestaudio/best',
 
31
  # Get info for filename
32
  info = ydl.extract_info(url, download=False)
33
  title = info.get('title', 'soundcloud_track')
34
+ duration_full = info.get('duration', 0)
35
+
36
+ # Validate against full duration
37
+ if duration_full and end_sec > duration_full:
38
+ raise Exception(f"End time ({end_sec}s) exceeds track duration ({duration_full}s)")
39
+
40
  safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).rstrip()
41
 
42
  # Download
 
54
  raise Exception("Downloaded file is empty")
55
 
56
  # Create output filename
57
+ output_file = os.path.join(temp_dir, f"{safe_title}_{start_sec}-{end_sec}s.mp3")
58
 
59
+ # Use ffmpeg to trim with start and end times
60
  cmd = [
61
  'ffmpeg',
62
  '-i', input_file, # Input file
63
+ '-ss', str(start_sec), # Start time
64
+ '-to', str(end_sec), # End time
65
  '-acodec', 'libmp3lame', # MP3 codec
66
  '-q:a', '2', # Good quality
67
  '-y', # Overwrite output
 
78
  if not os.path.exists(output_file) or os.path.getsize(output_file) == 0:
79
  raise Exception("Trimmed file is empty")
80
 
81
+ return output_file, f"{safe_title}_{start_sec}-{end_sec}s.mp3", duration_full
82
 
83
  except Exception as e:
84
  # Clean up on error
 
91
  with gr.Blocks(title="SoundCloud Snippet", theme=gr.themes.Soft()) as demo:
92
  gr.Markdown("""
93
  # 🎵 SoundCloud Snippet Downloader
94
+ Download any segment of a public SoundCloud track
95
  """)
96
 
97
  with gr.Row():
 
103
  )
104
 
105
  with gr.Row():
106
+ with gr.Column(scale=1):
107
+ start_slider = gr.Slider(
108
+ minimum=0,
109
+ maximum=600,
110
+ value=0,
111
+ step=1,
112
+ label="Start Time (seconds)"
113
+ )
114
+ start_number = gr.Number(
115
+ value=0,
116
+ label="Start (seconds)",
117
+ precision=0,
118
+ minimum=0,
119
+ maximum=600
120
+ )
121
+
122
+ with gr.Column(scale=1):
123
+ end_slider = gr.Slider(
124
+ minimum=1,
125
+ maximum=600,
126
+ value=30,
127
+ step=1,
128
+ label="End Time (seconds)"
129
+ )
130
+ end_number = gr.Number(
131
+ value=30,
132
+ label="End (seconds)",
133
+ precision=0,
134
+ minimum=1,
135
+ maximum=600
136
+ )
137
+
138
+ with gr.Column(scale=1):
139
+ duration_display = gr.Textbox(
140
+ label="Segment Duration",
141
+ value="30 seconds",
142
+ interactive=False
143
+ )
144
+ max_duration = gr.Textbox(
145
+ label="Track Duration",
146
+ value="Unknown",
147
+ interactive=False,
148
+ visible=False
149
+ )
150
 
151
  with gr.Row():
152
  download_btn = gr.Button("Download Snippet", variant="primary")
 
155
  audio_player = gr.Audio(label="Preview", type="filepath")
156
  download_file = gr.DownloadButton("Save MP3", visible=False)
157
 
158
+ # Store file path and track duration
159
  file_path = gr.State()
160
+ track_duration = gr.State(0)
161
+
162
+ # Sync sliders and number inputs
163
+ def sync_start(start_val):
164
+ return start_val, start_val
165
+
166
+ def sync_end(end_val):
167
+ return end_val, end_val
168
+
169
+ start_slider.change(
170
+ sync_start,
171
+ inputs=[start_slider],
172
+ outputs=[start_number, start_slider]
173
+ )
174
+
175
+ start_number.change(
176
+ sync_start,
177
+ inputs=[start_number],
178
+ outputs=[start_slider, start_number]
179
+ )
180
+
181
+ end_slider.change(
182
+ sync_end,
183
+ inputs=[end_slider],
184
+ outputs=[end_number, end_slider]
185
+ )
186
+
187
+ end_number.change(
188
+ sync_end,
189
+ inputs=[end_number],
190
+ outputs=[end_slider, end_number]
191
+ )
192
+
193
+ # Update duration display
194
+ def update_duration(start, end):
195
+ duration = end - start
196
+ if duration <= 0:
197
+ return "Invalid (start must be before end)", gr.update(visible=False)
198
+ return f"{duration} seconds", gr.update(visible=True)
199
+
200
+ start_slider.change(
201
+ update_duration,
202
+ inputs=[start_slider, end_slider],
203
+ outputs=[duration_display, download_btn]
204
+ )
205
+
206
+ end_slider.change(
207
+ update_duration,
208
+ inputs=[start_slider, end_slider],
209
+ outputs=[duration_display, download_btn]
210
+ )
211
 
212
+ def process_download(url, start, end):
213
  if not url or 'soundcloud.com' not in url.lower():
214
  raise gr.Error("Please enter a valid SoundCloud URL")
215
 
216
+ if start >= end:
217
+ raise gr.Error("Start time must be before end time")
218
+
219
  try:
220
+ filepath, filename, full_duration = download_snippet(url, start, end)
221
+
222
+ # Update max duration display if we have it
223
+ max_dur_update = gr.update(
224
+ value=f"{full_duration} seconds" if full_duration > 0 else "Unknown",
225
+ visible=True
226
+ )
227
+
228
  return {
229
  audio_player: filepath,
230
  download_file: gr.DownloadButton(visible=True),
231
+ file_path: filepath,
232
+ max_duration: max_dur_update,
233
+ track_duration: full_duration
234
  }
235
  except Exception as e:
236
  raise gr.Error(f"Download failed: {str(e)}")
237
 
238
  download_btn.click(
239
  process_download,
240
+ inputs=[url, start_slider, end_slider],
241
+ outputs=[audio_player, download_file, file_path, max_duration, track_duration]
242
  )
243
 
244
  download_file.click(
 
246
  inputs=[file_path],
247
  outputs=None
248
  )
249
+
250
+ # Auto-adjust end slider when track duration is known
251
+ def adjust_sliders(full_duration):
252
+ if full_duration and full_duration > 0:
253
+ return (
254
+ gr.update(maximum=min(600, full_duration)),
255
+ gr.update(maximum=min(600, full_duration), value=min(30, full_duration)),
256
+ gr.update(maximum=min(600, full_duration)),
257
+ gr.update(maximum=min(600, full_duration), value=min(30, full_duration))
258
+ )
259
+ return (
260
+ gr.update(maximum=600),
261
+ gr.update(maximum=600),
262
+ gr.update(maximum=600),
263
+ gr.update(maximum=600)
264
+ )
265
+
266
+ # This would need to be triggered after we get track duration
267
+ # For simplicity, we'll update when the download completes
268
 
269
  if __name__ == "__main__":
270
  demo.launch()