プロジェクトアーキテクチャ
本ドキュメントは FlowPick の内部設計を深く理解したい開発者向けで、システムアーキテクチャ、モジュール責任範囲、データフロー、重要な設計判断を網羅します。

システムアーキテクチャ概要
FlowPick は ブラウザ拡張機能 と オンラインツール Web サイト という 2 つの主要なプロダクト形態から構成されます。両者はコアダウンロードエンジンを共有していますが、メディア検知とネットワークリクエスト処理には違いがあります。
┌─────────────────────────────────────────────────────────┐
│ FlowPick │
│ │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ ブラウザ拡張機能 │ │ オンラインツールサイト │ │
│ │ │ │ │ │
│ │ · ネットワークリクエスト監視 │ │ · M3U8 ダウンローダー │ │
│ │ · メディア自動検知 │ │ · DASH ダウンローダー │ │
│ │ · ポップアップ UI │ │ · ドキュメントシステム │ │
│ │ · クロスオリジンリクエスト能力 │ │ · トップページ表示 │ │
│ └────────┬─────────┘ └────────────┬─────────────┘ │
│ │ │ │
│ └───────────┬───────────────┘ │
│ │ │
│ ┌───────────▼───────────────┐ │
│ │ コアダウンロードエンジン │ │
│ │ │ │
│ │ · useStreamMerge.ts │ │
│ │ · useFFmpeg.ts │ │
│ │ · セグメントダウンロードと並列制御 │ │
│ │ · AES-128 復号 │ │
│ │ · ストリーミングファイル書き込み │ │
│ │ · フォーマット変換 │ │
│ └───────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
2 種類のプロダクト形態のポジショニング:
| 次元 | ブラウザ拡張機能 | オンラインツールサイト |
|---|---|---|
| ターゲットユーザー | 日常的な高頻度使用者 | 一時的使用者、拡張機能インストール不可の環境 |
| コアアドバンテージ | 自動検知、ワンクリックダウンロード | インストール不要、クロスプラットフォーム |
| 技術的制約 | Manifest V3 制限 | 同一生成元ポリシー制限 |
| アクセス方法 | ツールバーアイコンクリック | 直接 Web URL アクセス |
モジュール詳細
コアダウンロードエンジン
ダウンロードエンジンは 2 つの composable から構成され、app/composables/ に配置されています。

useStreamMerge.ts
ストリーミングメディアセグメントのダウンロード、復号、結合、書き込みを担当します。これはシステム全体で最もコアなモジュールです。
主な責任範囲:
| 機能 | 実装方式 |
|---|---|
| セグメント並列ダウンロード | Worker Pool パターン、共有タスクカウンター |
| リトライ機構 | 指数バックオフ、最大 3 回 |
| AES-128 復号 | Web Crypto API |
| セグメント結合 | バイナリ直接結合(TS)または FFmpeg リマックス(MP4) |
| ファイル書き込み | FSA → StreamSaver → Blob の 3 段階ダウングレード |
| 進捗追跡 | スライディングウィンドウ速度計算 + 残り時間推定 |
| メモリ管理 | サンプル予測 + 閾値チェック + 戦略選択 |
エクスポートインターフェース:
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 ロード | オンデマンドロード、マルチスレッド/シングルスレッド自動切り替え対応 |
| マルチスレッド検出 | SharedArrayBuffer と crossOriginIsolated をチェック |
| 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>
}
}
ページモジュール
M3U8 ダウンローダー (m3u8-downloader.vue)
HLS ストリーミングメディアプロトコルのダウンロードページを処理します。
処理フロー:
ユーザー URL 入力
↓
fetch M3U8 コンテンツ
↓
m3u8-parser 解析
↓
┌─ Master Playlist? ──→ 画質リスト表示 → ユーザー選択
│
└─ Media Playlist? ──→ セグメントリスト抽出
↓
#EXT-X-KEY チェック(暗号化情報)
↓
キーファイルダウンロード(該当する場合)
↓
並列セグメントダウンロード + 復号
↓
結合/リマックス
↓
ディスクに書き込み
主要な依存関係:
m3u8-parser:M3U8 プレイリスト解析useStreamMerge:セグメントダウンロードと結合useFFmpeg:フォーマット変換
DASH ダウンローダー (dash-downloader.vue)
DASH ストリーミングメディアプロトコルのダウンロードページを処理します。
処理フロー:
ユーザー URL 入力
↓
fetch MPD コンテンツ
↓
mpd-parser 解析
↓
ContentProtection チェック(DRM)
↓
AdaptationSet 抽出(動画/音声)
↓
ストリームリスト表示 → ユーザー選択
↓
初期化セグメントダウンロード(該当する場合)
↓
並列メディアセグメントダウンロード + 復号
↓
FMP4 再構成 / 音视频結合
↓
ディスクに書き込み
主要な依存関係:
mpd-parser:MPD プレイリスト解析useStreamMerge:セグメントダウンロードと結合useFFmpeg:FMP4 再構成と音视频結合
ドキュメントシステム
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 が統一されたドキュメントページレイアウトを提供し、トップナビゲーション、サイドバー、コンテンツ領域を含みます。
データフロー
完全なダウンロードフロー
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ プレイリスト取得 │ → │ プレイリスト解析 │ → │ セグメントダウンロード │ → │ 結合書き込み │
└─────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
▼ ▼ ▼ ▼
fetch() m3u8-parser Worker Pool useStreamMerge
mpd-parser AES-128 復号 useFFmpeg
fetch() 並列 FSA/SS/Blob
各フェーズの詳細説明:
| フェーズ | 入力 | 処理 | 出力 | 失敗可能な原因 |
|---|---|---|---|---|
| プレイリスト取得 | M3U8/MPD URL | HTTP GET リクエスト | プレイリストテキスト | ネットワークエラー、URL 有効期限切れ、404 |
| プレイリスト解析 | プレイリストテキスト | m3u8/mpd-parser 解析 | セグメントリスト + メタデータ | 非標準フォーマット、DRM 保護 |
| セグメントダウンロード | セグメント URL リスト | Worker Pool 並列ダウンロード | ArrayBuffer 配列 | CORS、帯域制限、トークン有効期限切れ |
| 結合書き込み | ArrayBuffer 配列 | 結合/リマックス + 書き込み | ディスクファイル | メモリ不足、書き込み権限 |
書き込み戦略選択フロー
書き込み開始
│
▼
File System Access API 利用可能?
│
├── 是 → FSA ストリーミング書き込み使用(最適)
│ · 16MB バッファ
│ · 任意サイズ対応
│
└── 否 → StreamSaver 利用可能?
│
├── 是 → StreamSaver ストリーミング書き込み使用
│ · Service Worker プロキシ
│ · 大容量ファイル対応
│
└── 否 → ファイルサイズ予測
│
├── < 1.5GB → Blob モード
│ · 全部メモリにロード
│ · ブラウザダウンロードトリガー
│
└── > 1.5GB → ダウンロード拒否
· ブラウザ切り替えを促す
重要な設計判断
FFmpeg WASM を選択した理由は何か?サーバーサイド処理ではないのか?
判断:すべてのメディア処理をブラウザサイドで完結させる。
理由:
- プライバシー保護:ユーザーファイルをサーバーにアップロードしない
- サーバーコストゼロ:トランスコードサーバークラスター維持不要
- 即座に利用可能:アップロード待ち時間やキュー不要
- オフライン能力:理論上 PWA モードでオフライン使用可能
代償:
- ネイティブ FFmpeg よりパフォーマンスが劣る(約 10-20%)
- 初回ロード時に WASM ファイルのダウンロードが必要(約 8MB)
- ブラウザメモリ制限を受ける
3 段階書き込みダウングレード戦略を採用した理由は?
判断:FSA → StreamSaver → Blob の優先度チェーン。
理由:
- FSA API が最適解だが Chrome/Edge のみサポート
- StreamSaver は互換性が広いが Service Worker 依存
- Blob はフォールバック方案ですべてのブラウザでダウンロード可能
代償:
- 3 セットの書き込みロジックを維持必要
- ダウングレード動作によりユーザー体験の一貫性が損なわれる可能性
並列ダウンロードで Worker Pool を採用した理由は?事前分割ではないのか?
判断:共有カウンター + 動的タスク割り当てを使用。
理由:
- セグメントサイズが不均一の場合、事前分割により一部の Worker がアイドル状態になる
- 共有カウンターによりすべての Worker がタスク完了まで継続的に稼働
- 実装が簡単で複雑な負荷分散ロジック不要
拡張機能とオンラインツールがコアエンジンを共有している理由は?
判断:useStreamMerge と useFFmpeg を独立した composable として、拡張機能 API に依存しない。
理由:
- コード再利用、2 セットのダウンロードロジック維持不要
- オンラインツールを拡張機能機能のデモおよびダウングレード方案として使用可能
- テスト容易:コアロジックを通常の Web ページ環境でデバッグ可能
依存関係図
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 | 排他的選択 | 優先度に基づき 1 種類の書き込み方式を選択 |
ページ → 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 フィルタリング ダウンロードトリガーと管理
関連ドキュメント
- 貢献ガイド — 開発環境セットアップ、コード規範と PR フロー
- ダウンロードエンジンアーキテクチャ — コアダウンロードエンジンの完全な技術実装
- 動画メディア検知 — HLS/DASH プレイリスト解析と暗号化検出
- フォーマット変換 — FFmpeg WASM エンジンと出力フォーマット
- ブラウザ互換性 — 各ブラウザ API サポートとダウングレード戦略
- オンラインツール — オンラインツールの使用と制限
- 設定参考 — 並列数、フィルタなどの設定項目
- インストールガイド — 拡張機能インストールとアクティベーション
- 既知の問題 — 現在のバージョンの技術的制限と境界ケース
- 一般的な問題のトラブルシューティング — 診断ツールとトラブルシューティング手順
- プライバシーとセキュリティ — 権限説明とプライバシー保護