MoonTV —— 一次 Vibe Coding 尝试
大概半年前,就一直在关注一个项目 LibreTV,这是一个影视聚合项目,聚合各种采集站的资源做统一搜索和播放。当时想着给 npy 部署一个追剧平台,就关注了一下这个项目,后面还给这个项目贡献了不少代码,包括更换 html5 播放器,对这个项目的运行逻辑也愈发熟稔。后面用着用着,这个项目的弊端也不断展现出来:这是一个纯前端项目,播放记录等信息都存储在浏览器的 localstorage 中,更换浏览器就会丢失;代码都是纯 JS 写就的,经过不同人手,代码也比较混乱,由于没有静态检查,很多代码都不太敢动,也很难进行大改。
大概过了几个月,同事告诉我 cursor 支持支付宝付款了,精神为之一振。此前一直使用 Trae,模型能力较差不说,高峰期还需要排队,后面又开始收费,免费版问答动辄排队三四百人,几乎到了不可用的状态。于是火速入了一个月 Cursor Pro,准备体验一波,正好拿来重写 LibreTV。
完全没有任何前端经验的我,开始了 Vibe Coding,大部分代码都是和 Cursor 对话后直接完成。最终,MoonTV 应运而生:
Loading repository data...
目前只过去了一个月,就已经有了 4.6K Star 和 5.4K Fork(出现不 star 的白嫖党),Star 趋势如下,足以见得国内对于 D 版视频的需求还是很大(全拜爱优腾的各种不做人的操作和那一堵高高的墙所赐)。
项目基于 NextJS 和 React,支持 Vercel、Cloudflare Pages 和 Docker 部署,支持 localstorage、Redis 和 Cloudflare D1 三种存储方式,Redis 和 Cloudflare D1 存储时支持多账户信息隔离和跨浏览器信息同步,以及方便快捷的管理面板,后续还计划支持对接 Upstash。
这也是我第一次 Vibe Coding 尝试、第一次前端/全栈(全栈这个词似乎只有 node/JS 生态在使用)项目尝试和第一次(比较正经的)开源项目尝试。由于 Cursor 的加持,MoonTV 的大部分开发工作从 Code 变成了 Code Review。有什么需求只需要和 Cursor 聊一聊,就可以完成。当然这个聊一聊也是有一些技巧的。
本次 Vibe Coding 体验遇到的第一个困难就出现在体验的最开头。由于对前端一窍不通,对很多流行技术栈也只是有“只知其名不知其意”的了解程度。为了追新,我决定使用对 Serverless 友好的 NextJS 和 Tailwind CSS 作为项目的主体框架。于是,新建文件夹,我要求 AI 帮我初始化一个 NextJS 和 Tailwind 项目。然后反复折腾了半个下午,AI 初始化的项目都无法成功运行,基本都是样式丢失,页面看起来像是纯文本堆出来了,百思不得其解之下,最终放弃,在 github 上找了个脚手架项目作为基座,后续开发才得以正常进行。
正式的开发过程都挺顺利,AI 模型我是基本使用这两个:Claude Sonnet 4.0 和 GPT O3。整体使用下来,这两个模型偏好也很明显:Sonnet 4.0 适合对整体项目做大修改,有时候即使是修一点小问题,AI 也会尝试重构整个项目,更适合用来实现新功能。GPT O3 则更适合对项目做小修小补,它会控制修改在一个很小的范围内,不会无序地去修改整个项目的文件,更适合用来修补一些已有功能的 bug。
当然也有一些 AI 力所未逮的情况。在实现播放页时,由于状态转移复杂,很多数据之间存在依赖和关联,AI 实现的版本中,数据依赖几乎成了一个网状结构,一旦有一个数据变更,如切换集数或更换播放源,最终传递到播放器的就可能是重复的五六次初始化,导致播放器闪烁。试了很多模型都无法解决这个问题,最终亲自上手,梳理了数据依赖传播路径,才最终解决了重复初始化的问题。
另外一些 AI 无法解决的情况就比较小众了,比如 m3u8 的视频大部分播放器不会开启 airplay 或 chromecast 投屏功能,但实际上是可以通过一些小 trick 强制开启的。这个需求直接问 AI,要么直接告诉你不可能,要么就是给出一些错误的解决方案,甚至可能会告诉你 xxx 播放器可以要不我们换 xxx 播放器吧。最终搜遍全网,在 hls.js 的一个角落发现了这个 issue,最终成功开启了 airplay。以 AI 目前的能力只能解决一些比较大众化的问题,过于小众或者甚至人类都无法解决的问题,暂时就不要指望 AI 了。
更大的困难其实来自于多平台多环境的适配,项目立项时的计划就是支持 vercel、CloudFlare pages 和 docker 部署,nextjs 原生就可以在 Vercel 上运行,只在 vercel 上倒是没有遇到什么困难。项目使用了一个 next-on-pages 依赖将代码翻译为 Cloudflare pages 代码,所以时常会出现一些兼容性问题。而对于 docker 的适配就更加复杂了,docker 下如接口处理只能跑在 node 环境下,但 nextjs 又保留了一个 edge 环境用于中间件处理(如鉴权等),这两个环境的内存也不是共享的,开发时需要严格地区分不同环境的依赖和可使用的 api。除此之外,客户端环境也是很复杂的一个情况,困难点主要来自于 webkit 内核,在 iOS 和 iPadOS 下,所有浏览器都只能使用 webkit 内核,而 webkit 内核的很多接口或 css 样式和 chromium 内核都有很大的差异,需要进行特殊的适配。
MoonTV 由于脱胎于 LibreTV,项目最初的宣传都是直接在 LibreTV 的交流群中进行的,项目专用的讨论群也是直接使用的 LibreTV 的讨论群。在主动宣传上基本只在 linux.do 论坛上进行,实际上也只是在有一些大更新的时候发一篇 changelog。到项目中后期发现很多科技博主也开始自发宣传起了 MoonTV:X 上关注的一些博主、TG 关注的科技频道,甚至 Youtube 和 Bilibili 以至于小红书上的很多 up 都开始主动教授起了 MoonTV 的搭建方式。
项目使用的人一多,各种 issue 就纷至沓来。issue 大概能分为如下两类:Bug report,这类 issue 会占大多数,写代码谁能不出点 bug 呢,更何况都是 AI 写的(转移矛盾);Feature Request:FR 又可以分为两种,合理的需求 Request 是为了项目可以发展的更好,多是一些布局上、交互逻辑上或对接数据上的需求;最不受欢迎的就是商业化功能的 Request,这类人大概只是想着搭建这个项目后拿出去运营收费,但由于项目本身没有商业化功能,自己又没有能力实现,就跑去提 FR。这种人好逸恶劳只想着白嫖,自然是希望越少越好的。曾有个哥们一口气提了好多个类似的 issue,被 ban 了后还跑到 tg 群里继续纠缠,属实是讨厌可恶。还有一类:无效 bug,多是因为没有自己阅读 Readme 导致的部署失败,或者自身网络问题无法搜索,于是在 issue 中大呼小叫,遇到这类 issue,连讨论群群友路过都会吐口唾沫再走。
当然一个项目名声起来后,围绕这个项目产生的周边项目也会开始出现。MoonTV 就有一个 OrionTV 的客户端实现,专供 Android TV,可以直接使用 MoonTV 作为其后端,同步播放记录。交流群群友们也有各自实现的 Android 版和 iOS 版,当然由于各种限制 iOS 版是无法上架的。
Loading repository data...
以上。这篇文章本来计划是 7 月处写完,那时 star 也仅有 2k,但一直拖延,毕竟写文章肯定没有写代码有趣。我这个人的特点就是如此,一旦手头有什么事情,就无时无刻不去想着,无论是吃饭还是睡觉。两三点想到一个 bug 爬起来修都是常有的事情,连续两周每天只睡了六个多小时,也算是夜以继日日以继夜了。