Fix stuck transcriptions and make transcripts stoppable and searchable.
Raise queue retry_after above the job timeout, recover orphaned runs, allow stop/restart with any engine, and keep finished transcripts searchable in the recordings list.
This commit is contained in:
@@ -13,17 +13,28 @@ class TranscribeRecording implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* The number of times the job may be attempted.
|
||||
*/
|
||||
public int $tries = 1;
|
||||
|
||||
/**
|
||||
* The number of seconds the job can run before timing out.
|
||||
*/
|
||||
public int $timeout = 600;
|
||||
public int $timeout;
|
||||
|
||||
/**
|
||||
* ISO-8601 transcription_started_at this job owns (ignored after cancel/restart).
|
||||
*/
|
||||
public ?string $runStartedAt = null;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(public Recording $recording)
|
||||
{
|
||||
//
|
||||
$this->timeout = max(60, (int) config('ai.transcription_timeout', 600));
|
||||
$this->runStartedAt = $recording->transcription_started_at?->toIso8601String();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,7 +42,21 @@ class TranscribeRecording implements ShouldQueue
|
||||
*/
|
||||
public function handle(TranscriptionService $transcription): void
|
||||
{
|
||||
$this->recording->refresh();
|
||||
$recording = Recording::query()->find($this->recording->id);
|
||||
|
||||
if ($recording === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->recording = $recording;
|
||||
|
||||
if ($this->runStartedAt !== null && ! $this->recording->ownsTranscriptionRun($this->runStartedAt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->runStartedAt === null && ! $this->recording->isTranscribing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->recording->forceFill([
|
||||
'transcription_status' => 'processing',
|
||||
@@ -39,15 +64,27 @@ class TranscribeRecording implements ShouldQueue
|
||||
'transcription_error' => null,
|
||||
])->save();
|
||||
|
||||
$this->recording->reportProgress('Preparing audio file…', 15);
|
||||
$this->runStartedAt ??= $this->recording->transcription_started_at?->toIso8601String();
|
||||
|
||||
$this->reportIfOwned('Preparing audio file…', 15);
|
||||
|
||||
try {
|
||||
$text = $transcription->transcribe(
|
||||
$this->recording,
|
||||
fn (string $message, int $percent) => $this->recording->reportProgress($message, $percent),
|
||||
function (string $message, int $percent): void {
|
||||
$this->reportIfOwned($message, $percent);
|
||||
},
|
||||
);
|
||||
|
||||
$this->recording->reportProgress('Saving transcript…', 90);
|
||||
if (! $this->recording->ownsTranscriptionRun($this->runStartedAt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->reportIfOwned('Saving transcript…', 90);
|
||||
|
||||
if (! $this->recording->ownsTranscriptionRun($this->runStartedAt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->recording->forceFill([
|
||||
'transcript' => $text,
|
||||
@@ -58,18 +95,17 @@ class TranscribeRecording implements ShouldQueue
|
||||
'transcribed_at' => now(),
|
||||
])->save();
|
||||
} catch (Throwable $e) {
|
||||
if (! $this->recording->ownsTranscriptionRun($this->runStartedAt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log::error('Transcription failed', [
|
||||
'recording_id' => $this->recording->id,
|
||||
'driver' => $this->recording->transcription_driver,
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
$this->recording->forceFill([
|
||||
'transcription_status' => 'failed',
|
||||
'transcription_progress' => 'Transcription failed',
|
||||
'transcription_percent' => $this->recording->transcription_percent ?: 0,
|
||||
'transcription_error' => $e->getMessage(),
|
||||
])->save();
|
||||
$this->recording->markTranscriptionFailed($e->getMessage());
|
||||
|
||||
throw $e;
|
||||
}
|
||||
@@ -80,10 +116,23 @@ class TranscribeRecording implements ShouldQueue
|
||||
*/
|
||||
public function failed(?Throwable $e): void
|
||||
{
|
||||
$this->recording->forceFill([
|
||||
'transcription_status' => 'failed',
|
||||
'transcription_progress' => 'Transcription failed',
|
||||
'transcription_error' => $e?->getMessage() ?: 'Transcription stopped unexpectedly.',
|
||||
])->save();
|
||||
$recording = Recording::query()->find($this->recording->id);
|
||||
|
||||
if ($recording === null || ! $recording->ownsTranscriptionRun($this->runStartedAt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$recording->markTranscriptionFailed(
|
||||
$e?->getMessage() ?: 'Transcription stopped unexpectedly.',
|
||||
);
|
||||
}
|
||||
|
||||
private function reportIfOwned(string $message, int $percent): void
|
||||
{
|
||||
if (! $this->recording->ownsTranscriptionRun($this->runStartedAt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->recording->reportProgress($message, $percent);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user