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

#概要
・[【Python】LINEからYoutubeの曲や動画を落としたい1/3](https://qiita.com/Trusted_Dream/items/9e1019efe2a14b6e5199)
・[【Python】LINEからYoutubeの曲や動画を落としたい2/3](https://qiita.com/Trusted_Dream/items/b02e8d84e149b15280bc)
・[【Python】LINEからYoutubeの曲や動画を落としたい3/3](https://qiita.com/Trusted_Dream/items/f564041ce8c6cb7f3513)

・[GitHub](https://github.com/Trusted-Dream/line)へアップロードしました。

・Zennの記事の方が最新です。
https://zenn.dev/trusted_dream/articles/6fbb229fb71216

#LINEへの返答、GoogleDriveへのアップロード部分の作成
[前回](https://qiita.com/Trusted_Dream/items/9e1019efe2a14b6e5199)では、youtubeやsoundcloud、ニコニコ動画に対応するダウンローダーのプログラムを作成しました。
今回はyoutube-dlを使い実際にLINEに応答を返したりGoogleDriveへアップロードする部分を作成します。

まず、始める前にGoogleDriveへアップロードできるか確認しておきましょう。
##GoogleDriveへのアップロードができるか確認する(手順は大きく3つ)
①GoogleDriveで共有のフォルダを一つ作成する。
GoogleDrive上で右クリック → 新しいフォルダ → 名前を入力 → 名前を付けたフォルダを右クリック → 
共有可能なリンクを取得 → [制限付き▼]をクリック → **リンクを知っている全員**に変えておく。
※ここを変えておかないと、LINEから直接音楽を聴いたりができなくなります。
(LINEサーバ上にデータは保管されず、あくまでストリーミングとしてGoogleDrive上の音楽を取得しに行ってるからです。)

②共有フォルダーのフォルダーIDを確認する。
フォルダーを作成できたらそのフォルダを開く → URLを確認 → https://drive.google.com/drive/folders/
以降のランダムな文字列がフォルダーIDになります。

③GoogleDriveへのアップロードを行う部分のテストプログラムを作成して確認する
前回作成したyoutube.pyと同じディレクトリ上で作業します。

**・client_idとclient_secret、GoogleDriveAPIの有効化が必要です。[参考](https://qiita.com/akabei/items/f25e4f79dd7c2f754f0e)**

![ID.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672611/9098de9a-0bee-3519-e957-e2ec7d8e1e0d.png)
**※[JSONをダウンロード]も実施して名前を「client_secrets.json」に変更し、$HOME/lineに設置します。**

####まずはsettings.yamlファイルを作成します
```console
cd $HOME/line
vim settings.yaml
```
```settings.yaml
client_config_backend: settings
client_config:
  client_id: "ここにGoogleClientID"
  client_secret: "ここにSecretID"

save_credentials: True
save_credentials_backend: file
save_credentials_file: credentials.json

get_refresh_token: True

oauth_scope:
  - https://www.googleapis.com/auth/drive.file
  - https://www.googleapis.com/auth/drive.install
```

####次にyoutube-dlを使用してGoogleDriveへのアップロードまでのプログラム
```console
vim upload.py
```
```upload.py
#!/bin/env python
import youtube
import os
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive

#ダウンロードディレクトリ
dl_dir = "youtube/"
#GoogleFolderID
folder_id = 'フォルダーID'
#GoogleDrive認証設定
gauth = GoogleAuth()
gauth.CommandLineAuth()
drive = GoogleDrive(gauth)

message = "/mp3 GoogleDriveへアップロードしたいyoutubeの曲"
url = message.split()[1]
transaction = message.split()[0]

file_name = youtube.main(transaction,url,dl_dir)

for i in range(len(file_name)):
    f = drive.CreateFile({'title': file_name[i],
        'mimeType': 'audio/mpeg',
        'parents': [{'kind': 'drive#fileLink', 'id':folder_id}]})
    f.SetContentFile(dl_dir + f['title'])

    #UploadGoogleDrive
    f.Upload()
```
ここまでできたら、実行してアップロードできるか確認してみましょう。

```console
$ chmod +x upload.py
$ ./upload.py
```
問題なくアップロードできたら、次にLINE BOTアプリを作成していきます。
##LINE BOTアプリを作成する

###環境変数に登録する
```console
//未登録であることを確認
export -p
//登録を実施
echo 'export LINE_CHANNEL_ACCESS_TOKEN="ここにアクセストークン"' >> ~/.bash_profile
echo 'export LINE_CHANNEL_SECRET="ここにチャンネルシークレット"' >> ~/.bash_profile
source ~/.bash_profile
//登録されていることを確認
export -p
```

アプリの作成

```console
vim app.py
```
```app.py
#!/bin/env python
import os
import re
import youtube
import math
from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (
   MessageEvent, TextMessage, TextSendMessage,
   AudioSendMessage
)
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
from mutagen.mp3 import MP3

#アプリケーションフレームワーク
app = Flask(__name__)
#LINE Channel Secret
LINE_CHANNEL_SECRET = os.environ['LINE_CHANNEL_SECRET']
#LINE Access Token
LINE_CHANNEL_ACCESS_TOKEN = os.environ['LINE_CHANNEL_ACCESS_TOKEN']

line_bot_api = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(LINE_CHANNEL_SECRET)

#DownloadDirectory
dl_dir = "youtube/"
os.makedirs(dl_dir, exist_ok=True)

#GoogleDriveAuthSettings
gauth = GoogleAuth()
gauth.CommandLineAuth()
drive = GoogleDrive(gauth)

#GoogleFolderShareID
folder_id = 'GoogleDriveの共有フォルダーIDを記入'

@app.route("/callback", methods=['POST'])
def callback():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)
    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)
    return 'OK'

#Message
def messagebox(event,transaction,url):
    transaction_dict = {
        '/mp3':'曲',
        '/mov':'本の動画',
        '/nomov':'本の動画'
    }
    set_transaction = transaction_dict.get(transaction)
    if "&list=" in url:
        line_bot_api.reply_message(event.reply_token,
                TextSendMessage(text="プレイリストの" + set_transaction
                    + "をGoogleDriveにアップロードします。\n"
                    + "処理に時間が掛かる場合があります。"))
    elif "nicovideo" in url:
        line_bot_api.reply_message(event.reply_token,
                TextSendMessage(text="ニコニコ動画は処理に時間が掛かる場合があります。"))
    else:
        line_bot_api.reply_message(event.reply_token,
                TextSendMessage(text="1" + set_transaction  + "をGoogleDriveにアップロードします。"))

#Music
def music(get_id,file_name):
    for i in range(len(file_name)):
        f = drive.CreateFile({'title': file_name[i],
                      'mimeType': 'audio/mpeg',
                      'parents': [{'kind': 'drive#fileLink', 'id':folder_id}]})
        f.SetContentFile(dl_dir + f['title'])

        #UploadGoogleDrive
        f.Upload()

        file_id = drive.ListFile({'q': 'title =\"' + file_name[i] +  '\"'}).GetList()[0]['id']

        if len(file_name) == 1 :
            file_length = MP3(dl_dir + file_name[i]).info.length
            link = "https://drive.google.com/uc?export=view&id=" + file_id
            dur = math.floor(file_length * 1000)
            line_bot_api.push_message(get_id,messages=(
                        TextSendMessage(text=file_name[i]),
                        AudioSendMessage(original_content_url=link,duration=dur)))

            line_bot_api.push_message(get_id,messages=(TextSendMessage(text=link)))
            os.remove(dl_dir + file_name[i])
            break

        os.remove(dl_dir + file_name[i])

    else:
        line_bot_api.push_message(get_id,messages=(
            TextSendMessage(text=str(len(file_name))
                + "曲をGoogleDriveにアップロードしました。\n"
                + "https://drive.google.com/drive/folders/" + folder_id)))
#Video
def video(get_id,file_name):
    video_ext_dicts = {
        '.mp4':'video/mp4',
        '.webm':'video/webm',
        '.mkv':'video/x-matroska'
    }
    for i in range(len(file_name)):
        root, ext = os.path.splitext(file_name[i])
        mimeType = video_ext_dicts.get(ext)

        f = drive.CreateFile({'title': file_name[i],
                      'mimeType': mimeType,
                      'parents': [{'kind': 'drive#fileLink', 'id':folder_id}]})
        f.SetContentFile(dl_dir + f['title'])

        #UploadGoogleDrive
        f.Upload()
        os.remove(dl_dir + file_name[i])

    line_bot_api.push_message(get_id,messages=(
        TextSendMessage(text=str(len(file_name))
            + "本をGoogleDriveにアップロードしました。\n"
            + "https://drive.google.com/drive/folders/" + folder_id)))

@handler.add(MessageEvent, message=(TextMessage))
def contents(event):
    #LINE Message
    message = event.message.text
    try:
        get_id = event.source.group_id
    except AttributeError:
        get_id = event.source.user_id

    param = ["/mp3","/mov","/nomov"]
    prm = [l for l in param if message.split()[0] in l]

    try:
        if message.startswith(prm[0]):
            url = message.split()[1]
            messagebox(event,prm[0],url)

            #youtube-dl
            file_name = youtube.main(prm[0],url,dl_dir)
            if str(file_name) in "ERROR" or file_name == []:
                line_bot_api.push_message(get_id,messages=(
                    TextSendMessage(text="ダウンロード処理に失敗しました。")))
            if prm[0] == "/mp3":
                music(get_id,file_name)
            else:
                video(get_id,file_name)
    except IndexError: pass
							
if __name__ == "__main__":
    app.run(host="127.0.0.1", port="9000")
```
ここまでできればほぼ完成です。あとはシェルスクリプトを書いて、挙動を確認して終了です。
ではまた[次回](https://qiita.com/Trusted_Dream/items/f564041ce8c6cb7f3513)でお会いしましょう。