自动摘要
正在生成中……
问题背景
飞牛影视的媒体库突然出现大量重复项目:同一个视频文件在界面中显示两到三次,但文件管理器里实际上只有一个源文件。

随后,应用中心也开始出现异常:影视应用无法正常启用,点击“启用”后长期停留在“启动中”,最终提示本地应用启动失败。

- 影视应用显示“未启用”;
- 点击“启用”后一直停留在“启动中”;
- 最终提示“本地应用启动失败”;
- 后台出现多个
trim-media 进程;
- 强制终止后,进程有时又会被重新拉起。
最初看起来像是媒体数据库损坏,甚至一度考虑卸载重装。但深入排查后发现,根因并不在影视应用本身,而是一条全局 curl 代理配置。
一、确认重复的是索引,而不是文件
首先检查媒体源目录,确认同名视频在文件系统中只有一个实体,并不存在重复文件或硬链接。
进一步检查影视数据库后发现:
- 相同文件路径对应多个不同的媒体 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,恢复了正确的状态检测和单实例运行。问题解决后,影视可以正常启动,无需重新安装。