この記事は、ケーシーエスキャロット Advent Calendar 2018 の16日目の記事です。
音楽を聴きながら仕事をしていると、ふと気になる曲が流れることがありますよね。
私はSpotifyを使用しているのですが、曲名を確認する程度ならSpotMenuが便利ですが、
プレイリストに登録する作業は、毎回Spotifyアプリを開いてポチポチしなければなりません。
仕事中には結構な手間ですし、集中が途切れる要因になります。
概要
そこで、AWS Lambdaを使って現在再生中の曲をプレイリストに追加するコードを書き、
IoTボタンと組み合わせて、1プッシュで実行出来るようにしたいと思います。
Spotifyのアカウントは取得済みを前提とします。
また、ローカルの開発端末はmacを想定としていますので、それ以外の方は適宜読み替えてください。
SpotifyのAPIについて
SpotifyのAPIは、検索やトラック情報取得といったグローバルなAPIは
Client IDとClient Secretを使ってトークンを取得するだけで使用出来るのですが、
自分が今再生している曲を取得するようなプライベートな情報を扱うAPIは、OAuth認証が必要です。
つまり、インタラクティブな処理が必要ということになります。
どう実現するか
SpotifyのAuthorization Code Flowを見ると、
GET https://accounts.spotify.com/authorize
で返却されたログイン画面に、
ユーザー名とパスワードでログインし、リダイレクト先URLのパラメータの値を取得する必要があります。
ここの部分をLambdaで実現するために、Lambda上でseleniumを使い、headless chromeを動かすことにします。
手順
- Client IDとClient Secretの取得
- Redirect URIsの設定
- 登録先プレイリストの作成とIDの取得
- Lambdaの設定
- serverless-chromiumのインストール
- chromedriverのインストール
- Redirect URIsの設定
- 登録先プレイリストの作成とIDの取得
- lambda_function.pyの作成
Client IDとClient Secretの取得
Spotify for Developersからログインし、DashboardページにあるCREATE A CLIENT ID
から、必要な情報を入力して作成します。
-
App or Hardware Name
- 適当で大丈夫です
-
App or Hardware Description
- 適当で大丈夫です
-
What are you building?
- 適当で大丈夫です、
I don't know
にでもチェック付けておきましょう
- 適当で大丈夫です、
次ページで3つのチェックボックスにチェックをつけ、SUBMITをクリックすると、
作成したアプリケーションのページに遷移します。
SHOW CLIENT SECRET
をクリックし、Client IDとClient Secretを控えておきましょう。
Redirect URIsの設定
先ほどのアプリケーション画面にあるEDIT SETTINGS
を開き、
Redirect URIs
にhttp://localhost
を記載し、ADDしてからSAVEしましょう。
ここに登録されたuriのみが、後ほど出てくる認証時のリダイレクト先のパラメータで指定出来ます。
登録先プレイリストの作成とIDの取得
Lambda実行時に登録するプレイリストをアプリなどから作成します。
IDの取得方法については、以下の通りです。
途中ログイン画面が入った場合は適宜ログインしてください。
- Spotify for Developersのコンソールからプレイリスト取得APIを開く
- OAuth Tokenの
GET TOKEN
をクリック -
Required scopes for this endpoint:
に記載されているplaylist-read-private
にチェック -
REQUEST TOKEN
をクリック -
TRY IT
をクリック - 出力された結果から、対象プレイリストの"id"の値を取得
Lambdaの設定
今回は最終的にデプロイパッケージのサイズが50MB程になるため、コンソールからはソースが編集出来ません。
なので、ローカルでソースを編集し、zipファイルにしてS3にアップロードしたファイルを使用します。
Lambdaでheadless chromeを使用する方法については、AWS Lambda上のheadless chromeをPythonで動かすを参考にさせて頂きましたので、そちらをご参照ください。
こちらでは引用して記載します。
serverless-chromiumのインストール
mkdir -p bin/
curl -SL https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-37/stable-headless-chromium-amazonlinux-2017-03.zip > headless-chromium.zip
unzip headless-chromium.zip -d bin/
rm headless-chromium.zip
chromedriverのインストール
curl -SL https://chromedriver.storage.googleapis.com/2.37/chromedriver_linux64.zip > chromedriver.zip
unzip chromedriver.zip -d bin/
rm chromedriver.zip
必要なパッケージのインストール
現状、pip install spotipy -t ./
でインストールすると、
current_user_playing_track()がないため以下のようにインストールします。
pip install --upgrade git+https://github.com/plamere/spotipy.git -t ./
pip install selenium -t ./
lambda_function.pyの作成
いよいよ本題です。
seleniumを使ってログイン処理をし、authorization_code
を取得した後、
それを使ってAPIからアクセストークンを取得し、現在再生中の曲をプレイリストに入れる処理になります。
from spotipy import Spotify
from spotipy.oauth2 import SpotifyOAuth
from urllib.parse import urlencode, urlparse, parse_qs, quote
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
AUTHORIZE_URL = 'https://accounts.spotify.com/authorize'
CLIENT_ID = 'xxxxxxxxxxxxxxxxxxxxxxx'
CLIENT_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxx'
LOGIN_USERNAME = 'xxxxxxxxxxxxxxxxxxxxxxx'
LOGIN_PASSWORD = 'xxxxxxxxxxxxxxxxxxxxxxx'
PLAYLIST_ID = 'xxxxxxxxxxxxxxxxxxxxxxx'
SCOPE = 'user-read-currently-playing playlist-modify-public playlist-modify-private'
LOCALHOST = 'http://localhost'
def lambda_handler(event, context):
options = webdriver.ChromeOptions()
options.binary_location = './bin/headless-chromium'
# headlessで動かすために必要なオプション
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('--window-size=1280x1696')
options.add_argument('--disable-application-cache')
options.add_argument('--disable-infobars')
options.add_argument('--no-sandbox')
options.add_argument('--hide-scrollbars')
options.add_argument('--enable-logging')
options.add_argument('--log-level=0')
options.add_argument('--single-process')
options.add_argument('--ignore-certificate-errors')
options.add_argument('--homedir=/tmp')
driver = webdriver.Chrome('./bin/chromedriver', chrome_options=options)
params = {
'client_id': CLIENT_ID,
'response_type': 'code',
'redirect_uri': LOCALHOST,
'scope': SCOPE
}
# ヘッドレスブラウザでログイン処理をし、アクセストークン生成に必要なcodeの値を取得する
driver.get('{}?{}'.format(AUTHORIZE_URL, urlencode(params, quote_via=quote)))
WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.ID, 'login-username')))
driver.find_element_by_id('login-username').send_keys(LOGIN_USERNAME)
driver.find_element_by_id('login-password').send_keys(LOGIN_PASSWORD)
driver.find_element_by_id('login-button').click()
code = parse_qs(urlparse(driver.current_url).query).get('code')[0]
# アクセストークンの取得
so = SpotifyOAuth(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, redirect_uri=LOCALHOST, scope=SCOPE)
res_token = so.get_access_token(code=code)
access_token = res_token['access_token']
# 現在再生中の曲を取得してプレイリストに登録する
sp = Spotify(auth=access_token)
track_info = sp.current_user_playing_track()
if track_info is None:
return 'No music is being played.'
response = sp.user_playlist_add_tracks(LOGIN_USERNAME, PLAYLIST_ID, [track_info['item']['uri']])
return response
あとは作成したリソースをzipファイルにし、S3に配置したURLをLambdaで指定して実行するだけです。
なお、AWS IoTボタンとの連携はこちらを参考に実施しました。
実行してみる
処理時間を計ってみると、スムーズに終わって約20秒弱かかったので、Lambdaのタイムアウト値を1分程度に設定しておくのが良さそうです。
また、メモリも128MBだと少々心許ないので192MBに設定しておくことをオススメします。
最後に
曲のラストで「この曲いい!」と思ってポチッと実行しても、登録が間に合わない可能性があるのがネックですが、それは今後の課題として改善出来たらなと思っています。
少しざっくりとした説明になってしまったところもあるので、質問等があればコメントお願いします。
それでは皆さま職場での良き音楽ライフをお過ごしください!