From 3498861184d997d8acf08fd3fbd0650f33ac6bda Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 12 Aug 2026 15:34:36 +0200 Subject: [PATCH] Recover transcripts falsely marked orphaned mid-run. Keep finishing Whisper jobs from overwriting a superseded run, while restoring results when orphan detection failed the same run too early. --- app/Jobs/TranscribeRecording.php | 81 +++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 16 deletions(-) diff --git a/app/Jobs/TranscribeRecording.php b/app/Jobs/TranscribeRecording.php index 6735be6..65730b5 100644 --- a/app/Jobs/TranscribeRecording.php +++ b/app/Jobs/TranscribeRecording.php @@ -76,26 +76,16 @@ class TranscribeRecording implements ShouldQueue }, ); - if (! $this->recording->ownsTranscriptionRun($this->runStartedAt)) { + if (! $this->claimSuccessfulTranscript($text)) { return; } - - $this->reportIfOwned('Saving transcript…', 90); - - if (! $this->recording->ownsTranscriptionRun($this->runStartedAt)) { - return; - } - - $this->recording->forceFill([ - 'transcript' => $text, - 'transcription_status' => 'done', - 'transcription_progress' => 'Transcription complete', - 'transcription_percent' => 100, - 'transcription_error' => null, - 'transcribed_at' => now(), - ])->save(); } catch (Throwable $e) { if (! $this->recording->ownsTranscriptionRun($this->runStartedAt)) { + Log::warning('Transcription exception ignored after run was superseded', [ + 'recording_id' => $this->recording->id, + 'message' => $e->getMessage(), + ]); + return; } @@ -103,6 +93,7 @@ class TranscribeRecording implements ShouldQueue 'recording_id' => $this->recording->id, 'driver' => $this->recording->transcription_driver, 'message' => $e->getMessage(), + 'exception' => $e::class, ]); $this->recording->markTranscriptionFailed($e->getMessage()); @@ -127,6 +118,64 @@ class TranscribeRecording implements ShouldQueue ); } + /** + * Persist a finished transcript when this job still owns the run. + * + * Also recovers runs that were falsely marked failed by orphan detection + * while Whisper was still working. + */ + private function claimSuccessfulTranscript(string $text): bool + { + $this->recording->refresh(); + + if ($this->recording->ownsTranscriptionRun($this->runStartedAt)) { + $this->reportIfOwned('Saving transcript…', 90); + $this->recording->refresh(); + } + + if ($this->recording->ownsTranscriptionRun($this->runStartedAt)) { + $this->recording->forceFill([ + 'transcript' => $text, + 'transcription_status' => 'done', + 'transcription_progress' => 'Transcription complete', + 'transcription_percent' => 100, + 'transcription_error' => null, + 'transcribed_at' => now(), + ])->save(); + + return true; + } + + // Same run was wrongly marked failed as "orphaned" while Whisper was still running. + if ( + $this->recording->transcription_status === 'failed' + && $this->recording->matchesTranscriptionRun($this->runStartedAt) + && str_contains((string) $this->recording->transcription_error, 'worker stopped') + ) { + Log::warning('Recovering transcript after false orphan failure', [ + 'recording_id' => $this->recording->id, + ]); + + $this->recording->forceFill([ + 'transcript' => $text, + 'transcription_status' => 'done', + 'transcription_progress' => 'Transcription complete', + 'transcription_percent' => 100, + 'transcription_error' => null, + 'transcribed_at' => now(), + ])->save(); + + return true; + } + + Log::info('Discarding transcript because transcription run was superseded', [ + 'recording_id' => $this->recording->id, + 'status' => $this->recording->transcription_status, + ]); + + return false; + } + private function reportIfOwned(string $message, int $percent): void { if (! $this->recording->ownsTranscriptionRun($this->runStartedAt)) {