tonhowtf/omniget · error
waveform_peaks/detect_shot_changes error surfaced to user…
Error message
waveform_peaks/detect_shot_changes error surfaced to user via err() (localized toast)
What it means
loadMedia computes waveform peaks and shot-change timestamps via Rust commands waveform_peaks and detect_shot_changes; if either invoke rejects, err() shows the failure as a localized toast and peaks/shots stay empty (analysis features disabled for that media).
Solutions
- Check the toast text to identify which of the two invokes failed and why.
- Verify the media file exists and its codec is supported by the backend decoder (test with ffmpeg directly if bundled).
- Invoke waveform_peaks first in isolation to pinpoint the failing command.
- Handle the two invokes with separate try/catch so one failure doesn't discard the other's result.
Example fix
// before
try {
peaks = await invoke<number[]>("waveform_peaks", { input: sel, buckets: 600 });
shots = await invoke<number[]>("detect_shot_changes", { input: sel, threshold: 0.4 });
} catch (e) { err(e); }
// after
try { peaks = await invoke<number[]>("waveform_peaks", { input: sel, buckets: 600 }); } catch (e) { err(e); }
try { shots = await invoke<number[]>("detect_shot_changes", { input: sel, threshold: 0.4 }); } catch (e) { err(e); } Defensive patterns
Strategy: validation
Validate before calling
if (!sel || typeof sel !== "string") return;
const exists = await invoke<boolean>("path_exists", { path: sel }).catch(() => false);
if (!exists) { showToast("error", $t("downloads.sw.media_missing")); return; } Type guard
function isMediaPath(v: unknown): v is string {
return typeof v === "string" && /\.(mp4|mkv|webm|mp3|wav|m4a|flac)$/i.test(v);
} Try / catch
try {
peaks = await invoke<number[]>("waveform_peaks", { input: sel, buckets: 600 });
shots = await invoke<number[]>("detect_shot_changes", { input: sel, threshold: 0.4 });
} catch (e) {
err(e);
} finally {
busy = false;
} Prevention
- Only offer media analysis for codecs/formats the backend decoder supports.
- Run the two invokes independently so one failure doesn't discard the other's result.
- Reset peaks/shots at the start of loadMedia to avoid stale data on failure.
- Pre-check large files and warn about long analysis times.
When it happens
Trigger: invoke('waveform_peaks',{input,buckets:600}) or invoke('detect_shot_changes',{input,threshold:0.4}) rejecting — media file missing/unreadable, unsupported codec/container that the backend decoder (e.g. ffmpeg) can't open, or backend decode failure.
Common situations: Selecting a video with a codec the bundled decoder lacks; huge files causing backend memory/time failures surfaced as invoke errors; media path changed since selection; backend ffmpeg integration missing.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- $t('common.error') | mapped: ai_not_configured ->…
- subtitle_load error surfaced to user via err() (localized…
- subtitle_translate error surfaced to user via err()…
- subtitle_grammar_fix error surfaced to user via err()…
- escolha a pasta de destino para organizar
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/5cb10775229ee192.
Report an issue: GitHub.
Appendix: source
Thrown at src/components/downloads/SubtitleWorkshop.svelte:86
loadedPath = sel;
peaks = [];
shots = [];
} catch (e) {
err(e);
} finally {
busy = false;
}
}
async function loadMedia() {
const sel = await openDialog({ multiple: false });
if (!sel || typeof sel !== "string") return;
busy = true;
try {
peaks = await invoke<number[]>("waveform_peaks", { input: sel, buckets: 600 });
shots = await invoke<number[]>("detect_shot_changes", { input: sel, threshold: 0.4 });
} catch (e) {
err(e);
} finally {
busy = false;
}
}
function applyShift() {
if (!shiftMs) return;
cues = cues.map((c) => ({
...c,
start_ms: Math.max(0, c.start_ms + shiftMs),
end_ms: Math.max(0, c.end_ms + shiftMs),
}));
}
function applyReplace() {
if (!findText) return;
let n = 0;
cues = cues.map((c) => {View on GitHub (pinned to 8600b91f42)