Self-hosted streaming / 部署文档
把影视、直播
放进一台服务器。
冬瓜TV 是一个自托管影视聚合站。Docker 一条命令启动,后台添加采集站与直播源,TMDb 资料、PanSou 网盘搜索和 Android 客户端都在同一套服务里。
界面预览
截图来自实际界面,后台示例使用虚构站点。首页会按屏幕宽度选择横版剧照或竖版海报,详情页使用大背景、海报缩略图和 TMDb 透明 Logo。点开任意图片可查看原图。
部署
方式 A:Docker Compose(推荐)
会同时启动「主站」和「PanSou 网盘搜索」两个容器,适合绝大多数人。
git clone https://github.com/Minerchu/dongguaTV.git
cd dongguaTV
# 可选:复制配置模板,至少修改管理员密码
cp .env.example .env
docker compose up -d --build
启动后访问:
- 主站:
http://服务器IP:3000 - 后台:
http://服务器IP:3000/admin.html
采集站和后台设置保存在 ./data/db.json,docker compose down
或重建容器都不会丢。
页面地址可以直接分享
搜索、影视详情和播放页现在都有独立地址,浏览器前进/后退和刷新都能恢复:
/:首页/search?wd=庆余年:聚合搜索结果/title/movie/671、/title/tv/1399:电影或剧集详情/watch?name=片名&site=站点key&id=影片ID:播放页
Docker 的 HOST_PORT 只影响网址里是否带端口。不想显示 :3100 时,用 Nginx / Caddy 把域名代理到该端口即可,见下一节。
方式 B:单容器镜像(All-in-One)
主站和 PanSou 打进同一个镜像,只跑一个容器。适合不方便用 compose 的面板。
docker build -f Dockerfile.allinone -t dongguatv:allinone .
docker run -d --name dongguatv \
-p 3000:3000 \
-v dongguatv-data:/app/data \
-e ADMIN_PASSWORD=改成你自己的密码 \
dongguatv:allinone
PanSou 已内置,只在容器内部监听 8888,不需要也不建议把 8888 开到公网。
ghcr.io/fish2018/pansou。如果服务器拉不动,
请先用代理或镜像加速拉取,或者直接用方式 A 的 compose。
方式 C:不用 Docker(Node.js)
npm install
cp .env.example .env
# 编辑 .env,至少填写 ADMIN_PASSWORD;TMDb 可以启动后在后台填
npm start
需要 Node.js 18 以上(推荐 20 / 22)。TMDb 凭据由服务端读取,不需要改前端。
需要准备的东西
| 项目 | 是否必须 | 说明 |
|---|---|---|
| TMDb 凭据 | 需要 | 短 API Key 或长 Read Access Token 二选一,登录后台「系统设置」填写 |
| 采集站接口 | 必须 | 在后台自行添加,格式见第四节 |
| PanSou | 可选 | 网盘搜索;compose / all-in-one 已经内置 |
修改端口
先记住一句话:左边是宿主机端口,右边是容器端口。
ports:
- "8080:3000"
# ↑ ↑
# 浏览器访问 8080
# 容器里的程序仍然监听 3000
1. 只改访问端口(最常见)
推荐在 .env 里改 HOST_PORT,容器内部端口不用动:
HOST_PORT=8080
docker compose up -d
也可以直接改 docker-compose.yml 里冒号左边的数字:
ports:
- "8080:3000"
之后访问 http://服务器IP:8080 即可,程序内部不用改。
2. 同时修改容器内部端口
如果 3000 被别的程序占用,或者你想让程序本身也监听 8080:
ports:
- "8080:8080"
environment:
- PORT=8080
用 docker run 时同理:
docker run -d --name dongguatv \
-e PORT=8080 \
-p 8080:8080 \
dongguatv:allinone
3. PanSou 的端口(一般不用改)
Compose 和 All-in-One 都让 PanSou 只在容器内部通信, 不需要对公网开放端口,平时不用改。只有容器内部端口冲突, 或你确实想单独访问 PanSou 时才需要动它。
Compose 方式(改完要让主站指向新端口):
pansou:
environment:
- PORT=18888
# 同时把主站的 PANSOU_URL 改成 http://pansou:18888
All-in-One 方式(只改容器内部端口,不发布到宿主机):
docker run -d --name dongguatv \
-p 3000:3000 \
-e PANSOU_PORT=18888 \
dongguatv:allinone
PANSOU_URL 指向容器内的
http://127.0.0.1:${PANSOU_PORT},你不用手动改。
4. 不用 Docker 时
编辑项目根目录的 .env:
PORT=8080
或者临时启动:
PORT=8080 node server.js
5. 域名和 HTTPS
用域名访问、去掉网址里的端口,需要做反向代理。Cloudflare、Nginx 和宝塔三种方式都在 第八节,按你现有的环境选一种即可。
首次使用
- 打开
http://服务器IP:端口/admin.html - 输入管理员密码。默认是
admin - 登录后先做三件事:改密码、填 TMDb 凭据、加采集站
填写 TMDb 凭据
打开后台「系统设置」,只粘贴其中一个凭据,点「保存 TMDb 配置」,再点「测试连接」确认可用。
- API Key:大约 32 位字母数字,使用 TMDb 的 v3 接口。
- API Read Access Token:以
eyJ开头的很长一串,使用 Bearer 认证。
tmdb、Bearer 或“密钥”这些字。
保存后后台只显示末尾四位;要更换就重新粘贴,要删除点「清除后台凭据」。后台填写的值优先于 .env。
修改管理员密码
在 docker-compose.yml 或 .env 里改:
ADMIN_PASSWORD=一个复杂密码
docker compose up -d
admin 只能用于本地测试。部署到公网前必须改掉。
添加采集站
项目里不内置任何采集站,也不在 GitHub 上保存任何站点地址。 你需要自己找公开的 Maccms V10 JSON 接口,然后在后台添加。
1. 正确格式长什么样
不是网站首页,也不是播放页,而是接口地址,通常以这些形式结尾:
https://example.com/api.php/provide/vod
https://example.com/inc/apijson.php
2. 怎么找
在 Google 里搜索下面任意一个关键词组合:
"api.php/provide/vod"inurl:"api.php/provide/vod"maccms 采集接口"inc/apijson.php"
也可以搜「影视采集站 分享」「Maccms 资源站 接口」等中文关键词。
3. 验证能不能用
把地址复制到浏览器,后面加上 ?ac=list&pg=1:
https://example.com/api.php/provide/vod?ac=list&pg=1
看到这样的 JSON 就说明格式正确:
{
"code": 1,
"msg": "数据列表",
"list": [
{ "vod_id": 123, "vod_name": "示例电影", "vod_pic": "https://..." }
]
}
如果看到的是一整页网页、XML、404 或 500,就换一个。
4. 添加到后台
- 打开「采集站管理」
- 填站点名称和接口地址
- 点「测试并添加」,显示「连接正常」就成功了
5. 批量导入与查重
站点多的时候,打开左边的「导入采集源」,不用一条条手填。
- TVBox 配置:每行粘贴一个配置地址,支持明文 JSON、Base64、图片隐写、2423/2324 加密和 urls 仓库。
- 苹果CMS 接口:每行一个接口,可以只填地址,也可以写成「名称,地址」。
我的资源站,https://example.com/api.php/provide/vod
另一个站,https://example.org/inc/apijson.php
点「解析并查重」后会先出预览表。已存在的地址标灰,不能再选;新接口勾选后点「导入选中」即可。
csp_ 内置源和 Jar 源要靠播放器运行,网页后台无法直接调用,导入时会自动跳过并显示原因。
能导入的是配置里公开的 Maccms HTTP 接口。
db.json(已在 .gitignore 中),
不要把自己的 db.json 上传到 GitHub,也不要截图公开接口地址。
6. 播放直连与手动切源
前台和安卓客户端会直接用采集站提供的 m3u8 地址播放,视频和分片都不会经过本站服务器,不占用服务器带宽。 播放速度取决于用户到源站之间的线路。
站内播放器也可以单独调用:/player/?url=你的m3u8地址&name=影片名。
当前线路播放失败时,播放器只提示手动切换,不再自动尝试其他线路。每条线路是否可用受源站接口、播放格式、m3u8 是否存在以及源站是否允许浏览器跨域访问影响。 遇到跨域或 403 时,换其他线路即可。
7. 搜索聚合与 PING 巡检
同一个片名和年份会合并成一张卡片,显示「X 站 / Y 条线路」,点进去再选站点。 服务每 10 分钟对启用的采集站做一次 HTTP PING,只看是否能连通和延迟;连续失败 3 次先冷却 5 分钟,之后自动重新参与搜索,不会永久关闭。
后台「采集站管理」能看到延迟、成功率和冷却剩余时间,也可以点「PING 巡检」立即检查;PING 正常不代表接口一定返回 m3u8。
8. 观看记录和收藏
浏览记录、收藏和继续观看保存在浏览器本地,不上传服务器,也不需要注册账号。 清除浏览器数据会一起清掉这些记录。
9. 自定义源适配器
内置采集只认 Maccms JSON。自建 JSON、字段名不同或需要特殊请求头的站点,可以用服务端适配器接入。
适配器是放在 data/adapters/ 里的 .js 文件,不是浏览器插件。
- 把适配器文件放进宿主机
data/adapters/。 - 后台「采集站管理」点「重载适配器」。
- 添加采集站时选择「自定义适配器」,填写基础地址和 JSON 配置。
mkdir -p data/adapters
cp examples/adapters/generic-json.js data/adapters/
# 然后回后台点「重载适配器」,不需要重启容器
通用示例支持配置 searchPath、queryParam、detailPath 和 testQuery。
适配器接口说明见仓库 examples/adapters/README.md。
网盘搜索(PanSou)
PanSou 是一个独立的 Go 服务,负责搜索 Telegram 频道和网盘插件。 本项目的 Compose 和 All-in-One 镜像已经把它打包进去,不需要你单独部署。
docker compose up -d 同时起两个容器;
方式 B 是同一个镜像里跑两个进程。两种方式第一次启动就可用。
Compose 方式
docker compose up -d 会同时启动 pansou 容器,
主站通过 PANSOU_URL=http://pansou:8888 自动连接,后台不用填地址。
All-in-One 方式
镜像内部会自动启动 PanSou,并设置好 PANSOU_URL,开箱即用。
使用已有的 PanSou
# 不启动自带的 pansou 服务,把地址改成你自己的
PANSOU_URL=http://你的地址:8888
也可以在后台「网盘搜索」页填写地址、Token、开关,点保存即可。
前台搜索页会按夸克、百度、阿里、UC 等网盘类型显示筛选按钮,复制链接和提取码都在结果卡片里。
访问不了 Telegram?
给 pansou 容器配置代理:
pansou:
environment:
- PROXY=socks5://host.docker.internal:7890
- HTTPS_PROXY=http://host.docker.internal:7890
- HTTP_PROXY=http://host.docker.internal:7890
检索结果太少?
PanSou 默认频道/插件有限,可以在 compose 里覆盖它自己的环境变量:
CHANNELS、ENABLED_PLUGINS。具体取值见
PanSou 文档。
电视直播
后台「电视直播」可以添加 M3U / M3U8 订阅,前台首页会出现「直播」入口。频道列表、分组、搜索、收藏和 HLS 播放都在 /live。
添加直播源
- 登录后台,打开「电视直播」。
- 点「添加直播订阅」,填写一个名称和订阅地址。填整份频道列表,不是单个频道的播放地址。
- 点「测试」,能显示频道数和分组数后再点「保存直播设置」。
- 打开
/live或首页的「直播」入口,选择频道播放。
订阅文件一般长这样:
#EXTM3U
#EXTINF:-1 tvg-logo="https://example.com/logo.png" group-title="央视",CCTV-1
https://example.com/live/cctv1.m3u8
#EXTINF:-1 group-title="卫视",湖南卫视
https://example.com/live/hunan.m3u8
rtp://、udp:// 会被忽略。
播放优先直连源站,失败后才走服务器签名代理。订阅里带私人 Token 时,只会保存在服务器的
data/db.json,不会写进前端源码。
没有直播源时,后台可以添加多条订阅并单独启停。某一条订阅失效不会影响其他订阅。
Android 原生客户端
客户端使用 Kotlin + Jetpack Compose 编写,直接调用本站的
/api/search、/api/detail、/api/cloud/search 和 TMDb 同源接口,
播放器使用 Media3 / ExoPlayer。它不是网页套壳,也不需要 WebView。
docker compose 部署时,Android SDK、Gradle、apktool、zipalign 和 apksigner
都已经包含在镜像里。站长不需要安装 Android Studio,也不需要在电脑上编译 APK。
生成客户端
- 先在「采集站管理」添加至少一个可用的 Maccms JSON 接口。
- 打开后台的「Android 客户端」页。
- 设置应用名称,上传正方形 PNG 图标;浏览器会自动裁剪成 512×512。
- 填写手机能访问的服务器地址,例如
https://tv.example.com。 - 点击「保存并生成 APK」。服务器会自动排队编译,通常需要 1–3 分钟。
- 状态变成「已生成」后,在后台或首页下载 APK。
服务器地址怎么填
- 必须是手机实际能访问的地址,不要填
127.0.0.1、容器名或容器内部端口。 - 已经配置域名和 HTTPS 时,填最终公网地址,例如
https://tv.example.com。 - 局域网测试可以填
http://192.168.1.10:3000,手机和服务器要在同一网络。 - 客户端内置地址不可用时,也可以在客户端「设置」页手动修改服务器地址。
扫码绑定
后台 Android 页面会显示服务器二维码。安装 APK 后,在首次连接页或「设置」里点「扫码绑定」, 扫码即可写入地址并测试连接,不需要在手机上手动输入。
二维码只带服务器地址,不带后台密码。修改服务器地址并保存后,二维码会自动更新。
更新和签名
data/android-client/
├── donggua-tv.apk # 生成好的安装包
├── signing.jks # 本站独立签名
└── signing.pass # 签名密码
修改名称、图标、版本号或服务器地址后,保存会自动重新生成。
更新服务时必须保留 data/android-client,否则新 APK 会使用不同签名,手机无法覆盖安装。
如需排查失败原因,运行 docker compose logs -f dongguatv 查看构建日志。
All-in-One 镜像不包含 APK 编译组件;需要 Android 客户端时请使用标准 Dockerfile / Compose。
客户端「设置」页可以检查站点是否发布了新版本;发现新版本会直接打开 APK 下载地址。 播放页右上角可以把当前集交给系统里的其他播放器或投屏应用。
网站域名与反向代理
http://127.0.0.1:3000;
TMDb 反代的目标只有 api.themoviedb.org 和 image.tmdb.org,
不要把 3000 端口填进后台的「TMDb 反代根地址」。TMDb 专用配置见
第九节。
先确定源站地址
默认 Docker 对外端口是 3000,反代目标就是 http://127.0.0.1:3000。
如果你在第二节把 HOST_PORT 改成了 3100,下面配置里的 3000
也要一起换掉。先在服务器上确认能访问:
curl -I http://127.0.0.1:3000
域名服务商处添加一条 A 记录,指向服务器公网 IP。等解析生效后,从下面三种方案里只选一种。
80 和 443,不要长期把
3000 直接暴露在公网。反代必须原样转发 /api、/tmdb
和 /admin.html,不要加路径前缀,也不要开启静态缓存。
方案 A:Cloudflare 反向代理
适合不想手动续证书、希望顺带使用 CDN 和防护的人。注意:Cloudflare 的普通橙色云代理只从
80/443 回源,不能直接把橙色云指向服务器的 3000。源站需要先用 Nginx
或 Caddy 监听 443,或者改用 Cloudflare Tunnel。
- 在 Cloudflare 添加域名,DNS 里新建 A 记录,填写服务器 IP,先开启橙色云。
- 如果源站已经有有效证书,SSL/TLS 模式选 Full (strict); 源站暂时只有 HTTP 时只能选 Full 或 Flexible,但安全性较差,不推荐长期这样用。
- 在 SSL/TLS → Edge Certificates 打开 Always Use HTTPS。
- 源站按下面 Nginx 教程把
443转发到3000,或者使用 Tunnel。
不想开放公网端口时,在 Cloudflare Zero Trust → Networks → Tunnels 新建 Tunnel,
装好连接器后添加 Public Hostname,Service 填 http://127.0.0.1:3000。
临时测试也可以先运行:
cloudflared tunnel --url http://127.0.0.1:3000
方案 B:Nginx 反向代理
Ubuntu / Debian 安装 Nginx 和 Certbot:
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
新建 /etc/nginx/sites-available/dongguatv,把域名和端口换成自己的:
server {
listen 80;
server_name tv.example.com;
client_max_body_size 64m;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_request_buffering off;
}
}
启用配置并申请证书:
sudo ln -s /etc/nginx/sites-available/dongguatv /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d tv.example.com
之后访问 https://tv.example.com。如果 Nginx 本身也在 Docker 里,
把 127.0.0.1:3000 换成主站容器的服务名,例如 http://dongguatv:3000。
方案 C:宝塔面板反向代理
- 软件商店确认已安装 Nginx,然后在「网站」→「添加站点」添加域名,PHP 版本选纯静态,不创建数据库。
- 进入站点设置 →「SSL」,申请 Let's Encrypt 证书,开启「强制 HTTPS」。
- 打开「反向代理」→「添加反向代理」,目标 URL 填
http://127.0.0.1:3000,发送域名填$host。 - 在反向代理配置中开启 WebSocket,关闭缓存,把超时时间设为
3600秒。 - 保存后访问域名检查首页、后台、搜索和播放。站点目录不需要放任何文件。
如果宝塔生成的配置里没有下面几项,可在反向代理配置中补上;已经存在就不要重复添加:
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
反向代理完成后检查
- 浏览器地址栏只显示
https://你的域名,不再带:3000。 - 后台能登录,搜索、详情和播放都正常;如果登录后反复跳回登录页,检查
X-Forwarded-Proto。 - 播放中断时,优先检查
proxy_buffering off和两个3600s超时。 - 后台生成 Android 客户端时,服务器地址必须填最终域名,例如
https://tv.example.com,不能填127.0.0.1。
.env 填
DOMAIN=tv.example.com,再执行
docker compose -f docker-compose.yml -f docker-compose.https.yml up -d --build。
这种方式占用 80/443,不能和宝塔/Nginx 同时监听同一端口。
TMDb 功能
首页和详情页已经用到的功能
- 首页推荐:本周趋势、华语剧集、日韩剧集、港台电影、正在热映、高分电影、今日播出。
- 海报与 Logo:电脑和电视使用横版剧照,手机使用无字竖版海报;详情页叠加 TMDb 透明 Logo,没有 Logo 时自动回退文字标题。
- 影视详情:评分、上映日期、片长、分级、类型、关键词、主创、演员和简介。
- 系列作品:电影属于系列时,展示同系列其他影片,可直接继续查看。
- 预告片:优先使用 YouTube 官方预告;新片没有预告时不会显示空按钮。
- 在哪儿看:按浏览器地区显示 Netflix、Disney+ 等平台,也可手动切换国家/地区。
- 相似推荐与找片源:点击推荐继续浏览;点“找片源”会带片名回到 Maccms 聚合搜索,同时搜索 PanSou 网盘资源。
TMDb 请求由本站服务端转发,浏览器只访问当前站点域名,凭据不会进入前端:
- 前端
/tmdb/3/...→https://api.themoviedb.org/3/... - 海报
/tmdb/t/p/...→https://image.tmdb.org/t/p/... - TMDb 凭据只保存在服务端或
data/db.json,不会出现在网页源码里
请求链路:
浏览器 → https://你的域名/tmdb/... → 容器内 server.js → TMDb
服务器本身能访问 TMDb 时,不需要填反代。修改 Docker 对外端口也不会影响它,
因为前端始终请求当前网站地址下的 /tmdb/...。
/tmdb 是容器内的服务端转发,不是 Cloudflare 反代。服务器在国内时,
容器本身仍然连不上 api.themoviedb.org,需要在后台填写下面的反代地址。
服务器在国内:反向代理 TMDb
https://api.themoviedb.org/3/... 和
https://image.tmdb.org/t/p/...。反代完成后,后台填的是这个代理的根地址,
例如 https://tmdb.example.com,不是 http://127.0.0.1:3000。
后台「系统设置 → TMDb 影视资料 → TMDb 网络线路」已经可以直接填写反代地址,保存后立即生效,
不需要改 .env,也不需要重建容器。
- 登录 Cloudflare Dashboard,进入 Workers & Pages,创建一个 Worker。
- 打开 Worker 的代码编辑器,把下面代码全部替换进去,然后点 Deploy。
- 访问 Worker 根地址,例如
https://tmdb-proxy.example.workers.dev, 看到{"ok":true}说明部署成功。 - 回到冬瓜TV 后台,把这个根地址填进「TMDb 反代根地址」,点「测试连接 → 保存 TMDb 设置」。
只填根地址,不要手动拼 /3 或 /t/p;系统会自动补齐。
如果 API 和图片使用不同的代理,再单独填写「图片反代地址」。
const API_ORIGIN = 'https://api.themoviedb.org';
const IMAGE_ORIGIN = 'https://image.tmdb.org';
function corsHeaders() {
return {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, HEAD, OPTIONS',
'Access-Control-Allow-Headers': 'Authorization, Content-Type, Range',
'Access-Control-Expose-Headers': 'Content-Length, Content-Range, Content-Type'
};
}
function textResponse(body, status) {
return new Response(body, {
status: status || 200,
headers: Object.assign({ 'Content-Type': 'text/plain; charset=utf-8' }, corsHeaders())
});
}
export default {
async fetch(request) {
const incoming = new URL(request.url);
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: corsHeaders() });
}
if (incoming.pathname === '/' || incoming.pathname === '/health') {
return new Response(JSON.stringify({ ok: true, service: 'donggua-tmdb-proxy' }), {
headers: Object.assign({ 'Content-Type': 'application/json; charset=utf-8' }, corsHeaders())
});
}
if (request.method !== 'GET' && request.method !== 'HEAD') {
return textResponse('Method Not Allowed', 405);
}
let origin = '';
if (incoming.pathname.startsWith('/3/')) origin = API_ORIGIN;
else if (incoming.pathname.startsWith('/t/p/')) origin = IMAGE_ORIGIN;
else return textResponse('Not Found', 404);
const headers = new Headers();
const passHeaders = ['accept', 'accept-language', 'authorization', 'content-type', 'if-modified-since', 'if-none-match', 'range'];
for (const name of passHeaders) {
const value = request.headers.get(name);
if (value) headers.set(name, value);
}
headers.set('user-agent', 'DongguaTV-TMDB-Proxy/1.0');
const target = origin + incoming.pathname + incoming.search;
const upstream = await fetch(target, {
method: request.method,
headers,
redirect: 'follow',
cf: {
cacheEverything: true,
cacheTtl: incoming.pathname.startsWith('/t/p/') ? 604800 : 300
}
});
const responseHeaders = new Headers(upstream.headers);
responseHeaders.delete('set-cookie');
responseHeaders.set('Access-Control-Allow-Origin', '*');
responseHeaders.set('Access-Control-Expose-Headers', 'Content-Length, Content-Range, Content-Type');
responseHeaders.set('X-Donggua-TMDB-Proxy', '1');
return new Response(request.method === 'HEAD' ? null : upstream.body, {
status: upstream.status,
statusText: upstream.statusText,
headers: responseHeaders
});
}
};
完整文件也在仓库:examples/cloudflare-tmdb-worker.mjs。
方式 A:绑定自己的 Cloudflare 域名
如果 *.workers.dev 在当前网络下访问不了,可以在 Worker 的
Settings → Domains & Routes → Custom Domain 绑定一个已经接入 Cloudflare 的域名,
例如 tmdb.example.com。绑定成功后在后台填 https://tmdb.example.com。
方式 B:Nginx 反代 TMDb
准备一个只给 TMDb 用的域名,例如 tmdb.example.com,把 A 记录指向这台海外服务器。
先写 HTTP 配置,证书交给 Certbot 自动补全。新建 /etc/nginx/sites-available/tmdb-proxy:
server {
listen 80;
server_name tmdb.example.com;
location = / {
default_type application/json;
return 200 '{"ok":true,"service":"donggua-tmdb-proxy"}';
}
location ^~ /3/ {
proxy_pass https://api.themoviedb.org/3/;
proxy_ssl_server_name on;
proxy_set_header Host api.themoviedb.org;
proxy_set_header Authorization $http_authorization;
proxy_set_header Accept-Language $http_accept_language;
proxy_set_header User-Agent DongguaTV-TMDB-Proxy/1.0;
proxy_set_header Range $http_range;
proxy_set_header If-None-Match $http_if_none_match;
add_header Access-Control-Allow-Origin "*" always;
add_header Cache-Control "public, max-age=300" always;
}
location ^~ /t/p/ {
proxy_pass https://image.tmdb.org/t/p/;
proxy_ssl_server_name on;
proxy_set_header Host image.tmdb.org;
proxy_set_header User-Agent DongguaTV-TMDB-Proxy/1.0;
add_header Access-Control-Allow-Origin "*" always;
expires 7d;
}
}
启用配置并申请证书。Certbot 成功后会自动补上 443 和证书路径:
sudo ln -s /etc/nginx/sites-available/tmdb-proxy /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d tmdb.example.com
浏览器打开 https://tmdb.example.com/ 应看到 {"ok":true}。
回到冬瓜TV 后台,把 https://tmdb.example.com 填进「TMDb 反代根地址」。
不要把这里的域名换成主站域名,也不要填 :3000。
方式 C:宝塔面板反代 TMDb
- 在宝塔「网站」里添加站点
tmdb.example.com,PHP 选纯静态,不建数据库。 - 进入站点设置 →「SSL」,申请 Let's Encrypt 证书并开启强制 HTTPS。
- 进入「反向代理」→「添加反向代理」。第一项:目标 URL 填
https://api.themoviedb.org, 发送域名填api.themoviedb.org,代理目录填/3/。 - 再添加一项:目标 URL 填
https://image.tmdb.org,发送域名填image.tmdb.org, 代理目录填/t/p/。 - 如果宝塔版本不支持同一站点配置多条路径代理,直接打开站点「配置文件」,把上面的 Nginx
location段粘进去,再执行nginx -t并重载。 - 后台「TMDb 反代根地址」填
https://tmdb.example.com,点「测试连接」。
宝塔、Nginx、Cloudflare 三种方式只选一种。TMDb 反代域名和主站域名可以分开,
但不要把主站的 http://127.0.0.1:3000 填进 TMDb 设置。
其他代理方式
在 .env 里填写代理:
HTTPS_PROXY=http://host.docker.internal:7890
HTTP_PROXY=http://host.docker.internal:7890
host.docker.internal 指向宿主机,Compose 已经配置好映射。
All-in-One 则直接把同样的变量写成 -e HTTPS_PROXY=...。
这层代理只解决服务器访问 TMDb 的问题;网站本身的域名和 HTTPS 仍按第八节配置。
接口与访问权限
前台页面和 Android 客户端需要调用同一套只读接口。它们只返回公开数据,不会返回后台密码、TMDb 凭据或采集站鉴权信息。
/api 设成只允许服务器本机访问,否则网页搜索、详情、播放、直播和 Android 客户端都会失效。需要收紧的是跨站调用和后台入口。
默认只允许本站同源
服务端已关闭任意来源 CORS。其他网站即使知道接口地址,浏览器也不能跨站读取本站 API。确实有另一个前端需要跨域调用时,再显式填写完整 Origin:
API_ALLOWED_ORIGINS=https://app.example.com
后台接口必须登录
/api/admin/* 需要管理员会话,未登录返回 401;登录失败次数过多会临时锁定。后台的写操作、备份、采集站导入和客户端编译都不会对未登录请求开放。
把后台限制到本机或固定 IP
如果后台只允许服务器本机访问,设置 ADMIN_ALLOWED_IPS=loopback,再通过 SSH 隧道打开后台。也可以填写固定公网 IP,多个地址用逗号分隔。
ADMIN_ALLOWED_IPS=loopback
# 或
ADMIN_ALLOWED_IPS=203.0.113.10,198.51.100.7
部署在 Nginx、Caddy 或宝塔后面时,还要设置 TRUST_PROXY=1,服务端才能识别真实 IP。直接映射端口时不要开启。
curl 直接请求公开只读接口。公开浏览类接口仍然必须能被访客浏览器调用;如果要挡机器人和高频请求,请在 Cloudflare 或反向代理层增加访问规则。
常见问题
启动后搜索没有任何结果?
先确认后台已经添加了采集站并且处于「启用」状态。新安装默认是空的。
添加时提示「不是 Maccms JSON 接口」?
说明地址不是 JSON 采集接口。检查是不是少了 /api.php/provide/vod,或者返回的是网页/XML。
非 Maccms 的站点怎么接入?
把适配器文件放进 data/adapters/,后台重载后选择「自定义适配器」。仓库的 examples/adapters/ 有完整示例。
搜索很慢?
关掉响应慢的站点,或者减少启用数量。默认单站 6 秒超时,可用 SEARCH_TIMEOUT 调整。
忘记后台密码了?
改 ADMIN_PASSWORD 环境变量后重启容器即可。
网盘搜索显示暂不可用?
检查 pansou 容器是否在运行、服务器能否访问 Telegram、代理是否正确。
TMDb 首页没有内容?
确认后台「系统设置」里填了 API Key 或 Read Access Token,并点过「测试连接」。如果服务器访问不了 TMDb,
在后台填 Cloudflare Worker 反代地址即可,参考TMDb 反代教程。
也可以继续使用 HTTPS_PROXY 或环境变量里的 TMDB_API_BASE / TMDB_IMAGE_BASE。
更新版本后站点数据会丢吗?
Compose 默认把 ./data 挂到 /app/data,
数据在宿主机的 data/db.json,重建容器不会丢。
用 docker run 时请加 -v dongguatv-data:/app/data;
没有挂载的话,删除容器会一起删掉配置。
必须单独安装 PanSou 吗?
不需要。Compose 和 All-in-One 都自带 PanSou。只有你想用自己的 PanSou
实例时,才需要把 PANSOU_URL 改成那个地址。
怎么备份?
后台「备份与恢复」可以直接下载、创建和恢复备份。服务每天自动备份一次,保留最近 7 份,
文件在 data/backups/。恢复前会再自动备份当前数据。
某条线路播放失败怎么办?
播放器不再自动切源,只提示手动切换其他线路。很多源本身不提供 m3u8,或者只返回 mp4/需要额外解析;换一条线路或换一个搜索结果即可。
后台显示站点冷却中,是坏了吗?
不是永久禁用。PING 连续失败 3 次后会冷却 5 分钟,结束后自动重新参与搜索。可以在后台点「PING 巡检」立即重试。
免责声明
本项目仅作为 Node.js 的学习项目开源,不存储、不发布、不参与任何影视内容的制作与传播。 采集站、网盘资源均由第三方提供,本项目只做技术聚合。
请只接入你有权使用的接口,并遵守当地法律法规。请支持正版影视内容。








