專案架構

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

依賴關係說明