せかいの世界(備忘録)

音楽・ITに関する色々なものを触ってみたり、競プロの記録を残したりします。

帰宅するとSpotifyのプレイリストをランダムで自動再生するシステムを構築してみる

Pythonを用いてSpotifyのプレイリストを作成&再生してみるの続き。

前回は、Spotifyのプレイリストを自動で作成&再生するプログラムを作りました。 しかしながら、プレイリスト再生の度にPCを起動して、そこからプログラムを実行するのは面倒です。 そこで今回は

これらの2つのサービスを利用して、帰宅すると自動的に音楽を再生システム を実現します。

事前準備

先に、以下の2つのサービスのアカウント作成を済ませておきましょう。

  • AWSアカウントの作成
  • IFTTTアカウントの作成

AWSの利用にはクレジットカード情報の登録が必要です。 登録後12ヶ月間は無料であれこれ使うことができますが、期間を過ぎるとサービス利用料が発生します。 料金の仕組みは色々あって自分も混乱していますが、

  • 通信量
  • 関数の呼び出し回数

などによって決まるみたいです。 今回の場合は、通信量も呼び出し回数も非常に少ないため、多くても数十円/月ぐらいだと思います。 心配な方はAWSの料金について調べておきましょう。

AWSの設定

AWSでは、LambdaとAPIGatewayの2種類のサービスを用います。各サービスは、ざっくり言うと以下の役割を持ちます。

  • Lambda
    自作したプログラムをLambda関数としてアップロードして置く場所
  • APIGateway
    Lambda関数をAWS以外のWebサービスからもアクセスできるようにするためのもの

APIGatewayの設定

まずAWSのコンソールにアクセスし、検索ボックスからLambdaを検索します。

f:id:tori114:20181020233941p:plain

画面から、「新しい関数を作成」ボタンを押します。

f:id:tori114:20181020234127p:plain

関数名、ロールを設定すると次の画面へ遷移します。

f:id:tori114:20181021153707p:plain

左のタブから、APIGateWayを選択し、Lambda関数のトリガーにセットします。 セットが完了すると、Lambda関数へのエンドポイントが作成されます。 このURLを外部サービスから叩くことによって、関数が実行するような仕組みです。 f:id:tori114:20181104201338p:plain

このエンドポイントは後に使用するので、どこかに控えておきましょう。

Lambdaの設定

次に、Lambda関数の中身をアップロードしていきます。 今回は、自分がSpotifyに登録しているプレイリストの中から、ランダムに1つ選んで再生するプログラムです。大まかな内容は前回と同じですが、ソースコードが見辛かったのと、Lambda用に改変する部分があったので修正しています。

まず、作業ディレクトリを作成します。

mkdir workspace
cd workspace

次に、以下のプログラムをlambda_function.pyという名前で保存します。

import spotipy
import spotipy.util as util
import random
import subprocess
import requests
import json
import os
from datetime import datetime

class PlaySpofity:

    def __init__(self, username, scope):


        self.username = username
        self.scope = scope

        self.token = util.prompt_for_user_token(self.username, self.scope)
        self.spotipy = spotipy.Spotify(auth=self.token)
        self.header = {'Authorization': 'Bearer {}'.format(self.token)}

        self.device_id = self.get_deviceid()
        self.playlist_id = None

    def get_deviceid(self):
        res = requests.get(
            "https://api.spotify.com/v1/me/player/devices",headers=self.header)
        devices = res.json()
        try:
            device_id = devices["devices"][0]['id']
        except IndexError:
            print("IndexError: Device did not found")
            exit()
        return device_id

    def generate_playlist(self, playlistname):
        playlist = self.spotipy.user_playlist_create(self.username, playlistname)
        playlist_id = playlist['id']
        return playlist_id

    def make_playlist(self, search_artist, playlistname="default"):
        self.make_playlist = self.generate_playlist(playlistname)
        artist_id_map = {}
        print (playlistname)
        if search_str == None:
            raise TypeError('Favorite artist is None')
        result = self.spotipy.search(q='artist:'+search_artist, limit=1)
        artist_id = result['tracks']['items'][0]['artists'][0]['id']
        print(result['tracks']['items'][0]['id'])
        artist_related_artists = self.spotipy.artist_related_artists(artist_id)
        track_ids = []
        for artist_list in artist_related_artists['artists']:
            result = self.spotipy.search(q='artist:'+artist_list['name'], limit=50)

            if len(result['tracks']['items']) > 1:
                track_ids.append(random.choice(
                    result['tracks']['items'])['id'])

        self.spotipy.user_playlist_add_tracks(username, self.playlist_id, track_ids)

    def random_select_playlist(self):
        current_playlists = self.spotipy.current_user_playlists(limit=50)
        playlist = random.choice(current_playlists['items'])
        return playlist

    def play_playlist(self, playlist_id):
        param = {'device_id': self.device_id,
             'context_uri': 'spotify:playlist:%s' % playlist_id}
        res = requests.put("https://api.spotify.com/v1/me/player/play",
                           data=json.dumps(param), headers=self.header)

def logging(errorLv, lambdaName, errorMsg):
    loggingDateStr=(datetime.now()).strftime('%Y/%m/%d %H:%M:%S')
    print(loggingDateStr + " " + lambdaName + " [" + errorLv + "] " + errorMsg)
    return


def lambda_handler(event, context):

    username = os.getenv('SPOTIFY_USERNAME')
    scope = os.getenv('SCOPE')

    sp = PlaySpofity(username, scope)
    playlist = sp.random_select_playlist()
    sp.play_playlist(playlist['id'])

    return {
            'isBase64Encoded': False,
            'statusCode': 200,
            'headers': {},
            'body': '{"message": "PlaySpofity was executed"}'
    }

if __name__ == '__main__':
    username = os.getenv('SPOTIFY_USERNAME')
    scope = os.getenv('SCOPE')
    sp = PlaySpofity(username, scope)
    playlist = sp.random_select_playlist()
    sp.play_playlist(playlist['id'])

次に、Lambdaの中でspotipyrequestsライブラリを使用するために、作業ディレクトリ内にライブラリを配置します。

pip install spotipy requests -t

次に、bashrcに以下の項目をセットしましょう

$ vi ~/.bash_profile
export SPOTIPY_CLIENT_ID='XXXX'
export SPOTIPY_CLIENT_SECRET='XXXX'
export SPOTIPY_REDIRECT_URI='http://example.com'
export SPOTIFY_USERNAME='XXXXXXXXX'
export SCOPE='user-read-playback-state,playlist-read-private,user-modify-playback-state,playlist-modify-public'

Lambdaにアップロードする前に、ローカルでプログラムを実行します。

python lambda_function.py

実行すると、http://example.comがブラウザで開かれます。このURLを丸ごとコピーして、 コンソールに貼り付けます。 この手順を踏むことで、Spotifyの認証に必要な情報(access_token等)が、

.cache-XXXXXX

としてlambda.pyと同じディレクトリに保存されます。 隠しファイルのままではlambdaにアップロード出来ないので、

mv ./.cache-XXXXXX cache-XXXXXX

とします。 また、 spottily/util.py内の50行目を

cache_path=".cache-"....
cache_path="cache-"

と変更します。

プログラムが無事実行されると、Spotifyのプレイリストが紐づいているデバイス上で再生されます。

ディレクトリ内をzipファイルにまとめます。

zip -r lambda.zip *

このzipファイルを、Lambdaにアップロードします。
コードエントリータイプから.zipファイルをアップロードを選択し、先ほどのLambda.zipをアップロードします。

f:id:tori114:20181021153433p:plain

最後に、Lambdaにもbash_profileと同じ環境変数をセットしましょう。

キー
SPOTIPY_CLIENT_ID 'XXXX'
SPOTIPY_CLIENT_SECRET 'XXXX'
SPOTIPY_REDIRECT_URI 'http://example.com'
SPOTIFY_USERNAME 'XXXX'
SCOPE 'user-read-playback-state,playlist-read-private,user-modify-playback-state,playlist-modify-public'

これでLambdaの設定は完了です。

IFTTTの設定

次に、作成したLambda関数のエンドポイントを叩くために、IFTTTと呼ばれるWebサービスを用います。 IFTTTは、「If This Then That」の頭文字を撮ったもので、系統の異なるWebサービスやデバイスを繋げて、システムを作ることができます。 今回は、

  • 入力:スマートフォンの位置情報
  • 出力:webhook(Lambda関数のエンドポイントを叩く)

となるようなシステムを作ります。

まず、IFTTTにアクセスします。

f:id:tori114:20181021154553p:plain

左上のMyAppletをクリックし、リンク先の右上にあるNewAppletをクリックします。 +thisを選択し、検索ボックスにlocationを打ち込み、選択します。

f:id:tori114:20181021153504p:plain

You enter an areaを選択すると、次のようなページに移動するので、ここで自宅の場所を選択します。

f:id:tori114:20181021153513p:plain

この選択した場所に入ることが、システムの入力となります。 選択後、下のCreate Triggerをクリック、次に+thenの設定へと進んでいきます。 検索ボックスから、Webhookを選択します。 Make a Web Requestを選択すると、次のページに移ります。

f:id:tori114:20181021153411p:plain

  • URL : APIGateway設定の際に控えたURL
  • Method : GET
  • Content type : 適当

設定の方はこれで完了です。あとはIFTTTのスマートフォンアプリをDLし、ログインをしておくことで実行可能になります。

Point

構築したシステムの動作を確かめる場合、スマートフォンを持って自宅付近をうろつき回るのは面倒です。 その場合、Locationの代わりにbutton widjetを選択することで、 スマートフォンのボタンウィジェットにIFTTTのボタンを作成することができます。

まとめ

今回は、帰宅するとSpotifyのプレイリストを自動再生するためのシステムを作りました。