Yotazo Lab.

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

YouTubeの動画からmp3を作ってみる

f:id:yotazo:20180125162038p:plain

前回作ったYouTube動画ダウンローダーを少し改造、YouTubeの動画からmp3を生成できるようにしてみます。環境は前回と同じく、Windows+python3で、pytubeという便利ライブラリを使います。今回はそれに加え、動画→mp3変換用にWindows版のffmpegを使ってみようと思います。

yotazo.hateblo.jp

目次

ffmpegインストール

ffmpegは下記から自分の環境に合ったものをダウンロードします。
https://ffmpeg.zeranoe.com/builds/

インストールが終わったら、どこからでもffmpegを実行できるようにpathを通しておきます。

ffmpegをC:\Program Files\FFmpegに置いた場合、pathは「C:\Program Files\FFmpeg\bin」です。

コマンドラインffmpegと打ってみて反応があればOK!(適当)

やりたいこと

今回やりたいことです

  • YouTubeの動画URLを入力すると、動画ストリームの一覧表示
  • 使いたいストリームの番号を選ぶとダウンロードが始まる
  • ダウンロードされたファイルはffmpegが自動でmp3へ変換
  • 変換前のファイルは選択削除

こんな感じです。

コード

pytubeを使うDownloaderと、ffmpegを使うConverterに分けてみました。
動画と音声が別々になっているadaptiveはダウンロードに時間がかかるので、progressive = Trueでフィルタをかけています。

入力の音質は一定ではなさそうなので、ffmpegの変換パラメータはいじらず、純粋に -i input output.mp3 だけにしています。

動画がダウンロードされるとコンバートが始まり、使い終わった動画ファイルを捨てるかとっておくか聞かれて終了です。

import os
from pytube import YouTube

class Downloader:
  def __init__(self, url):
    self.yt = YouTube(url)
    self.getStream()

  def getStream(self):
    list = self.yt.streams.filter(progressive=True).all()
    for i, j in enumerate(list):
      print('[{0}]:{1}'.format(i, j))

    targetNo = int(input("Enter Stream# : "))
    self.targetStream = list[targetNo]
    self.yt.register_on_progress_callback(self.progress)
    self.targetStream.download()


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


class Converter:
  def __init__(self, filename):
    self.filename = filename
    self.root, self.ext = os.path.splitext(self.filename)
    self.convToMp3()

  def convToMp3(self):
    convcmd = "ffmpeg -i {} -y {}".format("\""+self.filename+"\"", "\""+self.root+"\""+".mp3")
    os.system(convcmd)

    delfile = input("Delete the movie file? y/n: ")
    if delfile == "y":
      self.delete()

  def delete(self):
      delcmd = "del {}".format("\""+self.filename+"\"")
      os.system(delcmd)

if __name__ == "__main__":
  url = input("Enter YouTube Video URL : ")
  d = Downloader(url)
  c = Converter(d.targetStream.default_filename)
メモ

最後の部分をWhileにしてループにしておくと、次から次へとmp3が作れるヒャッハーモードになります。

  while True:
    url = input("Enter YouTube Video URL : ")
    d = Downloader(url)
    c = Converter(d.targetStream.default_filename)
メモ2

ファイル名が面倒。YouTubeオブジェクトのtitle(動画のタイトル)と、Streamオブジェクトのdefault_filenameやdownload()で保存されるファイル名に差異があってハマった。
よく見たらドキュメントに書いてあってわかったのですが、ファイル名はファイルシステムセーフになってるんですって! ファイル名から「.」(ドット)が消えるのは確認していますが、あとはわかりませんー。

空白(スペース)があるファイル名をそのままffmpegに渡せないので""で囲ったりエスケープしたり・・・面倒。

ダウンロード中の進捗がわかるようにした%表示部分。

p = file_handle.tell()/(file_handle.tell()+bytes_remaining)*100

file_handle.tell()+bytes_remainingはファイルサイズのことだと思ってStreamオブジェクトのfilesizeを使ってみたのですが処理が激重になりました。ファイルを扱うのは難しいす。

次はバラバラになっているadaptiveのやつをダウンロードして結合しませう。