/* eslint-disable no-console,@typescript-eslint/no-explicit-any */ import { NextResponse } from "next/server"; import { getConfig } from "@/lib/config"; export const runtime = 'nodejs'; export async function GET(request: Request) { const { searchParams } = new URL(request.url); const url = searchParams.get('url'); const source = searchParams.get('moontv-source'); if (!url) { return NextResponse.json({ error: 'Missing url' }, { status: 400 }); } const config = await getConfig(); const liveSource = config.LiveConfig?.find((s: any) => s.key === source); if (!liveSource) { return NextResponse.json({ error: 'Source not found' }, { status: 404 }); } const ua = liveSource.ua || 'AptvPlayer/1.4.10'; let response: Response | null = null; let reader: ReadableStreamDefaultReader | null = null; try { const decodedUrl = decodeURIComponent(url); response = await fetch(decodedUrl, { headers: { 'User-Agent': ua, }, }); if (!response.ok) { return NextResponse.json({ error: 'Failed to fetch segment' }, { status: 500 }); } const headers = new Headers(); headers.set('Content-Type', 'video/mp2t'); headers.set('Access-Control-Allow-Origin', '*'); headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); headers.set('Access-Control-Allow-Headers', 'Content-Type, Range, Origin, Accept'); headers.set('Accept-Ranges', 'bytes'); headers.set('Access-Control-Expose-Headers', 'Content-Length, Content-Range'); // 使用流式传输,避免占用内存 const stream = new ReadableStream({ start(controller) { if (!response?.body) { controller.close(); return; } reader = response.body.getReader(); const isCancelled = false; function pump() { if (isCancelled || !reader) { return; } reader.read().then(({ done, value }) => { if (isCancelled) { return; } if (done) { controller.close(); cleanup(); return; } controller.enqueue(value); pump(); }).catch((error) => { if (!isCancelled) { controller.error(error); cleanup(); } }); } function cleanup() { if (reader) { try { reader.releaseLock(); } catch (e) { // reader 可能已经被释放,忽略错误 } reader = null; } } pump(); }, cancel() { // 当流被取消时,确保释放所有资源 if (reader) { try { reader.releaseLock(); } catch (e) { // reader 可能已经被释放,忽略错误 } reader = null; } if (response?.body) { try { response.body.cancel(); } catch (e) { // 忽略取消时的错误 } } } }); return new Response(stream, { headers }); } catch (error) { // 确保在错误情况下也释放资源 if (reader) { try { (reader as ReadableStreamDefaultReader).releaseLock(); } catch (e) { // 忽略错误 } } if (response?.body) { try { response.body.cancel(); } catch (e) { // 忽略错误 } } return NextResponse.json({ error: 'Failed to fetch segment' }, { status: 500 }); } }