// /api/videos/hls/[...path]/route.ts // This endpoint serves HLS playlists and segments from the HLS directory import { NextResponse } from 'next/server'; import * as fs from 'fs'; import * as path from 'path'; // Path to HLS directory (should match UPLOADS_DIR/hls) const HLS_DIR = path.join(process.env.UPLOADS_DIR || '/uploads', 'hls'); export async function GET( request: Request, context: { params: Promise<{ path?: string[] }> } ) { try { const params = await context.params; const pathSegments = params?.path || []; if (pathSegments.length === 0) { return NextResponse.json( { error: 'Invalid HLS request' }, { status: 400 } ); } // Construct the file path and validate it const requestedPath = path.join(HLS_DIR, ...pathSegments); // Security: prevent directory traversal attacks if (!requestedPath.startsWith(HLS_DIR)) { return NextResponse.json( { error: 'Invalid request' }, { status: 403 } ); } // Check if file exists if (!fs.existsSync(requestedPath)) { console.log(`[HLS] File not found: ${requestedPath}`); return NextResponse.json( { error: 'Not found' }, { status: 404 } ); } // Read the file const fileContent = fs.readFileSync(requestedPath); // Determine content type based on file extension let contentType = 'application/octet-stream'; if (requestedPath.endsWith('.m3u8')) { contentType = 'application/vnd.apple.mpegurl'; } else if (requestedPath.endsWith('.ts')) { contentType = 'video/mp2t'; } else if (requestedPath.endsWith('.mp4')) { contentType = 'video/mp4'; } // Return the file with appropriate headers return new NextResponse(fileContent, { status: 200, headers: { 'Content-Type': contentType, 'Cache-Control': 'public, max-age=3600', // Cache for 1 hour 'Access-Control-Allow-Origin': '*', // Allow CORS if needed 'Accept-Ranges': 'bytes', }, }); } catch (error) { console.error('[HLS] Error serving HLS file:', error); return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ); } } // HEAD request support for playlist validation export async function HEAD( request: Request, context: { params: Promise<{ path?: string[] }> } ) { try { const params = await context.params; const pathSegments = params?.path || []; if (pathSegments.length === 0) { return new NextResponse(null, { status: 400 }); } const requestedPath = path.join(HLS_DIR, ...pathSegments); // Security: prevent directory traversal attacks if (!requestedPath.startsWith(HLS_DIR)) { return new NextResponse(null, { status: 403 }); } // Check if file exists if (!fs.existsSync(requestedPath)) { return new NextResponse(null, { status: 404 }); } return new NextResponse(null, { status: 200 }); } catch (error) { console.error('[HLS] Error in HEAD request:', error); return new NextResponse(null, { status: 500 }); } }