あくぽろぐ aqpolog

へなちょこエンジニアの備忘録

Youtube-dlで1080p動画ダウンロード手順を簡略化するスクリプトを書いた

かいた。

内容的にはタイトルで全部です。というのも流石に何なので、簡単に説明を書きます。

以前、youtube-dlで1080p画質でDLする手順を簡単にまとめた。

メモ:youtube-dlで1080p - あくぽろぐ aqpolog

実際日常的に使っていて、やっぱり手順が多くてめんどくさい
(そもそもなんで(best)指定で1080pを落としてくれないのか…1)。
というわけで、URL与えたら一発で1080pでDLするところまでやってくれるスクリプトを書いた。

もともとの手順

  1. 対象動画のURLをコピー
  2. コマンドライン上でyoutube-dl -f <URL>を実行
  3. 実行結果から対象(1080p)の動画フォーマットコードと音声フォーマットコードを見つける
  4. 見つけたフォーマットコードを元にyoutube-dl -F <vcd>+<acd> --merge-output-fomart mp4 <URL>のようなコマンドを実行

スクリプト使用時の手順

  1. 対象動画のURLをコピー
  2. コマンドライン上でpython ydl1080p.py <URL>を実行

実行に必要なもの

  • python 3.n (2.7環境でyoutube-dlが動かなくなってるっぽい?ので。動くなら2.nでも多分平気です)
  • youtube-dl 最新版
  • ffmpeg(--merge-output-format利用時に必要)
  • 多少の知識

動作確認は以下環境で実施。他環境の場合はよしなにしてください。

スクリプト(ydl1080p.py)

やってる内容としては、

  1. subprocessモジュールでyoutube-dlを実行、フォーマットコードリストを取得
  2. フォーマットコードリストを単純に一行ずつfindで検索して1080pのビデオフォーマットコード(とオーディオコード)を取得
  3. 取得したコードでyoutube-dl -F <vcd>+<acd> --merge-output-fomart mp4 <URL>コマンドを生成
  4. 生成したコマンドをsubprocessで実行(動画のDL実施)

という単純なやつです。コードも雑なので適当に参考にしてください。

  • 以下コードをテキストファイルにコピペして保存
  • テキストをydl1080.pyなどにリネーム
  • 動画をDLしたいフォルダに置く
  • コマンドプロンプトやターミナルでスクリプト置いたフォルダにcd
  • python ydl1080p.py <URL>を実行
#!/usr/bin/env python3
# last-update:2018/12/06

# import
import subprocess
import sys


class YoutubeDlSupport:

    def __init__(self):
        pass

    @classmethod
    def get_fmt_cd(cls, param_url=''):
        """
        get_fmt_cd: 1080p動画フォーマットを自動判定してDL
        :param param_url: 対象動画のURL
        :return:
        """

        if not param_url:
            print('Need "param_url" option to run this method')
            return

        # command = 'python ayd_sub.py {0}'.format(param_url)
        command = 'youtube-dl {0} -F'.format(param_url)
        with subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc:
            print('youtube-dl return code : {0}'.format(proc.returncode))
            outs, errs = proc.communicate()
            print('outs:{0} errs:{1}'.format(len(outs), len(errs)))

        if len(outs) == 0:
            return
        list_ex = outs.splitlines()
        # list_ex = str_return.readlines()

        # initialize
        audio_fmt_cd = ''
        video_fmt_cd = ''
        best_video_fmt_cd = ''
        p60_fmt_cd = ''
        no_audio_flg = False

        # get 1080p fmt code
        for i, elem in enumerate(list_ex):
            elem = elem.decode()
            if elem.find('m4a') >= 0:
                print('Found audio(m4a) format! On:{0}L -> {1}'.format(i, elem))
                audio_fmt_cd = elem[:3].strip()
                print('"{0}"'.format(audio_fmt_cd))
            if elem.find('x1080') >= 0 and elem.find('mp4') >= 0:
                print('Found 1080p(mp4)! On:{0}L -> {1}'.format(i, elem))
                if elem.find('p60') >= 0:
                    print('Found 1080p60(mp4)!')
                    p60_fmt_cd = elem[:3].strip()
                    print('"{0}"'.format(p60_fmt_cd))
                    continue
                video_fmt_cd = elem[:3].strip()
                print('"{0}"'.format(video_fmt_cd))
            if elem.find('(best)') >= 0:
                print('Found best! On:{0}L -> {1}'.format(i, elem))
                best_video_fmt_cd = elem[:3].strip()
                print('"{0}"'.format(best_video_fmt_cd))
        if not audio_fmt_cd:
            audio_fmt_cd = 'Not found...'
            no_audio_flg = True
        if not video_fmt_cd:
            print('Target video format is not found, so set BEST instead.')
            video_fmt_cd = best_video_fmt_cd
        if len(p60_fmt_cd) > 0:
            print('1080p60Hz video format is found, so set p60 instead.')
            video_fmt_cd = p60_fmt_cd

        # make ydl command
        if no_audio_flg:
            param_f = '-f {0}'.format(video_fmt_cd)
        else:
            param_f = '-f {0}+{1}'.format(video_fmt_cd, audio_fmt_cd)
        video_fmt_suffix = 'mp4'
        param_merge = '--merge-output-format {0}'.format(video_fmt_suffix)
        # param_out = '-o [%(timestamp)s] %(title)s(%(duration)s).%(ext)s'
        # TODO: Windowsの場合は%文字列二重にする必要あり?(要検証)
        # TODO: Linuxの場合はfmt_template部分はクオートで囲む必要あり?(要検証)

        # cmd_fmt = 'youtube-dl {0} {1} {2} {3}'.format(url, param_f, param_merge, param_out)
        cmd_fmt = 'youtube-dl {0} {1} {2}'.format(url, param_f, param_merge)
        # 'youtube-dl <VID or URL> -f nnn+nnn --merge-output-format mp4 -o hogehoge'
        print('Make cmd : {0}'.format(cmd_fmt))
        return cmd_fmt

    @classmethod
    def run_ydl(cls, cmd_ydl):
        if not cmd_ydl:
            print('Need "cmd" option to run this method')
            return
        # call youtube-dl with cmd-args
        print(cmd_ydl)
        with subprocess.Popen(cmd_ydl, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc:
            proc.wait()

# 起動パラメータ取得
args = sys.argv

# インスタンス化
ydls = YoutubeDlSupport()
url = ''
if len(args) > 0:
    url = args[1]
print(url)
cmd = ydls.get_fmt_cd(url)
ydls.run_ydl(cmd)
print('done')

  1. bestを指定した場合、最高画質でDLする仕様となっているが、best=720p止まり