Upload 33 files
Browse files- .dockerignore +2 -0
- Dockerfile +16 -18
- nginx.conf +2 -2
- package.json +9 -5
- readme.md +174 -0
- server.js +128 -0
.dockerignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.vscode/
|
| 2 |
+
readme.md
|
Dockerfile
CHANGED
|
@@ -1,25 +1,23 @@
|
|
| 1 |
-
FROM
|
| 2 |
-
LABEL maintainer="LibreTV Team"
|
| 3 |
-
LABEL description="LibreTV - 免费在线视频搜索与观看平台"
|
| 4 |
|
| 5 |
-
#
|
| 6 |
-
|
| 7 |
|
| 8 |
-
# 复制
|
| 9 |
-
COPY
|
| 10 |
|
| 11 |
-
#
|
| 12 |
-
|
| 13 |
-
RUN chmod +x /docker-entrypoint.sh
|
| 14 |
|
| 15 |
-
#
|
| 16 |
-
|
| 17 |
|
| 18 |
-
#
|
| 19 |
-
|
| 20 |
|
| 21 |
-
#
|
| 22 |
-
|
|
|
|
| 23 |
|
| 24 |
-
#
|
| 25 |
-
|
|
|
|
| 1 |
+
FROM node:18-alpine
|
|
|
|
|
|
|
| 2 |
|
| 3 |
+
# 创建工作目录
|
| 4 |
+
WORKDIR /app
|
| 5 |
|
| 6 |
+
# 复制 package.json 和 package-lock.json
|
| 7 |
+
COPY package*.json ./
|
| 8 |
|
| 9 |
+
# 安装依赖
|
| 10 |
+
RUN npm install --production
|
|
|
|
| 11 |
|
| 12 |
+
# 复制所有项目文件
|
| 13 |
+
COPY . .
|
| 14 |
|
| 15 |
+
# 暴露端口
|
| 16 |
+
EXPOSE 8080
|
| 17 |
|
| 18 |
+
# 设置环境变量
|
| 19 |
+
ENV PORT=8080
|
| 20 |
+
ENV PASSWORD=111111
|
| 21 |
|
| 22 |
+
# 启动应用
|
| 23 |
+
CMD ["npm", "start"]
|
nginx.conf
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
server {
|
| 2 |
-
listen
|
| 3 |
server_name localhost;
|
| 4 |
|
| 5 |
#access_log /var/log/nginx/host.access.log main;
|
|
@@ -61,7 +61,7 @@ server {
|
|
| 61 |
root /usr/share/nginx/html;
|
| 62 |
}
|
| 63 |
|
| 64 |
-
# proxy the PHP scripts to Apache listening on 127.0.0.1:
|
| 65 |
#
|
| 66 |
#location ~ \.php$ {
|
| 67 |
# proxy_pass http://127.0.0.1;
|
|
|
|
| 1 |
server {
|
| 2 |
+
listen 80;
|
| 3 |
server_name localhost;
|
| 4 |
|
| 5 |
#access_log /var/log/nginx/host.access.log main;
|
|
|
|
| 61 |
root /usr/share/nginx/html;
|
| 62 |
}
|
| 63 |
|
| 64 |
+
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
|
| 65 |
#
|
| 66 |
#location ~ \.php$ {
|
| 67 |
# proxy_pass http://127.0.0.1;
|
package.json
CHANGED
|
@@ -2,14 +2,18 @@
|
|
| 2 |
"name": "libretv",
|
| 3 |
"version": "1.1.0",
|
| 4 |
"description": "免费在线视频搜索与观看平台",
|
| 5 |
-
"
|
| 6 |
-
"type": "module",
|
| 7 |
"scripts": {
|
| 8 |
-
"
|
|
|
|
| 9 |
},
|
| 10 |
"dependencies": {
|
| 11 |
-
"
|
|
|
|
| 12 |
},
|
| 13 |
-
"
|
|
|
|
|
|
|
|
|
|
| 14 |
"license": "MIT"
|
| 15 |
}
|
|
|
|
| 2 |
"name": "libretv",
|
| 3 |
"version": "1.1.0",
|
| 4 |
"description": "免费在线视频搜索与观看平台",
|
| 5 |
+
"main": "server.js",
|
|
|
|
| 6 |
"scripts": {
|
| 7 |
+
"start": "node server.js",
|
| 8 |
+
"dev": "nodemon server.js"
|
| 9 |
},
|
| 10 |
"dependencies": {
|
| 11 |
+
"express": "^4.18.2",
|
| 12 |
+
"http-proxy-middleware": "^2.0.6"
|
| 13 |
},
|
| 14 |
+
"devDependencies": {
|
| 15 |
+
"nodemon": "^3.0.3"
|
| 16 |
+
},
|
| 17 |
+
"author": "LibreTV Team",
|
| 18 |
"license": "MIT"
|
| 19 |
}
|
readme.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# LibreTV - 免费在线视频搜索与观看平台
|
| 2 |
+
|
| 3 |
+
<div align="center">
|
| 4 |
+
<img src="https://images.icon-icons.com/38/PNG/512/retrotv_5520.png" alt="LibreTV Logo" width="120">
|
| 5 |
+
<br>
|
| 6 |
+
<p><strong>自由观影,畅享精彩</strong></p>
|
| 7 |
+
</div>
|
| 8 |
+
|
| 9 |
+
## 📺 项目简介
|
| 10 |
+
|
| 11 |
+
LibreTV 是一个轻量级、免费的在线视频搜索与观看平台,提供来自多个视频源的内容搜索与播放服务。无需注册,即开即用,支持多种设备访问。项目结合了前端技术和后端代理功能,可部署在支持服务端功能的各类网站托管服务上。
|
| 12 |
+
|
| 13 |
+
本项目基于 [bestK/tv](https://github.com/bestK/tv) 进行重构与增强。
|
| 14 |
+
|
| 15 |
+
<details>
|
| 16 |
+
<summary>点击查看项目截图</summary>
|
| 17 |
+
<img src="https://testingcf.jsdelivr.net/gh/bestZwei/imgs@master/picgo/image-20250406231222216.png" alt="项目截图" style="max-width:600px">
|
| 18 |
+
</details>
|
| 19 |
+
|
| 20 |
+
## 🥇 感谢赞助
|
| 21 |
+
|
| 22 |
+
- **[YXVM](https://yxvm.com)**
|
| 23 |
+
- **[VTEXS](https://vtexs.com)**
|
| 24 |
+
|
| 25 |
+
## 🚀 快速部署
|
| 26 |
+
|
| 27 |
+
选择以下任一平台,点击一键部署按钮,即可快速创建自己的 LibreTV 实例:
|
| 28 |
+
|
| 29 |
+
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FLibreSpark%2FLibreTV) [](https://app.netlify.com/start/deploy?repository=https://github.com/LibreSpark/LibreTV)
|
| 30 |
+
|
| 31 |
+
## 📋 详细部署指南
|
| 32 |
+
|
| 33 |
+
### Cloudflare Pages
|
| 34 |
+
|
| 35 |
+
1. Fork 或克隆本仓库到您的 GitHub 账户
|
| 36 |
+
2. 登录 [Cloudflare Dashboard](https://dash.cloudflare.com/),进入 Pages 服务
|
| 37 |
+
3. 点击"创建项目",连接您的 GitHub 仓库
|
| 38 |
+
4. 使用以下设置:
|
| 39 |
+
- 构建命令:留空(无需构建)
|
| 40 |
+
- 输出目录:留空(默认为根目录)
|
| 41 |
+
5. 点击"保存并部署"
|
| 42 |
+
6. 可选:在"设置" > "环境变量"中配置密码保护
|
| 43 |
+
|
| 44 |
+
### Vercel
|
| 45 |
+
|
| 46 |
+
1. Fork 或克隆本仓库到您的 GitHub/GitLab 账户
|
| 47 |
+
2. 登录 [Vercel](https://vercel.com/),点击"New Project"
|
| 48 |
+
3. 导入您的仓库,使用默认设置
|
| 49 |
+
4. 点击"Deploy"
|
| 50 |
+
5. 可选:在"Settings" > "Environment Variables"中配置密码保护
|
| 51 |
+
|
| 52 |
+
### Netlify
|
| 53 |
+
|
| 54 |
+
1. Fork 或克隆本仓库到您的 GitHub 账户
|
| 55 |
+
2. 登录 [Netlify](https://app.netlify.com/)
|
| 56 |
+
3. 点击"New site from Git",选择您的仓库
|
| 57 |
+
4. 构建设置保持默认
|
| 58 |
+
5. 点击"Deploy site"
|
| 59 |
+
6. 可选:在"Site settings" > "Build & deploy" > "Environment"中配置密码保护
|
| 60 |
+
|
| 61 |
+
### Docker
|
| 62 |
+
|
| 63 |
+
使用 Docker 运行 LibreTV:
|
| 64 |
+
|
| 65 |
+
```bash
|
| 66 |
+
docker run -d \
|
| 67 |
+
--name libretv \
|
| 68 |
+
-p 8899:80 \
|
| 69 |
+
-e PASSWORD=your_password_here \
|
| 70 |
+
bestzwei/libretv:latest
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
访问 `http://localhost:8899` 即可使用。
|
| 74 |
+
|
| 75 |
+
### Docker Compose
|
| 76 |
+
|
| 77 |
+
`docker-compose.yml` 文件:
|
| 78 |
+
|
| 79 |
+
```yaml
|
| 80 |
+
version: '3'
|
| 81 |
+
services:
|
| 82 |
+
libretv:
|
| 83 |
+
image: bestzwei/libretv:latest
|
| 84 |
+
container_name: libretv
|
| 85 |
+
ports:
|
| 86 |
+
- "8899:80"
|
| 87 |
+
environment:
|
| 88 |
+
- PASSWORD=111111
|
| 89 |
+
restart: unless-stopped
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
### 本地开发环境
|
| 93 |
+
|
| 94 |
+
项目包含后端代理功能,需要支持服务器端功能的环境:
|
| 95 |
+
|
| 96 |
+
```bash
|
| 97 |
+
# 安装依赖
|
| 98 |
+
npm install
|
| 99 |
+
|
| 100 |
+
# 启动开发服务器
|
| 101 |
+
npm run dev
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
> ⚠️ 注意:使用简单静态服务器(如 `python -m http.server` 或 `npx http-server`)时,视频代理功能将不可用,视频无法正常播放。完整功能测试请使用 Node.js 开发服务器。
|
| 105 |
+
|
| 106 |
+
## 🔧 自定义配置
|
| 107 |
+
|
| 108 |
+
### 密码保护
|
| 109 |
+
|
| 110 |
+
要为您的 LibreTV 实例添加密码保护,可以在部署平台上设置环境变量:
|
| 111 |
+
|
| 112 |
+
**环境变量名**: `PASSWORD`
|
| 113 |
+
**值**: 您想设置的密码
|
| 114 |
+
|
| 115 |
+
各平台设置方法:
|
| 116 |
+
|
| 117 |
+
- **Cloudflare Pages**: Dashboard > 您的项目 > 设置 > 环境变量
|
| 118 |
+
- **Vercel**: Dashboard > 您的项目 > Settings > Environment Variables
|
| 119 |
+
- **Netlify**: Dashboard > 您的项目 > Site settings > Build & deploy > Environment
|
| 120 |
+
- **Docker**: 使用 `-e PASSWORD=your_password` 参数
|
| 121 |
+
|
| 122 |
+
### API兼容性
|
| 123 |
+
|
| 124 |
+
LibreTV 支持标准的苹果 CMS V10 API 格式。添加自定义 API 时需遵循以下格式:
|
| 125 |
+
- 搜索接口: `https://example.com/api.php/provide/vod/?ac=videolist&wd=关键词`
|
| 126 |
+
- 详情接口: `https://example.com/api.php/provide/vod/?ac=detail&ids=视频ID`
|
| 127 |
+
|
| 128 |
+
**添加 CMS 源**:
|
| 129 |
+
1. 在设置面板中选择"自定义接口"
|
| 130 |
+
2. 接口地址只需填写到域名部分: `https://example.com`(不要包含`/api.php/provide/vod`部分)
|
| 131 |
+
|
| 132 |
+
## ⌨️ 键盘快捷键
|
| 133 |
+
|
| 134 |
+
播放器支持以下键盘快捷键:
|
| 135 |
+
|
| 136 |
+
- **空格键**: 播放/暂停
|
| 137 |
+
- **左右箭头**: 快退/快进
|
| 138 |
+
- **上下箭头**: 音量增加/减小
|
| 139 |
+
- **M 键**: 静音/取消静音
|
| 140 |
+
- **F 键**: 全屏/退出全屏
|
| 141 |
+
- **Esc 键**: 退出全屏
|
| 142 |
+
|
| 143 |
+
## 🛠️ 技术栈
|
| 144 |
+
|
| 145 |
+
- HTML5 + CSS3 + JavaScript (ES6+)
|
| 146 |
+
- Tailwind CSS (通过 CDN 引入)
|
| 147 |
+
- HLS.js 用于 HLS 流处理
|
| 148 |
+
- DPlayer 视频播放器核心
|
| 149 |
+
- Cloudflare/Vercel/Netlify Serverless Functions
|
| 150 |
+
- 服务端 HLS 代理和处理技术
|
| 151 |
+
- localStorage 本地存储
|
| 152 |
+
|
| 153 |
+
## 🔄 更新日志
|
| 154 |
+
|
| 155 |
+
<details>
|
| 156 |
+
<summary>点击查看更新日志</summary>
|
| 157 |
+
|
| 158 |
+
- **1.1.2** (2025-04-22): 新增豆瓣热门内容显示,设置中可开关
|
| 159 |
+
- **1.1.1** (2025-04-19):
|
| 160 |
+
- 修复 docker 部署时无法搜索的问题
|
| 161 |
+
- 修复播放页面进度保存与恢复的兼容性问题
|
| 162 |
+
- **1.1.0** (2025-04-17): 添加服务端代理功能,支持 HLS 流处理和解析,支持环境变量设置访问密码
|
| 163 |
+
- **1.0.3** (2025-04-13): 性能优化、UI优化、更新设置功能
|
| 164 |
+
- **1.0.2** (2025-04-08): 分离播放页面,优化视频源 API 兼容性
|
| 165 |
+
- **1.0.1** (2025-04-07): 添加广告过滤功能,优化播放器性能
|
| 166 |
+
- **1.0.0** (2025-04-06): 初始版本发布
|
| 167 |
+
|
| 168 |
+
</details>
|
| 169 |
+
|
| 170 |
+
## ⚠️ 免责声明
|
| 171 |
+
|
| 172 |
+
LibreTV 仅作为视频搜索工具,不存储、上传或分发任何视频内容。所有视频均来自第三方 API 接口提供的搜索结果。如有侵权内容,请联系相应的内容提供方。
|
| 173 |
+
|
| 174 |
+
本项目开发者不对使用本项目产生的任何后果负责。使用本项目时,您必须遵守当地的法律法规。
|
server.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const express = require('express');
|
| 2 |
+
const { createProxyMiddleware } = require('http-proxy-middleware');
|
| 3 |
+
const path = require('path');
|
| 4 |
+
const crypto = require('crypto');
|
| 5 |
+
const fs = require('fs');
|
| 6 |
+
|
| 7 |
+
const app = express();
|
| 8 |
+
const PORT = process.env.PORT || 8080;
|
| 9 |
+
|
| 10 |
+
// 中间件:解析请求体
|
| 11 |
+
app.use(express.json());
|
| 12 |
+
app.use(express.urlencoded({ extended: true }));
|
| 13 |
+
|
| 14 |
+
// 中间件:处理HTML文件中的环境变量注入
|
| 15 |
+
app.use((req, res, next) => {
|
| 16 |
+
if (req.path.endsWith('.html') || req.path === '/' || req.path.endsWith('/')) {
|
| 17 |
+
const filePath = req.path === '/' || req.path.endsWith('/')
|
| 18 |
+
? path.join(__dirname, 'index.html')
|
| 19 |
+
: path.join(__dirname, req.path);
|
| 20 |
+
|
| 21 |
+
if (fs.existsSync(filePath)) {
|
| 22 |
+
let content = fs.readFileSync(filePath, 'utf8');
|
| 23 |
+
|
| 24 |
+
// 替换密码占位符
|
| 25 |
+
const password = process.env.PASSWORD || '';
|
| 26 |
+
let passwordHash = '';
|
| 27 |
+
|
| 28 |
+
if (password) {
|
| 29 |
+
const hash = crypto.createHash('sha256');
|
| 30 |
+
hash.update(password);
|
| 31 |
+
passwordHash = hash.digest('hex');
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
content = content.replace(
|
| 35 |
+
'window.__ENV__.PASSWORD = "{{PASSWORD}}";',
|
| 36 |
+
`window.__ENV__.PASSWORD = "${passwordHash}"; // SHA-256 hash`
|
| 37 |
+
);
|
| 38 |
+
|
| 39 |
+
res.setHeader('Content-Type', 'text/html');
|
| 40 |
+
return res.send(content);
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
next();
|
| 44 |
+
});
|
| 45 |
+
|
| 46 |
+
// 创建代理中间件函数
|
| 47 |
+
function createDynamicProxy(req, res, next) {
|
| 48 |
+
// 从URL参数获取目标URL
|
| 49 |
+
const targetUrl = decodeURIComponent(req.params.url);
|
| 50 |
+
|
| 51 |
+
if (!targetUrl || !targetUrl.match(/^https?:\/\/.+/i)) {
|
| 52 |
+
return res.status(400).json({
|
| 53 |
+
success: false,
|
| 54 |
+
error: '无效的目标URL'
|
| 55 |
+
});
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
// 提取主机和协议
|
| 59 |
+
try {
|
| 60 |
+
const urlObj = new URL(targetUrl);
|
| 61 |
+
const target = `${urlObj.protocol}//${urlObj.host}`;
|
| 62 |
+
const pathToProxy = urlObj.pathname + urlObj.search;
|
| 63 |
+
|
| 64 |
+
// 创建代理
|
| 65 |
+
const proxy = createProxyMiddleware({
|
| 66 |
+
target,
|
| 67 |
+
changeOrigin: true,
|
| 68 |
+
pathRewrite: () => pathToProxy,
|
| 69 |
+
onProxyReq: (proxyReq, req, res) => {
|
| 70 |
+
// 设置请求头
|
| 71 |
+
proxyReq.setHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36');
|
| 72 |
+
proxyReq.setHeader('Accept', req.headers.accept || '*/*');
|
| 73 |
+
proxyReq.setHeader('Accept-Language', req.headers['accept-language'] || 'zh-CN,zh;q=0.9');
|
| 74 |
+
proxyReq.setHeader('Referer', req.headers.referer || target);
|
| 75 |
+
},
|
| 76 |
+
onProxyRes: (proxyRes, req, res) => {
|
| 77 |
+
// 设置CORS头
|
| 78 |
+
proxyRes.headers['Access-Control-Allow-Origin'] = '*';
|
| 79 |
+
proxyRes.headers['Access-Control-Allow-Methods'] = 'GET, HEAD, OPTIONS';
|
| 80 |
+
proxyRes.headers['Access-Control-Allow-Headers'] = '*';
|
| 81 |
+
|
| 82 |
+
// 设置缓存策略
|
| 83 |
+
proxyRes.headers['Cache-Control'] = 'public, max-age=86400';
|
| 84 |
+
|
| 85 |
+
// 移除可能导致问题的头部
|
| 86 |
+
delete proxyRes.headers['content-length'];
|
| 87 |
+
delete proxyRes.headers['content-encoding'];
|
| 88 |
+
}
|
| 89 |
+
});
|
| 90 |
+
|
| 91 |
+
proxy(req, res, next);
|
| 92 |
+
} catch (error) {
|
| 93 |
+
console.error(`代理错误: ${error.message}`);
|
| 94 |
+
return res.status(500).json({
|
| 95 |
+
success: false,
|
| 96 |
+
error: `代理请求失败: ${error.message}`
|
| 97 |
+
});
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
// 设置代理路由
|
| 102 |
+
app.use('/proxy/:url(*)', createDynamicProxy);
|
| 103 |
+
|
| 104 |
+
// OPTIONS请求处理
|
| 105 |
+
app.options('/proxy/:url(*)', (req, res) => {
|
| 106 |
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
| 107 |
+
res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
|
| 108 |
+
res.setHeader('Access-Control-Allow-Headers', '*');
|
| 109 |
+
res.setHeader('Access-Control-Max-Age', '86400');
|
| 110 |
+
res.status(204).end();
|
| 111 |
+
});
|
| 112 |
+
|
| 113 |
+
// 静态文件服务 - 所有其他请求
|
| 114 |
+
app.use(express.static(path.join(__dirname), {
|
| 115 |
+
maxAge: '1d'
|
| 116 |
+
}));
|
| 117 |
+
|
| 118 |
+
// 错误处理中间件
|
| 119 |
+
app.use((err, req, res, next) => {
|
| 120 |
+
console.error(`服务器错误: ${err.stack}`);
|
| 121 |
+
res.status(500).send('服务器内部错误');
|
| 122 |
+
});
|
| 123 |
+
|
| 124 |
+
// 启动服务器
|
| 125 |
+
app.listen(PORT, () => {
|
| 126 |
+
console.log(`LibreTV 服务器已启动,运行在 http://localhost:${PORT}`);
|
| 127 |
+
console.log(`代理服务可通过 http://localhost:${PORT}/proxy/{URL} 访问`);
|
| 128 |
+
});
|