项目架构

FlowPick 的技术架构详解,包括系统设计、模块划分、数据流和关键设计决策。

本文档面向希望深入理解 FlowPick 内部设计的开发者,涵盖系统架构、模块职责、数据流和关键设计决策。


系统架构概览

FlowPick 由两个主要产品形态组成:浏览器扩展在线工具网站。两者共享核心下载引擎,但在媒体检测和网络请求处理上有所不同。

┌─────────────────────────────────────────────────────────┐
│                      FlowPick                           │
│                                                         │
│  ┌──────────────────┐    ┌──────────────────────────┐   │
│  │   浏览器扩展      │    │     在线工具网站          │   │
│  │                  │    │                          │   │
│  │ · 网络请求监控    │    │ · M3U8 下载器            │   │
│  │ · 媒体自动检测    │    │ · DASH 下载器            │   │
│  │ · 弹出窗口 UI    │    │ · 文档系统               │   │
│  │ · 跨域请求能力    │    │ · 首页展示               │   │
│  └────────┬─────────┘    └────────────┬─────────────┘   │
│           │                           │                 │
│           └───────────┬───────────────┘                 │
│                       │                                 │
│           ┌───────────▼───────────────┐                 │
│           │      核心下载引擎          │                 │
│           │                           │                 │
│           │ · useStreamMerge.ts       │                 │
│           │ · useFFmpeg.ts            │                 │
│           │ · 分片下载与并发控制       │                 │
│           │ · AES-128 解密            │                 │
│           │ · 流式文件写入             │                 │
│           │ · 格式转换                 │                 │
│           └───────────────────────────┘                 │
└─────────────────────────────────────────────────────────┘

两种产品形态的定位

维度浏览器扩展在线工具网站
目标用户日常高频使用者临时使用者、无法安装扩展的环境
核心优势自动检测、一键下载无需安装、跨平台
技术约束Manifest V3 限制同源策略限制
入口方式工具栏图标点击直接访问网页 URL
关于扩展与在线工具的详细功能差异和选择建议,请参阅 在线工具 — 与扩展的对比。关于扩展的安装步骤,请参阅 安装指南

模块详解

核心下载引擎

下载引擎由两个 composable 组成,位于 app/composables/

useStreamMerge.ts

负责流媒体分片的下载、解密、合并和写入。这是整个系统最核心的模块。

主要职责

功能实现方式
分片并发下载Worker Pool 模式,共享任务计数器
重试机制指数退避,最多 3 次
AES-128 解密Web Crypto API
分片拼接二进制直接拼接(TS)或 FFmpeg 转封装(MP4)
文件写入FSA → StreamSaver → Blob 三级降级
进度追踪滑动窗口速度计算 + 剩余时间估算
内存管理采样预估 + 阈值检查 + 策略选择

导出接口

export interface StreamMergeOptions {
  segments: ArrayBuffer[] | AsyncGenerator<ArrayBuffer>
  totalSegments: number
  filename: string
  outputFormat?: 'mp4' | 'ts'
  onProgress?: (progress: StreamMergeProgress) => void
  signal?: AbortSignal
}

export interface StreamMergeProgress {
  phase: 'downloading' | 'merging'
  percent: number
  downloadedBytes: number
  totalBytes: number
  speed: string
  eta: string
}
关于下载引擎的完整技术实现(含并发控制、重试策略、内存管理、写入策略对比),请参阅 下载引擎架构。关于三级写入策略的降级逻辑,请参阅 下载引擎架构 — 策略对比总结

useFFmpeg.ts

负责 FFmpeg WASM 的加载、生命周期管理和媒体处理命令执行。

主要职责

功能实现方式
WASM 加载按需加载,支持多线程/单线程自动切换
多线程检测检查 SharedArrayBuffercrossOriginIsolated
TS → MP4 转封装ffmpeg -i input.ts -c copy output.mp4
音视频合并分别写入虚拟 FS,执行合并命令
FMP4 重组处理 DASH 的初始化段 + 媒体段
进度回调解析 FFmpeg 输出的 time 信息

导出接口

export const useFFmpeg = () => {
  return {
    ffmpeg: Ref<FFmpegType | null>
    loaded: Ref<boolean>
    loading: Ref<boolean>
    error: Ref<string | null>
    load: () => Promise<void>
    mergeToMP4: (options: MergeOptions) => Promise<ArrayBuffer>
    mergeAudioVideo: (options: MergeAudioVideoOptions) => Promise<ArrayBuffer>
    mergeFmp4: (options: MergeFmp4Options) => Promise<ArrayBuffer>
  }
}
关于 FFmpeg WASM 的加载流程、多线程检测和性能对比,请参阅 格式转换 — FFmpeg WASM 引擎。关于 SharedArrayBuffer 的配置要求,请参阅 浏览器兼容性 — 高级 API

页面模块

M3U8 下载器 (m3u8-downloader.vue)

处理 HLS 流媒体协议的下载页面。

处理流程

用户输入 URL
    ↓
fetch M3U8 内容
    ↓
m3u8-parser 解析
    ↓
┌─ Master Playlist? ──→ 展示画质列表 → 用户选择
│
└─ Media Playlist? ──→ 提取分片列表
    ↓
检查 #EXT-X-KEY(加密信息)
    ↓
下载密钥文件(如有)
    ↓
并发下载分片 + 解密
    ↓
合并/转封装
    ↓
写入磁盘

关键依赖

  • m3u8-parser:解析 M3U8 清单
  • useStreamMerge:分片下载与合并
  • useFFmpeg:格式转换
关于 HLS 协议的 Master Playlist 与 Media Playlist 的区别,请参阅 视频嗅探 — HLS 流。关于在线工具中 M3U8 下载器的使用方式,请参阅 在线工具

DASH 下载器 (dash-downloader.vue)

处理 DASH 流媒体协议的下载页面。

处理流程

用户输入 URL
    ↓
fetch MPD 内容
    ↓
mpd-parser 解析
    ↓
检查 ContentProtection(DRM)
    ↓
提取 AdaptationSet(视频/音频)
    ↓
展示流列表 → 用户选择
    ↓
下载初始化段(如有)
    ↓
并发下载媒体段 + 解密
    ↓
FMP4 重组 / 音视频合并
    ↓
写入磁盘

关键依赖

  • mpd-parser:解析 MPD 清单
  • useStreamMerge:分片下载与合并
  • useFFmpeg:FMP4 重组与音视频合并
关于 DASH 流的检测原理和 ContentProtection 处理,请参阅 视频嗅探 — DASH 流。关于 DRM 保护内容的限制说明,请参阅 已知限制 — DRM 保护内容

文档系统

基于 Nuxt Content v3 构建,文档以 Markdown 文件形式存放在 content/ 目录。

目录结构

content/zh-Hans/
├── 1.docs/                    # 文档主体
│   ├── 1.getting-started/     # 快速开始
│   ├── 2.features/            # 功能特性
│   ├── 3.advanced/            # 高级指南
│   ├── 5.troubleshooting/     # 故障排除
│   └── 6.developer/           # 开发者文档
├── 4.changelog/               # 更新日志
└── 6.legal/                   # 法律条款

导航系统:每个目录下的 .navigation.yml 定义该分类的标题和图标。Nuxt Content 自动收集所有文档并生成侧边栏导航。

文档布局app/layouts/docs.vue 提供统一的文档页面布局,包含顶部导航、侧边栏和内容区域。

关于文档的编写规范(Frontmatter、内容格式、多语言),请参阅 贡献指南 — 文档编写指南

数据流

完整下载流程

┌─────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│ 获取清单 │ → │ 解析清单  │ → │ 下载分片  │ → │ 合并写入  │
└─────────┘    └──────────┘    └──────────┘    └──────────┘
     │              │               │               │
     ▼              ▼               ▼               ▼
  fetch()      m3u8-parser    Worker Pool     useStreamMerge
                mpd-parser    AES-128 解密    useFFmpeg
                              fetch() 并发    FSA/SS/Blob

各阶段详细说明

阶段输入处理输出可能失败原因
获取清单M3U8/MPD URLHTTP GET 请求清单文本网络错误、URL 过期、404
解析清单清单文本m3u8/mpd-parser 解析分片列表 + 元数据格式不标准、DRM 保护
下载分片分片 URL 列表Worker Pool 并发下载ArrayBuffer 数组CORS、限流、token 过期
合并写入ArrayBuffer 数组拼接/转封装 + 写入磁盘文件内存不足、写入权限
关于下载引擎的完整数据流(含错误分类和重试机制),请参阅 下载引擎架构 — 数据流全景。关于各阶段失败时的排查方法,请参阅 常见问题排查

写入策略选择流程

开始写入
    │
    ▼
File System Access API 可用?
    │
    ├── 是 → 使用 FSA 流式写入(最优)
    │         · 16MB 缓冲区
    │         · 支持任意大小
    │
    └── 否 → StreamSaver 可用?
                │
                ├── 是 → 使用 StreamSaver 流式写入
                │         · Service Worker 代理
                │         · 支持大文件
                │
                └── 否 → 预估文件大小
                            │
                            ├── < 1.5GB → Blob 模式
                            │              · 全部加载到内存
                            │              · 触发浏览器下载
                            │
                            └── > 1.5GB → 拒绝下载
                                           · 提示切换浏览器
关于三级写入策略的详细对比(含各策略的适用场景和限制),请参阅 下载引擎架构 — 策略对比总结。关于各浏览器对 FSA API 的支持情况,请参阅 浏览器兼容性 — 功能支持矩阵

关键设计决策

为什么选择 FFmpeg WASM 而不是服务端处理?

决策:所有媒体处理在浏览器端完成。

理由

  • 隐私保护:用户文件不上传服务器
  • 零服务器成本:无需维护转码服务器集群
  • 即时可用:无需等待上传和排队
  • 离线能力:理论上可在 PWA 模式下离线使用

代价

  • 性能低于原生 FFmpeg(约 10-20%)
  • 首次加载需要下载 WASM 文件(约 8MB)
  • 受浏览器内存限制
关于 FFmpeg WASM 与原生版本的性能对比数据,请参阅 已知限制 — FFmpeg WASM 性能。关于隐私保护的完整说明,请参阅 隐私与安全

为什么使用三级写入降级策略?

决策:FSA → StreamSaver → Blob 的优先级链。

理由

  • FSA API 是最优方案,但仅 Chrome/Edge 支持
  • StreamSaver 兼容性更广,但依赖 Service Worker
  • Blob 是兜底方案,确保所有浏览器都能下载

代价

  • 需要维护三套写入逻辑
  • 降级行为可能导致用户体验不一致
关于三级策略的详细实现和降级触发条件,请参阅 下载引擎架构 — 文件写入模块。关于各浏览器的写入能力对比,请参阅 浏览器兼容性 — 功能降级策略

为什么并发下载使用 Worker Pool 而非预先分片?

决策:使用共享计数器 + 动态任务分配。

理由

  • 分片大小不均时,预先分片会导致部分 Worker 空闲
  • 共享计数器确保所有 Worker 持续工作直到任务完成
  • 实现简单,无需复杂的负载均衡逻辑
关于 Worker Pool 的并发控制实现和性能分析,请参阅 下载引擎架构 — 并发数与性能的关系。关于并发数的配置方法,请参阅 配置参考

为什么扩展和在线工具共享核心引擎?

决策useStreamMergeuseFFmpeg 作为独立 composable,不依赖扩展 API。

理由

  • 代码复用,避免维护两套下载逻辑
  • 在线工具可作为扩展功能的演示和降级方案
  • 便于测试:核心逻辑可在普通网页环境中调试
关于扩展与在线工具在 CORS 处理上的差异,请参阅 已知限制 — CORS 跨域限制。关于两种产品形态的功能对比,请参阅 在线工具 — 与扩展的对比

依赖关系图

m3u8-downloader.vue ──────┐
                          ├──→ useStreamMerge.ts ──→ Web Crypto API
dash-downloader.vue ──────┤         │                    fetch API
                          │         ├──→ useFFmpeg.ts ──→ @ffmpeg/ffmpeg
扩展弹出窗口 ─────────────┘         │                    @ffmpeg/util
                                    ├──→ File System Access API
                                    ├──→ StreamSaver.js
                                    └──→ Blob API

文档系统 ──→ @nuxt/content ──→ Markdown 文件
           └──→ @nuxt/ui ──→ Tailwind CSS

依赖关系说明

依赖类型说明
useStreamMerge → useFFmpeg可选依赖仅 MP4 输出或音视频合并时需要
useStreamMerge → Web Crypto API条件依赖仅 AES-128 加密流时需要
useStreamMerge → FSA / StreamSaver / Blob互斥选择按优先级选择一种写入方式
页面 → useStreamMerge直接依赖所有下载页面都依赖核心引擎
文档系统 → @nuxt/content框架依赖Nuxt Content 模块

扩展架构

扩展部分(不在本仓库中)的结构:

flowpick-extension/
├── manifest.json           # Manifest V3 配置
├── background/
│   └── service-worker.js   # Service Worker(网络监控)
├── popup/
│   ├── popup.html          # 弹出窗口
│   ├── popup.js            # 弹出窗口逻辑
│   └── popup.css           # 弹出窗口样式
├── content/
│   └── content.js          # 内容脚本(页面注入)
└── assets/
    └── icons/              # 扩展图标

扩展通过 webRequest API 监控网络请求,检测媒体资源后通过消息传递将 URL 列表发送给弹出窗口。弹出窗口复用在线工具的核心下载逻辑。

扩展通信流程

Content Script ←→ Background Service Worker ←→ Popup UI
      │                      │                      │
  页面 DOM 访问         网络请求监控            用户交互界面
  媒体元素检测          M3U8/MPD 过滤           下载触发与管理
关于 Manifest V3 对扩展的限制和应对措施,请参阅 已知限制 — Manifest V3 限制。关于扩展的安装和权限说明,请参阅 安装指南

相关文档