公众号旧文
为什么我一直沉迷于做 Web 服务
#因为方便分发
近几个月,我一直在折腾自己的 YouTube 下载站。
最开始的想法非常简单:输入一个 YouTube 链接,服务器帮我下载,然后浏览器直接拿到文件。
但真正做下去以后,我发现自己真正感兴趣的,似乎并不是 YouTube 下载本身。
我更喜欢的是另一件事:
把一个原本只能在自己电脑上运行的工具,变成任何设备打开浏览器就能使用的 Web 服务。
这也是为什么我一直沉迷于做 Web 服务。
原因特别简单:方便分发。
命令行工具很好用,但它不适合所有人
比如 yt-dlp。
对于熟悉 Linux、Python、命令行的人来说,它已经非常好用了。
SSH 登录服务器,然后执行:
yt-dlp 视频地址
几乎就够了。
但问题是,如果我想让另外一个人使用呢?
我要告诉他怎么安装 Python,怎么安装 yt-dlp,怎么更新版本,怎么下载 FFmpeg,还要解释 Cookie 放在哪里。
如果对方用的是手机,事情就更麻烦。
这时候 Web 的优势就出来了。
我只需要给对方一个网址。
他打开网页,粘贴链接,点击下载。
剩下所有复杂的东西,都留在服务器里面。
用户看到的是一个按钮,而服务器背后可能跑着几十行甚至几百行逻辑。
这就是 Web 服务最让我着迷的地方。
我的 YouTube 下载站,本质上就是给 yt-dlp 套了一层服务
从代码结构来看,我这个下载站其实并不复杂。
Flask 提供 HTTP 接口,真正负责下载的还是 yt-dlp。
用户提交链接以后,后端先创建任务,再生成一个 UUID 作为任务 ID,然后启动线程执行下载。
前端只需要不断查询任务状态,就可以知道当前是下载中、完成还是失败。
例如代码里面有:
POST /task
GET /status/
用户并不需要知道后台启动了什么命令。
他甚至不用知道服务器是 Linux。
这正是我想要的效果。
把复杂度留给服务端,把简单留给使用者。
真正折腾的,都是用户看不到的地方
下载视频这件事情,看起来只是运行一次 yt-dlp。
实际做成长期运行的 Web 服务以后,问题一下子就多起来了。
例如 Cookie。
YouTube 对数据中心 IP、登录状态、验证机制都有各种限制,所以我的代码并不是永远固定使用一个 Cookie。
服务器会扫描 Cookie 目录,按照更新时间排序,并选择最多两个 Cookie 作为可用槽位。第一个失败以后,还会尝试第二个。
真正执行任务的时候,我又不会直接拿原始 Cookie 文件运行。
每个下载任务都会复制一份独立的临时 Cookie,权限设置为 600,任务结束以后再删除,避免不同任务之间互相影响。
下载策略也不是简单粗暴地只尝试一种格式。
代码里面定义了多个下载 profile,会优先尝试较高质量的视频,如果数据中心 IP 遇到 CDN 限制,再逐步降低策略继续尝试。
下载之前还会提前估算文件大小,并根据预计大小计算所需磁盘空间。
因为视频下载过程中可能同时存在视频流、音频流、临时文件以及最终合并文件,所以代码不是简单判断还剩多少磁盘,而是额外预留了空间。
这些东西,用户都不会看到。
但是我觉得 Web 服务有意思的地方恰恰就在这里。
用户看到的越简单,后台通常越不简单。
我甚至给下载站做了垃圾回收
一个 Web 服务如果长期跑,还有一个很现实的问题:
磁盘会爆。
所以我的视频并不会永久保存在服务器上。
代码里面设置了:
FILE_EXPIRE = 1800
也就是下载完成的文件保存大约 30 分钟。
后台还有一个常驻清理线程,每隔一段时间扫描下载目录,把过期文件、残留的 .part 文件、.ytdl 文件,以及过期任务清理掉。
临时 Cookie 也有自己的清理机制。
运行超过一定时间的残留 Cookie 会被自动删除,并且清理程序会避开当前还在使用的 Cookie。
某种程度上,这已经不像一个简单脚本了。
它开始慢慢有了一个真正服务应该有的东西:
任务管理、资源隔离、失败处理、磁盘控制、日志、统计和自动清理。
为什么我越来越喜欢 Web 服务
以前我写一个脚本,只要自己能运行就行。
后来我越来越喜欢把脚本变成网页。
因为我发现:
软件真正好不好用,和功能强不强并不是完全一回事。
一个功能再强的工具,如果需要用户安装十几个依赖、配置环境变量、执行命令,他的使用门槛依然很高。
但是如果我把这些事情全部放到服务器上,对方只需要一个浏览器。
电脑可以用。
手机可以用。
iPad 可以用。
甚至完全不懂 Python、不懂 Linux 的人也可以用。
这就是 Web 最大的优势之一。
它天然就是一种分发方式。
服务器就是我的运行环境
我的 Flask 服务最终只监听:
127.0.0.1:5001
而不是直接暴露到公网。
外面再通过反向代理提供真正的网站入口。
这样一来,我真正维护的核心始终是服务器。
更新功能,我改服务器。
修复 Bug,我改服务器。
升级 yt-dlp,我还是改服务器。
用户什么都不用做。
下一次刷新网页,他看到的就已经是新版本。
这和桌面软件完全是两种思路。
如果是一个 exe,我每次更新,都要重新打包、重新发布,还要让用户重新下载。
Web 不一样。
我只需要部署一次,所有人立刻使用同一个版本。
这也是我为什么越来越喜欢 Web。
分发,比功能本身更让我上瘾
仔细想想,我折腾 YouTube 下载站这么久,真正让我持续有兴趣的,并不是下载视频这件事情。
下载视频早就已经有成熟工具了。
真正让我感兴趣的是:
怎么把它做成服务。
怎么让别人不需要安装任何东西。
怎么让手机也能用。
怎么处理 Cookie。
怎么做任务隔离。
怎么避免磁盘爆掉。
怎么统计到底有多少人在使用。
我的代码里专门记录了任务数量、成功数量、失败数量、下载次数、访问 IP 数量以及累计流量。
做到后面,你会发现自己已经不是单纯在写一个下载脚本了。
而是在不断思考:
怎样把一个能力包装起来,然后通过网络交给别人。
这大概就是我一直喜欢提供 Web 服务的原因。
我喜欢服务器。
喜欢域名。
喜欢反向代理。
喜欢把一个命令行工具包装成网页。
也喜欢看到一个原本只有我自己能运行的程序,最后变成别人只需要打开一个网址就能使用的东西。
因为对我来说,Web 最吸引人的地方从来不只是页面。
而是它解决了一个非常实际的问题:
分发。
写完程序只是第一步。
让别人能够方便地用上它,才是我真正觉得有意思的地方。