動画ファイルを簡単にチャプター付きで結合します

# 動画ファイルを簡単（再エンコードしないで）に結合して結合部分にチャプターを自動生成するスクリプトを、python を使って書いてみました

## 要件定義
1. mp4, mkv ファイルを再エンコードしないで結合したい
1. 楽したい！（使いまわしができるように）
1. Windows10 環境で動く
1. python, ffmpeg, mkvmerge などのインストールはできる前提
1. すでにチャプターがついているファイルはそのチャプターを利用したい

- こんな感じで要件を確定させて、実際にどのツールを使っていくのか？を検討していきます

## 検討結果（＝機能リスト）
1. Windows10でのみ検証しています（Linuxでも少しの修正で動くはず）
1. チャプターを簡単に作成するため最終的なファイルフォーマットを mkv にする
1. 結合するファイルのファイル名をチャプター名として利用
1. mkvファイルで、すでにチャプターがついている場合はそのまま利用する
1. 結合元のソースファイルは、mp4 or mkv のみ
1. 結合するファイルの順番は python の Sort関数順
1. ワイルドカードで結合ファイルを選択するので、結合するファイル名の先頭部分は同一にする必要がある
1. Codec(A,V), 画素数, アスペクト比等は、結合するファイルで統一されているのが望ましい
1. ファイルフォーマットが異なる場合はある程度統一します（再エンコーディングしますので要時間）
1. pythonのコーディング久しぶりなのでコードが美しくないのはご容赦ください

## 動作の流れ

1. ソースファイルを mkv に統一
   - ffmpeg 利用
   - mp4 to mkv
1. 動画フォーマットを統一
   - ffprobe で確認して必要であれば ffmpeg を利用します
```python
    # 動画ファイルの情報をjsonで出力します
    bat.write('"%s" "%s" -loglevel quiet -show_streams -print_format json > %s\n' 
        % (ffprobe, c, ffprobejson))
    bat.close()
```
```python
    # jsonファイルを確認して想定しているサイズと異なる場合は再エンコードのフラグをたてます
    json_f = open(ffprobejson, "r")
    json_data = json.load(json_f)
    json_f.close()
    reenc = False
    for stream in json_data['streams']:
        if stream['codec_type'] == 'video':
            print(stream['codec_type'])
            if stream['codec_name'] != codec_name_v:
                print(stream['codec_name'])
                reenc = True 
            if stream['width'] != width:
                print(stream['width'])
                reenc = True 
            if stream['height'] != height:
                print(stream['height'])
                reenc = True
```

3. チャプターを追加
   - 既存のチャプターを確認（mkvextract 利用） 
```python
    # チャプターが存在するとチャプター内容がファイル出力されます
    bat.write('"%s" "%s" chapters -s "%s"\n'
        % (mkvextract, mkv, chkchapfile))
    bat.close()
    # ファイルが生成されればチャプター付きなのでそのまま利用します
```

4. 結合前のmkv状態で、00:00:00部分にチャプターを追加
   - mkvmerge 利用（チャプターデータのファイルを生成してからmkvmergeで追加します）
```python
    metafile = open(metafilename, 'w+')
    metafile.write('CHAPTER01=00:00:00.000\n')
    metafile.write('CHAPTER01NAME=%s\n' % bname)
    metafile.close()
```
```python
    bat.write('"%s" --chapters "%s" -o "%s" "%s"\n'
        % (mkvmerge, metafilename, mkn, mkv))
    bat.close()
```

5. mkvファイルを結合
   - mkvmerge 利用

## ポイント
- mkvmerge で mkvファイルを結合するときにチャプターが残る仕様を活用しています
- 外部ツールを起動する場合に subprocess（ライブラリ）経由で batファイルを使っています

## 初期設定
- pythonファイルの先頭部分を修正してください

mkvmergeを起動するバッチファイル（ファイルが作成できる場所であればどこでも可）
> batfilename = 'H:\mp4mkvmerge_.bat'

チャプターを作成するときの作業用ファイル（ファイルが作成できる場所であればどこでも可）
> metafilename = 'H:\metadata.txt'

mkvmerge.exe の場所
ffmpeg.exe の場所
ffprobe.exe の場所
mkvextract.exe の場所
> mkvmerge = 'C:\Program Files\MKVToolNix\mkvmerge.exe'
> ffmpeg = 'C:\Program Files\ffmpeg\bin\ffmpeg.exe'
> ffprobe = 'C:\Program Files\ffmpeg\bin\ffprobe.exe'
> mkvextract = 'C:\Program Files\MKVToolNix\mkvextract.exe'

作業用の空ディレクトリ（存在している必要あり）
作業用ファイル名
> tempDir = 'C:\TEMP'
> mergefilename = '000.mkv'
> chkmp4filename = 'chk.mp4'
> chkmkvfilename = 'chk.mkv'
> chkchapterfilename = 'chap.txt'

結合時に異なるフォーマットの場合に揃えるパラメータ（タブレット向き？）
> codec_name_v = 'hevc'
> codec_name_a = 'aac'
> width = 1280
> height = 720
> sample_rate = '48000'
> reenc_codec_v = 'hevc_nvenc'
> reenc_codec_a = 'aac'
> reenc_bitrate = '1200k'

## サンプル
> python mp4mkvmerge.py "C:\Videos" "DORAMA" "DORAMA #01-12 END" "C:\DELETE"
1. C:\Videos ディレクトリにある DORAMAで始まるファイル（DORAMA*.mp4, DORAMA*.mkv）を（ファイル名を pythonの標準sort順で）結合します
2. DORAMA #01.mp4 と DORAMA #01.mkv が存在する場合は同一とみなし DORAMA #01.mkv を結合対象とします
3. 処理が終わったら、C:\Videos\DORAMA #01-12 END.mkv ファイルが生成されます
4. 結合前のファイルは、C:\DELETE フォルダに移動されます

## ソースコード
- GitHub
https://github.com/dev2dev2022/mp4mkvmerge

## カスタマイズ
- Windows の batファイルを生成しているところをシェルスクリプトに変更すればLinux対応できます
- 再エンコード の ffmpegパラメータは好きなようにカスタマイズしてください（筆者は NVEncC64 を使っているので適当ですいません）