
我用两天,把一个“批量保存推特长文”的念头,折腾成了一个真的能装到 Mac 里的软件
这两天,我和 Codex 干了一件挺荒唐的事。
一开始,我只是想把几个 X 博主写过的长文批量保存下来。结果两天之后,电脑里多了一个能安装、能打开、能批量下载、能自动同步、还能在本地播放视频的 macOS 软件。
它现在有了自己的名字:MarkX。
官网也已经上线了:markx.defou.ai
我不是程序员,就是一个经常在 X 上看文章、做内容、折腾 AI 的文科生。所以这篇不准备讲什么 Swift 技术教程,我想把这两天真实的开发过程写下来。
因为我第一次这么具体地感受到:AI 确实把“做软件”的门槛打下来了,但把一句需求变成一个真的能用的产品,中间隔着的东西,还是多得有点吓人。
事情是从一个很小的需求开始的
我平时会关注很多写长文的 X 博主。
问题是,X 上的内容适合刷,不太适合沉淀。今天看到一篇好文章,顺手点个书签,过段时间再想找,基本就进入了考古模式。
我想要的东西其实很简单:输入一个博主,软件自动找到他写过的 X Article,把标题、正文、段落、列表、代码、图片和视频全部保存成 Markdown,放在我自己选的文件夹里。
最好还能一次抓多个博主,后面有新文章就继续同步。
文章是我的资料,文件放在我的 Mac 上。哪天不用这个软件了,Markdown 也还能继续用,不会被锁在某个平台里。
听起来不复杂吧?
我一开始也这么觉得。
第一版很快就出来了,然后问题也一起出来了
我把需求告诉 Codex,先让它做技术验证,再写 PRD,然后才开始做软件。
第一天做出来的研发预览版已经挺像那么回事了:SwiftUI 原生三栏界面,左边是资料库和博主,中间是文章列表,右边可以直接看文章;本地会生成 Markdown、作者索引、总索引和 SQLite 数据库;图片可以缓存,也能接 uPic、S3 或 Cloudflare R2。
它不是网页壳,是一个真的能编译、打包、安装和打开的 Apple Silicon Mac 应用。
当时从 X 的真实响应里,已经能解析出一篇 792 字、包含 7 张图的 Article。自动化测试一共 34 项,31 项通过,3 项需要真实外部环境的测试明确跳过,0 失败。
第一版安装包也做出来了:App、ZIP、DMG 都有,DMG 里可以把软件图标直接拖到“应用程序”。
看到这里,我一度觉得:这不就快做完了吗?
事实证明,每次你觉得软件“快做完了”,一般都说明真正的问题还没出现。
第一个大转弯:能拿到文字,不等于拿到了文章
最早的方案依赖 X Developer API。
这个方案能取到内容,但很快就暴露出两个问题:一是按博主找文章,可能要扫描很多普通 Post,会产生成本;二是有些接口只给纯文字和媒体列表,文章里的标题层级、粗体、列表、代码块、图片位置,不一定能完整还原。
但我想保存的不是一坨“文字”。
我想保存的是一篇文章。
图片原本在第三段,就不能被统一扔到文末;引用的是另一篇 X Article,就不能把引用卡片里的作者、日期、互动数也塞进正文;页面里有一个我们不认识的内容块,也不能假装没看见,然后告诉我“下载成功”。
后来我们把整个采集架构推翻了。
MarkX 不再扫描博主的普通时间线,也不再依赖付费 API 去完成正常下载。它直接打开博主的 /articles 专属列表,只找真正的 X Article;然后复用我本机 Chrome 已经登录的 X 状态,在一个看不见的临时页面里,读取 X 已经渲染完成的文章结构。
这次改完,单个博主、批量博主、文章直链、重新拉取、立即同步和定时同步,正常链路全部变成了:X API 0 次调用。
更重要的是,MarkX 开始能按原来的顺序保存标题、段落、列表、引用、表格、粗体、斜体、代码块、链接和内文图片。
我们还定了一条有点轴的规则:不确定,就不提交。
页面没加载完整、图片数量对不上、正文顺序变了、遇到未知块、最终 Markdown 回读不一致,任何一个环节有问题,都只能标成“需要处理”或者“失败”,不能生成一篇看起来挺完整、实际上少了东西的假成品。
做资料归档,我宁可它诚实地失败,也不想几个月后才发现,自己保存的是残本。
然后我开始像一个真实用户一样挑刺
代码里的“大功能”跑起来之后,剩下的全是看起来很小、用起来很烦的东西。
比如“全部文章”最早还会按作者分组。我看着不顺眼,就改成按发布时间倒序平铺,作者只作为文章信息保留。选中某个博主时,再单独筛选他的文章。
比如已经添加过的博主,不能只能拉取一次。我需要今天下载 10 篇,下周还能重新检查 30 篇,或者直接跑完整历史。于是每个博主都加上了“重新拉取文章”,同一篇不重复,新文章继续加,内容有变化就安全更新。
比如删除文章,不能只是把数据库里那一行抹掉。
现在右键删除会先告诉我影响范围,再检查文件是不是真的位于归档目录里,然后把 Markdown 和来源快照移进 macOS 废纸篓,最后更新数据库和索引。远程图床里的图片和视频不会跟着误删,本地文件也还能恢复。
还有一些更细的东西:单个博主输入框就应该只有一行,按回车不能莫名其妙换行;侧边栏的“全部文章”“等待继续”“需要处理”应该显示真实数字;博主行别塞一堆常驻按钮,查看主页、重新拉取和删除放到右键菜单里就好。
下载窗口也改了好几次。
最早它更像一个开发调试窗口,后来才变成原生进度面板:显示正在发现哪篇文章、保存了几篇、跳过了几篇、失败了几篇、存了多少图片和视频。窗口可以隐藏到后台,关掉进度窗口也不会把正在下载的任务一起杀掉,想看时再从主窗口叫回来。
这些都不是什么“AI 黑科技”。
但一个软件好不好用,很多时候恰恰就死在这些地方。
最大的坑,是一篇带视频的真实文章
转折来自一次很普通的实测。
我在本地重新同步一个博主,发现一篇带视频的 X Article 下载失败了。那一刻我们才意识到:前面的图片链路做得再完整,只要文章里出现一个视频,整个“完整归档”的承诺就破了。
最省事的做法,是提示“暂不支持视频”,或者把视频跳过去,先把文字存下来。
但这不是我要的。
于是我们又把视频从头到尾补了一遍:从 X 页面和运行时媒体信息里找视频,只接受真实的 video.twimg.com 渐进式 MP4,排除 blob:、HLS、单独音轨和初始化分片;有多个清晰度时,选能够验证的最高分辨率版本。
下载下来之后还不能直接算成功。
MarkX 会继续检查 HTTPS、HTTP 状态、MIME、文件大小、MP4 的 ftyp 文件头和 SHA-256。选择图床时,上传完还要用 Range 请求回读,确认拿到的是 206、video/mp4 和有效文件头,最后才把视频放回文章原来的位置。
Markdown 里会生成可播放的视频块和备用下载链接,App 右侧详情页也要能直接播放。
技术上看,这条链路已经闭环了。
然后,App 一打开视频文章,崩了。
不是视频坏了,也不是下载错了。最后查到,是当前 macOS 26 上 SwiftUI 的 VideoPlayer 初始化触发了 _AVKit_SwiftUI 的运行时崩溃。
于是又返工,把播放器换成 AppKit 的 AVPlayerView。
换完以后,我们没有只看“程序不崩了”,而是拿那篇之前失败的真实文章重新跑了一遍:1 篇成功、0 篇失败、10 张图片、1 个视频。视频是 H.264/AAC,1438×720,时长 27.65 秒,文件 3,134,794 字节。
从最终 ZIP 安装出来的 MarkX 里,播放器识别到约 28 秒,并实际播放到 27.61 秒结束,App 进程保持稳定。
这才敢说:视频兼容完成了。
图床也给我上了一课
最早我希望 MarkX 直接复用 uPic 里的图床配置,这样图片、视频下载之后,就能自动传到我自己的 R2。
想法没毛病,但 macOS 的应用沙盒和容器权限很快给我们上强度了。MarkX 从自己的进程里读取 uPic 私有配置,会出现超时甚至假死。继续硬读,不但不稳定,还碰隐私边界。
最后的方案反而更干净:需要 uPic 时,就把已经校验过的本地文件交给 uPic 自己上传;MarkX 不读取、不复制、不打印它的密钥,只拿上传结果,再独立做 HTTPS 回读验证。
但做到这里,我又发现一个产品问题:为什么一个本地归档工具,第一次使用必须先配置图床?
这不是又给普通用户加了一道门槛吗?
所以 MarkX 后来把“本地存储”改成了默认选项。图片按 SHA-256 去重放到 media/images/,视频放到 media/videos/,Markdown 使用可移动的相对路径,不配任何图床也能直接用。
如果用户本来就在用 uPic,可以选择“本地 + uPic”:本地永远留一份备份,Markdown 使用验证后的远程链接。原生 S3 / R2 也继续保留。
这个变化看着只是设置里多了几个选项,背后其实是我对产品定位越来越清楚了:MarkX 首先是一个本地资料库,然后才是一个可以连接外部存储的工具。
真正难的不是成功,是失败以后怎么办
前面的版本还有一个让我很不舒服的问题:任务失败以后,用户很难知道到底发生了什么。
有时界面说队列结束了,但资料库里其实还有没处理完的文章;有时失败状态没有完整保留下来,重开软件以后像什么都没发生;有时重新下载,要从头再来。
后来我们把“失败”也当成正式产品能力来做。
现在每篇文章都有持久化状态:等待、处理中、失败、停止、完成。失败原因和最近 30 条处理日志会保留,重开 MarkX 还能看到;可以停止当前文章,可以单篇重新处理,也可以批量处理所有失败任务。
队列只有在一篇文章明确“已保存”“失败”或“用户停止”后,才会继续下一篇。结束时还会重新检查资料库,只要仍有未完成文章,就只能告诉我“本次队列结束,仍有未完成文章”,不能假装全部完成。
说实话,这条规则很笨,但我很喜欢。
因为很多软件最让我抓狂的,不是它失败,而是它明明失败了,还给我一个绿色对勾。
从“文件能保存”到“资料库真的能长期用”
随着文章越来越多,本地目录也开始暴露问题。
旧版本把每个博主的文件夹直接放在归档根目录,媒体、数据库和各种目录混在一起。后来我们把结构收拢成两个肉眼可见的根目录:Blogger/ 和 media/。
所有博主和 Markdown 进入 Blogger/,图片和视频进入 media/。旧版数据不会粗暴覆盖,而是先检查冲突,再迁移目录,同时更新数据库里的路径;发现同名不同内容就拒绝迁移,继续保留旧目录。
切换整个归档目录也一样:先处理 SQLite 的 WAL,再复制 Markdown、数据库、来源快照和媒体,逐个文件校验,全部通过之后才启用新位置。旧目录仍然保留。
这些功能写在发布介绍里不性感,甚至没人会因为“迁移前检查文件冲突”就转发一款软件。
但只要你真的拿它存过几百篇资料,就会知道,保护旧数据比做一个漂亮按钮重要多了。
做到最后,它才终于有了名字
前面很长一段时间,这个软件一直叫“X 长文归档器”。
名字很准确,也很像一个临时项目文件夹😂
直到核心链路基本稳定,我才把它正式改名为 MarkX。
这个名字里有 Markdown,也有 X,还有一点“把值得留下的内容标记下来”的意思。
图标也前后折腾了好几轮。最早想得太多,又是卡片、又是印章、又是折纸,后来还是回到最简单的黑白 X,在左上斜臂里放三条文章横线。
图形定了还没完。装进 macOS 之后才发现,主体太靠边、小尺寸不清楚,于是继续缩、继续留安全边距,重新生成整套 .icns,再重新打包。
到现在,MarkX 是 0.2.1 / Build 56。
从第一天的 Build 9 到今天的 Build 56,自动化测试从 34 项增加到了 76 项。刚刚重新跑了一遍:71 项通过,5 项需要 Chrome、uPic、真实 X 视频等外部环境的联调测试明确跳过,0 失败。
最终的 DMG 和 ZIP 也重新解包验证过:App 的版本、arm64 架构、签名、图标和“拖入应用程序”安装入口都通过,结果是 PACKAGE_VERIFY=PASS。
不过我还是要把边界说清楚:现在它仍然是研发预览版,只适合 Apple Silicon Mac;安装包目前是本机测试用签名,还没有做 Developer ID 公证,所以我不会把它包装成一个已经可以随便公开分发的正式产品。
能跑通,和能正式发布,是两回事。
这两天我最大的感受,不是“AI 会写代码”
AI 当然会写代码,而且确实很猛。
我一个文科生,能在两天里把一句模糊需求推进到一个原生 Mac 软件,放在以前,我连从哪里开始都不知道。现在我可以直接描述问题、看结果、挑刺、让它改,然后继续拿真实数据去试。
但这两天真正让我兴奋的,不是 Codex 一次写了多少行代码。
而是我开始理解,普通人和 AI 一起做产品,人的价值并没有消失,只是从“亲手敲每一行代码”,挪到了另外几个地方:
你到底有什么真实问题?
什么结果才算真的完成?
哪里只是测试绿了,用户其实还没法用?
失败时要保护什么?删除时不能碰什么?哪些数据必须留在本地?什么地方宁可报错,也不能给一个假的成功?
这些判断,AI 不会凭空替你负责。
MarkX 这两天几乎所有重要的变化,都不是来自一句“再优化一下”,而是来自一个特别具体的现场问题:文章列表看着别扭、旧博主不能重新拉、删除不够安全、下载窗口关掉会不会停、带视频的文章失败、播放器会崩、uPic 会超时、任务明明没完却说结束、文件夹越来越乱、图标在 Dock 里太挤……
一个个都不宏大。
但产品就是这样被磨出来的。
还有一个挺反常识的结论
两天做出一个软件,不代表软件开发只需要两天。
它只代表 AI 把“从想法到第一个可用版本”的距离压得非常短。以前很多普通人会死在第一步:不会编程、不会建工程、不会打包。现在第一步突然变得容易了,于是我们终于有机会撞上第二步、第三步和后面那一大堆更真实的问题。
怎么判断完整性,怎么处理隐私,怎么保护用户文件,怎么让失败可恢复,怎么证明最终安装包和开发目录里的代码是同一个东西,怎么避免一个真实视频把整个 App 干崩……
这些问题并没有消失。
只是现在,一个愿意把需求说清楚、愿意不断实测的普通人,也能坐到这张桌子上了。
这就是我这两天做 MarkX 最攒劲儿的地方。
它还不完美,甚至离正式发布还有签名、公证和更多真实样本要跑。但它已经不是一个聊天框里的想法,也不是一张好看的界面截图。
它是一个真的能装进 Mac、真的能把 X Article 变成本地 Markdown、真的会在出错时告诉我哪里没完成的软件。
接下来我还会继续拿它归档更多真实文章,继续挑刺,继续改。
如果你平时也会收藏很多 X 长文,你最希望 MarkX 下一步补上什么?
我想听听真实需求。搞不好,你随口说的一个痛点,就是下一个 Build 了。
我是得否,一个文科生 目前担任多家企业 AI 转型顾问 日常分享 OPC 实践和 AI 搞钱心得 如果你也想用 AI 做点真实的东西,欢迎关注我。 联系方式见 https://www.wangdefou.com/