尝试修复emby代理内存泄漏
This commit is contained in:
@@ -84,10 +84,19 @@ export async function GET(
|
|||||||
'User-Agent': client.getUserAgent(),
|
'User-Agent': client.getUserAgent(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 请求图片
|
// 创建 AbortController 用于超时控制
|
||||||
const imageResponse = await fetch(imageUrl, {
|
const abortController = new AbortController();
|
||||||
headers: requestHeaders,
|
const timeoutId = setTimeout(() => abortController.abort(), 20000); // 20秒超时
|
||||||
});
|
|
||||||
|
try {
|
||||||
|
// 请求图片
|
||||||
|
const imageResponse = await fetch(imageUrl, {
|
||||||
|
headers: requestHeaders,
|
||||||
|
signal: abortController.signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 清除超时定时器
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
if (!imageResponse.ok) {
|
if (!imageResponse.ok) {
|
||||||
console.error('[Emby Image] 获取图片失败:', {
|
console.error('[Emby Image] 获取图片失败:', {
|
||||||
@@ -123,6 +132,19 @@ export async function GET(
|
|||||||
status: imageResponse.status,
|
status: imageResponse.status,
|
||||||
headers,
|
headers,
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// 清除超时定时器
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (error instanceof Error && error.name === 'AbortError') {
|
||||||
|
console.error('[Emby Image] 请求超时');
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '请求超时' },
|
||||||
|
{ status: 504 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Emby Image] 错误:', error);
|
console.error('[Emby Image] 错误:', error);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
@@ -92,22 +92,42 @@ export async function GET(
|
|||||||
requestHeaders['Range'] = rangeHeader;
|
requestHeaders['Range'] = rangeHeader;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 流式代理视频内容
|
// 创建 AbortController 用于超时控制
|
||||||
let videoResponse = await fetch(embyStreamUrl, {
|
const abortController = new AbortController();
|
||||||
headers: requestHeaders,
|
const timeoutId = setTimeout(() => abortController.abort(), 300000); // 5分钟超时
|
||||||
});
|
|
||||||
|
|
||||||
// 如果返回 401,尝试重新认证并重试
|
try {
|
||||||
if (videoResponse.status === 401) {
|
// 流式代理视频内容
|
||||||
console.log('[Emby Play] 收到 401 错误,尝试重新认证');
|
let videoResponse = await fetch(embyStreamUrl, {
|
||||||
const { embyManager } = await import('@/lib/emby-manager');
|
|
||||||
embyManager.clearCache();
|
|
||||||
client = await getEmbyClient(embyKey);
|
|
||||||
embyStreamUrl = await client.getStreamUrl(itemId, true, true);
|
|
||||||
videoResponse = await fetch(embyStreamUrl, {
|
|
||||||
headers: requestHeaders,
|
headers: requestHeaders,
|
||||||
|
signal: abortController.signal,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
// 如果返回 401,尝试重新认证并重试
|
||||||
|
if (videoResponse.status === 401) {
|
||||||
|
console.log('[Emby Play] 收到 401 错误,尝试重新认证');
|
||||||
|
const { embyManager } = await import('@/lib/emby-manager');
|
||||||
|
embyManager.clearCache();
|
||||||
|
client = await getEmbyClient(embyKey);
|
||||||
|
embyStreamUrl = await client.getStreamUrl(itemId, true, true);
|
||||||
|
|
||||||
|
// 重置超时
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
const retryAbortController = new AbortController();
|
||||||
|
const retryTimeoutId = setTimeout(() => retryAbortController.abort(), 300000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
videoResponse = await fetch(embyStreamUrl, {
|
||||||
|
headers: requestHeaders,
|
||||||
|
signal: retryAbortController.signal,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
clearTimeout(retryTimeoutId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除超时定时器
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
if (!videoResponse.ok) {
|
if (!videoResponse.ok) {
|
||||||
console.error('[Emby Play] 获取视频流失败:', {
|
console.error('[Emby Play] 获取视频流失败:', {
|
||||||
@@ -147,11 +167,64 @@ export async function GET(
|
|||||||
// 使用 URL 中的文件名
|
// 使用 URL 中的文件名
|
||||||
headers.set('Content-Disposition', `inline; filename="${params.filename}"`);
|
headers.set('Content-Disposition', `inline; filename="${params.filename}"`);
|
||||||
|
|
||||||
// 流式返回视频内容,不等待下载完成
|
// 创建一个可以被中断的流
|
||||||
return new NextResponse(videoResponse.body, {
|
const { readable, writable } = new TransformStream();
|
||||||
|
const reader = videoResponse.body?.getReader();
|
||||||
|
|
||||||
|
if (!reader) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '无法读取视频流' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步管道传输,确保在客户端断开时清理资源
|
||||||
|
(async () => {
|
||||||
|
const writer = writable.getWriter();
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
await writer.write(value);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 客户端断开连接或其他错误
|
||||||
|
console.log('[Emby Play] 流传输中断:', error instanceof Error ? error.message : 'Unknown error');
|
||||||
|
// 取消上游 fetch,停止继续下载
|
||||||
|
try {
|
||||||
|
await reader.cancel();
|
||||||
|
} catch (e) {
|
||||||
|
// 忽略取消错误
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// 确保资源被释放
|
||||||
|
try {
|
||||||
|
reader.releaseLock();
|
||||||
|
await writer.close();
|
||||||
|
} catch (e) {
|
||||||
|
// 忽略关闭错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
// 流式返回视频内容
|
||||||
|
return new NextResponse(readable, {
|
||||||
status: videoResponse.status,
|
status: videoResponse.status,
|
||||||
headers,
|
headers,
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// 清除超时定时器
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (error instanceof Error && error.name === 'AbortError') {
|
||||||
|
console.error('[Emby Play] 请求超时');
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '请求超时' },
|
||||||
|
{ status: 504 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Emby Play] 错误:', error);
|
console.error('[Emby Play] 错误:', error);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
Reference in New Issue
Block a user