From a7696429241cb587de66de19289d258c6c5fc69f Mon Sep 17 00:00:00 2001 From: mtvpls Date: Sun, 26 Apr 2026 21:29:22 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BD=91=E7=9B=98=E4=BB=A3=E7=90=86=E5=86=85?= =?UTF-8?q?=E5=AD=98=E6=B3=84=E6=BC=8F=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/netdisk/baidu/proxy/route.ts | 101 +++++++++++++++++------ src/app/api/netdisk/quark/proxy/route.ts | 99 ++++++++++++++++------ 2 files changed, 149 insertions(+), 51 deletions(-) diff --git a/src/app/api/netdisk/baidu/proxy/route.ts b/src/app/api/netdisk/baidu/proxy/route.ts index 58db4ac..a0130fe 100644 --- a/src/app/api/netdisk/baidu/proxy/route.ts +++ b/src/app/api/netdisk/baidu/proxy/route.ts @@ -33,34 +33,83 @@ export async function GET(request: NextRequest) { const { url, headers } = await getBaiduDirectPlayUrl(session.meta, file.fid, cookie); const range = request.headers.get('range'); - const upstream = await fetch(url, { - headers: { - ...headers, - Cookie: cookie, - ...(range ? { Range: range } : {}), - }, - cache: 'no-store', - }); + const abortController = new AbortController(); + const timeoutId = setTimeout(() => abortController.abort(), 300000); - if (!upstream.ok || !upstream.body) { - return NextResponse.json( - { error: `百度网盘视频代理失败 (${upstream.status})` }, - { status: upstream.status || 500 } - ); + try { + const upstream = await fetch(url, { + headers: { + ...headers, + Cookie: cookie, + ...(range ? { Range: range } : {}), + }, + cache: 'no-store', + signal: abortController.signal, + }); + + clearTimeout(timeoutId); + + if (!upstream.ok || !upstream.body) { + return NextResponse.json( + { error: `百度网盘视频代理失败 (${upstream.status})` }, + { status: upstream.status || 500 } + ); + } + + const responseHeaders = new Headers(); + const copyHeaders = ['content-type', 'content-length', 'content-range', 'accept-ranges', 'etag', 'last-modified']; + copyHeaders.forEach((name) => { + const value = upstream.headers.get(name); + if (value) responseHeaders.set(name, value); + }); + responseHeaders.set('Cache-Control', 'private, no-store'); + + const { readable, writable } = new TransformStream(); + const reader = upstream.body.getReader(); + + void (async () => { + const writer = writable.getWriter(); + try { + let streamDone = false; + while (!streamDone) { + const { done, value } = await reader.read(); + if (done) { + streamDone = true; + } else { + await writer.write(value); + } + } + } catch { + try { + await reader.cancel(); + } catch { + void 0; + } + } finally { + try { + reader.releaseLock(); + } catch { + void 0; + } + try { + await writer.close(); + } catch { + void 0; + } + } + })(); + + return new Response(readable, { + status: range && upstream.headers.get('content-range') ? 206 : upstream.status, + headers: responseHeaders, + }); + } catch (error) { + clearTimeout(timeoutId); + if (error instanceof Error && error.name === 'AbortError') { + return NextResponse.json({ error: '百度网盘代理超时' }, { status: 504 }); + } + throw error; } - - const responseHeaders = new Headers(); - const copyHeaders = ['content-type', 'content-length', 'content-range', 'accept-ranges', 'etag', 'last-modified']; - copyHeaders.forEach((name) => { - const value = upstream.headers.get(name); - if (value) responseHeaders.set(name, value); - }); - responseHeaders.set('Cache-Control', 'private, no-store'); - - return new Response(upstream.body, { - status: range && upstream.headers.get('content-range') ? 206 : 200, - headers: responseHeaders, - }); } catch (error) { return NextResponse.json( { error: error instanceof Error ? error.message : '百度网盘代理失败' }, diff --git a/src/app/api/netdisk/quark/proxy/route.ts b/src/app/api/netdisk/quark/proxy/route.ts index 16792e2..7cf7ceb 100644 --- a/src/app/api/netdisk/quark/proxy/route.ts +++ b/src/app/api/netdisk/quark/proxy/route.ts @@ -59,33 +59,82 @@ export async function GET(request: NextRequest) { } const range = request.headers.get('range'); - const upstream = await fetch(selected.url, { - headers: { - ...getQuarkPlayHeaders(cookie), - ...(range ? { Range: range } : {}), - }, - cache: 'no-store', - }); + const abortController = new AbortController(); + const timeoutId = setTimeout(() => abortController.abort(), 300000); - if (!upstream.ok || !upstream.body) { - return NextResponse.json( - { error: `夸克视频代理失败 (${upstream.status})` }, - { status: upstream.status || 500 } - ); + try { + const upstream = await fetch(selected.url, { + headers: { + ...getQuarkPlayHeaders(cookie), + ...(range ? { Range: range } : {}), + }, + cache: 'no-store', + signal: abortController.signal, + }); + + clearTimeout(timeoutId); + + if (!upstream.ok || !upstream.body) { + return NextResponse.json( + { error: `夸克视频代理失败 (${upstream.status})` }, + { status: upstream.status || 500 } + ); + } + + const responseHeaders = new Headers(); + const copyHeaders = ['content-type', 'content-length', 'content-range', 'accept-ranges', 'etag', 'last-modified']; + copyHeaders.forEach((name) => { + const value = upstream.headers.get(name); + if (value) responseHeaders.set(name, value); + }); + responseHeaders.set('Cache-Control', 'private, no-store'); + + const { readable, writable } = new TransformStream(); + const reader = upstream.body.getReader(); + + void (async () => { + const writer = writable.getWriter(); + try { + let streamDone = false; + while (!streamDone) { + const { done, value } = await reader.read(); + if (done) { + streamDone = true; + } else { + await writer.write(value); + } + } + } catch { + try { + await reader.cancel(); + } catch { + void 0; + } + } finally { + try { + reader.releaseLock(); + } catch { + void 0; + } + try { + await writer.close(); + } catch { + void 0; + } + } + })(); + + return new Response(readable, { + status: range && upstream.headers.get('content-range') ? 206 : upstream.status, + headers: responseHeaders, + }); + } catch (error) { + clearTimeout(timeoutId); + if (error instanceof Error && error.name === 'AbortError') { + return NextResponse.json({ error: '夸克网盘代理超时' }, { status: 504 }); + } + throw error; } - - const responseHeaders = new Headers(); - const copyHeaders = ['content-type', 'content-length', 'content-range', 'accept-ranges', 'etag', 'last-modified']; - copyHeaders.forEach((name) => { - const value = upstream.headers.get(name); - if (value) responseHeaders.set(name, value); - }); - responseHeaders.set('Cache-Control', 'private, no-store'); - - return new Response(upstream.body, { - status: range && upstream.headers.get('content-range') ? 206 : 200, - headers: responseHeaders, - }); } catch (error) { return NextResponse.json( { error: error instanceof Error ? error.message : '夸克网盘代理失败' },