【2019年12月版】ラズパイ4をNode-RED+SpotifyでDJ-Botにしてみよう!


株式会社ピーアールオー(あったらいいな!を作ります) Advent Calendar 2019 の24日目メリークリスマスイブ〜!の夜にお届けする、筆者がクリスマスパーティーに添えたい最高にあったらいいな!の記事です。

パーティーを最高に盛り上げるBGMを pi4 からお届け!

今夜は🎄クリスマス🎄ということで、手元のラズパイをDJ-Bot🔊に変身させてみたいと思います!
当然、ただ単にSpotify Connectで繋ぐだけじゃ面白くないので、Node-RED から WebAPI 経由でのSpotify再生にチャレンジいたします!!

用意するもの

  • Spotify プレミアム (3ヶ月無料のうちにアカウントゲット!!)
  • Raspberry Pi 4
  • Raspbian Lite
  • SSH 接続

リスト再生、停止などのPlayerを直接、操作するAPIはプレミアムアカウントでないとできない?模様です。

今回も引き続きRaspbian Liteでデスクトップなしで、SSHからの操作だけで行ってみます。

1. Spotify クライアントをインストール

まずは、Spotify から 普通に音楽を再生できる 環境を整えます。
ラズパイ用 Spotify Connectクライアントは "Raspotify" というものがありますので、これをインストールします。

上記のサイトを参考に、以下のコマンドでいけました。

$ curl -sSL https://dtcooper.github.io/raspotify/key.asc | sudo apt-key add -v -
$ echo 'deb https://dtcooper.github.io/raspotify raspotify main' | sudo tee /etc/apt/sources.list.d/raspotify.list
$ sudo apt update
$ sudo apt install raspotify

apt にキーとリスト追加して、インストール、のいつもの流れでOKです。

今回は設定をいじって、

/etc/default/raspotify
...
DEVICE_NAME="raspotify-1"
...

機器の名前を raspotify-1に変えてみました。

設定ファイルを保存して、respotifyのサービスを再起動します。

$ sudo systemctl restart raspotify

スピーカーの設定はまた別の記事で取り上げますが、なんとか音が出るように調整してください。(これも結構、大変だったりする。)

これでSpotifyのプレイヤーにはデバイスとして以下のようにrespotify-1 が表示されてるようになると思います。

これでまず、Spotifyからの配信がラズパイで受け取れるようになりました。

2. Spotify Developer ログイン

続いて、Developerサイトにてログインしてアクセスキーなどを取得します。
手順は以下のサイトにて詳しく載っています。

この記事の内容で、ほぼ問題なかったので、ここでは詳細な説明を割愛させていただきます。

以下のSpotify開発者のサイトでログインして…

ダッシュボードにクライアントを登録して、クライアントIDとクライアントキーを取得してください。

この画面の Client IDClient Secretをコピペしておいてください。

3. Spotify の APIを叩いてみる

アクセスキーが入手できたので、Node-REDでの実装に移りたいところですが、実際にAPI呼び出す際には然るべき"scope"が付与されたOAuth2.0のトークンが必要になります。
この"scope"がAPIごとに異なるので、事前に使いたい機能(API)をチェックするのは必須です。
また、Node-REDで使用するのAPIの"ラッパー"になりますので実際のAPIの使い方とラッパーの実装、両方知っておく必要もあります。

そんなわけで、例えば肝心の 再生する 箇所のAPIを見てみましょう。

"Authorization"の欄に必要な"Scope"が明記してあります。このAPIはuser-modify-playback-stateというスコープが必要です。

APIの定義と引数のリストを眺めていると…?

"TRY IT" のボタンを発見いたしました。クリックしてみると…

実際に、APIを叩いて試せるフォームが用意されています。また、URLも見れますね。
ついでに、このフォームからOAuth2トークンも取得できるので、試してみます。

ポップアップダイアログが表示されて…

Scopeの選択ダイアログが表示されますが、ここでも必要なスコープを教えてくれます。
ここで取得したトークンで、APIが実際に叩けます。

... Spotify、すごい。

続いてデバイスの一覧も確認しておきます。
以下のページがデバイスの一覧が確認できるAPIです。

ここでも "TRY IT"をクリックしてAPIを叩いてみましょう。

raspotify-1が認識されていますね!このidは後ほど使用しますのでコピペしておきましょう。
というか・・・どうなってんの??

4. Node-RED のインストール

SpotifyのAPIについてだいたいわかりましたので、Node-REDからこれを叩いてみたいと思います。
まず、Node-REDのインストールです。
以下のサイトの手順を参考にしたいと思います。

$ bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
...

だらだら〜っとインストールプロセスが進行していきまして…

All done.
  You can now start Node-RED with the command  node-red-start
  or using the icon under   Menu / Programming / Node-RED
  Then point your browser to localhost:1880 or http://{your_pi_ip-address}:1880

Started  Fri 20 Dec 09:48:18 GMT 2019  -  Finished  Fri 20 Dec 09:50:42 GMT 2019

はい、インストール完了いたしました。Node-REDは1880ポートなんですね〜。

ついでにチュートリアルに従って簡単なフローを作成してみました。

inejctノードでタイムスタンプを送信してdebugノードで中身を表示してみるってだけですが…デプロイして"inject"ノードクリックするとタイムスタンプがデバッグ窓に表示されました。
なんとなく雰囲気は掴めましたね!

と言うか・・・ラズパイの処理の機敏さにびっくりする。。。

5. Spotify ノードをインストール

続いて、Node-REDでSpotify APIを叩けるノード、"node-red-contrib-spotify" がありますのでそれをインストールいたします。
公式のヘルプは以下です。

まずパレットの設定画面を表示します。

"ノード追加"タブをクリックし、ここから Spotify を検索します。

node-red-contrib-spotify、ありました。
あとは ノードを追加をクリックするだけでインストール完了です。

6. OAuth トークンを取得

上記の "node-red-contrib-spotify" 公式ヘルプにはノードが"2つある"とありますが、Spotify Authのノードは見当たらず・・・

Spotifyノードを配置してみるとエラー?警告?のマークがついているのでダブルクリックします。

プロパティが開かれて…

ここからOAuthのトークンが取得できるようですね!編集のペンアイコンをクリックすると…クライアントIDとキー、スコープを指定するダイアログが表示されます。

先ほどのSpotify for Developersサイトで取得したIDとキーを入力し、"scope" には user-modify-playback-stateを指定します。

わかりにくいですが Start Authentication がボタン?リンク?担っておりますのでクリックすると認証画面が表示されます。

Spotifyの認証画面が表示されてトークンが取得できました。
これで準備完了です!

7. フローを作成

それでは、作りに入っていきましょう。個別に手順を解説すると大変なボリュームなので、まずは全体のフローを貼り付けてしまいます。

左上のinejctノードから例えば、JR スキースキーという文字列を msg.payloadに入れて出力します。(例えば、ですよ!!)
続いて "function"ノードのbuild queryで検索API用の引数を作成いたします。

var query = msg.payload

msg.params = [
    query , { limit : 1, offset : 0 }
]

return msg;

payloadから値を持ってきて、paramsを作るのですが… ここで重要なのは node-red-contrib-spotify は Spotify APIのラッパーである、ということです。つまり、引数の作り方はSpotify APIじゃなくて、node-red-contrib-spotifyの実装をチェックする必要がある、ということです。
で、なにを見ればいいの?ということですが、公式のヘルプは…

Check out the API description for more information about available functions.

と、ご親切にもリンクが張ってあります。ですが、リンク先は…

・・・モロ実装やないかい!

はい、というわけで検索APIを叩くには、

spotify-web-api.js
...
  /**
   * Search for playlists.
   * @param {string} query The search query.
   * @param {Object} options The possible options.
   * @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
   * @example searchPlaylists('workout', { limit : 1, offset : 0 }).then(...)
   * @returns {Promise|undefined} A promise that if successful, returns an object containing the
   *          search results. The result is paginated. If the promise is rejected,
   *          it contains an error object. Not returned if a callback is given.
   */
  searchPlaylists: function(query, options, callback) {
    return this.search(query, ['playlist'], options, callback);
  },
...

を参照してね、ということですか。。。こっ、こいつ。。。

この node-red-contrib-spotify のほんのり勝手が悪い点ですが、msg.params で渡すのは常に配列で、上記の callback 以外の引数 を渡す、という点が注意です。
これ第1引数がobjectで2番目がcallbackでも msg.params = [{ option: some }]みたいな渡し方が必要なのです。
ちょっと戸惑いましたね・・・

はい、それでは引き続いて次の"Spotify" ノードではAPIに"searchPlayList" を指定します。これで指定したキーワードで検索に引っかかったプレイリストが1件取得できます。

このAPIのレスポンスはデバッグとして出力させるものと次の再生APIに渡す用の二手に分かれます。
デバッグにはレスポンスのJSON出てますが、ちょっと長いので割愛します。。。

再生用の引数を作る"function"ノードsetPlayListではプレイリストの先頭の曲を流すようにいたします。
ここで先ほど取得したデバイス"raspotofy-1"のidを使用して、このラズパイで再生されるように指定します。

msg.params = [
    {
        device_id: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        context_uri: msg.payload.playlists.items[0].uri,
        offset: {"position":0}
    }]

return msg

payload.playlists.items[0].uriがプレイリストのURIとなります。
そしてこれを Spotify ノードの play APIに流せば再生開始です。

このままでは常に最初の曲しか流れないので、シャッフル再生させてみましょう、というのが次のspotifyノードです。
Spotifyはプレイリスト切り替えるとシャッフルがオフになって先頭から再生されるようなので、再生後に毎度、切り替える必要があるようです。(まぁ、そんなもんでしょう。局の順番は重要ですし。。)

"function"ノードsetShuffle:trueでシャッフルを有効にする引数を作ります。

msg.params = [
    {
        state: 'true'
    }
    ]
return msg;

この引数も spotify-web-api.js をよく見ないとなかなかアレですが…
で、これを"Spotify"ノードsetShuffleに投げます。

最後に"次の曲"を流す"Spotify"ノードnextを呼び出してシャッフル再生、完了です。

最後に右上の"デプロイ"をクリックして完了です。

実装(?)は以上です!

8. 実行!

まずは、Spotifyのアプリなどで他のプレイリストを再生しておきましょう。今回は Mac版のSpotifyアプリを使用しています。
ノリノリの EDM が流れているけど~

Node-RED で injectノードをクリック!

はい、Zoo キタ――(゚∀゚)――!!

Node-RED上でキーワードを"ドリカム"に修正して"デプロイ"→injectノードクリックしてみましょう。

はい!ドリカムに切り替わりました!
Node-RED上でキーワードを再度、"JR スキースキー"に戻して"デプロイ"→injectノードクリックしてみましょう。

プレイリストが戻っても別の曲が流れます。ランダム再生も効いている模様です!

これで今夜のクリスマスパーティーの🔊BGM🔊はバッチリですね〜!!
明日も、2回も続けてウザめにお送りいたします!!