Add resumable bulk lyrics operations
This commit is contained in:
@@ -17,26 +17,30 @@ export function registerTranscriptionRoutes(app, { db, adminAuth, workerAuth, wo
|
||||
if (id.startsWith('upl_')) return db.getUpload(id);
|
||||
const row = await db.getMedia(id); return row?.status === 'ready' ? row : null;
|
||||
}
|
||||
app.post('/api/admin/transcriptions/:video', adminAuth, async c => {
|
||||
const id = c.req.param('video');
|
||||
if (!ID.test(id)) return c.json({ ok: false, error: 'invalid video id' }, 400);
|
||||
if (!workerEnabled) return c.json({ ok: false, error: 'Whisper is unavailable: the lyrics worker is not configured.' }, 503);
|
||||
async function enqueue(videoId) {
|
||||
const id = String(videoId || '');
|
||||
if (!ID.test(id)) return { status: 400, body: { ok: false, error: 'invalid video id' } };
|
||||
if (!workerEnabled) return { status: 503, body: { ok: false, error: 'Whisper is unavailable: the lyrics worker is not configured.' } };
|
||||
const audio = await source(id);
|
||||
if (!audio) return c.json({ ok: false, error: 'Save this video on the server before transcribing its audio.' }, 409);
|
||||
if (Number(audio.duration) > 3600) return c.json({ ok: false, error: 'Whisper requests are limited to one hour of audio.' }, 422);
|
||||
if (!audio) return { status: 409, body: { ok: false, error: 'Save this video on the server before transcribing its audio.' } };
|
||||
if (Number(audio.duration) > 3600) return { status: 422, body: { ok: false, error: 'Whisper requests are limited to one hour of audio.' } };
|
||||
const notes = await db.getNotes(id);
|
||||
// One active job per song and a bounded queue, including across restarts.
|
||||
const existing = (await sql.execute({ sql: "SELECT * FROM lyric_transcriptions WHERE video_id=? AND status IN ('queued','running')", args: [id] })).rows[0];
|
||||
if (existing) return c.json({ ok: true, job: publicJob(existing) });
|
||||
if (existing) return { status: 200, body: { ok: true, job: publicJob(existing) } };
|
||||
const active = Number((await sql.execute("SELECT COUNT(*) AS n FROM lyric_transcriptions WHERE status IN ('queued','running')")).rows[0].n);
|
||||
if (active >= 25) return c.json({ ok: false, error: 'The transcription queue is full. Try again after a job finishes.' }, 429);
|
||||
if (active >= 25) return { status: 429, body: { ok: false, error: 'The transcription queue is full. Try again after a job finishes.' } };
|
||||
const time = now();
|
||||
await sql.execute({ sql: `INSERT OR IGNORE INTO lyric_transcriptions (id,video_id,base_rev,created_at,updated_at)
|
||||
SELECT ?,?,?,?,? WHERE (SELECT COUNT(*) FROM lyric_transcriptions WHERE status IN ('queued','running')) < 25`,
|
||||
args: [randomUUID(), id, notes.lyrics?.rev || 0, time, time] });
|
||||
const row = (await sql.execute({ sql: "SELECT * FROM lyric_transcriptions WHERE video_id=? AND status IN ('queued','running')", args: [id] })).rows[0];
|
||||
if (row) await audit(db, 'admin', 'lyrics.whisper.enqueue', id, { jobId: row.id });
|
||||
return row ? c.json({ ok: true, job: publicJob(row) }, 202) : c.json({ ok: false, error: 'The transcription queue is full.' }, 429);
|
||||
return row ? { status: 202, body: { ok: true, job: publicJob(row) } } : { status: 429, body: { ok: false, error: 'The transcription queue is full.' } };
|
||||
}
|
||||
app.post('/api/admin/transcriptions/:video', adminAuth, async c => {
|
||||
const result = await enqueue(c.req.param('video'));
|
||||
return c.json(result.body, result.status);
|
||||
});
|
||||
app.get('/api/admin/transcriptions/:video', adminAuth, async c => {
|
||||
const id = c.req.param('video');
|
||||
@@ -78,4 +82,5 @@ export function registerTranscriptionRoutes(app, { db, adminAuth, workerAuth, wo
|
||||
await sql.execute({ sql: "DELETE FROM lyric_transcriptions WHERE status IN ('complete','failed') AND updated_at<?", args: [time - 30 * 86400000] });
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
return { enqueue };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user