【未完成】youtube-dlで動画の指定範囲を切り抜きたい（しかも高画質で）

# 背景
各種ストリーミングサイトで配信業が活発になるにつれて、見どころを切り抜くという文化も発展してきた。
そこで、切り抜きを作る諸氏は、YouTubeからの動画のダウンロードにyoutube-dlを使っているかと思われるが、数時間にわたる長時間配信の一部を利用したい場合であっても、
動画のすべてをDLしなければならず、このことは大きな時間とPCリソースがかかってしまう。

そこで、動画の指定範囲のみを切り抜けるスクリプトを製作したので公開する。

# ffmpeg の利用

https://github.com/ytdl-org/youtube-dl/issues/4821#issuecomment-386753006
こちらの記事に __ffmpeg__ を用いて指定時間の範囲を切り抜き可能なコードが載っている。

```
ffmpeg -i "URL" -ss 開始時間 -t 継続時間 "保存名"
```

 で指定範囲の切り抜きが可能である。

youtube-dl の --get-url URLを取得する（動画のDLはしない）のオプションと併用することで、指定範囲の保存は完了する。

youtube-dl は -f best 指定により、動画サイトからもっとも高画質なフォーマットを選択して、ダウンロードすることができるのだが、
YouTubeでは最高音質と最高画質のファイルはそれぞれ音声専用、映像専用となっておりbestしていだと高々720pの動画しか取得することができない。

![スクリーンショット 2021-12-21 3.44.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662712/93f00518-b6f6-4520-c683-40d5d7302978.png)
図のように、best 指定では最下行22番の映像がダウンロードされてしまう。

そこで、youtube-dlではフォーマットで bestvideo+bestaudio にてもっともよい状態のファイルをそれぞれダウンロードし ffmpeg でマージするのが一般的となっている(※1)。
しかし、上記のffmpegを利用した時間指定ダウンロードだと bestvideo+bestaudio 指定が使えないため、それぞれのファイルを ffmpeg でダウンロードしマージするシェルスクリプトを組んだ。

※1 bestvideo+bestaudioの例

```
youtube-dl "URL" -f bestvideo+bestaudio --merge-output-format mp4 -o '%(title)s.%(ext)s'
```

# コード
```ytdl-clip.sh
# ytdl_clip $1 $2 $3 $4
# $1: URL
# $2: starting time
# $3: duration time
# $4: output file type
echo "Get url"
audio_url="$(youtube-dl -f bestaudio --get-url "$1")"
video_url="$(youtube-dl -f bestvideo --get-url "$1")"
echo "Get filename"
audio_fn="$(youtube-dl -f bestaudio --get-filename "$1" -o '%(title)s-%(id)s-audio.%(ext)s')"
video_fn="$(youtube-dl -f bestvideo --get-filename "$1" -o '%(title)s-%(id)s-video.%(ext)s')"
filename="$(youtube-dl  --get-filename "$1" -o '%(title)s-%(id)s')"
echo "Get audio file"
ffmpeg -loglevel warning -hide_banner -stats -ss $2 -i "$audio_url" -c copy -t $3 "$audio_fn"
echo "Get video file"
ffmpeg -loglevel warning -hide_banner -stats -ss $2 -i "$video_url" -c copy -t $3 "$video_fn"
echo "Merge video+audio"
ffmpeg -loglevel warning -hide_banner -stats -i "$video_fn" -i "$audio_fn" -c:v copy -c:a aac -map 0:v:0 -map 1:a:0 "$filename.$4"
# echo "Remove temp file"
# rm -i $audio_fn $video_fn
echo "Saved to $filename.$4"
```

プログラムの実行サンプル

```
./ytdl-clip.sh "https://www.youtube.com/watch?v=xxxxxxxxxxx" 3:55:30 500 mp4
```

# 解説
youtube-dlのオプション
--get-urlで最高音質と、最高画質のファイルURLを取得する。
--get-filenameで、それらのファイル名を生成する。
-oオプションで「動画タイトル名-動画id-(audio/video).拡張子」に成形している。
本当はファイル拡張子さえわかれば、ファイル名の取得は「動画タイトル名-動画id」の1回でいいのだが、
結局、拡張子がわからないので、愚直にファイル名と、拡張子で3回取得している。

### ffmpeg のオプション
-loglebel warning 警告未満のログをださない。 
-hide_banner コピーライトやライブラリの情報をださない。
-stats 進行状況の表示

Merge部分の解説について
https://qiita.com/niusounds/items/f69a4438f52fbf81f0bd

rm はコメントアウトしている。
デバック中というのもあるが、ffmpeg のダウンロード段階で、すでに存在するファイル名を指定した場合、Overwriteの警告が出される。
そこで、noを選択したまま下の行が実行されて削除されてしまうことを防ぐためである（一応、-iオプションをつけた）。

# 問題点
現在、ある問題点により未完成に終わっている

### ① videoとaudioの取得がずれる
![スクリーンショット 2021-12-21 9.28.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662712/ce9c7237-e5cc-cfbf-e322-58a6ef56dcdd.png)
音声ファイル、長さ=Duration が08:29.98 指定の500秒より10秒ほど長い
![スクリーンショット 2021-12-21 9.29.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662712/3d2556b8-23c5-d384-e5d3-dc57390f03f8.png)
動画ファイル、長さ=Duration が08:25.02 指定の500秒より5秒ほど長い

```ffmpeg -i "ファイル名"``` でファイルの詳細を取得できるのだが、DURATIONの項目からわかるように、動画と音声で長さが違う。
また、それぞれ指定したdurationからずれている。

ここで、ffmpeg のマージ機能を利用したところ、音声と映像がずれてしまう。
この事例だと、音声が前後5秒ほど長く、映像が後ろに5秒長かったので、映像の後ろに合わせればよかったが、
他に試したところ、音声が前に長く、映像が後ろに長いという例もあり、機械的に合わせることが不可能だった。
そのため、手動で調整が必要という結論に至った。

動画も音声も範囲指定から不足なく取得できている（本来の範囲から不足することはないが、超過して取得してしまう）ことは確認できているが、同期が取れないため、単純ではない。

### ② ダウンロード時間が想像よりかかる
![スクリーンショット 2021-12-21 3.20.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662712/eda91035-ee0e-7166-2bbe-788dbfadf47d.png)

何回か試したが、videoファイルのダウンロード速度が恐ろしく低い。
速さの観点でいうと、数時間あるファイルから数分を切り取るならこちらのほうが早いが、
この割合が大きくなるほど、敢えてこの手法を使う必要はなさそうである

### ③ video ダウンロード時のコンソール出力がうるさい
うるさい
オプションの同じ audio ダウンロード時は静かなのになぜ？
おまかん？

# ①の解決策?
###・ファイルの種類が違うから、ずれる？
→video only ファイルと、音声+映像のファイルを取得し、前者の映像と後者の音声を組み合わせると行ける？

例えば、bestで取得したmp4の音声と、bestvideoで取得した音声なしmp4を組み合わせるなど。
今後、検証。

###・愚直に合わせる
ffmpeg で音声と動画をオフセットする方法
https://lzone.de/blog/Easily-fix-async-video-with-ffmpeg

でも、ダウンロードまでできたら、双方を動画編集ソフトにぶっこみGUIで調整したほうが楽そう。
なので、ある区間の動画を取得したい場合、上のスクリプトのMergeより上を実行すればよいと思います。

ここでも音声ファイルの .webm → .mp3 の変換にffmpegが使えます
https://qiita.com/suzutsuki0220/items/43c87488b4684d3d15f6

###・720pの動画で妥協

```
ffmpeg -loglevel warning -hide_banner -stats -ss $2 -i "$(youtube-dl -f best --get-url "$1")" -t $3 "$(youtube-dl -f best --get-filename "$1" -o '%(title)s').$4""
$1: URL, $2: start time, $3: duration, $4: format
```

この1行で十分

###・フルでダウンロードしたあとに、カットするなら
https://nico-lab.net/cutting_ffmpeg/

```
ffmpeg -ss [start_time] -i input.mp4 -t [duration] -c copy output.mp4
ffmpeg -ss [start_time] -i input.mp4 -to [end_time] -c copy output.mp4
```

確認したところ -to というオプションがあるらしい、上記の -t を -to に置き換えても実行できるかもしれない。

# 更新（2021/12/21 21:20）
章「問題点」の①について言葉足らずな点があったので加筆した。
「フルでダウンロードしたあとに、カットするなら」のセクション追加。
誤字の修正。