五个半月,从一条 cron 长成一条媒体流水线:我的夸克 → Jellyfin 折腾全记录
写在前面
今年 5 月 23 日晚上十一点多,我把一个 quark_auto_save.py 扔到服务器上,第二天凌晨五点建了第一条 cron。到今天,一百六十二天。
这五个半月里,这东西换过三次架构,被推倒重来过两轮,我给它打了七个回滚点,最近三天还被一个 AI 团队连着审了五轮、提交了三十五次代码。现在它每天安安静静地跑,库里躺着 1644 条播放文件,一集新番从频道里冒出来到我在 Jellyfin 上点开,大概两分钟。
这篇不算教程,就是过程记录。写给自己看,也给同样在这条路上踩坑的人。
阶段零:最初那版特别朴素
痛点特别具体:我在 Telegram 上关注资源频道,看到想看的剧,得手动复制分享链接、打开夸克、粘贴、选目录、转存。一天来回十几次,全是体力活。
第一版就干这一件事:Telethon 登录,监听频道新消息,正则抓 pan.quark.cn/s/xxx,按关键词过滤(斗破苍穹、完美世界这些),调夸克接口转存到指定目录,同名去重,失败重试,存完推我一条通知。
那时候功能就这些:
- 实时监听频道,秒级响应;
- 关键词过滤,只存我感兴趣的;
- 同一个资源五天内不重复转存;
- 转存结果推微信。
6 月 15 日我写过一篇介绍它的文章,代码开源在 GitHub 上。那一版有效逻辑一百多行,但省掉了我百分之八十的重复劳动。这个比例后来被一次次打破,因为我慢慢发现,「能用」和「好用」之间隔着五个月。
阶段一:能存下来不等于能看
真正麻烦的是存下来之后。
我得用播放器去看夸克里的文件,每次都要输密码、翻目录、等缓冲。于是我开始折腾 strm——一种只有一行 URL 的文本文件,播放器读到它就当成一个视频来播。只要生成一堆 strm、让 Jellyfin 去扫这个目录,网盘里的片子就成了「媒体库里的条目」。
那个月的关键词是 strm、画质、4K、失效:
- strm 里到底该写什么 URL,直链有有效期,过期就得重新解析;
- 一集有 1080P、4K、HDR 好几个版本时留哪个;
- 分享者把链接封了怎么办,「源已失效」这四个字我见过太多次。
到 7 月底,思路定下来了:网盘只当仓库,本地只放 strm 壳子,播放器自己去拉流。
阶段二:302,让字节不经过我的服务器
8 月 1 日,我第一次在对话里写下「302」这个词。回头看,这是整件事里最重要的一次选择。
问题是这样:就算 strm 里是网盘地址,如果让服务器去代理拉流再吐给播放器,一部 4K 电影几个 G 的流量就全算在我这台 1.6G 内存的小 VPS 头上,一晚上就能把它打死。
302 重定向解决的就是这个:服务器只回一句话,你要的东西在那个地址,自己去拿,然后播放器直接连夸克的 CDN。我在服务器上实测过,整个响应 497 字节。片子一个字节都不经过我的机器。
所以服务器实际只干三件事:拿着我的网盘凭据去问夸克要真实地址;把这个地址包成 302 回给播放器;剩下的什么都不管。
一台 38G 硬盘的单核小机器能扛住整个媒体库的播放,秘密就这一句话。
8 月还有件事:我第一次让 AI 参与 review。当时只是找人帮忙看看边界情况,没想到两个月后这变成了主要工作方式。
阶段三:OpenList 和 Jellyfin 落地
10 月 19 日是个转折点。那天我连建了三个 cron:官方自动转存、strm 同步、Cookie 健康检查,同时把 OpenList 接进来——它负责跟网盘对话(列目录、解析直链),nginx 负责把请求转给它、再把它返回的 302 转出去。
从那天起,jellyfin.wenxixixi.cc 和 quark.wenxixixi.cc 两个域名同时在干活:一个给我挑片子看,一个给播放器取流。
也是从那天起,我第一次实现「在手机或电视上点开就看」,不下载、不等待、不占本地空间。
阶段四:从跟着官方,到自己会找
10 月 26 日我在对话里第一次写「灰度」,说明要上硬东西了:事件驱动的发现器,我管它叫 p1。
在那之前是被动的。夸克官方的「自动转存」分享更新了,我才能拿到新集。这个模式有个致命问题:分享者一旦停更,或者链接被封,这条剧就死在那儿了,而我这边什么都不知道。后来我数了一下,42 个历史分享里,13 个源已经 dead。
新思路是主动的:常驻监听 Telegram 频道,把每一条新分享当成一个「事件」写进事件流,再每五分钟消费一批,按剧名匹配、按集数判断该不该存。它跟官方追更形成两条腿,一条找新,一条兜底。
10 月 31 日晚上七点五十四分,发现器上线,第一条事件落盘。同一晚我还加了两个东西:三方对账(Jellyfin 里的条目、nginx 的映射表、磁盘上的 strm 必须完全一致),和 watchdog(轮次卡住、连续失败、清单落后、ffmpeg 泄漏全报警)。
那天晚上我做了个决定:把这摊活交给 DeepSeek Harness。它是我跑在 Mac 上的另一个 agent,我让它带团队(Claude Code 和 Codex)接手做全面审查和重构。
阶段五:把「能跑」变成「跑得对」
这是最密集的 48 小时。二十六小时里 35 次提交,五轮审查,七个回滚点。
先说它审出来的东西有多严重,这些都不是体验问题,是会安静丢数据的问题。
fid 毒丸。 标题兜底和人工确认生成的文件对象里没有 fid 字段,保存函数一取就 KeyError;而异常发生在事件偏移写回之前,于是每五分钟崩一次、偏移永远不动。这条链等于死了,但表面上一切正常。
事件一次性消费。 保存失败时事件偏移照样提交,网络抖一下那条事件就永久消失,不会重试。
跨季 E01 被跳过。 集号只按数字归一,第二季的 E01 被判成「第 1 集已存在」,直接跳过。
预告片被当正片。 更新列表里是 166话 4K预告片.mp4 和 扫一扫去群里面.jpg,脚本不分类、无差别全存,存完还报「有新集落盘」,而真正的 165 还在分享里没拿到。
落点写错。 这条最隐蔽。官方追更链路根本没有选目录的能力:saveas-update 只会问夸克「这条分享上次存哪」,然后塞回去;白名单又把「来自:分享」当合法目录放行;脚本从没引用过剧名映射表;回读校验对这个目录永远返回「未校验」。于是它一直写错、还一直报成功。
修的时候我没自己动手写代码,是让 agent 团队分工:Claude Code 管「落点」那条线,查为什么写到「来自:分享」、怎么纠偏;Codex 管「预告片」那条线,查为什么被当正片、怎么过滤;主 agent 也就是 DSH,负责复核 diff、跑测试、备份、部署、线上验收。
它们立的几条规矩,我觉得比结果更值得记:代码只在本地改、禁止 agent 直接写服务器、每次上线前打备份点、改完必须在服务器上用隔离状态做沙盒回归。
最后上线的改动是:统一的文件分类模块(正片、特典、噪音三分),官方追更只存正片,按剧名纠偏落点,认不出归属就 fail-closed 不写,按(季,集)去重,再加两道护栏挡住「预告片被兜底成某一集」。
现在的效果是:19:11 那轮把一个原本会落错目录的分享自动纠偏到仙逆,只存正片,回读校验成功;同一集 165 有三个不同压制的来源,库里有且只有一条;噪音 strm 为零。
现在这套长什么样
几个现在的数字:
| 项目 | 数值 |
|---|---|
| 播放文件(strm) | 1644 条 |
| 媒体库噪音(预告、花絮、图片) | 0 条 |
| 追更周期 | 固定 10 分钟,单轮 34 到 75 秒 |
| 服务器规格 | 单核、1.6G 内存、38G 硬盘(美国洛杉矶) |
| 片子存放 | 全部在夸克云端,本地只有壳子 |
| 播放流量经过服务器 | 0 字节,只发一个 302 |
同期顺手做掉的老账也记一下:把 76 个越界集号(第 100 集以上被识别成空)全部归位,给公网暴露的 8096 端口拉了 ufw,把 12 个脚本里硬编码的网盘弱口令轮换掉,备份从 829MB 每 25 分钟压到 85MB 每 2 分 21 秒。
关于让 AI 参与这件事,说几句实在的
这五个月里我最大的认知变化不在技术上,而在「怎么跟 AI 一起干活」。
第一,它写得比我快,但我得会验收。 那些 bug 里有一半是「看起来成功」的,日志写着「转存完成」,实际上写错了目录、丢了一条事件。只看它自述,我会以为一切正常。所以后来我养成习惯:它说修好了,我就自己去看状态文件、数一遍集合差、curl 一次那条播放地址,看是不是真的 302 到网盘。
第二,好的工作形态是可以要求的。 这轮让我满意的不是它修得快,而是它自己做的这些:改之前打备份点、新代码先放隔离目录、dry-run 用独立状态文件、失败显性化、每个结论都附证据(md5 对比、行号、磁盘实测)。这些不是我提的要求,是它交给外部 agent 干活时本来的样子。但我以后会拿这个标准去要求下一轮。
第三,代价也得说。 这两天我自己的时间基本花在核验上:它说改了五个文件,我核 md5;它说验收全绿,我数 strm 数量;它说把错位目录搬回来了,我去 OpenList 里看还在不在。这不是不信任,这是这种协作方式的固定成本。
自研还是用现成的,我的判断变过一次
审完 GitHub 之后我的结论改过一次,记下来。
市面上确实有成熟方案,MoviePilot 支持夸克、有整套 strm 加 302 的做法;115-media-hub 甚至做了「同步 TG 公开频道、盘搜、多网盘入库」这一整套。所以「用 TG 找源再入库到网盘」这个想法本身,不是我的独创。
但它解决的是另一类人的问题:网盘当库,一切围绕网盘,尤其是 115 为主库的那套生态。我的要求正好倒过来:夸克是主库、302 直连、字节不能过服务器,还要按我自己的口径判断——不历史回填、不删源文件、4K 优先但 HDR 扣分、同为 4K 不升级、待确认的集等我一句话。
这些口径在现成工具里都得改它的规则来适配,改别人的架构比自己维护还费劲。所以自研是成立的,前提是我得接受「重新踩一遍别人踩过的坑」这个成本。这两天那五轮审查,本质上就是在补这一课。
给后来人的坑清单
如果哪天你也想搭一套,下面这些是我真金白银踩出来的。
- 网盘直链有有效期,别把直链写进数据库,每次现解析现发 302。
- 别让服务器碰视频字节。1.6G 内存的机器能服务整个媒体库,靠的就是这一条。
- 「存成功」不等于「进库成功」。一定要有回读校验,而且校验必须有能力返回失败,永远返回「未校验」的校验等于没有。
- 同一集多来源要按(季,集)去重,不然 4K 和 1080P 各来一份,库里全是重复。
- 预告片、花絮、扫一扫二维码这些文件一定会进来,提前做分类,别等它变成某一集。
- 事件偏移只在成功之后推进,否则一次网络抖动就永久丢一条。
- 通知要能降噪。我一度每十分钟收到一条「播放清单落后 17968 秒」,上午八点五十到十二点整收了 20 条,后来发现那个指标本身就是错的,改了判据才停。
- 监控指标要验证它是不是真指标。我最惨的一条告警,比的是文件修改时间和扫描时间,这两个永远对不上,所以它永远不会自愈。
代码
这一整套的代码在这里,欢迎拍砖:
- 主项目:wenxixixi-miao/quark-jellyfin-strm,夸克网盘 → OpenList → Jellyfin 全自动媒体库,strm 加 nginx 302 直连,视频字节不经服务器。
- 早期版本:wenxixixi-miao/telegram-quark-monitor,最初那个 Telegram 频道监听加关键字转存的小工具,现在已经退役,留个纪念。
最后
这套东西现在的状态是:我不折腾它的时候,根本感觉不到它在工作。只在有新集的时候,它会安静地出现在媒体库里。
我不确定还会不会继续加东西,按这五个半月的规律,大概会。但有一点是确定的:它已经从「我自己写的一个脚本」,变成了「我要对它的正确性负责的一个系统」。这中间的差别,大概就是这篇记录的全部意义。