自动摘要
正在生成中……
飞牛影视媒体库中突然出现大量重复项目,同一视频在界面中显示两三次,但文件系统里只有一个源文件。随后,应用中心的影视应用也无法正常启用,一直停留在“启动中”,最终提示“本地应用启动失败”。最初看起来像媒体数据库损坏,实际根因却是一条全局 curl 代理配置。
问题背景
故障表现可以归纳为:
- 影视媒体库出现大量重复项目,同一个视频文件在界面中显示两到三次;
- 文件管理器中实际只有一个源文件,没有重复文件或硬链接;
- 影视应用在应用中心显示“未启用”;
- 点击“启用”后一直停留在“启动中”;
- 最终提示“本地应用启动失败”;
- 后台出现多个
trim-media 进程;
- 强制终止后,进程有时又会被重新拉起。
一、确认重复的是索引,而不是文件
首先检查媒体源目录,确认同名视频在文件系统中只有一个实体,并不存在重复文件或硬链接。
进一步检查影视数据库后发现:
- 相同文件路径对应多个不同的媒体 GUID;
-
CleanVideos 中有 19 个文件路径发生重复;
- 共存在 38 条多余数据库记录;
- 部分文件在界面中正好显示三次。
因此可以确定:重复媒体来自影视数据库索引,而不是磁盘上的源文件。这也意味着,直接删除源视频不仅不能解决问题,反而可能造成数据损失。
二、发现多个 trim-media 进程
检查影视进程和 8005 端口:
pgrep -a -x trim-media
ss -ltnp 'sport = :8005'
结果显示,系统中同时存在多个 trim-media 进程,但只有第一个进程成功监听 8005。
影视日志中出现了明确的端口冲突:
listen tcp :8005: bind: address already in use
listen udp 0.0.0.0:7359: bind: address already in use
问题在于,后续进程虽然无法绑定端口,却没有立即退出,仍然可能继续运行文件监听、扫描和数据库任务。
于是形成了这样的结果:
多个 trim-media 实例
→ 同时扫描同一个媒体目录
→ 分别向数据库写入媒体记录
→ 同一路径生成多个 GUID
→ 媒体库出现重复项目
三、是谁在重复启动 trim-media
通过进程父子关系,找到了完整启动链:
systemd
└─ /usr/trim/bin/trim_app_center
└─ /var/apps/trim.media/cmd/main start
└─ trim-media
真正发起启动的是飞牛应用中心服务:trim_app_center.service。影视并没有单独的 systemd 服务,而是由应用中心调用应用自带的控制脚本启动。
影视控制脚本位于:
/var/apps/trim.media/cmd/main
/var/apps/trim.media/cmd/common
控制脚本通过下面的请求判断影视是否启动成功:
curl -s --max-time 3 localhost:8005/v/api/v1/sys/pid
正常情况下,该接口应该返回当前 trim-media 的 PID。但实际现象是:
- 影视已经成功监听 8005;
- PID 接口检查却一直失败;
- 控制脚本不断记录
trim.media is not running;
- 应用中心因此长期停留在“启动中”;
- 自动重试或再次点击“启用”时,又会启动新的实例。
更麻烦的是,启动脚本没有单实例锁,状态检查也没有核对 PID 文件或现有进程。因此,只要 PID 接口检查失败,每次启动任务都可能创建一个新的 trim-media。
四、PID 接口真的坏了吗
原本很自然会怀疑 localhost:8005/v/api/v1/sys/pid 接口可能出现了兼容性问题。但使用详细模式测试后发现,curl 并没有直接访问本机 8005,而是连接到了 Mihomo 的 SOCKS5 代理:
Trying <MIHOMO_IP>:7890...
SOCKS5 connect to localhost:8005
继续检查 root 用户的配置:
nl -ba /root/.curlrc
发现其中配置了全局代理:
-x socks5h://<MIHOMO_IP>:7890
这就是整个问题的关键。
为什么 socks5h 会影响 localhost
socks5h 中的 h 表示由代理端解析目标主机名。因此,当系统执行:
curl localhost:8005
请求流程实际上变成了:
飞牛主机
→ Mihomo SOCKS5 代理
→ 代理端解析 localhost
→ localhost 指向代理所在环境,而不是飞牛主机
影视的 8005 端口明明监听在飞牛主机上,但请求却被送到了代理端的 localhost,自然无法获得 PID。
应用中心运行在 root 身份下,因此它调用的 curl 会自动读取:
/root/.curlrc
这也解释了为什么手动访问影视正常,而应用中心始终判断启动失败。
五、最终解决方法
最终同时采用了两层修复。
1. 为本机地址设置代理例外
在 /root/.curlrc 中加入:
--noproxy localhost,127.0.0.1,::1
完整配置可以写成:
--connect-timeout 10
--noproxy localhost,127.0.0.1,::1
-x socks5h://<MIHOMO_IP>:7890
这样:
- 外部网络请求仍然经过 Mihomo;
-
localhost、127.0.0.1 和 ::1 始终直接连接本机;
- 其他依赖 curl 的本地系统服务也不会被错误送入代理。
2. 让影视控制脚本忽略 .curlrc
影视控制脚本中有两处 PID 请求,分别用于状态检查和停止服务。
将原命令:
RESPONSE=$(curl -s --max-time 3 $URL)
改为:
RESPONSE=$(curl -q -s --max-time 3 "$URL")
这里的 -q 必须放在第一个参数位置,其作用是禁止 curl 加载默认配置文件。
两处都需要修改:
/var/apps/trim.media/cmd/common
对应功能分别是:
-
daemon_status:判断服务是否已经启动;
-
stop_daemon:停止服务前获取 PID。
其中,--noproxy 解决系统范围的本机直连问题,curl -q 则为影视启动脚本增加一层独立保护。
需要注意:影视应用升级后,官方安装包可能覆盖控制脚本,因此 .curlrc 中的 --noproxy 规则才是更加持久的基础修复。
六、修复后的验证
修改完成后,分别测试普通 curl 和忽略配置文件的 curl:
curl -sv --max-time 3 \
localhost:8005/v/api/v1/sys/pid
curl -q -sv --max-time 3 \
localhost:8005/v/api/v1/sys/pid
两次请求均直接连接:
127.0.0.1:8005
接口返回:
HTTP/1.1 200 OK
并正确输出当前 PID。
最后检查应用状态:
pgrep -a -x trim-media
ss -ltnp 'sport = :8005'
appcenter-cli status trim.media
验证结果:
- 只有一个
trim-media 进程;
- 8005 只有一个监听者;
- 应用中心状态为
running;
- 飞牛影视可以正常打开;
- 不需要卸载重装。
七、排查过程中得到的经验
不要看到重复媒体就直接删除文件
应先判断重复发生在文件系统还是媒体数据库。相同路径对应多个数据库记录,通常意味着扫描器或多个服务实例重复写入。
UI 状态不等于实际状态
应用中心显示“启动中”时,服务可能已经启动。应同时检查:
pgrep
ss
以及日志。
不要连续点击“启用”
如果第一次启动已经创建了后台进程,但健康检查失败,重复点击可能继续创建新实例。
谨慎使用 root 用户的全局代理配置
/root/.curlrc 不只影响手动执行的命令,也可能影响所有以 root 身份调用 curl 的系统服务和应用脚本。
全局配置代理时,至少应排除:
localhost,127.0.0.1,::1
本地健康检查应避免继承用户配置
系统服务中的本地健康检查,更稳妥的写法是:
curl -q -s --max-time 3 \
http://127.0.0.1:8005/v/api/v1/sys/pid
更完善的启动脚本还应加入:
- PID 文件核验;
- 进程唯一性检查;
-
flock 单实例锁;
- 端口占用检查;
- 启动失败后终止残留进程。
总结
这次故障表面上表现为三个独立问题:
- 飞牛影视媒体库重复;
- 应用中心一直停留在“启动中”;
- 后台存在多个
trim-media 进程。
实际上,它们来自同一条故障链:
/root/.curlrc 强制 localhost 走 socks5h
→ PID 健康检查访问了错误的 localhost
→ 应用中心误判影视没有启动
→ 重复拉起 trim-media
→ 多个实例同时扫描和写数据库
→ 媒体库产生重复记录
最终通过为本机地址设置 --noproxy,并让影视控制脚本使用 curl -q,恢复了正确的状态检测和单实例运行。问题解决后,影视可以正常启动,无需重新安装。
原文链接:https://d.cellmean.com/p/d9208c3049d4
FAQ
1. 为什么同一个视频在飞牛影视中重复显示两三次,但文件系统里只有一个文件?
这是数据库索引重复,而不是磁盘文件重复。影视后台出现了多个 trim-media 实例,同时扫描同一个媒体目录,分别向数据库写入媒体记录,导致同一文件路径对应多个不同媒体 GUID。直接删除源文件不能解决问题,反而可能造成数据损失。
2. 为什么影视应用一直显示“启动中”,最后提示“本地应用启动失败”?
影视实际可能已经启动并监听 8005 端口,但应用中心控制脚本的健康检查失败。原因是脚本执行的 curl localhost:8005 被 /root/.curlrc 中的 socks5h:// 代理转发到代理端,请求没有到达飞牛本机的影视服务。应用中心误判影视没有启动,不断重新拉起新实例,进而导致端口冲突和后台多进程问题。
3. 为什么 .curlrc 里配置了 socks5h 代理,会导致 curl localhost 访问不到本机服务?
因为 socks5h 中的 h 表示目标主机名由代理服务器解析。请求 curl localhost:8005 时,代理会在代理所在环境中解析 localhost,最终指向代理端,而不是飞牛主机本身,因此无法访问飞牛影视的 8005 PID 接口。
4. 这次故障最终是如何修复的?
有两层修复:
- 在
/root/.curlrc 中加入 --noproxy localhost,127.0.0.1,::1,保证本机地址不走代理;
- 修改
/var/apps/trim.media/cmd/common 中两处 PID 请求,把 curl 改为 curl -q,让影视控制脚本忽略 /root/.curlrc。
5. 影视应用升级后,是否需要重新修改控制脚本?
有可能需要。影视应用升级后,官方安装包可能覆盖控制脚本,因此 curl -q 的修改可能丢失。更持久的基础修复是在 /root/.curlrc 中配置 --noproxy localhost,127.0.0.1,::1,让本机地址始终直连。