公众号旧文

为什么我一直沉迷于做 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 最吸引人的地方从来不只是页面。

而是它解决了一个非常实际的问题:

分发。

写完程序只是第一步。

让别人能够方便地用上它,才是我真正觉得有意思的地方。