All checks were successful
Build & Push Docker Image / build-and-push (push) Successful in 48s
- Add cancelJob() to whisper.ts: sends DELETE /jobs/:id to the whisper server (best-effort, errors silently ignored) - DELETE /api/jobs/[id] now calls cancelJob() when cancelling an active job that has a whisperJobId, stopping GPU use immediately - Webhook handler guards against locally-cancelled jobs: returns ok early so whisper's late completion cannot overwrite cancelled status or send a phantom 'Transcript ready' notification - Replace blind sleep(Retry-After + 1s) in submitJob() with waitForModelReady(): subscribes to /model/events SSE and proceeds as soon as state:ready arrives; falls back to the Retry-After timeout if SSE is unreachable or closes without model_ready - Refactor retry tests to use URL-aware makeJobFetch() helper; add 7 new tests (3 SSE-triggered retry, 3 cancelJob, 1 webhook cancelled-guard) — 144/144 passing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
60 lines
2.0 KiB
TypeScript
60 lines
2.0 KiB
TypeScript
import { json, error } from '@sveltejs/kit';
|
|
import { getJob, updateJob, setJobStatus } from '$lib/server/db.js';
|
|
import { deduplicateSegments } from '$lib/server/postprocess.js';
|
|
import { writeOutputs } from '$lib/server/formatter.js';
|
|
import { sendNotification } from '$lib/server/push.js';
|
|
import { cleanupJobTmp } from '$lib/server/downloader.js';
|
|
import { emitProgress } from '$lib/server/pipeline.js';
|
|
import type { Segment, WhisperJob } from '$lib/types.js';
|
|
|
|
export async function POST({ params, request }) {
|
|
const jobId = params.jobId;
|
|
const job = getJob(jobId);
|
|
if (!job) throw error(404, 'Job not found');
|
|
|
|
// Discard the result if the job was cancelled locally while whisper was running
|
|
if (job.status === 'cancelled') {
|
|
return json({ ok: true });
|
|
}
|
|
|
|
const whisperJob = (await request.json()) as WhisperJob;
|
|
|
|
if (whisperJob.status === 'failed' || whisperJob.status === 'cancelled') {
|
|
const msg = whisperJob.error ?? `Whisper job ${whisperJob.status}`;
|
|
updateJob({ id: jobId, status: 'failed', error: msg });
|
|
emitProgress(jobId, { type: 'error', message: msg });
|
|
return json({ ok: true });
|
|
}
|
|
|
|
try {
|
|
setJobStatus(jobId, 'processing', 90);
|
|
emitProgress(jobId, { type: 'status', status: 'processing', progress: 90 });
|
|
|
|
const rawSegments = whisperJob.segments as Segment[];
|
|
const segments = deduplicateSegments(rawSegments);
|
|
|
|
const paths = await writeOutputs(segments, job.title, jobId);
|
|
const outputDir = paths.srt.replace(/\/[^/]+$/, '');
|
|
|
|
updateJob({
|
|
id: jobId,
|
|
status: 'done',
|
|
progress: 100,
|
|
segmentsJson: JSON.stringify(segments),
|
|
outputDir
|
|
});
|
|
|
|
emitProgress(jobId, { type: 'done', status: 'done' });
|
|
|
|
await sendNotification(jobId, '✅ Transcript ready', job.title);
|
|
await cleanupJobTmp(jobId);
|
|
|
|
return json({ ok: true });
|
|
} catch (err: unknown) {
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
updateJob({ id: jobId, status: 'failed', error: message });
|
|
emitProgress(jobId, { type: 'error', message });
|
|
return json({ ok: false, error: message }, { status: 500 });
|
|
}
|
|
}
|