Skip to content

Commit e257918

Browse files
committed
fix(wavesurfer): add loading indicator and error handling (#28)
- Add indeterminate linear progress bar that shows on loading and hides on ready/error - Add error label that displays on WaveSurfer error (filtered to fatal errors only) - Emit loading/ready/error events from JS to drive Python-side UI state - Guard serve_wav against non-existent paths early (returns empty string)
1 parent 96a8d29 commit e257918

4 files changed

Lines changed: 62 additions & 9 deletions

File tree

locales/app.pot

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -478,15 +478,19 @@ msgid ""
478478
"curve upward; negative values shift it downward"
479479
msgstr ""
480480

481-
#: utils/ui.py:353
481+
#: utils/ui.py:350
482+
msgid "Failed to load audio"
483+
msgstr ""
484+
485+
#: utils/ui.py:377
482486
msgid "Play/Pause"
483487
msgstr ""
484488

485-
#: utils/ui.py:354
489+
#: utils/ui.py:378
486490
msgid "Loop region"
487491
msgstr ""
488492

489-
#: utils/ui.py:355
493+
#: utils/ui.py:379
490494
msgid "Zoom"
491495
msgstr ""
492496

locales/en/LC_MESSAGES/app.po

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -525,15 +525,19 @@ msgstr ""
525525
"**Bias** offset added to the expression curve. Positive values shift the "
526526
"curve upward; negative values shift it downward"
527527

528-
#: utils/ui.py:353
528+
#: utils/ui.py:350
529+
msgid "Failed to load audio"
530+
msgstr "Failed to load audio"
531+
532+
#: utils/ui.py:377
529533
msgid "Play/Pause"
530534
msgstr "Play/Pause"
531535

532-
#: utils/ui.py:354
536+
#: utils/ui.py:378
533537
msgid "Loop region"
534538
msgstr "Loop region"
535539

536-
#: utils/ui.py:355
540+
#: utils/ui.py:379
537541
msgid "Zoom"
538542
msgstr "Zoom"
539543

locales/zh_CN/LC_MESSAGES/app.po

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -495,15 +495,19 @@ msgid ""
495495
"curve upward; negative values shift it downward"
496496
msgstr "添加到表情曲线的**偏置**偏移量;正值使曲线上移,负值使曲线下移"
497497

498-
#: utils/ui.py:353
498+
#: utils/ui.py:350
499+
msgid "Failed to load audio"
500+
msgstr "音频加载失败"
501+
502+
#: utils/ui.py:377
499503
msgid "Play/Pause"
500504
msgstr "播放/暂停"
501505

502-
#: utils/ui.py:354
506+
#: utils/ui.py:378
503507
msgid "Loop region"
504508
msgstr "选区循环播放"
505509

506-
#: utils/ui.py:355
510+
#: utils/ui.py:379
507511
msgid "Zoom"
508512
msgstr "缩放"
509513

utils/ui.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,30 @@ def _build(self) -> None:
327327
waveform_div.classes("w-full rounded-lg overflow-hidden")
328328
waveform_div.style(f"min-height:{self._height}px; background:var(--ws-bg);")
329329

330+
self._error = (
331+
ui.label('')
332+
.classes('w-full text-xs text-red-400')
333+
)
334+
self._error.set_visibility(False)
335+
336+
self._loader = (
337+
ui.linear_progress(show_value=False)
338+
.props('instant-feedback rounded indeterminate')
339+
.classes('w-full')
340+
.style('height:3px; margin:0; color:#c800c8;')
341+
)
342+
343+
ui.on(f'{iid}-loading', lambda e: (
344+
self._error.set_visibility(False),
345+
self._loader.set_visibility(True),
346+
))
347+
ui.on(f'{iid}-ready', lambda e: self._loader.set_visibility(False))
348+
ui.on(f'{iid}-error', lambda e: (
349+
self._loader.set_visibility(False),
350+
self._error.set_text(e.args.get('message') or _('Failed to load audio')),
351+
self._error.set_visibility(True),
352+
))
353+
330354
ws_opts: dict[str, Any] = {
331355
"container": f"#{iid}_waveform",
332356
"waveColor": self._wave_color,
@@ -371,6 +395,20 @@ def _build(self) -> None:
371395
372396
window['{iid}'] = {{ ws, regions, loop: {loop_init} }};
373397
398+
ws.on('loading', (percent) => {{
399+
emitEvent('{iid}-loading', {{ percent: percent }});
400+
}});
401+
402+
ws.on('ready', () => {{
403+
emitEvent('{iid}-ready', {{}});
404+
}});
405+
406+
ws.on('error', (err) => {{
407+
// Filter out non-fatal errors
408+
if (ws.getDuration() > 0) return;
409+
emitEvent('{iid}-error', {{ message: err?.message || '' }});
410+
}});
411+
374412
if ({show_controls_json}) {{
375413
// Overlay controls: inject a style block + overlay div into the waveform container
376414
const styleEl = document.createElement('style');
@@ -537,6 +575,9 @@ def serve_wav(wav_path: str) -> str:
537575
Each unique directory is registered once under /wav/<hashed_dir>.
538576
WaveSurfer then streams the file normally — no base64 overhead.
539577
"""
578+
if not wav_path or not os.path.exists(wav_path):
579+
return ''
580+
540581
import hashlib
541582
directory = os.path.dirname(os.path.abspath(wav_path))
542583
dir_hash = hashlib.md5(directory.encode()).hexdigest()[:8]

0 commit comments

Comments
 (0)