pytubeでYouTubeダウンロード パート3
ここ2回ほどpytubeで遊んでいますが、そろそろラスト。
目次
今回は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でファイルの合成が始まります。
合成が完了すると、動画のみのファイルを消しますか? 音声のみのファイルを消しますか? と聞かれる感じで終了。
一応「ファイルを残しますか?」などと聞かれますが、動画のみファイルのファイル名は「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に渡す時に楽かなと。