【Python】LINEからYoutubeの曲や動画を落としたい1/3

5 min read読了の目安(約5300字

概要

【Python】LINEからYoutubeの曲や動画を落としたい1/3
【Python】LINEからYoutubeの曲や動画を落としたい2/3
【Python】LINEからYoutubeの曲や動画を落としたい3/3

・docker-composeで実装しています。
Githubへアップロードしました。

動画でも説明しています。
【Python】Media-Downloader for LINE

https://www.youtube.com/watch?v=NHd-SMjSdNE

経緯について

Androidに曲を落としたいがYoutubeの曲落とすアプリだと広告うざい。
かと言ってWEBから落とせるのもあるが、ずっと変換終わるまで待たないといけない。
もういっそ、自分でアレンジして作ってみようって考えた末、開発に至りました。

本プログラムについて

・Youtube、SoundCloud、ニコニコ動画、Ticktok、Dailymotionに対応しています。
・Youtubeのプレイリストからでも曲、動画の複数ダウンロードに対応しています。
・データはGoogleDrive上に保管されます。
・LINEを閉じようが何しようが、一旦コマンドを送れば自動的に処理が終わります。
・LINE MessageAPIとWebhook、GoogleDriveAPI、Youtube-dlを使用します。
・さくらのVPSサーバを使用しています。

出来上がりの状態

出来上がり
完成後のGIFアニメーション

仕様について

・SoundCloudのURLで間違って動画を取得しようとした際は取得できないエラーが発生します。
 ※ただしプログラムが停止してしまうことはありません。

前提条件

GoogleDriveAPI(GCPを使用)が使用できること。参考にした記事
LINE MessageAPIが使用できること。参考
ngrokが利用できること。参考にした記事
・Pythonの基礎知識があること。(なくてもほぼコピペで動くようには書くつもり)

動作環境

・Python 3.8.4 (3系なら動く)
・CentOS Linux release 7.8.2003 (Core) (7系ならOK)
・ngrok version 2.3.35
・pip 20.1.1
・line-bot-sdk 1.16.0
・google-api-python-client 1.9.3
・PyDrive 1.3.1
・youtube-dl 2020.06.16.1
・Flask 1.1.2
・ffmpeg 3.4.8
・httplib 0.15.0(推奨)0.15以上だとGBを超えるファイルで以下のエラーが出ます。

・httplib 0.15.0 (最新の0.16.0では大きい動画だとGoogleDriveへアップロード時に以下のエラーが出ます)
Redirected but the response is missing a Location: header. 詳細はこちら

2020/07/22 0.18.1が来てました。こちらでは問題なく動いたようです。

ディレクトリ構造

$HOME/
   ┝ line/
       ┝ app.py
       ┝ youtube.py
       ┝ start.sh
       ┝ credentials.json
       ┝ settings.yaml
       ┕ youtube/
              ┕ sound.mp3,movie.mp4

実装に必要なライブラリ等のインストール

# pip install line-bot-sdk
# pip install google-api-python-client
# pip install PyDrive
# pip install youtube-dl
//最新にアップデート推奨
# pip install -U httplib

以下から一般ユーザーで作成しています。

ディレクトリ作成

$ mkdir $HOME/line/youtube
$ cd $HOME/line

では早速、youtube-dlのプログラムから作成していきます。
オプション設定についてはこちらをご参考ください。

youtube.pyの作成

$ vim youtube.py
youtube.py
#!/bin/env python3
import youtube_dl
import sys
import os
import re
from glob import glob

def option_setting(opt,dl_dir):
    options = {
        'outtmpl':dl_dir + '%(title)s.%(ext)s',
        'restrictfilenames':'True',
        'quiet':'True',
        'default_search':'error'
    }
    options.update(opt)
    return options

def download(option,url):
    try:
        with youtube_dl.YoutubeDL(option) as ydl: ydl.download([url])
    except Exception as e:
        return e

def convert(op,files):
    ext_dict = {
        '.m4a' : ['ffmpeg -y -i "%s" -ab 256k "%s" -loglevel quiet','/mp3'],
        '.mp4' : ['ffmpeg -y -i "%s" -ab 256k "%s" -loglevel quiet','/mp3'],
        '.webm': ['ffmpeg -y -i "%s" "%s" -loglevel quiet','/mov'],
        '.mkv' : ['ffmpeg -y -i "%s" -vcodec copy "%s" -loglevel quiet','/mov'],
    }
    root, ext = os.path.splitext(files)
    formats = ext_dict.get(ext)
    if formats != None and formats[1] == op:
        if ext != ".mp4" and op == "/mov":
            cnv_mp4 = '%s.mp4' % root
            cmd = formats[0] % (root+ext, cnv_mp4)
            os.system(cmd)
            os.remove(root+ext)

        else:
            cnv_mp3 = '%s.mp3' % root
            cmd = formats[0] % (root+ext, cnv_mp3)
            os.system(cmd)
            os.remove(root+ext)

def main(operation,url,dl_dir):
    #ニコニコ動画は変換なし
    if "nicovideo" in url:
        opt = {}
    elif operation == "/mp3":
        opt = ({'format':'bestaudio[ext=mp3]/bestaudio[ext=m4a]/bestaudio'})
    elif operation == "/mov" or operation == "/nomov":
        opt = ({'format':'bestvideo+bestaudio'})

    option = option_setting(opt,dl_dir)
    msg = download(option,url)
    if "retries" in str(msg):
        while True:
            msg = download(option,url)
            if not "retries" in str(msg):
                break

    if operation == "/mp3" or operation == "/mov":
        fileExtensions = [ "mp4", "m4a","mkv","webm"]
        files_grabbed = []
        for ext in fileExtensions:
            files_grabbed.extend(glob(dl_dir + "*." + ext))

        for cnv_file in files_grabbed:
            convert (operation,cnv_file)

    all_file = [f.strip(dl_dir) for f in glob(dl_dir + "*.*")]
#    print (all_file)
    return all_file
#if __name__ == "__main__":
#    dl_dir = "youtube/"
#    main("/mp3","https://www.youtube.com/watch?v=9swXEd6SWjA",dl_dir)

下部の4つのコメント部分を外して実際に動くか確認してみるといいです。
フリーBGMの[Shall we meet?.mp3]が取得できればまずは成功です。

※/nomovについては動画変換なしにそのままアップロードする機能です。

今回は一旦ここまでとします。次回はこのyoutube-dlを使ってプログラムを組んでいきます。