Yotazo Lab.

ほぼ自分用。でも誰かの役に立つかもしれない話題

pytubeでYouTubeダウンロード パート3

f:id:yotazo:20180125162038p:plain

ここ2回ほどpytubeで遊んでいますが、そろそろラスト。

yotazo.hateblo.jp

yotazo.hateblo.jp

目次

今回はDASH用に準備されている、動画のみ、音声のみのストリームをダウンロードしてみます。progressiveは720p以下ですが、DASHには1080pの動画が準備されていることもあるので試す価値はあるかなと。

ダウンロードした動画と音声は、ffmpegで勝手に合成が始まるようにします。

前回との変更点

ファイルを選択→ダウンロード→ffmpegという流れは前回とほとんど同じなのですが、動画ファイルと音声ファイルの2ファイルがあること。加えてそれぞれの拡張子が未定ということで、思ったよりもやっかいでした。

操作の流れとしても、
>動画選択 → 動画DL → 音声選択 → 音声DL
になるよりも、
>動画選択 → 音声選択 → 動画+音声DL
のほうがスムースなのでその点を変更しました。

動かしてみる

起動してYouTubeの動画URLを入れると、動画のみの一覧が出てきます。

ここで出て来る一覧は、pytubeのStreamQuery Objectのフィルタで、adaptive=True, only_video=True, subtype="mp4" にした結果です。

self.showList(st.filter(adaptive=True, only_video=True, subtype="mp4"))

※mp4に限定しているのは、ffmpegの出力を -vcodec copy で mp4にしているため。

ここで使いたい動画の選択しますが、入力するのはitagの番号です。

動画と音声ファイルを連続でダウンロードする仕様にしたので、ユニークで使えるitagでファイルを指定するようにしました。


続いて同じように音声のみのファイルを選びます。こちらもitagの番号で指定。
フィルタはこれが効いています。

self.showList(st.filter(adaptive=True, only_audio=True))

音声を選択すると、動画のダウンロードと音声のダウンロードが始まり、それぞれのダウンロードが終わるとffmpegでファイルの合成が始まります。
f:id:yotazo:20180214114137p:plain

合成が完了すると、動画のみのファイルを消しますか? 音声のみのファイルを消しますか? と聞かれる感じで終了。
f:id:yotazo:20180214114822p:plain

一応「ファイルを残しますか?」などと聞かれますが、動画のみファイルのファイル名は「tmp_Video」、音声のみは「tmp_Audio」と、完全なテンポラリ仕様になっているのであまり親切ではありません。注意。

ちなみに完成ファイルは「タイトル+mp4」になります。
ffmpegの設定はここです。

    convcmd = "ffmpeg -i {} -i {} -y -vcodec copy {}".format("tmp_Video"+self.videoExt, "tmp_Audio"+self.audioExt, "\""+self.filename+"\""+".mp4")

コード

import os
from pytube import YouTube

class Downloader:

  def __init__(self, url):
    self.yt = YouTube(url)
    self.videoTitle = self.yt.title
    self.getStream()


  def getStream(self):
    print('\n[Title] {0}\n'.format(self.videoTitle))

    st = self.yt.streams
    self.showList(st.filter(adaptive=True, only_video=True, subtype="mp4"))
    itag_v = self.selectStream("Video")

    self.showList(st.filter(adaptive=True, only_audio=True))
    itag_a = self.selectStream("Audio")

    self.yt.register_on_progress_callback(self.progress)

    root, self.ext_v = os.path.splitext(st.get_by_itag(itag_v).default_filename)
    root ,self.ext_a = os.path.splitext(st.get_by_itag(itag_a).default_filename)

    st.get_by_itag(itag_v).download("", "tmp_Video")
    st.get_by_itag(itag_a).download("", "tmp_Audio")   


  def showList(self, list):
    for i in list.all():
      print(i)

  def selectStream(self, fileType):
    while True:
      itag = int(input("Select " + fileType + " itag#: "))
      if self.yt.streams.get_by_itag(itag) != None:
        break
    return itag


  def progress(self, stream, chunk, file_handle, bytes_remaining):
    p = round(file_handle.tell()/(file_handle.tell()+bytes_remaining)*100,1)
    print(file_handle, p,"%")


class Converter:

  def __init__(self, filename, vext, aext):
    self.filename = filename
    self.videoExt = vext
    self.audioExt = aext
    self.convert()


  def convert(self):
    convcmd = "ffmpeg -i {} -i {} -y -vcodec copy {}".format("tmp_Video"+self.videoExt, "tmp_Audio"+self.audioExt, "\""+self.filename+"\""+".mp4")

    os.system(convcmd)

    self.deleteFile(self.delete, "Video")
    self.deleteFile(self.delete, "Audio")


  def deleteFile(self, function, fileType):
    q = input("Delete " + fileType + "-Only-File? y/n : ")
    if q == "y":
      function(fileType)


  def delete(self, f):
    if f == "Video":
      target = "tmp_Video"+self.videoExt
    elif f == "Audio":
      target = "tmp_Audio"+self.audioExt

    os.system("del "+target)


if __name__ == "__main__":
  url = input("Enter YouTube Video URL : ")
  d = Downloader(url)
  c = Converter(d.videoTitle, d.ext_v, d.ext_a)

動画と音声はそれぞれ「tmp_Video」「tmp_Audio」の名前で固定することにしました。

st.get_by_itag(itag_v).download("", "tmp_Video")
st.get_by_itag(itag_a).download("", "tmp_Audio")   

こうしておけば、ffmpegに渡す時に楽かなと。

フルオート版

deleteFile()のifを取る

  def deleteFile(self, function, fileType):
    function(fileType)

ファイルを残しますか? と聞かずにテンポラリを消去
ファイルを選べばあとはフルオート

こっちのほうが気持ちいいかも

傾福さん [DVD]

傾福さん [DVD]

  • 発売日: 2018/02/19
  • メディア: DVD