一、前言:为什么值得自己部署一套
FlowPick 是一个隐私优先 的流媒体下载工具,同时提供浏览器扩展和在线网页两种形态,支持 HLS(.m3u8)和 DASH(.mpd)协议的视频流、音频、图片的嗅探、下载、合并与转码。它的核心设计是:所有媒体处理都在浏览器内用 FFmpeg WASM 完成,文件不落第三方服务器。
正因为开源、本地处理、可改可部署,很多团队会把它改造成内部工具(比如合规的网课归档、素材采集)。本文以FlowPick官方仓库为例,带你把项目在本地跑起来,并给出二次开发和上线的实操要点。
二、环境准备
| 依赖 | 版本建议 | 说明 |
|---|---|---|
| Node.js | 20 LTS 及以上 | Nuxt 4 的运行要求 |
| 包管理器 | pnpm | 仓库脚本基于 pnpm;npm/yarn 亦可但需自行对齐 lockfile |
| Git | 任意新版 | 拉取源码 |
确认环境:
bash
node -v # 建议 v20.x 或更高
pnpm -v
三、克隆与依赖安装
以官方仓库为例(若你已 fork,替换成自己的地址即可):
bash
git clone https://github.com/ezwebtools/flowpick.git
cd flowpick
pnpm install
注意:
package.json里配了postinstall自动执行nuxt prepare,会生成类型声明和路由清单,首次安装稍慢属正常。如果中途中断,可手动补一次pnpm exec nuxt prepare。
四、本地启动与调试
启动开发服务器:
bash
pnpm dev
默认监听 http://localhost:3000。几个常用入口:
/------ 落地页/m3u8-downloader------ HLS 下载器/dash-downloader------ DASH 下载器/docs------ 文档站(基于 @nuxt/content)/blog------ 博客
调试下载逻辑时建议直接开 /m3u8-downloader 或 /dash-downloader 页面,粘贴流地址即可,比扩展更方便。
五、构建与本地预览
生产构建:
bash
pnpm build
pnpm preview
pnpm build 产物在 .output/(Nitro 预设为 cloudflare-pages-static,输出的是静态资源)。本地预览用 pnpm preview 验证产物是否符合预期。
要跑类型检查或 lint:
bash
pnpm typecheck
pnpm lint
六、项目结构速览
bash
app/
├── pages/ # 路由页:index / m3u8-downloader / dash-downloader / docs / blog ...
├── composables/ # 核心逻辑
│ ├── useStreamMerge.ts # 切片下载、解密、合并、写入(下载引擎核心)
│ └── useFFmpeg.ts # FFmpeg WASM 加载与转码
├── components/ # UI 组件(Nuxt UI)
content/ # 文档/博客/多语言落地页(yml + md,Zod 校验)
nuxt.config.ts # 模块、i18n、路由头、runtimeConfig
下载引擎的两个 composable 是二次开发的重点,对外暴露的接口很清晰,例如:
typescript
// useStreamMerge.ts 核心入参
export interface StreamMergeOptions {
segments: ArrayBuffer[] | AsyncGenerator<ArrayBuffer>
totalSegments: number
filename: string
outputFormat?: 'mp4' | 'ts'
onProgress?: (progress: StreamMergeProgress) => void
signal?: AbortSignal
}
七、二次开发实战
1. 改文案 / 文档(零代码)
content/ 下是 Markdown 与 YAML,落地页、文档、博客都在这。改完即生效(dev 热更新),且字段受 content.config.ts 里的 Zod schema 约束,写错会在构建时报出来。比如想加一个中文使用场景文档,直接在 content/zh-Hans/1.docs/ 下新建 .md 并在对应 .navigation.yml 登记即可。
2. 调下载引擎参数
打开 app/composables/useStreamMerge.ts:
- 并发数:默认 2,范围 1--8,可按网络调;
- 重试策略:指数退避,最多 3 次(仅对网络级临时失败重试,4xx 不重试);
- 三层写入降级:FSA → StreamSaver → Blob,自动按浏览器能力和文件大小选择;
- AES-128 解密:走 Web Crypto API,密钥不外传。
想换默认输出格式(MP4/TS)或加自定义合并逻辑,改这里最直接。
3. 改配置(nuxt.config.ts)
- 多语言 :
i18n.locales已内置 en / zh-Hans / zh-Hant / ja / ko,增减语言在此; - 统计/埋点 :
runtimeConfig.public有clarityId、gaId,对应环境变量NUXT_PUBLIC_CLARITY_ID、NUXT_PUBLIC_GA_ID,不想接就留空; - 路由头 :下载器路由配了
Cross-Origin-Opener-Policy: same-origin+Cross-Origin-Embedder-Policy: require-corp,这是刻意开启 cross-origin isolation 的,目的见下一节。
八、部署上线要点
默认 Nitro 预设是 cloudflare-pages-static,构建出静态产物,可直接推到 Cloudflare Pages,也能放 Vercel / Netlify / 任意静态托管。
如果要自托管 Node 服务 ,把 nuxt.config.ts 里 nitro.preset 改为 node-server 再构建,用 node .output/server/index.mjs 起服务即可。
环境变量示例(部署平台的环境变量面板配置):
bash
NUXT_PUBLIC_GA_ID=G-XXXXXXXXXX
NUXT_PUBLIC_CLARITY_ID=xxxxxxxx
九、避坑提示
- 千万别删 COOP/COEP 头 。下载器路由的
require-corp决定了 cross-origin isolation 是否生效------它同时影响两件事:FFmpeg WASM 能否跑多线程(依赖SharedArrayBuffer),以及大文件能否走 StreamSaver 跨源流式写入。删了头,大文件下载会退化甚至失败。 - FFmpeg WASM 首次加载约 8MB,属正常,后续有缓存。
- DRM 内容解不了(如 Widevine),这是设计边界,不是 bug。
- Node 版本别太低,Nuxt 4 在 Node 18 及以下会直接起不来。