Skip to content

mp4ファイルをアップロード可能にしたい #302

@Kewton

Description

@Kewton

Note: このIssueは 2026-02-18 にレビュー結果(Stage 1, Stage 3, Stage 5)を反映して更新されました。
詳細: dev-reports/issue/302/issue-review/

概要

ワークツリーのファイル管理機能において、mp4ファイルのアップロードとブラウザ内再生(プレビュー)を可能にする。現在はアップロード可能なファイル形式に動画が含まれておらず、mp4ファイルを保存・閲覧できない。

背景・課題

開発中のアプリケーションのデモ動画をワークツリー内に保存・管理したいが、現在のUPLOADABLE_EXTENSIONSにmp4が含まれていないためアップロードできない。デモ動画をプロジェクトディレクトリに手動でコピーする必要があり、CommandMateのUI上で一元管理できていない。

提案する解決策

既存のアップロード・ファイル表示パイプラインを拡張し、mp4形式をサポートする。

主要な変更点

  • UPLOADABLE_EXTENSIONSにmp4のバリデータを追加(MIME: video/mp4、magic bytes検証)
  • FileContent型にisVideoフラグを追加
  • ファイル表示API(GET /api/worktrees/[id]/files/[...path])に動画ファイル判定ロジックを追加
  • VideoViewerコンポーネントを新規作成(HTML5 <video>タグ、コントロール付き)
  • FileViewerコンポーネントに動画表示の分岐を追加
  • next.config.jsbodySizeLimit'16mb'に引き上げ、CSPヘッダーにmedia-srcを追加
  • upload APIのサイズ検証順序を最適化(file.sizeチェックをarrayBuffer()前に移動)

設計方針

  • ファイルサイズ上限は15MB(mp4専用の上限。既存の画像等は5MBのまま)。デモ動画を想定
  • Base64 data URI方式で配信(15MB以下なのでストリーミング不要)
    • 性能に関する注意: 15MBのバイナリをBase64エンコードすると約20MB(約1.33倍)のデータ量になる。レスポンスの読み込みに数秒かかる可能性があるため、VideoViewerにローディングインジケーターを実装する。低スペック端末での動作も考慮し、将来的に頻繁な利用が想定される場合はストリーミング方式への移行を検討する
  • mp4のみ対応(webm, mov等は対象外)
  • ImageViewerと同じパターンでVideoViewerを実装(統一性のある設計)
  • HTML5 <video>タグのcontrols属性を使用し、ブラウザネイティブの再生コントロールを利用する(カスタムコントロールは不要)
  • MP4 magic bytes仕様: MP4はISOBMFF構造を持ち、先頭4バイトがボックスサイズ、offset 4-7がftypシグネチャ。magicBytes: [{ bytes: [0x66, 0x74, 0x79, 0x70], offset: 4 }]で検証する
  • src/config/video-extensions.tsimage-extensions.tsと同じパターンで新規作成し、動画判定ユーティリティを独立管理する(isVideoExtension()VIDEO_EXTENSIONS定数、getMimeTypeByVideoExtension()等)
  • CSP media-src: blob: URIは現時点では不要(Base64 data URI方式のため)。将来ストリーミング方式に移行する場合はblob:の追加を検討する
  • bodySizeLimitの適用範囲: next.config.jsexperimental.serverActions.bodySizeLimitはServer Actions専用であり、Route Handler(upload API)には適用されない可能性がある。実装時に15MBのmp4ファイルで実アップロードテストを行い、制限に達する場合はRoute Handler固有の設定(route segment config等)を追加する
  • upload APIのサイズ検証最適化: file.sizeチェックをarrayBuffer()呼び出し前に移動することで、サイズ超過ファイルの早期拒否を行い、不要なメモリ消費を防止する(15MB上限引き上げに伴うメモリ効率改善)
  • page.tsxの型統一方針: src/app/worktrees/[id]/files/[...path]/page.tsxのローカルFileContent型はsrc/types/models.tsFileContent型のimportに切り替える(DRY原則・型乖離解消)。これによりisImage/isVideo等のフラグが自動的に追従され、今後のフィールド追加時にも型の不整合が発生しない

実装タスク

  • src/config/video-extensions.ts を新規作成(image-extensions.tsパターンに準拠: VIDEO_EXTENSIONS定数、isVideoExtension()getMimeTypeByVideoExtension()、magic bytes定義)
  • src/config/uploadable-extensions.ts にmp4バリデータを追加(maxFileSize: 15MB、magic bytes: offset 4でftyp [0x66, 0x74, 0x79, 0x70]を検証)
  • src/types/models.tsFileContentインターフェースにisVideo?: booleanフラグを追加(オプショナル、後方互換性維持)
  • src/app/api/worktrees/[id]/files/[...path]/route.ts に動画ファイル表示ロジックを追加(isVideoExtension()による分岐、画像パターンと同じ構造)
  • src/app/worktrees/[id]/files/[...path]/page.tsx のローカルFileContent型をsrc/types/models.tsFileContent型のimportに切り替え(DRY原則・型乖離解消)、動画・画像ファイルの両方に対してVideoViewer/ImageViewerを使った表示分岐を実装する
  • src/components/worktree/VideoViewer.tsx を新規作成(ローディングインジケーター付き)
  • src/components/worktree/FileViewer.tsx に動画表示分岐を追加し、canCopyロジックを修正(!content.isImage && !content.isVideoで動画ファイルにコピーボタンが表示されないようにする)
  • next.config.jsexperimental.serverActions.bodySizeLimit'16mb'(15MB + overhead)に更新する
  • next.config.js の CSPヘッダーに media-src 'self' data: を追加する
  • src/app/api/worktrees/[id]/upload/[...path]/route.ts でupload APIのサイズ検証順序を最適化する(file.sizeチェックをarrayBuffer()前に移動し、サイズ超過ファイルの早期拒否でメモリ効率を改善)
  • src/app/api/worktrees/[id]/upload/[...path]/route.ts で15MBのmp4アップロードが実際に動作するかを検証し、bodySizeLimitがRoute Handlerに適用されない場合はRoute Handler固有の設定を追加する
  • ユニットテスト追加:
    • tests/unit/config/uploadable-extensions.test.ts にmp4バリデーションテスト追加(isUploadableExtensionvalidateMimeTypevalidateMagicBytesgetMaxFileSize
    • tests/unit/config/video-extensions.test.ts を新規作成(image-extensions.test.tsパターン準拠: isVideoExtensiongetMimeTypeByVideoExtensionテスト)
  • 結合テスト追加:
    • tests/integration/api/file-upload.test.ts にmp4アップロードのバリデーション結合テスト追加
    • tests/integration/api-file-operations.test.ts にGET APIでの動画ファイル取得テスト追加

受入条件

  • mp4ファイルをUIからアップロードできること
  • アップロードしたmp4ファイルがファイルツリーに表示されること
  • mp4ファイルをクリックするとブラウザ内で再生(プレビュー)できること(再生/停止コントロール付き)
  • 15MBを超えるmp4ファイルのアップロードが拒否されること
  • 不正なファイル(拡張子をmp4に偽装した非動画ファイル)がmagic bytes検証(offset 4でftypシグネチャ)で拒否されること
  • 動画ファイル選択時にコピーボタンが表示されないこと
  • 動画読み込み中にローディングインジケーターが表示されること
  • /worktrees/[id]/files/path/to/video.mp4 への直接URLアクセスで動画が正しく再生されること(FileViewerPage対応)
  • /worktrees/[id]/files/path/to/image.png への直接URLアクセスで画像が正しく表示されること(FileViewerPageでのImageViewer統合。page.tsxのローカル型をmodels.tsのimportに切り替えることで対応)
  • 既存のアップロード機能(画像、テキスト等)に影響がないこと
  • 全既存テストがパスすること

影響範囲

変更対象ファイル

ファイル 変更内容
next.config.js bodySizeLimit'16mb'に更新、CSPヘッダーにmedia-src 'self' data:追加
src/config/uploadable-extensions.ts mp4バリデータ追加(15MB上限)
src/types/models.ts FileContentisVideo?: booleanフラグ追加(オプショナル、後方互換性維持)
src/app/api/worktrees/[id]/files/[...path]/route.ts 動画ファイル判定・Base64変換ロジック追加
src/app/api/worktrees/[id]/upload/[...path]/route.ts サイズ検証順序最適化(file.sizeチェックをarrayBuffer()前に移動)、15MB対応の検証
src/app/worktrees/[id]/files/[...path]/page.tsx ローカルFileContent型をsrc/types/models.tsのimportに切り替え、動画・画像ファイルの表示分岐をVideoViewer/ImageViewerで実装
src/components/worktree/VideoViewer.tsx 新規作成 - HTML5動画プレーヤー(ローディングインジケーター付き)
src/components/worktree/FileViewer.tsx 動画表示分岐追加、canCopyロジック修正

新規作成ファイル

ファイル 内容
src/config/video-extensions.ts 動画拡張子定義(VIDEO_EXTENSIONS)・magic bytes・MIME定義・isVideoExtension()getMimeTypeByVideoExtension()image-extensions.tsパターン準拠)
src/components/worktree/VideoViewer.tsx 動画再生コンポーネント
tests/unit/config/video-extensions.test.ts 動画拡張子ユーティリティのユニットテスト

関連コンポーネント(変更不要だが影響確認が必要)

  • src/config/binary-extensions.ts - mp4は既に直接リテラルとして登録済み(変更不要)。ただしvideo-extensions.ts新規作成に伴い、image-extensions.tsIMAGE_EXTENSIONSスプレッドと同様にVIDEO_EXTENSIONSのスプレッド統合パターンを将来的に検討する(webm等の拡張子追加時の一貫性確保のため)
  • src/components/worktree/WorktreeDetailRefactored.tsx - アップロードUIはUPLOADABLE_EXTENSIONSを動的参照しているため自動対応。なお、input要素のaccept属性は拡張子のみで指定しているが、モバイルブラウザでの動画ファイル選択に問題が見つかった場合はMIMEタイプ(video/mp4)の併記を検討する(実装時テストで確認)
  • src/lib/file-search.ts - バイナリ除外リストにmp4が既に含まれている(変更不要)
  • src/components/worktree/FileTreeView.tsx - FileIconコンポーネントのcolorMapにmp4用エントリが未定義(デフォルト色text-gray-400で表示される)。動画ファイルを視覚的に区別するためにmp4用のアイコン色追加(例: text-purple-500)はNice to Haveとして検討

レビュー履歴

Stage 1 レビュー (2026-02-18)

Must Fix:

  • FINDING-001: next.config.jsbodySizeLimit更新を実装タスク・影響範囲に追加
  • FINDING-002: CSPヘッダーのmedia-srcディレクティブ追加を実装タスク・影響範囲に追加

Should Fix:

  • FINDING-003: FileViewer.tsxcanCopyロジック修正(isVideo除外)を実装タスク・受入条件に明記
  • FINDING-004: MP4 magic bytes仕様(offset 4でftyp = [0x66, 0x74, 0x79, 0x70])を設計方針・実装タスクに追記
  • FINDING-005: video-extensions.tsを候補から確定に変更、image-extensions.tsパターン準拠の設計を明記
  • FINDING-006: 15MB Base64の性能注意事項(約20MBデータ量、ローディングインジケーター必要)を設計方針に追記
  • FINDING-007: binary-extensions.tsVIDEO_EXTENSIONSスプレッド統合パターン検討事項を影響範囲に追記

Stage 3 影響範囲レビュー (2026-02-18)

Must Fix:

  • FINDING-S3-001: bodySizeLimitはServer Actions専用でRoute Handlerに適用されない可能性 - 設計方針にbodySizeLimit適用範囲の注意事項を追記、実装タスクに15MB実アップロードテストとRoute Handler固有設定の検討を追加
  • FINDING-S3-002: page.tsxのローカルFileContent型がisVideo未対応 - 変更対象ファイルにpage.tsxを追加、実装タスクにローカル型のisVideo対応を追加、受入条件に直接URLアクセスでの再生確認を追加

Should Fix:

  • FINDING-S3-003: テスト対象の網羅性不足 - 実装タスクのテスト項目を具体的なファイル名・テストケースに分解
  • FINDING-S3-004: FileTreeView.tsxのFileIconにmp4用アイコン色未定義 - 影響確認セクションにNice to Haveとして記載
  • FINDING-S3-005: CSPのmedia-srcにblob: URIが不要であることの確認 - 設計方針にblob不要の根拠と将来方針を追記
  • FINDING-S3-006: FileContent型のisVideoフラグの後方互換性 - 実装タスクにオプショナルフラグであることを明記
  • FINDING-S3-007: upload APIのfile.sizeチェックをarrayBuffer()前に移動する最適化 - 設計方針と実装タスクにサイズ検証順序最適化を追記、変更対象ファイルにupload APIを追加

Stage 5 レビュー (2026-02-18)

Should Fix:

  • FINDING-S5-001: page.tsxのローカルFileContent型をmodels.tsのimportに統一する方針を明確化 - 設計方針に「page.tsxの型統一方針」セクションを追加、実装タスクをimport切り替え方式に更新、変更対象ファイルの説明を更新
  • FINDING-S5-002: page.tsxでの画像・動画ファイル表示の両方の対応を明確化 - 実装タスクにVideoViewer/ImageViewer両方の統合を明記、受入条件に画像の直接URLアクセス表示確認を追加

Nice to Have:

  • FINDING-S5-003: WorktreeDetailRefactored.tsxのinput accept属性にMIMEタイプ併記の検討 - 関連コンポーネントの説明に実装時テストでの確認事項として追記

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions