From a3cdfa9dc2adbc7214aafe07d7eea3e40418a7fd Mon Sep 17 00:00:00 2001 From: vegard Date: Wed, 18 Mar 2026 06:03:59 +0000 Subject: [PATCH] =?UTF-8?q?Fullf=C3=B8rer=20oppgave=2017.7:=20FFmpeg=20fei?= =?UTF-8?q?lmeldinger=20til=20bruker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tre endringer som sammen gir brukeren innsyn i FFmpeg-feil: 1. Backend: Nytt GET /query/job_status-endepunkt i queries.rs. Frontenden pollet allerede denne URLen, men endepunktet manglet. Returnerer status, result og error_msg fra job_queue. 2. RenderDialog: Ny error-tilstand med formatFfmpegError() som trekker ut lesbar feilmelding fra FFmpeg stderr-dump. Viser kort oppsummering + ekspanderbar full feilmelding via
. 3. Studio-side: Sender renderError til RenderDialog som errorMessage. Toast-varselet vises kun når dialogen er lukket (unngår duplisering). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../lib/components/studio/RenderDialog.svelte | 50 +++++++++++++++- frontend/src/routes/studio/[id]/+page.svelte | 5 +- maskinrommet/src/main.rs | 1 + maskinrommet/src/queries.rs | 57 +++++++++++++++++++ tasks.md | 3 +- 5 files changed, 110 insertions(+), 6 deletions(-) diff --git a/frontend/src/lib/components/studio/RenderDialog.svelte b/frontend/src/lib/components/studio/RenderDialog.svelte index 467508c..5a96de4 100644 --- a/frontend/src/lib/components/studio/RenderDialog.svelte +++ b/frontend/src/lib/components/studio/RenderDialog.svelte @@ -6,13 +6,33 @@ rendering: boolean; jobId: string | null; resultNodeId: string | null; + errorMessage: string | null; onconfirm: (format: string) => void; onclose: () => void; } - let { operations, rendering, jobId, resultNodeId, onconfirm, onclose }: Props = $props(); + let { operations, rendering, jobId, resultNodeId, errorMessage, onconfirm, onclose }: Props = $props(); let format = $state('mp3'); + + /** Trekk ut brukerlesbar FFmpeg-feil fra stderr-dump. */ + function formatFfmpegError(raw: string): { summary: string; detail: string | null } { + // Fjern "ffmpeg feilet: "-prefiks fra maskinrommet + let cleaned = raw.replace(/^ffmpeg feilet:\s*/i, ''); + + // Finn siste linje som inneholder en feilmelding (typisk FFmpeg-mønster) + const lines = cleaned.split('\n').filter((l) => l.trim()); + const errorLine = lines.findLast( + (l) => + /error|invalid|no such|not found|unknown|unsupported|conversion failed/i.test(l) && + !/^frame=/i.test(l) + ); + + const summary = errorLine?.trim() || lines.at(-1)?.trim() || 'Ukjent FFmpeg-feil'; + const detail = lines.length > 1 ? cleaned.trim() : null; + + return { summary, detail }; + } @@ -27,7 +47,33 @@ >

Render lyd

- {#if resultNodeId} + {#if errorMessage} + + {@const parsed = formatFfmpegError(errorMessage)} +
+
+ + + +
+

Rendering feilet

+

{parsed.summary}

+ {#if parsed.detail} +
+ Vis full feilmelding +
{parsed.detail}
+
+ {/if} +
+
+
+ + {:else if resultNodeId}

Rendering fullfort!

diff --git a/frontend/src/routes/studio/[id]/+page.svelte b/frontend/src/routes/studio/[id]/+page.svelte index 24c2031..02ac178 100644 --- a/frontend/src/routes/studio/[id]/+page.svelte +++ b/frontend/src/routes/studio/[id]/+page.svelte @@ -533,8 +533,8 @@ {/if}
- -{#if renderError} + +{#if renderError && !showRenderDialog}
@@ -560,6 +560,7 @@ {rendering} jobId={renderJobId} {resultNodeId} + errorMessage={renderError} onconfirm={handleRenderConfirm} onclose={() => { showRenderDialog = false; rendering = false; renderJobId = null; resultNodeId = null; renderError = null; }} /> diff --git a/maskinrommet/src/main.rs b/maskinrommet/src/main.rs index 05e1d7d..9c9c789 100644 --- a/maskinrommet/src/main.rs +++ b/maskinrommet/src/main.rs @@ -248,6 +248,7 @@ async fn main() { .route("/admin/health", get(health::health_dashboard)) .route("/admin/health/logs", get(health::health_logs)) .route("/query/audio_info", get(intentions::audio_info)) + .route("/query/job_status", get(queries::query_job_status)) .route("/pub/{slug}/feed.xml", get(rss::generate_feed)) .route("/pub/{slug}", get(publishing::serve_index)) // A/B-testing: klikk-sporing (oppgave 14.17) diff --git a/maskinrommet/src/queries.rs b/maskinrommet/src/queries.rs index 7b6e66d..1f21985 100644 --- a/maskinrommet/src/queries.rs +++ b/maskinrommet/src/queries.rs @@ -1336,6 +1336,63 @@ pub async fn query_presentation_elements( })) } +// ============================================================================= +// GET /query/job_status — jobbstatus for polling fra frontend (oppgave 17.7) +// ============================================================================= + +#[derive(Deserialize)] +pub struct JobStatusParams { + job_id: Uuid, +} + +#[derive(Serialize)] +pub struct JobStatusResponse { + pub status: String, + pub result: Option, + pub error_msg: Option, +} + +/// GET /query/job_status?job_id=... +/// +/// Returnerer status, resultat og feilmelding for en jobb. +/// Brukes av frontend for å polle etter rendering-resultater. +pub async fn query_job_status( + State(state): State, + _user: AuthUser, + axum::extract::Query(params): axum::extract::Query, +) -> Result, Response> { + let row = sqlx::query_as::<_, (String, Option, Option)>( + r#"SELECT status::text, result, error_msg FROM job_queue WHERE id = $1"#, + ) + .bind(params.job_id) + .fetch_optional(&state.db) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ErrorResponse { + error: format!("DB-feil: {e}"), + }), + ) + .into_response() + })?; + + match row { + Some((status, result, error_msg)) => Ok(Json(JobStatusResponse { + status, + result, + error_msg, + })), + None => Err(( + StatusCode::NOT_FOUND, + Json(ErrorResponse { + error: "Jobb ikke funnet".to_string(), + }), + ) + .into_response()), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/tasks.md b/tasks.md index 6af96f9..89cd7c2 100644 --- a/tasks.md +++ b/tasks.md @@ -195,8 +195,7 @@ Ref: Kodegjennomgang av `b4c4bb8` (Lydstudio: lydredigering via FFmpeg). - [x] 17.4 Frontend input-begrensninger: legg til `min`/`max` på alle tallfelter i OperationPanel (silenceThreshold, fadeMs, normTarget, compRatio). Hindre ugyldig input. - [x] 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. - [x] 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. -- [~] 17.7 FFmpeg feilmeldinger til bruker: propager stderr fra FFmpeg-feil til frontend via strukturert feilrespons. Vis i RenderDialog. - > Påbegynt: 2026-03-18T05:58 +- [x] 17.7 FFmpeg feilmeldinger til bruker: propager stderr fra FFmpeg-feil til frontend via strukturert feilrespons. Vis i RenderDialog. ## Fase 18: AI-verktøy (arbeidsflate)