Spaces:
Sleeping
Sleeping
Improve progress reporting for audio separation
Browse filesRefactored process_video_with_progress to handle structured progress updates using value and description, and updated separate_tracks to yield progress as dictionary events with normalized float values. Enhanced clarity and granularity of progress feedback throughout the audio separation workflow.
- app.py +4 -3
- modules/yt_audio_get_tracks.py +19 -12
app.py
CHANGED
|
@@ -199,7 +199,8 @@ def process_video_with_progress(
|
|
| 199 |
status_lines = []
|
| 200 |
|
| 201 |
def on_progress(message):
|
| 202 |
-
|
|
|
|
| 203 |
|
| 204 |
try:
|
| 205 |
if cookies_upload is not None:
|
|
@@ -227,7 +228,7 @@ def process_video_with_progress(
|
|
| 227 |
progress(0.4, desc="Separating tracks")
|
| 228 |
yield "", "\n".join(status_lines)
|
| 229 |
|
| 230 |
-
separation = separate_tracks(audio_path, job_id
|
| 231 |
while True:
|
| 232 |
try:
|
| 233 |
event_type, payload = next(separation)
|
|
@@ -235,7 +236,7 @@ def process_video_with_progress(
|
|
| 235 |
break
|
| 236 |
|
| 237 |
if event_type == "progress":
|
| 238 |
-
|
| 239 |
yield "", "\n".join(status_lines)
|
| 240 |
elif event_type == "result":
|
| 241 |
drums, vocals, guitar, bass, other, piano, music, full = payload
|
|
|
|
| 199 |
status_lines = []
|
| 200 |
|
| 201 |
def on_progress(message):
|
| 202 |
+
if isinstance(message, str):
|
| 203 |
+
status_lines.append(message)
|
| 204 |
|
| 205 |
try:
|
| 206 |
if cookies_upload is not None:
|
|
|
|
| 228 |
progress(0.4, desc="Separating tracks")
|
| 229 |
yield "", "\n".join(status_lines)
|
| 230 |
|
| 231 |
+
separation = separate_tracks(audio_path, job_id)
|
| 232 |
while True:
|
| 233 |
try:
|
| 234 |
event_type, payload = next(separation)
|
|
|
|
| 236 |
break
|
| 237 |
|
| 238 |
if event_type == "progress":
|
| 239 |
+
progress(payload["value"], desc=payload["desc"])
|
| 240 |
yield "", "\n".join(status_lines)
|
| 241 |
elif event_type == "result":
|
| 242 |
drums, vocals, guitar, bass, other, piano, music, full = payload
|
modules/yt_audio_get_tracks.py
CHANGED
|
@@ -163,12 +163,13 @@ def separate_tracks(input_wav, job_id, progress_callback=None):
|
|
| 163 |
if not os.path.exists(input_wav):
|
| 164 |
raise FileNotFoundError(f"{input_wav} does not exist")
|
| 165 |
|
| 166 |
-
def
|
| 167 |
-
|
| 168 |
-
|
|
|
|
| 169 |
|
| 170 |
-
yield
|
| 171 |
-
yield
|
| 172 |
|
| 173 |
output_dir = 'separated'
|
| 174 |
|
|
@@ -178,6 +179,7 @@ def separate_tracks(input_wav, job_id, progress_callback=None):
|
|
| 178 |
], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
|
| 179 |
|
| 180 |
progress_pattern = re.compile(r'(\d+)%\|')
|
|
|
|
| 181 |
|
| 182 |
# Read progress in real time
|
| 183 |
for line in proc.stdout or []:
|
|
@@ -186,15 +188,20 @@ def separate_tracks(input_wav, job_id, progress_callback=None):
|
|
| 186 |
match = progress_pattern.search(line)
|
| 187 |
if match:
|
| 188 |
percent = int(match.group(1))
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
elif "Separating track" in line:
|
| 191 |
-
yield
|
| 192 |
|
| 193 |
proc.wait()
|
| 194 |
if proc.returncode != 0:
|
| 195 |
raise RuntimeError("Demucs failed")
|
| 196 |
|
| 197 |
-
yield
|
| 198 |
|
| 199 |
base = os.path.join(output_dir, 'htdemucs_6s', job_id)
|
| 200 |
|
|
@@ -205,20 +212,20 @@ def separate_tracks(input_wav, job_id, progress_callback=None):
|
|
| 205 |
piano = f'{base}/piano.mp3'
|
| 206 |
other = f'{base}/other.mp3'
|
| 207 |
|
| 208 |
-
yield
|
| 209 |
music = AudioSegment.from_mp3(bass).overlay(AudioSegment.from_mp3(other))
|
| 210 |
music_path = os.path.join(base, 'music.mp3')
|
| 211 |
music.export(music_path, format="mp3")
|
| 212 |
|
| 213 |
# Full mix export (rest of your code)
|
| 214 |
full_path = os.path.join(base, 'full.mp3')
|
| 215 |
-
yield
|
| 216 |
src = AudioSegment.from_file(input_wav)
|
| 217 |
src.export(full_path, format="mp3")
|
| 218 |
|
| 219 |
-
yield
|
| 220 |
os.remove(input_wav)
|
| 221 |
-
yield
|
| 222 |
|
| 223 |
yield ("result", (drums, vocals, guitar, bass, other, piano, music_path, full_path))
|
| 224 |
|
|
|
|
| 163 |
if not os.path.exists(input_wav):
|
| 164 |
raise FileNotFoundError(f"{input_wav} does not exist")
|
| 165 |
|
| 166 |
+
def _emit_progress_event(value, desc):
|
| 167 |
+
if progress_callback is not None:
|
| 168 |
+
progress_callback(desc)
|
| 169 |
+
return ("progress", {"value": value, "desc": desc})
|
| 170 |
|
| 171 |
+
yield _emit_progress_event(0.0, "Validating input audio...")
|
| 172 |
+
yield _emit_progress_event(0.05, "Starting Demucs separation (htdemucs_6s)...")
|
| 173 |
|
| 174 |
output_dir = 'separated'
|
| 175 |
|
|
|
|
| 179 |
], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
|
| 180 |
|
| 181 |
progress_pattern = re.compile(r'(\d+)%\|')
|
| 182 |
+
last_percent = None
|
| 183 |
|
| 184 |
# Read progress in real time
|
| 185 |
for line in proc.stdout or []:
|
|
|
|
| 188 |
match = progress_pattern.search(line)
|
| 189 |
if match:
|
| 190 |
percent = int(match.group(1))
|
| 191 |
+
if percent != last_percent:
|
| 192 |
+
last_percent = percent
|
| 193 |
+
yield _emit_progress_event(
|
| 194 |
+
0.1 + (percent / 100.0) * 0.7,
|
| 195 |
+
f"Demucs progress: {percent}%",
|
| 196 |
+
)
|
| 197 |
elif "Separating track" in line:
|
| 198 |
+
yield _emit_progress_event(0.1, "Demucs: Starting separation...")
|
| 199 |
|
| 200 |
proc.wait()
|
| 201 |
if proc.returncode != 0:
|
| 202 |
raise RuntimeError("Demucs failed")
|
| 203 |
|
| 204 |
+
yield _emit_progress_event(0.82, "Demucs separation complete. Loading stems...")
|
| 205 |
|
| 206 |
base = os.path.join(output_dir, 'htdemucs_6s', job_id)
|
| 207 |
|
|
|
|
| 212 |
piano = f'{base}/piano.mp3'
|
| 213 |
other = f'{base}/other.mp3'
|
| 214 |
|
| 215 |
+
yield _emit_progress_event(0.88, "Creating music stem (bass + other)...")
|
| 216 |
music = AudioSegment.from_mp3(bass).overlay(AudioSegment.from_mp3(other))
|
| 217 |
music_path = os.path.join(base, 'music.mp3')
|
| 218 |
music.export(music_path, format="mp3")
|
| 219 |
|
| 220 |
# Full mix export (rest of your code)
|
| 221 |
full_path = os.path.join(base, 'full.mp3')
|
| 222 |
+
yield _emit_progress_event(0.94, "Exporting full mix...")
|
| 223 |
src = AudioSegment.from_file(input_wav)
|
| 224 |
src.export(full_path, format="mp3")
|
| 225 |
|
| 226 |
+
yield _emit_progress_event(0.98, "Cleaning up...")
|
| 227 |
os.remove(input_wav)
|
| 228 |
+
yield _emit_progress_event(1.0, "Separation complete.")
|
| 229 |
|
| 230 |
yield ("result", (drums, vocals, guitar, bass, other, piano, music_path, full_path))
|
| 231 |
|