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

[株式会社ピーアールオー（あったらいいな！を作ります） Advent Calendar 2019 の24日目](https://qiita.com/advent-calendar/2019/pro-japan1)、*メリークリスマスイブ〜！*の夜にお届けする、筆者がクリスマスパーティーに添えたい**最高にあったらいいな！**の記事です。

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

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

## 用意するもの

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

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

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

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

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

- [How to Setup Spotify Connect on the Raspberry Pi](https://pimylifeup.com/raspberry-pi-spotify/)

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

```shell
$ 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です。

今回は設定をいじって、

```conf:/etc/default/raspotify
...
DEVICE_NAME="raspotify-1"
...
```
機器の名前を `raspotify-1`に変えてみました。

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

```shell
$ sudo systemctl restart raspotify
```

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

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

![SpotifyPlayerデバイスリスト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/66cfb90e-1a5f-f89a-19e6-74dd18dfa967.png)

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

## 2. Spotify Developer ログイン

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

- [Spotify Web APIでデバイスから指定した音楽を再生してみる](https://dev.classmethod.jp/etc/play-music-via-spotify-web-api/)

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

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

- [Spotify for Developers](https://developer.spotify.com/)

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

![My Dashboard アプリ詳細.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/94dd7cc6-a64d-ce01-f2ae-59e4c8ecb532.png)

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

## 3. Spotify の APIを叩いてみる

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

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

https://developer.spotify.com/documentation/web-api/reference/player/start-a-users-playback/

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

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

![Start Resume a User s Playback  _Try_it  Spotify for Developers.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/9e7cb48a-17d0-506f-8fc4-d9c54d540d33.png)

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

![Start Resume a User s Playback   Spotify for Developers.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/4e6a6d05-ace6-c164-7739-779edaaa6a84.png)


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

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

![Start Resume a User s Playback  Scope Select Spotify for Developers.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/cb476a5f-f60a-9608-3b93-864c24fbbb96.png)

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

... **Spotify、すごい。**

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

- [Get a User's Available Devices](https://developer.spotify.com/console/get-users-available-devices/)

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

![Get a User s Available Devices   Spotify for Developers.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/4074b743-f6cb-0b0a-61d4-6542242bf0d8.png)

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

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

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

- https://nodered.jp/docs/getting-started/raspberrypi

```shell
$ 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`ポートなんですね〜。

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

https://nodered.jp/docs/tutorials/first-flow

![Node-REDで初めてのフロー実行.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/b8848ecd-015e-3e7e-0285-c892f6fd138f.png)

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

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

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

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

- [node-red-contrib-spotify](https://flows.nodered.org/node/node-red-contrib-spotify)

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

![Node-RED パレットの管理.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/14426b39-948e-1aa6-03f1-98a14957cecb.png)

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

![Node-RED Add_node.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/5864811e-3a4d-c43b-306c-35e218858eb6.png)

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

## 6. OAuth トークンを取得

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

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

![Node-RED _ spotify alert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/e9dc82a9-3c7e-92a8-cef4-656b5f7a7afa.png)

プロパティが開かれて…

![Node-RED Create_Token.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/c326a647-4b7d-21ba-2aa0-e914460b6c35.png)

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

![Node-RED ADD_Token.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/4db5e243-55c6-8acf-100e-5fc7b485f66c.png)

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

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

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

## 7. フローを作成

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

![Node-RED_全体.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/59f2b9be-261e-9f69-901d-3a765c64216e.png)

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

```js:
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.

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

- [spotify-web-api.js](https://github.com/thelinmichael/spotify-web-api-node/blob/master/src/spotify-web-api.js)

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

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

```js: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件取得できます。

![Node-RED spotify searchPlaylist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/da1743f1-bbeb-21d3-7982-88461e3cd714.png)


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

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

```js
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に流せば再生開始です。

![Node-RED Spotify Play.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/c76f1648-1374-2d6a-57b6-21deecc4cc3f.png)

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

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

```js
msg.params = [
    {
        state: 'true'
    }
    ]
return msg;
```
この引数も `spotify-web-api.js` をよく見ないとなかなかアレですが…
で、これを"Spotify"ノード`setShuffle`に投げます。

![Node-RED spotify setShuffle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/b3ac8074-f9b9-0742-6224-ee1bee3f0efa.png)

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

![Node-RED Spotify SkipToNext.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/a4aa975e-5c5f-c250-e2ae-9fed69bc7142.png)

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

実装(?)は以上です！

## 8. 実行！

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

<img width="915" alt="スクリーンショット 2019-12-24 1.09.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/76c02bf2-efb8-2b20-b4b2-bd5384759622.png">

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

![スクリーンショット 2019-12-24 1 10 21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/a858588f-8911-eb14-5067-ab21ff80508b.png)

はい、**Zoo ｷﾀ――(ﾟ∀ﾟ)――!!**

Node-RED上でキーワードを"ドリカム"に修正して"デプロイ"→`inject`ノードクリックしてみましょう。
![スクリーンショット ドリ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/b5d3ba63-8952-b598-8727-c914e6953622.png)


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

![スクリーンショットJR スキースキー2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/fd37ab4f-e79d-36a0-be3b-4218bf56b4bb.png)

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

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