nextav/src/app/api/transcode/start/route.ts

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 });
}
}