{"record":{"id":"5cb10775229ee192","repo":"tonhowtf/omniget","slug":"waveform-peaks-detect-shot-changes-error-surfaced-to-user","errorCode":null,"errorMessage":"waveform_peaks/detect_shot_changes error surfaced to user via err() (localized toast)","messagePattern":"waveform_peaks/detect_shot_changes error surfaced to user via err\\(\\) \\(localized toast\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/components/downloads/SubtitleWorkshop.svelte","lineNumber":86,"sourceCode":"      loadedPath = sel;\n      peaks = [];\n      shots = [];\n    } catch (e) {\n      err(e);\n    } finally {\n      busy = false;\n    }\n  }\n\n  async function loadMedia() {\n    const sel = await openDialog({ multiple: false });\n    if (!sel || typeof sel !== \"string\") return;\n    busy = true;\n    try {\n      peaks = await invoke<number[]>(\"waveform_peaks\", { input: sel, buckets: 600 });\n      shots = await invoke<number[]>(\"detect_shot_changes\", { input: sel, threshold: 0.4 });\n    } catch (e) {\n      err(e);\n    } finally {\n      busy = false;\n    }\n  }\n\n  function applyShift() {\n    if (!shiftMs) return;\n    cues = cues.map((c) => ({\n      ...c,\n      start_ms: Math.max(0, c.start_ms + shiftMs),\n      end_ms: Math.max(0, c.end_ms + shiftMs),\n    }));\n  }\n\n  function applyReplace() {\n    if (!findText) return;\n    let n = 0;\n    cues = cues.map((c) => {","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src/components/downloads/SubtitleWorkshop.svelte#L68-L104","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ntry {\n  peaks = await invoke<number[]>(\"waveform_peaks\", { input: sel, buckets: 600 });\n  shots = await invoke<number[]>(\"detect_shot_changes\", { input: sel, threshold: 0.4 });\n} catch (e) { err(e); }\n// after\ntry { peaks = await invoke<number[]>(\"waveform_peaks\", { input: sel, buckets: 600 }); } catch (e) { err(e); }\ntry { shots = await invoke<number[]>(\"detect_shot_changes\", { input: sel, threshold: 0.4 }); } catch (e) { err(e); }","handlingStrategy":"validation","validationCode":"if (!sel || typeof sel !== \"string\") return;\nconst exists = await invoke<boolean>(\"path_exists\", { path: sel }).catch(() => false);\nif (!exists) { showToast(\"error\", $t(\"downloads.sw.media_missing\")); return; }","typeGuard":"function isMediaPath(v: unknown): v is string {\n  return typeof v === \"string\" && /\\.(mp4|mkv|webm|mp3|wav|m4a|flac)$/i.test(v);\n}","tryCatchPattern":"try {\n  peaks = await invoke<number[]>(\"waveform_peaks\", { input: sel, buckets: 600 });\n  shots = await invoke<number[]>(\"detect_shot_changes\", { input: sel, threshold: 0.4 });\n} catch (e) {\n  err(e);\n} finally {\n  busy = false;\n}","preventionTips":["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."],"tags":["tauri","invoke","media-analysis","waveform"],"backgroundTag":"file-read-failed","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}