Amazon Polly を使用して Raspberry Pi に YouTube Live のチャットを読み上げてもらう
はじめに
テントの中から失礼します、CX事業本部のてんとタカハシです!
YouTube Live で配信を行っていると、視聴者の方からチャットにコメントを頂くことがあります。ただ、たま〜にコメントが来る程度だと、なかなか気付けないことが多かったりします。せっかくコメントを頂いたのに、気が付いて返信をした時にはもう配信を見てもらえていない、、、なんてこともあります。
んじゃあ、コメントが来たら読み上げてくれるツールか何かを作れば良いんじゃね?と思いまして、タイトル通りのモノを作ってみました。まあ、似たようなツールは既に存在しているような気がしますが、年末年始の自由研究ネタとしては程良かったのでやってみました。
ソースコードは下記に置いてあります。
GitHub - iam326/read-aloud-youtube-live-chat
作ったもの
YouTube Live のチャットにコメントが書かれると Raspberry Pi ちゃんが読み上げてくれます。
登場人物
Raspberry Pi
緑色のやつです。今回はこの子にチャットを読み上げてもらいます。読むと言っても実際は MP3 ファイルを再生するだけです。
本当は Raspberry Pi 4 を使いたいのですが、Raspberry Pi 2を5台持っている都合で、なかなか購買意欲が高まりません。ただ、今回は2でも3でも4でも関係なく動作すると思います。
YouTube Live Streaming API
この API では、YouTube Live で配信されている(もしくは配信済み)の動画情報を取得したり、チャットのコメントを取得/書き込みを行ったりすることが可能です。詳細は下記のドキュメントをご参照ください。
YouTube Live Streaming API Overview
今回はこの API を使用して、配信中のチャットからコメントを取得します。
尚、YouTube Live Streaming API では、必ず OAuth 2.0 による認証を通す必要があります。
However, all of the methods for the YouTube Live Streaming API require OAuth 2.0 authorization
YouTube Live Streaming API - Obtaining authorization credentials
そのため、GCP で認証情報(OAuth クライアント)を作成する必要があるのですが、手順は YouTube Data API を使用する場合と全く同じになります。
Amazon Polly
テキストを音声に変換してくれる AWS のサービスです。YouTube Live Streaming API で取得したチャットのコメントを Amazon Polly に渡して、MP3 ファイルに変換してもらいます。
私の配信では、ほとんどが英語のコメントなので、今回は英語のみ対応することにします(日本語、韓国語、アラビア語などが飛んでくることもあるので、その辺もその内対応したい)。
環境
Raspberry Pi 上の環境は下記になります。言語は Python を使います。AWS CLI が叩ける状態を前提とさせてください。
$ cat /proc/device-tree/model Raspberry Pi 2 Model B Rev 1.1 $ lsb_release -a No LSB modules are available. Distributor ID: Raspbian Description: Raspbian GNU/Linux 10 (buster) Release: 10 Codename: buster $ python3 --version Python 3.7.3 $ pip3 --version pip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7) $ aws --version aws-cli/1.18.199 Python/3.7.3 Linux/5.4.51-v7+ botocore/1.19.39
準備
OAuth クライアントを作成する
YouTube Live Streaming API を叩くために OAuth クライアントを作成する必要があります。下記を参考にして、OAuth クライアントの情報を含む JSON ファイルを作成してください。また、作成した JSON ファイルの名前をclient_secrets.json
に変更して、後に実装するプログラムと同じディレクトリに格納してください。
イヤホン or スピーカーを取り付ける
音声を確認するため、Raspberry Pi の 3.5 mm ジャックにイヤホンもしくは、スピーカーを接続してください。今回、私が使用したスピーカーは下記になります。
ELECOM - スマホ用モノラルスピーカー - ASP-SMP050YL
必要なパッケージのインストール
下記のコマンドを実行します。
$ pip3 install boto3 $ pip3 install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
実装
YouTube Live Streaming API 周り
YouTube Live Streaming API をラップするモジュール youtube_live_streaming_api_client.py
を実装します。このモジュールでは、API を叩くために OAuth 2.0 認証を行ったり、配信のチャット ID を取得、そのチャット ID を使ってコメントを取得する処理を載せています。
下記のドキュメントを参考にしました。
#!/usr/bin/env python3 import pickle import os.path from googleapiclient.discovery import build from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request API_SERVICE_NAME = 'youtube' API_VERSION = 'v3' class YoutubeLiveStreamingApiClient(): def __init__(self, client_secrets_file, scopes): self.__client = self.get_authenticated_service( client_secrets_file, scopes) # OAuth 2.0 による認証を通して、API クライアントを取得する def get_authenticated_service(self, client_secrets_file, scopes): creds = None # The file token.pickle stores the user's access and refresh tokens, and is # created automatically when the authorization flow completes for the first # time. if os.path.exists('token.pickle'): with open('token.pickle', 'rb') as token: creds = pickle.load(token) # If there are no (valid) credentials available, let the user log in. if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( client_secrets_file, scopes) creds = flow.run_local_server(port=0) # Save the credentials for the next run with open('token.pickle', 'wb') as token: pickle.dump(creds, token) return build(API_SERVICE_NAME, API_VERSION, credentials=creds) # ライブ ID を基にチャット ID を取得する(ライブ ID 自体は配信中の URL から抜き出す) def get_live_chat_id(self, live_id): live_broadcasts = self.__client.liveBroadcasts().list( part='snippet', id=live_id, fields='items(snippet(liveChatId))').execute() return live_broadcasts['items'][0]['snippet']['liveChatId'] # チャット ID を基にコメントの一覧を取得する def get_live_chat_messages(self, live_chat_id, next_page_token=None): live_chat_messages = self.__client.liveChatMessages().list( liveChatId=live_chat_id, part='snippet', maxResults=200, pageToken=next_page_token, fields='nextPageToken,items(snippet(displayMessage))' ).execute() # ここで返す next_page_token を使用して、このメソッドを再度呼び出すことで、以降のコメントを取得できる return { 'next_page_token': live_chat_messages['nextPageToken'], 'messages': [i['snippet']['displayMessage'] for i in live_chat_messages['items']] }
Raspberry Pi にコメントを読ませる
上記のモジュールを使って、定期的にチャットのコメントを取得します。また、取得したチャットのコメントを Amazon Polly に渡して、MP3 ファイルに変換した後、再生してあげます。
#!/usr/bin/env python3 import boto3 import pygame.mixer import urllib.parse from contextlib import closing from time import sleep from youtube_live_streaming_api_client import YoutubeLiveStreamingApiClient YOUTUBE_CLIENT_SECRETS_FILE = 'client_secrets.json' YOUTUBE_CLIENT_SCOPES = [ 'https://www.googleapis.com/auth/youtube.readonly'] DEST_AUDIO_FILE = 'message.mp3' WAIT_SEC = 10 polly = boto3.client('polly') pygame.mixer.init() # Amazon Polly でテキストを MP3 ファイルに変換する def convert_to_voice(text, path): result = polly.synthesize_speech(Text=text, OutputFormat='mp3', VoiceId='Joanna', Engine='neural') with closing(result['AudioStream']) as stream: with open(path, 'wb') as file: file.write(stream.read()) # 指定した音声ファイルを再生する def play_sound(path): pygame.mixer.music.load(path) pygame.mixer.music.play(1) while pygame.mixer.music.get_busy(): sleep(0.1) def main(): youtube = YoutubeLiveStreamingApiClient( YOUTUBE_CLIENT_SECRETS_FILE, YOUTUBE_CLIENT_SCOPES) # 配信中の URL を入力して、ライブ ID を抜き出す url = input('YouTube Live URL: ') live_id = urllib.parse.urlparse(url).path[1:] live_chat_id = youtube.get_live_chat_id(live_id) next_page_token = None try: while True: # 10秒おきにチャットのコメントを取得する print('get live chat messages ...') live_chat_messages = youtube.get_live_chat_messages( live_chat_id, next_page_token) for message in live_chat_messages['messages']: print(message) # テキストを MP3 に変換した後、再生する convert_to_voice(message, DEST_AUDIO_FILE) play_sound(DEST_AUDIO_FILE) sleep(1) next_page_token = live_chat_messages['next_page_token'] sleep(WAIT_SEC) except KeyboardInterrupt: pass if __name__ == '__main__': main()
プログラムの実行
プログラムを実行すると、ブラウザが起動して、Google アカウントを選択する画面が表示されます(初回のみ)。
$ python3 main.py Please visit this URL to authorize this application: https://accounts.google.com/hogehoge...
下記記事の「プログラムを実行する」を参考にして、Google アカウントへのアクセスを許可してください。
Google アカウントへのアクセスを許可すると、配信中の URL を入力するように求められます。
$ python3 main.py Please visit this URL to authorize this application: https://accounts.google.com/hogehoge... YouTube Live URL:
配信画面下部にあるシェアボタンをクリックして、
動画リンクをコピーしてください。
コピーした URL を貼り付けると、チャットのコメントを取得して、Raspberry Pi が読み上げてくれます。
$ python3 main.py Please visit this URL to authorize this application: https://accounts.google.com/hogehoge... YouTube Live URL: https://youtu.be/xxxxxxxxxxx get live chat messages ... hello good morning ...
おまけ - SSH して実行したい場合
Raspberry Pi に繋げるディスプレイやらが無い場合、少し工夫する必要があります。まず、1つは VNC 等を使って Raspberry Pi にリモート接続する方法です。ひとまずはこれで解決するのかなと思います。
が、VNC 等を使わず SSH して実行したい場合、Google アカウントを選択するために開くブラウザが表示できず、先の処理に進めなくなります。
解決策の手順としては下記になります。
- Mac 等の環境で、
client_secrets.json
を取得する - その環境で認証の処理だけ走らせる → generate_token.py
client_secrets.json
と↑の処理で生成されたtoken.pickle
を scp で Raspberry Pi にコピーする
これで、ブラウザが開くところのフローはスキップできるので実行可能になります。
おわりに
今回は英語のみの対応としていますが、他の言語にも対応できるように改良していきたいです。
また、今回作ったモノの対になる機能として、自分が喋った言葉を Raspberry Pi が録音 → テキスト化 → チャットにコメントできるようになると、文字ベースのやりとりが実際の会話っぽくなって面白いな〜と思っています。その内、実現できたらいいなあ。
今回は以上になります。最後まで読んで頂きありがとうございました!