fix(web): show why the last transcription failed in the derush tab
The transcript tab fell back to the generate button without any message; the error was only hinted at by a red badge in the rushes tab.
cc6717a parent: 0a62d55 modified
web/src/i18n/locales/en/derush.ts +1 -0 | @@ -22,6 +22,7 @@ export default { | ||
| 22 | 22 | transcribing: "Transcribing · {p}%", |
| 23 | 23 | intro: "The clips play back to back in the timeline. Generate the transcript to show the text and spot gaps and bad takes.", |
| 24 | 24 | generate: "Generate transcript", |
| 25 | + transcriptFailed: "The last transcription failed: {e}", | |
| 25 | 26 | rendering: "Rendering {name} · {p}%", |
| 26 | 27 | renderFailed: "Render failed", |
| 27 | 28 | cutsChanged: " · cuts changed since", |
| @@ -22,6 +22,7 @@ export default { | |||
| 22 | transcribing: "Transcribing · {p}%", | 22 | transcribing: "Transcribing · {p}%", |
| 23 | intro: "The clips play back to back in the timeline. Generate the transcript to show the text and spot gaps and bad takes.", | 23 | intro: "The clips play back to back in the timeline. Generate the transcript to show the text and spot gaps and bad takes.", |
| 24 | generate: "Generate transcript", | 24 | generate: "Generate transcript", |
| 25 | + transcriptFailed: "The last transcription failed: {e}", | ||
| 25 | rendering: "Rendering {name} · {p}%", | 26 | rendering: "Rendering {name} · {p}%", |
| 26 | renderFailed: "Render failed", | 27 | renderFailed: "Render failed", |
| 27 | cutsChanged: " · cuts changed since", | 28 | cutsChanged: " · cuts changed since", |
modified
web/src/i18n/locales/fr/derush.ts +1 -0 | @@ -22,6 +22,7 @@ export default { | ||
| 22 | 22 | transcribing: "Transcription en cours · {p} %", |
| 23 | 23 | intro: "Les rushes sont lus bout à bout dans la timeline. Génère le transcript pour afficher le texte, repérer les vides et les prises ratées.", |
| 24 | 24 | generate: "Générer le transcript", |
| 25 | + transcriptFailed: "La dernière transcription a échoué : {e}", | |
| 25 | 26 | rendering: "Rendu de {name} · {p} %", |
| 26 | 27 | renderFailed: "Rendu échoué", |
| 27 | 28 | cutsChanged: " · coupes modifiées depuis", |
| @@ -22,6 +22,7 @@ export default { | |||
| 22 | transcribing: "Transcription en cours · {p} %", | 22 | transcribing: "Transcription en cours · {p} %", |
| 23 | intro: "Les rushes sont lus bout à bout dans la timeline. Génère le transcript pour afficher le texte, repérer les vides et les prises ratées.", | 23 | intro: "Les rushes sont lus bout à bout dans la timeline. Génère le transcript pour afficher le texte, repérer les vides et les prises ratées.", |
| 24 | generate: "Générer le transcript", | 24 | generate: "Générer le transcript", |
| 25 | + transcriptFailed: "La dernière transcription a échoué : {e}", | ||
| 25 | rendering: "Rendu de {name} · {p} %", | 26 | rendering: "Rendu de {name} · {p} %", |
| 26 | renderFailed: "Rendu échoué", | 27 | renderFailed: "Rendu échoué", |
| 27 | cutsChanged: " · coupes modifiées depuis", | 28 | cutsChanged: " · coupes modifiées depuis", |
modified
web/src/project/steps/DerushStep.vue +3 -1 | @@ -29,7 +29,7 @@ const router = useRouter(); | ||
| 29 | 29 | const project = computed(() => store.project!); |
| 30 | 30 | const base = computed(() => `/api/projects/${project.value.id}`); |
| 31 | 31 | const derush = useDerush(project); |
| 32 | -const { analysis, ready, segsByRush, clips, flat, kept, transcribing, progress } = derush; | |
| 32 | +const { analysis, ready, segsByRush, clips, flat, kept, transcribing, progress, transcriptError } = derush; | |
| 33 | 33 | |
| 34 | 34 | /** Noms et durées des rushes et des timelines (leurs ids ne se croisent pas). */ |
| 35 | 35 | const names = computed(() => ({ |
| @@ -327,6 +327,7 @@ async function validate() { | ||
| 327 | 327 | </div> |
| 328 | 328 | <div v-else-if="analysis" class="state"> |
| 329 | 329 | <span class="state-text">{{ t("derush.intro") }}</span> |
| 330 | + <span v-if="transcriptError" class="state-error">{{ t("derush.transcriptFailed", { e: transcriptError }) }}</span> | |
| 330 | 331 | <button class="btn dark gen" @click="act('/rushes/transcribe')">{{ t("derush.generate") }}</button> |
| 331 | 332 | </div> |
| 332 | 333 | </template> |
| @@ -440,6 +441,7 @@ async function validate() { | ||
| 440 | 441 | padding: 20px; border: 1px dashed var(--dash); border-radius: 4px; text-align: center; |
| 441 | 442 | } |
| 442 | 443 | .state-text { font-size: 14px; color: var(--muted); max-width: 440px; line-height: 1.5; text-wrap: pretty; } |
| 444 | +.state-error { font-size: 13px; color: var(--red-ink); max-width: 440px; line-height: 1.5; overflow-wrap: anywhere; } | |
| 443 | 445 | .state-label { font-family: var(--mono); font-size: 12.5px; color: var(--muted); } |
| 444 | 446 | .progress.bar { width: 260px; } |
| 445 | 447 | .btn.gen { height: 38px; padding: 0 18px; font-size: 13.5px; } |
| @@ -29,7 +29,7 @@ const router = useRouter(); | |||
| 29 | const project = computed(() => store.project!); | 29 | const project = computed(() => store.project!); |
| 30 | const base = computed(() => `/api/projects/${project.value.id}`); | 30 | const base = computed(() => `/api/projects/${project.value.id}`); |
| 31 | const derush = useDerush(project); | 31 | const derush = useDerush(project); |
| 32 | -const { analysis, ready, segsByRush, clips, flat, kept, transcribing, progress } = derush; | 32 | +const { analysis, ready, segsByRush, clips, flat, kept, transcribing, progress, transcriptError } = derush; |
| 33 | 33 | ||
| 34 | /** Noms et durées des rushes et des timelines (leurs ids ne se croisent pas). */ | 34 | /** Noms et durées des rushes et des timelines (leurs ids ne se croisent pas). */ |
| 35 | const names = computed(() => ({ | 35 | const names = computed(() => ({ |
| @@ -327,6 +327,7 @@ async function validate() { | |||
| 327 | </div> | 327 | </div> |
| 328 | <div v-else-if="analysis" class="state"> | 328 | <div v-else-if="analysis" class="state"> |
| 329 | <span class="state-text">{{ t("derush.intro") }}</span> | 329 | <span class="state-text">{{ t("derush.intro") }}</span> |
| 330 | + <span v-if="transcriptError" class="state-error">{{ t("derush.transcriptFailed", { e: transcriptError }) }}</span> | ||
| 330 | <button class="btn dark gen" @click="act('/rushes/transcribe')">{{ t("derush.generate") }}</button> | 331 | <button class="btn dark gen" @click="act('/rushes/transcribe')">{{ t("derush.generate") }}</button> |
| 331 | </div> | 332 | </div> |
| 332 | </template> | 333 | </template> |
| @@ -440,6 +441,7 @@ async function validate() { | |||
| 440 | padding: 20px; border: 1px dashed var(--dash); border-radius: 4px; text-align: center; | 441 | padding: 20px; border: 1px dashed var(--dash); border-radius: 4px; text-align: center; |
| 441 | } | 442 | } |
| 442 | .state-text { font-size: 14px; color: var(--muted); max-width: 440px; line-height: 1.5; text-wrap: pretty; } | 443 | .state-text { font-size: 14px; color: var(--muted); max-width: 440px; line-height: 1.5; text-wrap: pretty; } |
| 444 | +.state-error { font-size: 13px; color: var(--red-ink); max-width: 440px; line-height: 1.5; overflow-wrap: anywhere; } | ||
| 443 | .state-label { font-family: var(--mono); font-size: 12.5px; color: var(--muted); } | 445 | .state-label { font-family: var(--mono); font-size: 12.5px; color: var(--muted); } |
| 444 | .progress.bar { width: 260px; } | 446 | .progress.bar { width: 260px; } |
| 445 | .btn.gen { height: 38px; padding: 0 18px; font-size: 13.5px; } | 447 | .btn.gen { height: 38px; padding: 0 18px; font-size: 13.5px; } |
modified
web/src/project/steps/derush/useDerush.ts +5 -1 | @@ -152,9 +152,13 @@ export function useDerush(project: Ref<Project>) { | ||
| 152 | 152 | const sum = all.reduce((a, r) => a + (r?.status === "done" ? 1 : r?.progress ?? 0), 0); |
| 153 | 153 | return all.length ? Math.round(sum / all.length * 100) : 0; |
| 154 | 154 | }); |
| 155 | + /** Message de la dernière transcription en échec, s'il y en a une. */ | |
| 156 | + const transcriptError = computed(() => project.value.rushes | |
| 157 | + .map((r) => project.value.runtime.rush_transcripts[r.id]) | |
| 158 | + .find((r) => r?.status === "error")?.error ?? null); | |
| 155 | 159 | |
| 156 | 160 | return { |
| 157 | - analysis, ready, segsByRush, transcribing, progress, | |
| 161 | + analysis, ready, segsByRush, transcribing, progress, transcriptError, | |
| 158 | 162 | timelines, mainId, current, clips, flat, kept, lengthOf, nameOf, canNest, |
| 159 | 163 | edit, undo, redo, canUndo, canRedo, add, create, rename, drop, |
| 160 | 164 | }; |
| @@ -152,9 +152,13 @@ export function useDerush(project: Ref<Project>) { | |||
| 152 | const sum = all.reduce((a, r) => a + (r?.status === "done" ? 1 : r?.progress ?? 0), 0); | 152 | const sum = all.reduce((a, r) => a + (r?.status === "done" ? 1 : r?.progress ?? 0), 0); |
| 153 | return all.length ? Math.round(sum / all.length * 100) : 0; | 153 | return all.length ? Math.round(sum / all.length * 100) : 0; |
| 154 | }); | 154 | }); |
| 155 | + /** Message de la dernière transcription en échec, s'il y en a une. */ | ||
| 156 | + const transcriptError = computed(() => project.value.rushes | ||
| 157 | + .map((r) => project.value.runtime.rush_transcripts[r.id]) | ||
| 158 | + .find((r) => r?.status === "error")?.error ?? null); | ||
| 155 | 159 | ||
| 156 | return { | 160 | return { |
| 157 | - analysis, ready, segsByRush, transcribing, progress, | 161 | + analysis, ready, segsByRush, transcribing, progress, transcriptError, |
| 158 | timelines, mainId, current, clips, flat, kept, lengthOf, nameOf, canNest, | 162 | timelines, mainId, current, clips, flat, kept, lengthOf, nameOf, canNest, |
| 159 | edit, undo, redo, canUndo, canRedo, add, create, rename, drop, | 163 | edit, undo, redo, canUndo, canRedo, add, create, rename, drop, |
| 160 | }; | 164 | }; |