はじめに
あなたの人生を変えたWebサービスを教えてください
と問われたらこの三つを答えます。
メルカリ
Twitter
そしてSpotifyです。
Spotifyとは
スウェーデン発のサブスクリプション型音楽ストリーミングサービスです。
有料会員数1億800万人、配信曲5000万曲以上、無料〜1480円のプランが用意されていています。
ランニングやジムなど運動中のBGMにしたり、近所の肉のハナマサ(髭男率高め)
でかかっていた印象的な曲を調べたり、レコメンドででた曲が良かったのでライブに足を運んだり、学生時代にCDで聴いていた曲を見つけて胸が熱くなったり、
私の音楽生活を一つ上のステージに連れて行ってくれた素敵なサービスです。
Spotify Connect
曲数やプランそれぞれのメリットはAppleMusicやLINE MUSICなど様々あるとおもいますがSpotifyで特徴的なのがSpotify Connectです。
これは例えばスマートフォンからPCなど他端末のSpotifyを操作することできます。
【Spotify Connectのイメージ】
【iPadからPCのSpotifyを操作する】
【iPad からPCのSpotifyを操作する】 pic.twitter.com/DjlRZOSDvD
— ウーリズム (@Uh_rhythm) July 29, 2020
デバイスをリモコン化するイメージだとお考えください。
obnizとは
obnizとは
マイコンボードの一種でJavaScriptで動かすことができます。
LEDライトやスピーカー、温度センサーといったパーツと組み合わせて簡単にIoTデバイスを
作ることができます。
構成図
obnizのスイッチングのイベントを受けてnode.jsからSpotifyのAPIを叩いてプレーヤーを操作をします。
Requestで曲の再生/停止、巻き戻し/早送り、Responseで曲の情報を取得してobnizの画面に
曲の情報を表示させます。
機能
まずこのリモコンでは曲の選択機能はないのであらかじめスマホやPCのプレーヤーで曲を選んでおいてください。
プレイリストを作成しておくのがいいですね。
再生/停止
【再生/停止】 pic.twitter.com/5L7b0F3chG
— ウーリズム (@Uh_rhythm) July 29, 2020
再開
【再開】 pic.twitter.com/VG8FjKhZEB
— ウーリズム (@Uh_rhythm) July 29, 2020
巻き戻し/早送り
【巻き戻し/早送り】 pic.twitter.com/BkiCyLYSo0
— ウーリズム (@Uh_rhythm) July 29, 2020
曲情報の切り替え
【曲情報の切り替え】 pic.twitter.com/aQRTSrkTop
— ウーリズム (@Uh_rhythm) July 29, 2020
今日も生きたね
【RGBライトでムーディーに】 pic.twitter.com/YnfI6jAXsO
— ウーリズム (@Uh_rhythm) July 29, 2020
楽観の深奥で燻る魔(ry
【表示文字数の課題】
— ウーリズム (@Uh_rhythm) July 29, 2020
楽観の深奥で燻る魔は、万人が宿す普遍的無意識の『罪』の残滓。 pic.twitter.com/9DOTzhoLY1
実装
https://gist.github.com/UhRhythm/5e208cce89f3e845ba942f5f081fc7f9
ものすごく単純です。
obnizのスイッチのイベントに合わせて各々のAPIを呼び出すだけです。
各動作に割り当てられたAPIは下記のようになっております。
obniz | 動作 | httpメソッド | エンドポイントURL |
---|---|---|---|
push | 再生 | PUT | https://api.spotify.com/v1/me/player/play |
push | 停止 | PUT | https://api.spotify.com/v1/me/player/pause |
right | 早送り | POST | https://api.spotify.com/v1/me/player/next |
left | 巻き戻し | POST | https://api.spotify.com/v1/me/player/previous |
none | 再生中の曲を取得 | GET | https://api.spotify.com/v1/me/player/currently-playing |
【コード】
'use strict'require('dotenv').config();constrequest=require('request');constsync_request=require('sync-request');// obniz認証情報constobniz_id=process.env.OBNIZ_ID;constObniz=require('obniz');constobniz=newObniz(obniz_id);// spotify認証情報(.envから読み込む)constaccess_token=process.env.ACCESS_TOKEN;constclient_id=process.env.CLIENT_ID;// Your client idconstclient_secret=process.env.CLIENT_SECRET;// Your secretconstredirect_uri=process.env.REDIRECT_URI;// Your redirect uricoconst{createCanvas}=require('canvas');// ヘッダー情報varheaders={'Accept':'application/json','Content-Type':'application/json','Authorization':`Bearer ${access_token}`};obniz.onconnect=asyncfunction(){// RGB LEDを呼び出すvarrgbled=obniz.wired('WS2811',{gnd:0,vcc:1,din:2});// ディスプレイ処理obniz.display.clear();// 一旦クリアするobniz.display.print('Hello obniz-spotify!');varstate=awaitobniz.switch.getWait();// スイッチの反応を常時監視obniz.switch.onchange=function(state){varresult=getTrackInfo();if(state==='push'){varis_playing=result['is_playing'];// 再生if(!is_playing){console.log('play');playTrack();// 停止}else{console.log('stop');pauseTrack();}// ディスプレイ処理obniz.display.clear();constr=random(0,255);constg=random(0,255);constb=random(0,255);console.log(r,g,b);rgbled.rgb(r,g,b);}elseif(state=='none'){varinfo=getDispInfo(result);obniz.display.clear();}elseif(state==='right'){// 右にスイッチを倒したときconsole.log('skip');rgbled.rgb(0,255,0);skipTrack();// ディスプレイ処理obniz.display.clear();// 一旦クリアするobniz.display.print('skip');// pressed right という文字を出す }elseif(state==='left'){// 左にスイッチを倒したとき(やさしく)console.log('back');rgbled.rgb(255,0,255);backTrack();// ディスプレイ処理obniz.display.clear();// 一旦クリアするobniz.display.print('back');}}setInterval(asyncfunction(){varresult=getTrackInfo();if(state=='none'&&result['is_playing']){rgbled.rgb(random(0,255),random(0,255),random(0,255));}else{rgbled.rgb(0,0,0);}},1200);setInterval(asyncfunction(){if(state=='none'){varresult=getTrackInfo();varinfo=getDispInfo(result);console.log(info);constcanvas=createCanvas(128,64);constctx=canvas.getContext('2d');ctx.fillStyle="white";ctx.font="15px Avenir";ctx.fillText(info,0,40);obniz.display.draw(ctx);}},3000);}// RGBをランダムでvarrandom=function(min,max){min=Math.ceil(min);max=Math.floor(max);returnMath.floor(Math.random()*(max-min))+min;//The maximum is exclusive and the minimum is inclusive}// 再生functionplayTrack(){console.log("play func");varoptions={url:'https://api.spotify.com/v1/me/player/play',method:'PUT',headers:headers,};functioncallback(error,response,body){if(!error&&response.statusCode==200){console.log("success");returnbody;}}request(options,callback);}// 巻き戻しfunctionbackTrack(){console.log("back func");varoptions={url:'https://api.spotify.com/v1/me/player/previous',method:'POST',headers:headers};functioncallback(error,response,body){if(!error&&response.statusCode==200){console.log(body);}}request(options,callback);}// 早送りfunctionskipTrack(){console.log("skip func");varoptions={url:'https://api.spotify.com/v1/me/player/next',method:'POST',headers:headers};functioncallback(error,response,body){if(!error&&response.statusCode==200){console.log(body);}}request(options,callback);}// 再生中の曲functiongetTrackInfo(){varresponse=sync_request('GET','https://api.spotify.com/v1/me/player/currently-playing',{headers:headers,});returnJSON.parse(response.getBody('utf8'));}// ディスプレイ表示用の曲情報functiongetDispInfo(data){vartitle=data["item"]["name"];varname=data["item"]["artists"]["0"]["name"];returntitle+"\n"+name;}// 停止functionpauseTrack(){console.log("stop func");varoptions={url:'https://api.spotify.com/v1/me/player/pause',method:'PUT',headers:headers};functioncallback(error,response,body){if(!error&&response.statusCode==200){console.log(body);}}request(options,callback);}
非同期処理の理解が甘いのと雑なコーディングなのは自覚しているので、これからリファクタリングしていく所存であります。
工夫したところはsetIntervalのところでライトがランダムに光るところです。
イケていないところは再生中にずっとライトが点滅していて結構目障りなところです。
暗闇で眺めている分にはすごく良いのですが。
さらイケてないところはsetIntervalのところで曲情報を取得するリクエストを三秒に一回
投げているところです。
もっと適した方法はあったはずです・・・。
感想
APIドキュメントをじっくり読んだことがあまりなかったので、実現したい機能を実装するために
必要なパラメータだったり書き方だったりを英語と格闘しながら調べました。
おかげでAPIドキュメントの勘所を少しは抑えれるようになりました。
日本語表示も対応できたし、なにより大好きなサービスを使って物づくりができるというのは
ここまで楽しいことなのかと思いました。
前回の課題では本来の理想であったnode.jsでの実装をほぼほぼGASでしてしまった後悔があったのですが
今回はnode.jsでゴリゴリ書けたしobnizの関数も上手く盛り込むことができ、バランスよく作れたため達成感があります。
人参は生産できないけど八百屋さんで購入して美味しいカレーがつくれるように、世の中に提供されている便利なAPIを使用してちょっとだけSpotifyライフの質を向上させることができました。
クックパッドを見て料理を作るようにドキュメントを読んで楽しく課題ができたと思います。