feat(api): capture routes YouTube/Vimeo URLs to ingest.video
POST /api/capture with a youtube.com / youtu.be / vimeo.com URL enqueues ingest.video (Python worker) instead of ingest.url (Node worker). Detection by URL hostname; idempotency_key + response shape unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,13 @@ function key(space_id, url) {
|
|||||||
return crypto.createHash('sha256').update(space_id + '\x00' + url).digest('hex');
|
return crypto.createHash('sha256').update(space_id + '\x00' + url).digest('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VIDEO_HOST_RE = /(^|\.)(youtube\.com|youtu\.be|vimeo\.com)$/i;
|
||||||
|
|
||||||
|
function isVideoUrl(url) {
|
||||||
|
try { return VIDEO_HOST_RE.test(new URL(url).hostname); }
|
||||||
|
catch { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
export const router = Router();
|
export const router = Router();
|
||||||
|
|
||||||
router.post('/',
|
router.post('/',
|
||||||
@@ -46,7 +53,8 @@ router.post('/',
|
|||||||
job_id: null, idempotency_key: idem, ref_id: existing.id
|
job_id: null, idempotency_key: idem, ref_id: existing.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const job_id = await queue.enqueue('ingest.url', { space_id, url });
|
const job_name = isVideoUrl(url) ? 'ingest.video' : 'ingest.url';
|
||||||
|
const job_id = await queue.enqueue(job_name, { space_id, url });
|
||||||
res.status(202).json({ job_id, idempotency_key: idem });
|
res.status(202).json({ job_id, idempotency_key: idem });
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -69,4 +69,25 @@ describe('capture api', () => {
|
|||||||
.send({ space_id: sp.id, url: 'https://example.com/a' });
|
.send({ space_id: sp.id, url: 'https://example.com/a' });
|
||||||
expect(res.status).toBe(401);
|
expect(res.status).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('POST /api/capture with YouTube URL enqueues ingest.video', async () => {
|
||||||
|
const res = await request(app).post('/api/capture').set(ownerHeaders)
|
||||||
|
.send({ space_id: sp.id, url: 'https://youtu.be/abc' });
|
||||||
|
expect(res.status).toBe(202);
|
||||||
|
expect(res.body.job_id).toBeTruthy();
|
||||||
|
const { default: jobsRepo } = { default: await import('../../lib/db/repos/jobs.js') };
|
||||||
|
const rows = await jobsRepo.list({ name: 'ingest.video' });
|
||||||
|
expect(rows.find(r => r.id === res.body.job_id)).toBeTruthy();
|
||||||
|
const urlRows = await jobsRepo.list({ name: 'ingest.url' });
|
||||||
|
expect(urlRows.find(r => r.id === res.body.job_id)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('POST /api/capture with vimeo URL enqueues ingest.video', async () => {
|
||||||
|
const res = await request(app).post('/api/capture').set(ownerHeaders)
|
||||||
|
.send({ space_id: sp.id, url: 'https://vimeo.com/123' });
|
||||||
|
expect(res.status).toBe(202);
|
||||||
|
const { default: jobsRepo } = { default: await import('../../lib/db/repos/jobs.js') };
|
||||||
|
const rows = await jobsRepo.list({ name: 'ingest.video' });
|
||||||
|
expect(rows.find(r => r.id === res.body.job_id)).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user