Node.jsのchild_processとFFmpegで作る!非対応動画ファイルのMP4自動一括変換ツール
Node.jsのchild_processとFFmpegで作る!非対応動画ファイルのMP4自動一括変換ツール
ソースコード: https://github.com/hayate-hu6/local-media-viewer.git
はじめに
古いPCの画像や動画を整理していたところ、ブラウザで再生できない動画ファイルが多数見つかりました。何とかwebアプリで再生できないかと考えた結果、多様な動画形式に対応するのは困難なため、ブラウザ対応の形式に変換することにしました。
上記の通りWebブラウザは全ての動画形式を再生できるわけではありません。MP4 (H.264/AAC) や WebM は問題ありませんが、古いデジカメで撮影した .avi や .mpg、昔のネット動画 .flv、DVDデータの .vob などは、そのままではブラウザで再生できません。
そこで、これらの動画をブラウザで再生可能な形式(MP4)に自動変換するスクリプト convert_media.js を作成しました。
Node.jsから外部コマンドを実行する
Node.jsで動画変換を行う場合、JavaScriptだけで処理するのは現実的ではありません(処理速度の問題)。通常は、動画処理のデファクトスタンダードである FFmpeg を外部コマンドとして呼び出します。
const { execSync } = require('child_process');
// Check FFmpeg
try {
execSync('ffmpeg -version', { stdio: 'ignore' });
} catch (e) {
console.error('CRITICAL ERROR: FFmpeg is not installed...');
process.exit(1);
}
child_process モジュールの execSync を使うと、シェルコマンドを実行し、その終了を待つことができます。まず最初に ffmpeg -version を叩いて、FFmpegがシステムにインストールされているかを確認しています。
変換ディレクトリの再帰スキャン
サーバー側のファイルスキャンと同様に、再帰的にフォルダを巡回して変換対象ファイルを探します。
const EXTS_TO_CONVERT = ['.avi', '.flv', '.3gp', '.mkv', '.mpg', '.mpeg', '.rm', '.wmv'];
function processDirectory(directory) {
// ...出力先フォルダはスキップする処理...
const items = fs.readdirSync(directory);
for (const item of items) {
// ...再帰処理...
const ext = path.extname(item).toLowerCase();
if (EXTS_TO_CONVERT.includes(ext)) {
convertFile(fullPath);
}
}
}
ここでのポイントは、変換されたファイルを保存するための「出力ディレクトリ(converted)」もスキャン対象に含まれてしまうと、無限ループ(変換したファイルをまた変換しようとする)に陥る可能性がある点です。そのため、ディレクトリパスの解決 (path.resolve) を行って同一判定をし、スキップするガード処理を入れています。
FFmpegコマンドの構築と実行
ここが核心部分です。見つけたファイルをMP4に変換します。
function convertFile(filePath) {
const outputPath = getOutputFilePath(filePath); // filename.avi -> output/filename.mp4
// 既に変換済みならスキップ(二重変換防止)
if (fs.existsSync(outputPath)) {
console.log(`[SKIP] Already converted: ${path.basename(filePath)}`);
return;
}
// FFmpegコマンドの組み立て
const cmd = `ffmpeg -y -hide_banner -loglevel error -i "${filePath}" -c:v libx264 -preset fast -crf 23 -c:a aac -b:a 128k "${outputPath}"`;
try {
execSync(cmd, { stdio: 'inherit' });
console.log(` -> Success`);
} catch (err) {
console.error(` -> FAILED: ${err.message}`);
}
}
FFmpegオプション解説
-y: 出力ファイルが既に存在していても強制上書き(このコードでは事前にチェックしているので安全策)-i "input": 入力ファイルパス-c:v libx264: 映像コーデックを H.264 に指定(Web互換性最高)-preset fast: 変換速度を優先(画質とのトレードオフ)-crf 23: 画質設定(数値が低いほど高画質、23はバランスが良いデフォルト値)-c:a aac: 音声コーデックを AAC に指定-b:a 128k: 音声ビットレート
execSync に { stdio: 'inherit' } を渡すことで、FFmpegの進捗ログ(プログレスバーなど)をそのままコンソールに表示させることができ、ユーザーは変換の進行状況を目視できます。
運用フロー
このスクリプトは、サーバー(server.js)とは独立しています。
ユーザーは以下のフローで運用します。
- メディアフォルダに古い動画ファイルを入れる。
node convert_media.jsを実行する。- スクリプトがフォルダを巡回し、非互換ファイルを
convertedフォルダにMP4として生成する。 - Local Media Viewerを開くと、元のファイルと変換後のMP4ファイルが表示される(変換後はMP4の方を見れば良い)。
まとめ
Node.jsの強力なファイルシステム操作能力と、FFmpegという外部ツールを組み合わせることで、実用的なメディアコンバーターを簡単に作成できました。
child_process を使いこなすことで、Node.js単体では不可能な重い処理(動画変換、画像加工、システム操作など)を既存のCLIツールに委譲する設計パターンは、バックエンド開発において非常に強力な武器になります。
全7回にわたり、Local Media Viewerのソースコードを解説してきました。 一見単純なビューアーアプリですが、Expressの基礎、セキュリティ、ファイルシステム、アルゴリズム、Vanilla JS、そして外部プロセス連携と、Webアプリケーション開発に必要なエッセンスが詰まっています。
同シリーズ記事
- 第1回:Node.jsとExpressで構築するローカルメディアビューアー:プロジェクト構成と全体アーキテクチャ
- 第2回:Node.js Expressによるセキュアなメディア配信サーバーの構築手法と実装の全貌
- 第3回:DB不要!Node.js CryptoモジュールとHttpOnly Cookieで実現する安全な簡易認証システム
- 第4回:Node.jsで数千ファイル規模のディレクトリツリーを高速に解析する再帰アルゴリズムの実装
- 第5回:React不要!標準JavaScriptで実装する軽量SPAの状態管理と高速DOM操作テクニック
- 第6回:快適なメディア閲覧体験を作る!クライアントサイド・ページネーションと高機能モーダルのUI/UX実装
- 第7回:Node.jsのchild_processとFFmpegで作る!非対応動画ファイルのMP4自動一括変換ツール