import { NextResponse } from 'next/server'; import fs from 'fs-extra'; import mime from 'mime'; export async function GET(request: Request) { const { searchParams } = new URL(request.url); const filePath = searchParams.get('path'); if (!filePath) { return NextResponse.json({ error: 'Path is required' }, { status: 400 }); } try { if (!await fs.pathExists(filePath)) { return NextResponse.json({ error: 'File not found' }, { status: 404 }); } const stat = await fs.stat(filePath); if (!stat.isFile()) { return NextResponse.json({ error: 'Not a file' }, { status: 400 }); } const fileSize = stat.size; const range = request.headers.get('range'); const contentType = mime.getType(filePath) || 'application/octet-stream'; if (range) { const parts = range.replace(/bytes=/, "").split("-"); const start = parseInt(parts[0], 10); const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1; const chunksize = (end - start) + 1; const file = fs.createReadStream(filePath, { start, end }); // Convert stream to ReadableStream for NextResponse const stream = new ReadableStream({ start(controller) { file.on('data', (chunk) => { try { controller.enqueue(chunk); } catch (e) { // Controller likely closed, stop reading file.destroy(); } }); file.on('end', () => { try { controller.close(); } catch (e) { } }); file.on('error', (err) => { try { controller.error(err); } catch (e) { } }); }, cancel() { file.destroy(); } }); return new NextResponse(stream, { status: 206, headers: { 'Content-Range': `bytes ${start}-${end}/${fileSize}`, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize.toString(), 'Content-Type': contentType, }, }); } else { const file = fs.createReadStream(filePath); // Convert stream to ReadableStream for NextResponse const stream = new ReadableStream({ start(controller) { file.on('data', (chunk) => { try { controller.enqueue(chunk); } catch (e) { file.destroy(); } }); file.on('end', () => { try { controller.close(); } catch (e) { } }); file.on('error', (err) => { try { controller.error(err); } catch (e) { } }); }, cancel() { file.destroy(); } }); return new NextResponse(stream, { headers: { 'Content-Length': fileSize.toString(), 'Content-Type': contentType, }, }); } } catch (error) { console.error('Error serving audio file:', error); return NextResponse.json({ error: 'Failed to serve file' }, { status: 500 }); } }