48 lines
1.8 KiB
TypeScript
48 lines
1.8 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { ConcurrencyLimitError, transcodeOrchestrator, TranscodeDisabledError } from '@/lib/transcode/orchestrator';
|
|
|
|
export const runtime = 'nodejs';
|
|
export const dynamic = 'force-dynamic';
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const body = await request.json();
|
|
const mediaId = Number(body.mediaId);
|
|
const startTime = Number(body.startTime || 0);
|
|
const profile = typeof body.profile === 'string' ? body.profile : undefined;
|
|
|
|
if (!Number.isInteger(mediaId) || mediaId <= 0) {
|
|
return NextResponse.json({ error: 'Invalid mediaId' }, { status: 400 });
|
|
}
|
|
|
|
if (!Number.isFinite(startTime) || startTime < 0) {
|
|
return NextResponse.json({ error: 'Invalid startTime' }, { status: 400 });
|
|
}
|
|
|
|
const job = await transcodeOrchestrator.start(mediaId, profile, startTime);
|
|
return NextResponse.json({
|
|
jobId: job.id,
|
|
playlistUrl: `/api/transcode/${job.id}/master.m3u8`,
|
|
etaSeconds: 1,
|
|
profile: job.profile,
|
|
decision: job.decision.kind,
|
|
duration: job.knownDuration,
|
|
startTime: job.startTime,
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof TranscodeDisabledError) {
|
|
return NextResponse.json({ error: error.message, code: 'TRANSCODE_DISABLED' }, { status: 403 });
|
|
}
|
|
|
|
if (error instanceof ConcurrencyLimitError) {
|
|
return NextResponse.json(
|
|
{ error: error.message, code: 'TRANSCODE_BUSY', retryAfterSeconds: error.retryAfterSeconds },
|
|
{ status: 503, headers: { 'Retry-After': error.retryAfterSeconds.toString() } },
|
|
);
|
|
}
|
|
|
|
console.error('[TranscodeAPI] start failed:', error);
|
|
return NextResponse.json({ error: error instanceof Error ? error.message : 'Failed to start transcode' }, { status: 500 });
|
|
}
|
|
}
|