Files
StudyDiary-20261009/02-学习笔记/NAS媒体刮削TMDB接口访问问题完整排查+落地文档.md
CrazyJasonwell e97be9ac8c commit all
2026-10-09 22:06:40 +08:00

430 lines
14 KiB
Markdown
Executable File
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.
# NAS媒体刮削TMDB接口访问问题完整排查\+落地文档
# NAS 媒体刮削 TMDB 接口访问问题完整排查 \+ 落地文档
> 文档来源:本次全部对话记录整理,包含:故障现象、完整排错过程、根因分析、两套可落地解决方案、配置模板、验证命令、故障清单
> 环境:MacOS Clash Verge Rev、群晖 NAS(Mihomo\+Hysteria2 正常)、飞牛 fnOS(Docker 部署 MoviePilot、tinyMediaManager)
>
>
## 目录
1. 故障现象汇总
2. 前期网络排错全过程(curl、TLS、Hysteria2 排查)
3. 根因分析
4. 方案一【主方案|推荐日常使用】飞牛容器复用群晖 Mihomo 代理
5. 方案二【备用兜底方案】Cloudflare Worker TMDB 中转(鉴权 \+ 缓存,无需代理)
6. 两套方案切换规则
7. 统一故障排查清单
8. 附录:测试命令、参考脚本
---
# 1\. 故障现象汇总
## 1\.1 网络侧现象
1. MacOS 客户端:Clash Verge Rev(Mihomo 内核),Hysteria2 节点,访问`api.themoviedb.org`正常,TMDB 接口可访问。
2. 群晖 NAS:部署 Mihomo,**同一份 config\.yaml、相同内核版本**,Hysteria2 节点正常,MoviePilot、TinyMediaManager 媒体刮削,TMDB 接口调用全部正常。
3. 飞牛 fnOS:
- 飞牛系统无系统级全局代理;
- 飞牛 Docker 内部署 Mihomo,订阅同样 Hysteria2 节点,节点列表加载成功,但所有 Hysteria2 节点 TLS 握手失败,`SSL_ERROR_SYSCALL`;
- 在飞牛上部署 MoviePilot、tinyMediaManager,调用 TMDB API 接口全部超时失败,无法刮削媒体元数据。
> 测试现象:curl 访问`https://api.themoviedb.org`,TLS 握手卡在 Client Hello 后直接断开,报错:`curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL`。
>
>
## 1\.2 测试过程关键现象
1. 裸网直接访问`api.themoviedb.org:443`:TCP 能建立连接,TLS 握手中断,SNI 被网络拦截。
2. 使用本地 SOCKS5 代理(7890),未使用`--socks5-hostname`:本地解析域名,拿到污染 IP,握手失败;使用`--socks5-hostname`,域名交由代理远端解析。
3. Hysteria2 依赖 UDP QUIC 协议;飞牛 fnOS 的 OVS 虚拟交换机 \+ 家庭路由器会拦截 UDP 出站,导致 HY2 QUIC 握手超时。
4. 飞牛 Docker 容器**不会自动继承宿主机的代理**,容器内程序(MoviePilot/TMM)不会自动走宿主机 Mihomo。
---
# 2\. 前期网络排错全过程记录
## 2\.1 基础 curl 裸网测试
```bash
# 测试80端口HTTP
curl -v api.themoviedb.org
# HTTPS裸网测试,TLS握手失败
curl -v https://api.themoviedb.org
```
现象:TCP 连接成功,发送 TLS Client Hello 之后直接断开,报错 `SSL_ERROR_SYSCALL`。
> 尝试指定 TLS 版本`--tls-max 1.2`,依旧握手失败。
>
>
## 2\.2 SOCKS 代理测试(本地 7890 端口)
```bash
# 错误:本地解析域名
curl -v --socks5 127.0.0.1:7890 https://api.themoviedb.org
# 正确:远端域名解析(socks5h)
curl -v --socks5-hostname 127.0.0.1:7890 https://api.themoviedb.org
# 验证代理出口IP
curl -v --socks5-hostname 127.0.0.1:7890 https://ifconfig.me
```
> 结论:`--socks5` 是本地解析域名,容易拿到污染 IP;**`--socks5-hostname`** 将域名解析交给远端代理服务器,规避 DNS 污染。
>
>
## 2\.3 协议选型评估:mieru /anytls/hysteria2
|协议|特点|本次环境适配性|
|---|---|---|
|Hysteria2|QUIC/UDP,速度好;**强依赖 UDP 出站**|Mac / 群晖正常;飞牛 fnOS 受 OVS、路由器 UDP 拦截,无法稳定运行|
|AnyTLS|TCP 隧道,不依赖 UDP;存在内层 TLS1\.3 报文被篡改风险|UDP 不通场景备选,稳定性弱于 HY2|
|Mieru|TCP 隧道,抗干扰;同样 TCP 底层,TLS1\.3 握手存在概率异常|备选|
> 结论:飞牛环境不适合本地部署 Hysteria2,放弃在飞牛本机跑代理。
>
>
## 2\.4 Mihomo 日志排查方法(飞牛 Docker)
1. 修改 Mihomo 配置,开启 debug 日志
```yaml
log-level: debug
external-controller: 127.0.0.1:9090
```
2. 查看容器实时日志
```bash
# 查看容器名
docker ps | grep mihomo
# 实时跟踪日志
docker logs -f --tail 100 mihomo
# 导出日志到文件
docker logs mihomo > mihomo-debug.log
```
3. 关键词识别
- `quic: connection timed out` → UDP 出站被拦截(本次核心问题)
- `unsupported proxy type: hysteria2` → Mihomo 内核版本过低
- `tls: certificate verify failed` → 需要`skip-cert-verify: true`
> 验证连通性(飞牛容器内 curl 测试)
>
>
```bash
docker exec -it moviepilot curl -v --socks5-hostname 群晖IP:7890 https://api.themoviedb.org/3/movie/550?api_key=你的TMDBKEY
```
---
# 3\. 根因分析
1. 飞牛 fnOS 自带 OVS 虚拟交换机,会拦截 UDP 数据包;家庭路由器 / 光猫也会限制 UDP 出站,Hysteria2(QUIC/UDP)无法正常建立隧道。
2. 飞牛 Docker 容器隔离:容器内程序不会自动复用宿主机代理,MoviePilot、TMM 裸网访问 TMDB,遭遇 DNS 污染 \+ SNI 阻断。
3. Mac、群晖网络环境 UDP 不受限,所以同样配置、同样节点可以正常工作。
> 最终方案思路:**不再在飞牛部署 Mihomo 代理**,采用双冗余架构:
> ✅ 主方案:飞牛容器内 MoviePilot/TMM 直接复用**群晖 Mihomo 的 SOCKS5 代理**
> ✅ 备用方案:CF Worker 中转 TMDB API,**完全不需要代理**,群晖 / 代理故障时兜底刮削。
>
>
---
# 4\. 方案一【主方案|推荐日常使用】飞牛容器复用群晖 Mihomo 代理
## 4\.1 群晖 Mihomo 前置配置(必须)
群晖 Mihomo `config.yaml` 关键配置,**允许局域网访问**
```yaml
mixed-port: 7890
allow-lan: true
listen-address: 0.0.0.0
ipv6: false
```
> 要求:群晖防火墙放行 7890 端口;飞牛与群晖在同一局域网,无跨 VLAN 隔离。
>
>
## 4\.2 MoviePilot docker\-compose\.yml(飞牛 fnOS)
> 修改:`192.168.1.100` 替换为**群晖内网 IP**;修改 volume 路径为飞牛实际路径
>
>
```yaml
version: "3.8"
services:
moviepilot:
image: jxxghp/moviepilot:latest
container_name: moviepilot
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- /volume1/moviepilot:/app/config
environment:
- TZ=Asia/Shanghai
# socks5h = 远端域名解析,规避DNS污染
- PROXY_HOST=socks5h://192.168.1.100:7890
# Github资源加速,解决插件下载失败
- GITHUB_PROXY=https://mirror.ghproxy.com/
network_mode: bridge
```
### MoviePilot WebUI 配置
- TMDB 地址保持官方默认,**PROXY\_HOST 已由环境变量注入,无需重复填写**
- 填入你的 TMDB API Key
> 启动命令:`docker-compose up -d`
>
>
## 4\.3 TinyMediaManager \(TMM\) docker\-compose\.yml(飞牛 fnOS)
```yaml
version: "3.8"
services:
tinymediamanager:
image: tinymediamanager/tinymediamanager:latest
container_name: tinymediamanager
restart: unless-stopped
ports:
- "4000:4000"
volumes:
- /volume1/tmm:/config
environment:
- TZ=Asia/Shanghai
- ALL_PROXY=socks5h://192.168.1.100:7890
- NO_PROXY=127.0.0.1,localhost,192.168.0.0/16
network_mode: bridge
```
### TMM WebUI 代理配置(推荐优先使用网页内配置,比环境变量更稳定)
- Proxy type: `SOCKS5`
- Host:群晖内网 IP
- Port:7890
- ✅ 勾选:Resolve hostnames remotely(远端域名解析,等价 socks5h)
## 4\.4 连通验证命令
```bash
docker exec -it moviepilot curl -v --socks5-hostname 192.168.1.100:7890 https://api.themoviedb.org/3/movie/550?api_key=你的TMDB密钥
```
> 返回 JSON 代表链路正常。
>
>
---
# 5\. 方案二【备用兜底方案】Cloudflare Worker TMDB 中转(鉴权 \+ 缓存,无需代理)
> 适用场景:群晖关机、代理节点失效、UDP 链路彻底不可用。仅接管 TMDB 接口,Github、豆瓣等其他外网 API 仍然需要代理。
> 免费限制:CF Worker 免费版每日 10 万次请求,日常刮削完全够用。
>
>
## 5\.1 主中转 Worker 脚本(鉴权 \+ 缓存)
登录 Cloudflare 后台 → Workers \& Pages → 创建 Worker,清空原有代码粘贴下面代码
```javascript
export default {
async fetch(request, env) {
// ========== 自定义你的访问密钥,自行修改 ==========
const ACCESS_SECRET = "自定义随机密钥";
// 缓存有效期,单位秒:3600=1小时;600=10分钟;86400=24小时
const CACHE_TTL = 3600;
// ================================================
const auth = request.headers.get("X-Tmdb-Secret");
if (!auth || auth !== ACCESS_SECRET) {
return new Response("Access Denied", { status: 403 });
}
const targetHost = "api.themoviedb.org";
const url = new URL(request.url);
url.hostname = targetHost;
const newReq = new Request(url, request);
newReq.headers.set("Host", targetHost);
// GET请求开启缓存,提升速度、节约额度
if (request.method === "GET") {
const cacheKey = new Request(newReq.url, newReq);
const cache = caches.default;
let response = await cache.match(cacheKey);
if (!response) {
response = await fetch(newReq);
response = new Response(response.body, response);
response.headers.set("Cache-Control", `s-maxage=${CACHE_TTL}`);
await cache.put(cacheKey, response.clone());
}
return response;
}
// POST等其他请求直接透传,不缓存
return await fetch(newReq);
}
}
```
保存部署,得到 worker 域名,示例 `tmdb-proxy.xxx.workers.dev`
## 5\.2 缓存清理三种方案
1. **单影片临时绕过缓存(最简单)**
请求地址末尾追加:`&_nocache=随机数字`
示例:
`https://tmdb-proxy.xxx.workers.dev/3/movie/550?api_key=xxx&_nocache=20260925`
2. **全局一键清空全部缓存(批量重刷媒体库)**
修改 Worker 代码任意一行,重新部署;CF 所有边缘节点缓存自动全部失效。
3. **精准单条缓存删除(进阶 Purge Worker)**
新建独立 Worker(tmdb\-purge),粘贴下面代码,密钥和主 Worker 保持一致
```javascript
export default {
async fetch(request, env) {
const ACCESS_SECRET = "和主Worker相同的密钥";
const auth = request.headers.get("X-Tmdb-Secret");
if (!auth || auth !== ACCESS_SECRET) {
return new Response("Access Denied", { status: 403 });
}
const url = new URL(request.url);
const targetPath = url.searchParams.get("path");
if (!targetPath) {
return new Response("Missing ?path=/3/movie/550", { status: 400 });
}
// 替换为你的主worker域名
const cacheUrl = new URL(`https://tmdb-proxy.xxx.workers.dev${targetPath}`);
const cacheReq = new Request(cacheUrl);
const cache = caches.default;
await cache.delete(cacheReq);
return new Response(`Purged: ${cacheUrl.toString()}`);
}
}
```
调用示例:
```bash
curl -v -H "X-Tmdb-Secret: 自定义随机密钥" "https://tmdb-purge.xxx.workers.dev/?path=/3/movie/550?api_key=你的TMDBKEY"
```
## 5\.3 MoviePilot 切换 CF Worker 配置
1. 清空 PROXY\_HOST(关闭代理)
2. TMDB API 地址:`https://tmdb-proxy.xxx.workers.dev/3`
3. 自定义请求头添加一行:
`X-Tmdb-Secret: 自定义随机密钥`
4. 保留原有 TMDB API Key
## 5\.4 TMM 切换 CF Worker 配置
1. TMDB API URL 替换为 worker 地址 `https://tmdb-proxy.xxx.workers.dev/3`
2. Custom HTTP Headers 添加:`X-Tmdb-Secret: 自定义随机密钥`
3. 关闭 TMM 内部代理设置
## 5\.5 curl 测试命令
```bash
curl -v -H "X-Tmdb-Secret: 自定义随机密钥" "https://tmdb-proxy.xxx.workers.dev/3/movie/550?api_key=你的TMDBKEY"
```
返回 JSON 即为正常。
---
# 6\. 两套方案切换规则
|方案|适用场景|优点|缺点|
|---|---|---|---|
|复用群晖 Mihomo(主方案)|日常长期使用,群晖保持开机|全覆盖 TMDB/Github/ 豆瓣,无额度限制,速度稳定|依赖群晖在线,群晖关机则刮削失效|
|CF Worker 中转(备用)|群晖关机、代理节点故障、UDP 被拦截|零代理依赖,不受 UDP/OVS 限制,自带缓存加速|仅 TMDB 接口生效,CF 免费版有每日额度上限|
> 使用策略:日常使用主方案;代理故障 / 群晖关机,临时切换到 CF Worker 兜底刮削。
>
>
---
# 7\. 统一故障排查清单
1. ❌ 容器内不要填写`127.0.0.1`作为代理地址,容器内 \[127\.0\.0\.1\]\(127\.0\.0\.1\) 指向容器自身,必须填写群晖内网 IP。
2. ✅ 群晖 Mihomo 必须开启`allow-lan: true`,监听地址`0.0.0.0`,否则飞牛无法访问 7890 端口。
3. ✅ 飞牛环境关闭 IPv6,规避 TLS 握手异常。
4. ❌ 放弃飞牛本地部署 Hysteria2,OVS \+ 路由器 UDP 拦截属于硬限制。
5. ✅ MoviePilot/TMM 优先使用软件原生内置代理配置,优于`ALL_PROXY`环境变量。
6. 测试链路优先使用`--socks5-hostname`,远端解析域名,规避 DNS 污染。
7. 代理测试失败优先查看 Mihomo debug 日志,定位是 UDP 超时、证书错误还是鉴权错误。
---
# 8\. 附录:常用命令汇总
```bash
# 查看docker容器
docker ps | grep mihomo
docker ps | grep moviepilot
# 实时查看mihomo日志
docker logs -f --tail 100 mihomo
# 进入moviepilot容器curl测试代理连通
docker exec -it moviepilot curl -v --socks5-hostname 群晖IP:7890 https://api.themoviedb.org/3/movie/550?api_key=你的TMDBKEY
# 测试CF Worker中转
curl -v -H "X-Tmdb-Secret: 自定义密钥" "https://tmdb-proxy.xxx.workers.dev/3/movie/550?api_key=你的TMDBKEY"
```
---
如果你想要,我可以再输出 **Markdown 纯文本版本**(不带任何 canvas 标签,直接复制到 Typora/Obsidian/ 语雀保存)。你要 Markdown 版本吗?
> (注:部分内容可能由 AI 生成)