Rust で Spotify Web API を叩いて遊んでみた
はじめに
テントの中から失礼します、CX事業本部のてんとタカハシです!
Spotify が完全に仕事のお供になっています。というのと、Rust 書きたいな〜という気分から、Rust で Spotify Web API を叩いて遊んでみましたので、記事にしようと思います。
今回は、生で API を叩くのではなく、Rust で Spotify Web API をラップした rspotify という ライブラリを使用します。こちらのライブラリですが、ドキュメントに各エンドポイントのサンプルコードが置いてあり、とても親切です。
Developers.IO では、過去に Spotify Web APIの使い方 という記事が公開されていますので、併せて参考にして頂けると良いのかなと思います。Spotify for Developers にてアプリの新規作成を行う手順については、UI が新しくなっていたりするので、改めてこの記事でご紹介しようと思います。
また、下記のリポジトリにソースコード一式を置いていますので、併せて参考にして頂ければと思います。
前提
- Spotify のアカウントが作成済みであること
- Spotify のプレミアムプランが登録済みであること
- Spotify のアプリが PC (Mac) にインストールされていること
- Rust がインストール済みであること
環境
$ sw_vers ProductName: Mac OS X ProductVersion: 10.15.6 BuildVersion: 19G2021 $ cargo version cargo 1.46.0 (149022b1d 2020-07-17) $ rustc --version rustc 1.46.0 (04488afe3 2020-08-24)
Spotify Web API で何ができるの
下記ができます。詳細は API の ドキュメント をご参照ください。
- 楽曲の検索
- プレイヤーの操作(楽曲の再生や停止、スキップなど)
- プレイリストの操作(作成や楽曲の追加など)
- ユーザーの試聴傾向に基づいた情報の取得
- などなど
尚、エンドポイントの中には、Beta 版のものが存在しています。例えば、プレイヤーの操作に関するものは全て Beta 版です。今回ご紹介する実装でも、Beta 版のエンドポイントを使用していたりするので、ある日突然動作しなくなる可能性があります。ご注意ください。
Spotify Web API を使用するための準備
Spotify Web API を叩くために必要な Client ID を取得したり、Redirect URI の設定を行います。
Spotify for Developers にログインする
Spotify for Developers にログインします。
アプリを新規作成する
ログインするとダッシュボードが表示されます。「CREATE AN APP」ボタンをクリックしてください。
※ 私の環境では既に2つアプリを作成済みのため、下記のような画面となっております
ダイアログが表示されますので、App name、App description を入力した後、チェックボックスにチェックを入れて、「CREATE」ボタンをクリックしてください。
すると、アプリが作成されて、下記の画面に遷移します。画面左側に Client ID が表示されます。また、その下の「SHOW CLIENT SECRET」をクリックすることで、Client Secret が表示されます。これら2つの情報は、API を叩く際に必要となります。
Redirect URI を設定する
API を叩くために1つだけ設定が必要になります。画面右上にある「EDIT SETTINGS」ボタンをクリックしてください。すると、設定ダイアログが表示されますので、Redirect URIs の入力欄に http://127.0.0.1:8080/
と入力した後、「ADD」ボタンをクリックしてください。その後、ダイアログの下部までスクロールして、「SAVE」ボタンをクリックしてください。
これで、Spotify for Developers 画面上での設定は完了となります。
実装前の準備
Rust のプロジェクトを作成する
ここでは、rust-spotify
という名前にします。
$ cargo new rust-spotify
使用するクレートを設定する
Cargo.toml
内の dependencies を下記に変更してください。
[package] ... [dependencies] rspotify = { version = "0.10"} tokio = { version = "0.2", features = ["full"] }
環境変数を設定する
先ほど、Spotify for Developers にて取得した Client ID、Client Secret と Redirect URI を環境変数に設定します。ここで設定した環境変数を rspotify が読み込んで、API を叩く際に使用されます。環境変数ではなく、.env
ファイルを読み込ませる方法もありますが、今回は説明を省きます。
export CLIENT_ID="<SPOTIFY_CLIENT_ID>" export CLIENT_SECRET="<SPOTIFY_CLIENT_SECRET>" export REDIRECT_URI="http://127.0.0.1:8080/"
実装
今回は Spotify Web API を使用して、下記をやってみようと思います。
- アーティストの情報を検索する
- 指定したアーティストの楽曲人気 TOP10 を表示する
- 指定した楽曲を再生する
それぞれを1ファイルとして src/bin
の中に実装していきます。
アーティストの情報を検索する
実装は下記になります。引数で検索するアーティスト名を受け取るようにしています。
extern crate rspotify; use std::env; use rspotify::client::Spotify; use rspotify::oauth2::{SpotifyClientCredentials, SpotifyOAuth}; use rspotify::senum::{Country, SearchType}; use rspotify::util::get_token; #[tokio::main] async fn main() { let args: Vec<String> = env::args().collect(); if args.len() == 1 { println!("Usage: cargo run --bin search_artist <ARTIST_NAME>"); return; } let artist_name = &args[1]; let mut oauth = SpotifyOAuth::default().scope("user-read-private").build(); match get_token(&mut oauth).await { Some(token_info) => { let client_credential = SpotifyClientCredentials::default() .token_info(token_info) .build(); let spotify = Spotify::default() .client_credentials_manager(client_credential) .build(); // Reference: https://developer.spotify.com/documentation/web-api/reference/search/search/ let result = spotify .search( // q: 検索キーワード artist_name, // type: 検索タイプ(album, artist, playlist, track, etc.) SearchType::Artist, // limit: 取得するデータの最大数 1, // offset: 取得するデータの開始位置 0, // market: 指定した国名コードで再生可能なコンテンツのみを返す Some(Country::Japan), // include_external: audio を指定すると関連するオーディオコンテンツがレスポンスに含まれる None, ) .await .unwrap(); match result { rspotify::model::search::SearchResult::Artists(artists) => { let artist = &artists.items[0]; println!("artist: {}\nid: {}\npopularity: {}", artist.name, artist.id, artist.popularity); }, err => println!("search error!{:?}", err), } } None => println!("auth failed"), }; }
下記で実行します。ここでは、DA PUMP
を検索しようと思います。
$ cargo run --bin search_artist "DA PUMP"
すると、ブラウザが開いて下記のページが表示されるので、「同意する」ボタンをクリックします。
すると、ページが表示できなくなりますが、これでOKです。この状態でページの URL をコピーします。
ターミナルが下記のようになっているので、コピーした URL を貼り付けて Enter します。
Opened https://accounts.spotify.com/authorize?state=<STATE>&scope=user-read-private&response_type=code&client_id=<CLIENT_ID>&redirect_uri=http:%2F%2F127.0.0.1:8080%2F& in your browser Enter the URL you were redirected to:
すると、検索した DA PUMP の ID と 人気度が表示されます(APIから様々な情報が取得されるので、ここでは絞って表示しています)。
artist: DA PUMP id: 3NRXKeatDxKe4apH6XawKX popularity: 53
この ID を使って、次は DA PUMP の楽曲人気 TOP10 を表示しましょう。
指定したアーティストの楽曲人気 TOP10 を表示する
実装は下記になります。先ほどと同様に、引数でアーティストの ID を受け取るようにしています。
extern crate rspotify; use std::env; use rspotify::client::Spotify; use rspotify::oauth2::SpotifyClientCredentials; use rspotify::senum::Country; #[tokio::main] async fn main() { let args: Vec<String> = env::args().collect(); if args.len() == 1 { println!("Usage: cargo run --bin artist_top_tracks <ARTIST_ID>"); return; } let artist_id = &args[1]; let client_credential = SpotifyClientCredentials::default().build(); let spotify = Spotify::default() .client_credentials_manager(client_credential) .build(); // Reference: https://developer.spotify.com/documentation/web-api/reference/artists/get-artists-top-tracks/ let result = spotify .artist_top_tracks( // id: artist の Spotify ID artist_id, // country: 国名コード Country::Japan ) .await .unwrap(); println!("# {} Top Tracks", result.tracks[0].artists[0].name); for track in result.tracks.iter() { println!("{}: {} ({})", track.name, track.popularity, track.id.as_deref().unwrap_or("none")); } }
下記で実行します。
$ cargo run --bin artist_top_tracks "3NRXKeatDxKe4apH6XawKX"
すると、下記が表示されます。少し見づらいですが、曲名と人気度、楽曲の ID を表示しています。やっぱり、if... と U.S.A. が人気なんですね。個人的には、Dragon Screamer 推しです。
# DA PUMP Top Tracks if...: 53 (78LQTUp1f8APWgFNZNOD5y) U.S.A.: 53 (4qZyuyqLoZ3CaH79rGvJFR) P.A.R.T.Y. 〜ユニバース・フェスティバル〜: 51 (7Avl3ZmZf5B80YSzfmdYuT) Dragon Screamer: 43 (623pmkD6sclgLBQrrPqyz4) Heart on Fire: 42 (5PLlnPQROKe1P9Vjrlxjvq) Fantasista〜ファンタジスタ〜: 42 (2MMVhnZEo1ldxODbqqm96K) 桜: 42 (5B36byNsxLer31rJz1jlKT) Feelin' Good 〜It's PARADISE〜: 40 (0g1NHilvp8AHX4HN8Ua26i) if...: 38 (156j6p6VwT2OYzEmeDKjUG) ごきげんだぜっ!〜Nothing But Something〜: 37 (0co9GJXSCrqVfIYjaihBjj)
次は楽曲の ID を使用して、Spotify アプリ上で楽曲を再生してみましょう。
指定した楽曲を再生する
実装は下記になります。これまでと同様に、引数で楽曲の ID を受け取るようにしています。
extern crate rspotify; use std::env; use rspotify::client::Spotify; use rspotify::model::offset::for_position; use rspotify::oauth2::{SpotifyClientCredentials, SpotifyOAuth}; use rspotify::util::get_token; #[tokio::main] async fn main() { let args: Vec<String> = env::args().collect(); if args.len() == 1 { println!("Usage: cargo run --bin play_track <TRACK_ID>"); return; } let track_id = &args[1]; let track_uri = "spotify:track:".to_string() + track_id; let mut oauth = SpotifyOAuth::default() .scope("user-modify-playback-state,user-read-playback-state") .build(); match get_token(&mut oauth).await { Some(token_info) => { let client_credential = SpotifyClientCredentials::default() .token_info(token_info) .build(); let spotify = Spotify::default() .client_credentials_manager(client_credential) .build(); // Reference: https://developer.spotify.com/documentation/web-api/reference/player/get-a-users-available-devices/ let result = spotify.device().await.unwrap(); let mut device_id = ""; for device in result.devices.iter() { if let rspotify::senum::DeviceType::Computer = device._type { if device.is_active == true { device_id = &device.id; } } } // Reference: https://developer.spotify.com/documentation/web-api/reference/player/start-a-users-playback/ match spotify .start_playback( // device_id: 対象とするデバイスのID Some(device_id.to_string()), // context_uri: 再生する album, artist, playlist の指定 None, // uris: 再生する track のリストを指定 Some(vec![track_uri]), // offset: album, playlist, track の再生を開始する位置 for_position(0), // position_ms: track の再生を開始する位置 None ) .await { Ok(_) => println!("start playback successful"), Err(_) => eprintln!("start playback failed"), } } None => println!("auth failed"), } }
楽曲を再生するには、Spotify のアプリをアクティブな状態にしておく必要があります。Spotify のアプリを開いて、テキトーな曲を再生 & 停止してください。
その後で、プログラムを実行します。
$ cargo run --bin play_track "623pmkD6sclgLBQrrPqyz4"
先ほどと同様に、ブラウザが開いてページが表示されるので、「同意する」ボタンをクリックします。
ページが遷移したら、URL をコピーして、ターミナルに貼り付けて Enter します。
Opened https://accounts.spotify.com/authorize?client_id=<CLIENT_ID>&response_type=code&redirect_uri=http:%2F%2F127.0.0.1:8080%2F&scope=user-modify-playback-state,user-read-playback-state&state=<STATE>& in your browser Enter the URL you were redirected to:
成功すると、start playback successful
の表示と共に、僕が推している Dragon Screamer が流れます。
start playback failed
と表示された場合は、再度 Spotify のアプリがアクティブになっているか確認してから、プログラムを実行してみてください。
以上でお遊びは終わりになります。
おわりに
楽しかった。
Python で spotipy を使って Spotify Web API を叩いたことはあるのですが、今回は不慣れな Rust ということで苦戦しました。少しおかしな実装になっているかもしれませんが、ご了承ください(勉強します)。
今回はお試しで API を叩いてみましたが、Raspberry Pi とボタンとかの部品を組み合わせて、それっぽいものを作るのが次の目標です。とはいえ、現状仕事では全然使わない知識なので、完成がいつになるかは不明です。何かできたら記事にしようと思います。
今回は以上になります。最後まで読んで頂きありがとうございました!