YouTube Data APIを使ってみる

ぽおおお、っと奇声を上げながらラズパイを触っています。

from apiclient.discovery import build
YOUTUBE_API_KEY = 'XXXXXXX'
youtube = build('youtube', 'v3', developerKey=YOUTUBE_API_KEY)
search_response = youtube.playlists().list( part='snippet',
mine='true',
).execute()
print(search_response['items'][0])


(env) pi@raspberrypi:~/env/grpc $ python3 youtube-sample.py 
Traceback (most recent call last):
 File "youtube-sample.py", line 16, in <module>
   mine='true',
 File "/home/pi/env/lib/python3.7/site-packages/googleapiclient/_helpers.py", line 134, in positional_wrapper
   return wrapped(*args, **kwargs)
 File "/home/pi/env/lib/python3.7/site-packages/googleapiclient/http.py", line 915, in execute
   raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 401 when requesting https://youtube.googleapis.com/youtube/v3/channels?part=snippet&mine=true&key=XXXXXXXXXX=json returned "The request uses the <code>mine</code> parameter but is not properly authorized.". Details: "The request uses the <code>mine</code> parameter but is not properly authorized.">

しかし、mine=trueを、id(プレイリストを表示したときの、URLを持ってきます。https://www.youtube.com/playlist?list=XXX のXXXです。)に変えて実行すると、

(env) pi@raspberrypi:~/env/grpc $ python3 youtube-sample.py 
{'kind': 'youtube#playlist', 'etag': 'XXXX', 'id': 'XXX', 'snippet': {'publishedAt': '2016-11-30T01:25:09Z', 'channelId': 'XXXX’, 'title': 'お気に入り', 'description': '', 'thumbnails': {'default': {'url': 'https://i.ytimg.com/vi/X/default.jpg', 'width': 120, 'height': 90}, 'medium': {'url': 'https://i.ytimg.com/vi/X/mqdefault.jpg', 'width': 320, 'height': 180}, 'high': {'url': 'https://i.ytimg.com/vi/X/hqdefault.jpg', 'width': 480, 'height': 360}, 'standard': {'url': 'https://i.ytimg.com/vi/X/sddefault.jpg', 'width': 640, 'height': 480}, 'maxres': {'url': 'https://i.ytimg.com/vi/X/maxresdefault.jpg', 'width': 1280, 'height': 720}}, 'channelTitle': 'ゆうた', 'localized': {'title': 'お気に入り', 'description': ''}}}

ちゃんと答えが返ってきました。ちなみに、このプレイリストは公開設定にしてあります。

これの原因は、高く評価した動画は2019年12月に一律非公開になり、その情報を手にするには、OAuth同意が必要になります。そして、今実装しているPythonではOAuth同意のプロセスを踏んでいないため、エラーが出てきたようです。

ということで、OAuth同意のためにいくつか手順を踏んでいきます。

画像1



画像2


画像3

ここからjsonファイルをダウンロードし、scpなどでRaspberry pi に送ります。YouTube API: 再生時間が条件に合う動画を選んで再生リストを作成(2/2) で公開されているコードをいただきます。

(env) pi@raspberrypi:~/env/grpc $ cat oauth.py 
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
def get_credentials(client_secret_file, scopes,
                   token_storage_pkl='token.pickle'):
   '''google_auth_oauthlibを利用してOAuth2認証
       下記URLのコードをほぼそのまま利用。Apache 2.0
       https://developers.google.com/drive/api/v3/quickstart/python#step_1_turn_on_the_api_name
   '''
   creds = None
   # token.pickleファイルにユーザのアクセス情報とトークンが保存される
   # ファイルは初回の認証フローで自動的に作成される
   if os.path.exists(token_storage_pkl):
       with open(token_storage_pkl, 'rb') as token:
           creds = pickle.load(token)
           
   # 有効なクレデンシャルがなければ、ユーザーにログインしてもらう
   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_secret_file, scopes=scopes)
           creds = flow.run_local_server(port=0)
           
       # クレデンシャルを保存(次回以降の認証のため)
       with open(token_storage_pkl, 'wb') as token:
           pickle.dump(creds, token)
   return creds
'''OAuth認証とAPIのビルド実行'''
# 利用するAPIサービス
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'
# OAuthのスコープとクレデンシャルファイル
YOUTUBE_READ_WRITE_SCOPE = 'https://www.googleapis.com/auth/youtube'
CLIENT_SECRET_FILE = 'client_secret_XXXXibk.apps.googleusercontent.com.json'
#CLIENT_SECRET_FILE = 'config/client_secret.json'
# OAuth認証:クレデンシャルを作成
creds = get_credentials(
                   client_secret_file=CLIENT_SECRET_FILE,
                   scopes=YOUTUBE_READ_WRITE_SCOPE
                   )
# API のビルドと初期化
youtube_auth = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
                   credentials=creds)

これを実行します。


画像4


画像5


画像6


画像7

ええええええ。。。。

なぜかうまくいかないので、OK google !で使っていたコマンドでの認証を行います。

env) pi@raspberrypi:~/env/grpc $ google-oauthlib-tool --scope https://www.googleapis.com/auth/youtube --save --headless --client-secrets ./client_secret_XXXXjgheibk.apps.googleusercontent.com.json

このやりかただと、なぜかうまくいきました。(いただいたコードの何が悪かったのか、謎です)

が、嫌な予感がしたので、OK google !をここで実行すると…

(env) pi@raspberrypi:~/env/grpc $ ./okgoogle.sh 
INFO:root:Connecting to embeddedassistant.googleapis.com
INFO:root:Using device model XX and device id X
Press Enter to send a new request...
INFO:root:Recording audio request.
Traceback (most recent call last):
 File "pushtotalk.py", line 509, in <module>
   main()
 File "/home/pi/env/lib/python3.7/site-packages/click/core.py", line 722, in __call__
   return self.main(*args, **kwargs)
 File "/home/pi/env/lib/python3.7/site-packages/click/core.py", line 697, in main
   rv = self.invoke(ctx)
 File "/home/pi/env/lib/python3.7/site-packages/click/core.py", line 895, in invoke
   return ctx.invoke(self.callback, **ctx.params)
 File "/home/pi/env/lib/python3.7/site-packages/click/core.py", line 535, in invoke
   return callback(*args, **kwargs)
 File "pushtotalk.py", line 483, in main
   continue_conversation = assistant.assist()
 File "pushtotalk.py", line 149, in assist
   self.deadline):
 File "/home/pi/env/lib/python3.7/site-packages/grpc/_channel.py", line 416, in __next__
   return self._next()
 File "/home/pi/env/lib/python3.7/site-packages/grpc/_channel.py", line 803, in _next
   raise self
grpc._channel._MultiThreadedRendezvous: <_MultiThreadedRendezvous of RPC that terminated with:
	status = StatusCode.PERMISSION_DENIED
	details = "Request had insufficient authentication scopes."
	debug_error_string = "{"created":"@1608722632.023587446","description":"Error received from peer ipv4:172.217.174.106:443","file":"src/core/lib/surface/call.cc","file_line":1063,"grpc_message":"Request had insufficient authentication scopes.","grpc_status":7}"
>
Assertion 'pa_atomic_load(&(c)->_ref) >= 1' failed at pulse/context.c:1063, function pa_context_get_state(). Aborting.
./okgoogle.sh: 1 行: 10118 中止                  python3 pushtotalk.py --device-model-id x

はい。Youtube だけのスコープで認証をし直したので、OK google !が死にました。

google-oauthlib-tool --scope https://www.googleapis.com/auth/assistant-sdk-prototype --scope https://www.googleapis.com/auth/gcm --scope https://www.googleapis.com/auth/youtube --save --headless --client-secrets client_secret_xxxx.apps.googleusercontent.com.json

これを解決するには、↑のようにスコープを全部含めて承認してもらうしかありません。



この記事が気に入ったらサポートをしてみませんか?