Files
myplayer-vue/proxy.js
2026-03-23 18:08:59 +08:00

125 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import express from 'express';
import fetch from 'node-fetch';
import { URL } from 'url';
const app = express();
const port = 3000;
// 允许跨域请求
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Range');
next();
});
// 本地静态文件代理:/local -> C:\Users\xyf17\Merlin\data
const localDataPath = 'C:\\Users\\xyf17\\Merlin\\data';
app.use('/local', express.static(localDataPath));
// 通用代理路由
app.get('/proxy', async (req, res) => {
console.log('Received Range header:', req.headers.range);
// 获取目标 URL
const targetUrl = req.query.url;
console.log('Fetching data from:', targetUrl);
if (!targetUrl) {
return res.status(400).json({ error: 'URL parameter is required' });
}
try {
// 设置请求头
const headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
};
// 避免上游压缩导致 Range/MSE 解析异常mp4 一般不会压缩,但这里保守处理)
headers['Accept-Encoding'] = 'identity';
if (req.headers.range) {
headers['Range'] = req.headers.range; // 转发 Range 请求头,支持视频分段加载
}
// 向目标 URL 发起请求
const response = await fetch(targetUrl, { headers });
// 检查响应状态
if (!response.ok) {
return res.status(response.status).json({
error: `Failed to fetch: ${response.statusText}`,
status: response.status
});
}
// 关键Range 请求上游通常返回 206必须把状态码透传给浏览器
res.status(response.status);
// 设置响应头
response.headers.forEach((value, key) => {
// 跳过一些可能导致问题的头信息
if (key.toLowerCase() !== 'content-encoding' &&
key.toLowerCase() !== 'transfer-encoding' &&
key.toLowerCase() !== 'connection') {
res.setHeader(key, value);
}
});
// 特殊处理 Transfer-Encoding 和 Content-Length
if (response.headers.get('transfer-encoding') === 'chunked') {
res.removeHeader('Content-Length');
}
// 将响应体直接流式传输给客户端
if (response.body) {
response.body.pipe(res, { end: true });
// 错误处理
response.body.on('error', (err) => {
console.error('Error during data transfer:', err);
if (!res.headersSent) {
res.status(500).json({ error: 'Error during data transfer' });
}
});
} else {
res.status(500).json({ error: 'No response body' });
}
} catch (error) {
console.error('Error fetching data:', error);
// 根据错误类型返回更具体的错误信息
let errorMessage = 'Error fetching data';
let statusCode = 500;
let hostname = '';
try {
hostname = new URL(targetUrl).hostname;
} catch (e) {
hostname = 'unknown';
}
if (error.code === 'ENOTFOUND') {
errorMessage = `DNS解析失败无法解析域名 "${hostname}"。请检查URL是否正确或网络连接是否正常。`;
statusCode = 502;
} else if (error.code === 'ECONNREFUSED') {
errorMessage = `连接被拒绝:无法连接到目标服务器 "${hostname}"。`;
statusCode = 502;
} else if (error.code === 'ETIMEDOUT') {
errorMessage = `请求超时:目标服务器 "${hostname}" 响应时间过长。`;
statusCode = 504;
}
if (!res.headersSent) {
res.status(statusCode).json({
error: errorMessage,
code: error.code,
targetUrl: targetUrl
});
}
}
});
app.listen(port, () => {
console.log(`Proxy server running at http://localhost:${port}`);
});