diff --git a/maskinrommet/src/audio.rs b/maskinrommet/src/audio.rs index a636d6a..06a939a 100644 --- a/maskinrommet/src/audio.rs +++ b/maskinrommet/src/audio.rs @@ -387,6 +387,41 @@ pub fn validate_operations(ops: &[EdlOperation]) -> Result<(), String> { } } +/// Valider fade-varigheter mot faktisk lydvarighet. +/// Kalles fra process_audio etter at vi kjenner varigheten. +pub fn validate_fade_durations(ops: &[EdlOperation], duration_ms: i64) -> Result<(), String> { + let mut errors: Vec = Vec::new(); + + for (i, op) in ops.iter().enumerate() { + let idx = i + 1; + match op { + EdlOperation::FadeIn { duration_ms: fade_ms } => { + if *fade_ms as i64 > duration_ms { + errors.push(format!( + "Operasjon {idx} (fade_in): varighet ({fade_ms} ms) \ + overstiger lydens varighet ({duration_ms} ms)" + )); + } + } + EdlOperation::FadeOut { duration_ms: fade_ms } => { + if *fade_ms as i64 > duration_ms { + errors.push(format!( + "Operasjon {idx} (fade_out): varighet ({fade_ms} ms) \ + overstiger lydens varighet ({duration_ms} ms)" + )); + } + } + _ => {} + } + } + + if errors.is_empty() { + Ok(()) + } else { + Err(format!("Ugyldig fade-varighet:\n- {}", errors.join("\n- "))) + } +} + // ─── EDL → FFmpeg filtergraf ────────────────────────────────────── /// Bygg ffmpeg-filtergraf fra EDL-operasjoner. @@ -509,10 +544,8 @@ pub fn build_filter_chain( } EdlOperation::FadeOut { duration_ms: dur } => { let d = *dur as f64 / 1000.0; - let start = (effective_duration_ms as f64 / 1000.0) - d; - if start > 0.0 { - filters.push(format!("afade=t=out:st={start:.3}:d={d:.3}")); - } + let start = ((effective_duration_ms as f64 / 1000.0) - d).max(0.0); + filters.push(format!("afade=t=out:st={start:.3}:d={d:.3}")); } _ => {} } @@ -540,6 +573,9 @@ pub async fn process_audio( // Hent info for fade-out beregning let info = get_audio_info(cas, &edl.source_hash).await?; + // Valider fade-varigheter mot faktisk lydvarighet + validate_fade_durations(&edl.operations, info.duration_ms)?; + // Sjekk om vi trenger to-pass loudnorm let has_normalize = edl.operations.iter().any(|op| matches!(op, EdlOperation::Normalize { .. })); @@ -703,8 +739,10 @@ async fn resolve_silence_cuts( { let regions = detect_silence(cas, &edl.source_hash, *threshold_db, *min_duration_ms).await?; for region in regions { - // Behold 200ms stillhet på hver side for naturlig lyd - let margin_ms = 200; + // Behold 200ms stillhet på hver side for naturlig lyd, + // men aldri mer enn halve regionens varighet + let region_duration = region.end_ms - region.start_ms; + let margin_ms = 200i64.min(region_duration / 2); let start = region.start_ms + margin_ms; let end = region.end_ms - margin_ms; if end > start { diff --git a/tasks.md b/tasks.md index d48431a..da0a8ae 100644 --- a/tasks.md +++ b/tasks.md @@ -191,8 +191,7 @@ Ref: Kodegjennomgang av `b4c4bb8` (Lydstudio: lydredigering via FFmpeg). - [x] 17.1 Responsivt studio-layout: `/studio/[id]` sidebar stacker under waveform på mobil. Verktøypanel som modal/sheet på små skjermer. Ref: feedback om at alt UI skal være responsivt uten unntak. - [x] 17.2 FFmpeg-parametervalidering: valider at alle numeriske verdier (threshold, gain, ratio, frekvenser) er innenfor sikre grenser i `audio.rs` før de interpoleres i filterstrenger. Avvis ugyldige verdier med feilmelding. -- [~] 17.3 Fade/silence-logikk: fiks negativ fade-out start (clamp til 0), og adaptiv silence-margin (margin skal ikke overstige halve regionens varighet). Gi feilmelding ved ugyldige fade-varigheter. - > Påbegynt: 2026-03-18T05:44 +- [x] 17.3 Fade/silence-logikk: fiks negativ fade-out start (clamp til 0), og adaptiv silence-margin (margin skal ikke overstige halve regionens varighet). Gi feilmelding ved ugyldige fade-varigheter. - [ ] 17.4 Frontend input-begrensninger: legg til `min`/`max` på alle tallfelter i OperationPanel (silenceThreshold, fadeMs, normTarget, compRatio). Hindre ugyldig input. - [ ] 17.5 Job-polling opprydding: rydd opp interval/timeout ved navigering bort fra studio-siden. Vis feilmelding etter N mislykkede polling-forsøk. Wrap metadata JSON.parse i try/catch. - [ ] 17.6 Temp-fil opprydding: legg til periodisk jobb i maskinrommet som sletter gamle temp-filer i CAS tmp-katalog. Bruk `/tmp` eller sett TTL.