nextav/src/app/api/transcode/[jobId]/seg/[name]/route.ts

47 lines
1.5 KiB
TypeScript

import fs from 'fs';
import { NextResponse } from 'next/server';
import { transcodeOrchestrator } from '@/lib/transcode/orchestrator';
import { segmentPath } from '@/lib/transcode/playlist';
export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';
async function waitForSegment(filePath: string, timeoutMs = 5000): Promise<boolean> {
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
if (fs.existsSync(filePath)) {
const stat = fs.statSync(filePath);
if (stat.size > 0) return true;
}
await new Promise(resolve => setTimeout(resolve, 150));
}
return false;
}
export async function GET(_request: Request, { params }: { params: Promise<{ jobId: string; name: string }> }) {
const { jobId, name } = await params;
const job = transcodeOrchestrator.get(jobId);
if (!job) {
return NextResponse.json({ error: 'Transcode job not found' }, { status: 404 });
}
const resolvedPath = segmentPath(job.outDir, name);
if (!resolvedPath || !(await waitForSegment(resolvedPath))) {
return new Response(null, { status: 404 });
}
const stat = fs.statSync(resolvedPath);
const stream = fs.createReadStream(resolvedPath);
return new Response(stream as any, {
status: 200,
headers: {
'Content-Type': 'video/mp2t',
'Content-Length': stat.size.toString(),
'Cache-Control': 'private, max-age=3600',
'ETag': `"${jobId}-${name}-${stat.size}-${stat.mtimeMs}"`,
'Access-Control-Allow-Origin': '*',
},
});
}