複数の.MTSファイルを.MOVや.MP4に一括で変換する


## 背景

AVCHD 内の.MTS ファイルを mp4 に一括変換したかったのですが、フリーソフトにろくなもんがないため、chatGPT に教えてもらいながら python でそれを実行できるコードを書きました

ffmpeg(v6)を使用しているので、ffmpeg が対応してるコーデックならなんでも変換できるはず(未検証)

## 簡単な機能説明

・指定ディレクトリ内の.MTS ファイルを.mp4 に変換して別の指定ディレクトリに置く
・その際ファイル名は動画ファイルの撮影日時を使用(2023-07-25-14-32.mp4 みたいな)
・コマンドラインで簡単めに実行できるといい

## 前提

Mac で動くことを確認してます(一応 windows でも動くはず)
python のバージョンは`3.11.4`です

## セットアップ

1. 任意の場所に`mp4-converter.py`を作成する
2. 下記のコードを貼り付け後保存

```python
import os
import argparse
import datetime
import platform
from tqdm import tqdm
import subprocess

input_formats = ["MTS", "AVI", "MOV", "MP4", "MKV", "WMV"]
output_formats = ["mp4", "avi", "mov", "mkv", "wmv"]

def run_ffmpeg_command(command):
    process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
    for line in process.stdout:
        yield line
    process.wait()

# 完了音
def play_notification_sound():
    if platform.system() == "Darwin":
        # Mac環境の場合はosaScriptを使って通知音を鳴らす
        try:
            subprocess.run(['osascript', '-e', 'display notification "すべてのファイルの変換が完了しました" with title "FFmpeg Conversion" sound name "Glass"'])
        except Exception as e:
            print(f"Error while trying to play notification sound: {e}")
    elif platform.system() == "Windows":
        try:
            import winsound
            # Windows環境の場合はwinsoundを使ってビープ音を鳴らす
            winsound.Beep(440, 500)
        except Exception as e:
            print(f"Error while trying to play notification sound: {e}")

def convert_to_mp4(input_directory, output_directory, extension="MTS",output_extension="mp4", quality="medium"):
    output_directory = os.path.expanduser(output_directory) # ホームディレクトリを展開
    os.makedirs(output_directory, exist_ok=True)
    files = [file for file in os.listdir(input_directory) if file.endswith(f".{extension}")]

    total_files = len(files)
    converted_count = 0

    if not os.path.exists(output_directory):
        os.makedirs(output_directory)

    for file in tqdm(files, desc="進捗", unit="ファイル"):  # tqdmを使って進捗バーを表示
        input_file = os.path.join(input_directory, file)
        creation_time = datetime.datetime.fromtimestamp(os.path.getmtime(input_file))   # ファイルの作成日時を取得
        output_file = os.path.join(output_directory, f"{get_datetime_string(creation_time)}.{output_extension}")
        crf = "18" if quality == "high" else "23"  # 高画質: CRF 18, 中画質: CRF 23

        # 既に同じ名前のファイルが存在する場合、連番をつける
        count = 1
        while os.path.exists(output_file):
            output_file = os.path.join(output_directory, get_datetime_string(creation_time) + f"-{count}.mp4")
            count += 1

        command = f"ffmpeg -i {input_file} -c:v libx264 -crf {crf} -loglevel warning -preset slow -c:a aac -strict experimental {output_file}"
        os.system(command)

        converted_count += 1
        print(f"変換完了: {converted_count}/{total_files}")

def get_datetime_string(dt):
    return dt.strftime("%Y-%m-%d-%H-%M")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="動画ファイルを指定のフォーマットに変換できます")
    parser.add_argument("input_directory", help="入力ディレクトリのパス")
    parser.add_argument("output_directory", help="出力ディレクトリのパス")
    parser.add_argument("--extension", choices=input_formats, default="MTS", help="変換する拡張子 (デフォルト: MTS)")
    parser.add_argument("--output_extension", choices=output_formats, default="mp4", help="変換先の拡張子 (デフォルト: mp4)")
    parser.add_argument("--quality", choices=["high", "medium"], default="medium", help="変換品質を選択 (デフォルト: medium)")
    args = parser.parse_args()

    convert_to_mp4(args.input_directory, args.output_directory, args.extension, args.output_extension, args.quality)

    play_notification_sound()

```

おわり

## 使い方

`mp4-converter.py`をおいたディレクトリに`cd`して実行します。

最低限のコマンドはこちら

```zsh
python3 mp4-converter.py {変換したいファイルがあるディレクトリのパス} {変換したファイルの格納先パス}
```

ただこれだと.MTS ファイルを mp4 に中画質で変換するコマンドなので、コード見てやりたいふうに変えてください。
例えば.mp4 を.mov に高画質で変換したい場合、以下のコマンドになります。(成功するかはわからない)

```zsh
python3 mp4-converter.py {変換したいファイルがあるディレクトリのパス} {変換したファイルの格納先パス}　mp４ mov high
```

実行には結構かかるので、気長に待ちましょう。
正常に実行されている場合このような画面になります。
![スクリーンショット 2023-07-25 21.50.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778030/1dd6cc80-8659-b71d-5a77-f06d942b626e.png)

`ModuleNotFoundError: No module named '~~~~'`のようなエラーが出た場合
`pip3 install ~~~~`でモジュールをダウンロードしてください

## 終わりに

chatGPT は神

## 参考

https://irilyuu.com/ffmpeg/ffmpeg-codec-change/#toc5