2016年3月30日に京都で開催された 関西モバイルアプリ研究会 #12 で、iOS 9.3 に追加された、Apple Music 関連のAPIについて紹介しました。 Apple Music は月額料金を払えば、ストリーミングで音楽を聴きまくれる最高のサービスなので、使ってない人はいますぐ契約しましょう。
今回の更新で主に以下のようなことが出来るようになりました。
SKCloudServiceController
MPMusicPlayerController
- Apple Music の曲を再生する
MPMediaLibrary
/MPMediaPlaylist
権限の取得や、Storefront ID を確認したあとは、おもむろに以下のようにして曲を再生できます。
let player = MPMusicPlayerController.systemMusicPlayer() player.setQueueWithStoreIDs([ "1017194380", "771599829" ])
非常に簡単ですね。この StoreID は同じ曲でも国ごとに違うので Storefront ID を確認しておく必要があります
詳しくは僕の資料や、以下の記事が参考になるかと思います。 発表したあとブログに書こうと思って追加でもうちょっと調べてたら、先にこの記事が出たので書くのをやめたという負け惜しみをひとつ。
ところで、この StoreID とやらはどうやって取得出来るのでしょうか。 残念ながら iOS の API だけでは取得することが出来ません。悲しい。 なんか思っていた夢の世界とはちょっと違う。
iTunes Search API を使う
ところで iTunes Search API を使うと iTunes Store 上の曲などの情報を取得できます。
curl https://itunes.apple.com/search?term=ESNO+とか。&country=JP&media=music&entiry=song&isStreamable=true
たとえばこのようなクエリで Search API を叩くと以下のようなJSONが得られます。
{ "resultCount": 1, "results": [ { "wrapperType": "track", "kind": "song", "artistId": 555290400, "collectionId": 1017193906, "trackId": 1017194380, "artistName": "ESNO", "collectionName": "Release", "trackName": "とか。 feat. きゃべこ - remix -", "collectionCensoredName": "Release", "trackCensoredName": "とか。 feat. きゃべこ - remix -", "artistViewUrl": "https://itunes.apple.com/jp/artist/esno/id555290400?uo=4", "collectionViewUrl": "https://itunes.apple.com/jp/album/toka-feat.-kyabeko-remix/id1017193906?i=1017194380&uo=4", "trackViewUrl": "https://itunes.apple.com/jp/album/toka-feat.-kyabeko-remix/id1017193906?i=1017194380&uo=4", "previewUrl": "http://a1896.phobos.apple.com/us/r20/Music5/v4/7c/06/ae/7c06aefa-3c4b-3916-20a3-660589df15dd/mzaf_2523222398560687944.plus.aac.p.m4a", "artworkUrl30": "http://is3.mzstatic.com/image/thumb/Music7/v4/8a/4e/42/8a4e42c8-0638-163e-bcb9-ec2a980999dc/source/30x30bb.jpg", "artworkUrl60": "http://is3.mzstatic.com/image/thumb/Music7/v4/8a/4e/42/8a4e42c8-0638-163e-bcb9-ec2a980999dc/source/60x60bb.jpg", "artworkUrl100": "http://is3.mzstatic.com/image/thumb/Music7/v4/8a/4e/42/8a4e42c8-0638-163e-bcb9-ec2a980999dc/source/100x100bb.jpg", "collectionPrice": 1800, "trackPrice": 200, "releaseDate": "2015-07-22T07:00:00Z", "collectionExplicitness": "notExplicit", "trackExplicitness": "notExplicit", "discCount": 1, "discNumber": 1, "trackCount": 12, "trackNumber": 9, "trackTimeMillis": 215507, "country": "JPN", "currency": "JPY", "primaryGenreName": "エレクトロニック", "isStreamable": true } ] }
このJSONの trackId
や collectionId
が今回求めている StoreID で、この ID さえあればAPIから再生やライブラリに追加することが出来ます。
呼び方がいろいろあってややこしいので、以下は trackId
に統一します。
ちなみに isStreamable
が true
の曲のみ Apple Music で有効です。
しかし、この Search API、意外と融通が効かない。このアーティストのこのアルバムなどのようなピンポイントの検索が出来ない。つまりいま聴いている曲から機械的に正しい trackId を取得することが困難です。変わった曲名や、変わったアルバム名なら項目指定で一発で引けそうですが、一般的な名前なら絶望的。
では、人間が検索して正しいのを選ぶとうのはどうでしょうか。良さそうですか? いやいや、我々はそういったことが出来る最高のアプリを知っているはずです。iTunes という最高のアプリがあるので使いましょう。
そう、つまりこの trackId
が自動的に分からなければ特になんのメリットもないとうことです。
再生中の曲を得る
ひとまず現在、再生中の曲をのぞいてみましょう。
一番簡単な方法は MPMusicPlayerController
の nowPlayingItem
プロパティを見ることです。これは現在 Music アプリで再生中の曲情報を MPMediaItem
のインスタンスとして取得出来ます。
取得した MPMediaItem
の valueForProperty(_:)
メソッドを使って各種情報を取得できます。ドキュメントにそう書かれているけど、ヘッダーを眺めているとそれぞれプロパティが用意されているようなのでそれを使うと良さそうです。
let player = MPMusicPlayerController.systemMusicPlayer() let currentTrack = player.nowPlayingItem print(currentTrack.valueForProperty(MPMediaItemPropertyTitle)) // => とか。 feat. きゃべこ - remix -
MPMusicPlayController
に指定することで、再生状態が変わったとき (MPMusicPlayerControllerPlaybackStateDidChangeNotification
) や、曲が変わったとき (MPMusicPlayerControllerNowPlayingItemDidChangeNotification
) に通知が発行することが出来るので、これを監視することで常にいま聴いている曲の情報を取得し続ける事ができます。
override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) // 通知を監視する NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateTrackTitle), name: MPMusicPlayerControllerPlaybackStateDidChangeNotification, object: self.player) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateTrackTitle), name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification, object: self.player) // 通知を送るようにする player.beginGeneratingPlaybackNotifications() } override func viewWillDisappear(animated: Bool) { // 通知を止める player.endGeneratingPlaybackNotifications() // 通知の監視を止める NSNotificationCenter.defaultCenter().removeObserver(self) super.viewWillDisappear(animated) }
これで聴いてる曲を共有出来るかと思いきや、残念ながら iTunes Store に関する情報は皆無です。(もしかしてなんか取得方法あったら教えてください!)
曲名とかは共有出来るけど、いま欲しいのは trackId
であって、曲名などどうでもいいのです。むしろ trackId
さえあれば Lookup API で各種情報は取得できる。
Music アプリから iTunes Store の URL を共有出来るのだから取得出来てもおかしくないのに。
一方 Mac では
だんだんと iPhone で聴いている曲を自動で共有するということをあきらめつつあり、ひとまず trackId を集めて、API を試したいという気持ちになってきました。 IDを集めるだけだったらふだん仕事中に Mac で音楽を聴いているわけで、Mac で取得出来たら得だな、なにか方法はないのかなと調べ始めました。
再生中の曲を得る
Mac には Scripting Bridge という仕組みがあって、アプリのインターフェースを定義しておくと、別のアプリから操作することが出来るというものです。
sdef
コマンドで定義されているインターフェースを取得することができ、さらに sdp
コマンドでヘッダーファイルなどに変換できます。
sdef /Applications/iTunes.app | sdp -fh --basename iTunes
iTunes だとこういう感じ。
この生成されたヘッダーファイルを使うと使うといいのですが、Swift だとうまくいきません。
あきらめて適当なプロトコルを定義して、SBObject
を拡張することで欲しい値を取得します。
@objc protocol iTunesTrack { optional var name: String { get } optional var album: String { get } optional var artist: String { get } } extension SBObject: iTunesTrack { } let iTunesApplication = SBApplication(bundleIdentifier: "com.apple.iTunes")! let currentTrack = iTunesApplication.valueForKey("currentTrack") as! iTunesTrack print(currentTrack.name) // => 夕暮れパラレリズム (feat. daoko)
しかし、残念ながら生成した iTunes.h
を眺めても iTunes Store に関する情報は取得できそうにありませんでした。
そして、さらに Stream 再生している曲の場合は、currentTrack
は取得出来るものに、name
などが空文字列になってしまって、正常に取得出来ないように見えました。
通知を眺めていると
iTunes の曲情報を取得するのは若干面倒だったのですが、再生状況が変わったときに iTunes から通知が発行されていてこれを拾うことは簡単です。
# どこかで監視を追加 NSDistributedNotificationCenter.defaultCenter().addObserver(self, selector: #selector(trackChanged(_:)), name: iTunesNotificationName, object: nil) // ... func trackChanged(notification: NSNotification) { print(notification) }
iOSとは違って通知は1種類なのですが、NSNotification
に各種情報が格納されています
再生中かどうか、現在選択されている曲の情報などが含まれます。
こいつを眺めてみると以下のようになっています。
__CFNotification 0x608000040360 {name = com.apple.iTunes.playerInfo; object = com.apple.iTunes.player; userInfo = { Album = "#501"; "Album Artist" = MIDICRONICA; "Album Rating" = 0; "Album Rating Computed" = 1; Artist = MIDICRONICA; "Artwork Count" = 1; "Back Button State" = Prev; "Disc Count" = 1; "Disc Number" = 1; "Display Line 0" = "San Francisco"; "Display Line 1" = "MIDICRONICA \U2014 #501"; Genre = "Hip Hop/Rap"; "Library PersistentID" = 4494643586137931510; "Like Status" = None; Name = "San Francisco"; PersistentID = "-1983237849806433000"; "Play Count" = 12; "Play Date" = "2016-03-18 02:06:26 +0000"; "Player State" = Playing; "Playlist PersistentID" = 5800905515721368577; "Rating Computed" = 1; "Skip Count" = 0; "Store URL" = "itms://itunes.com/album?p=500044680&i=500044854"; "Total Time" = 206733; "Track Count" = 15; "Track Number" = 9; Year = 2005; elapsedStr = "-3:27"; }}
( ゚д゚)
(つд⊂)ゴシゴシ
(;゚д゚)
Store URL
!!!!!!!!!!!!!!
ということで、iTunes Store での URL が取得出来ることが分かります。
よく見ると i=500044854
というパラメータがついており、これが求めていた trackId
です。
お疲れ様でした。
安心はできない
ここで Apple Music で再生出来るのは isStreamable
な曲だけだったことを思い出してください。
今回取得出来た情報だけだと、iTunes Store で販売されているということは分かりますが、Apple Music で配信されているかまでは分かりません。
そこでふたたび登場するのが iTunes Search API
です。このAPIには Lookup というのもあって、これは id を指定して商品(ここでは曲!)の情報を得られます。
以下のようにします。ドキュメントを読んでいると country
パラメータは不要に見えるのですが、指定しないと日本のストアで検索できないのがハマりポイントです。
curl https://itunes.apple.com/lookup?country=JP&id=500044854
すると以下のような曲情報が取得できて、isStreamable
が true
なので Apple Music で再生出来ることが分かりました。
{ "resultCount": 1, "results": [ { "trackId": 500044854, # (略) ... "isStreamable": true } ] }
結局
本来の目的は iOS 9.3 の Apple Music 関連APIを活用することだったのですが、trackId が分からないと何も出来ないし、Search API を検索するくらいなら iTunes 使うでしょということで、まずは trackId
を集まることに注目して調べてみました。結果、Mac アプリから取得できる iTunes の通知からとれる Store URL。そこから分かる id と lookup API を使って、自動的に trackId と正確な曲情報を取得出来ることが分かりました。
trackId
さえ集まれば再生やライブラリへの追加はできますし、プレイリストを Web から作れて、それを iOS で誰でも再生出来るとかのサービスは作れそうですが、どっちみち Web サービス前提となってしまって気軽には出来ません。今回の API 追加は夢だけは広がりましたが、どちらかというと既存の音楽系サービス向けのもので、一般ユーザー向けには iOS 上で再生中の曲の trackId を取得する手段が公開されるまでは使いどころが難しいのではないでしょうか。
おまけ
調べる過程で以下のようなのが出来ました。
サイトはどうでもよくてここで trackId を収集しつつ、Slack に Webhook を投げたりしていてちょっとだけべんり。
iOSのAPIをちょっと試してみたい人向けに ここ で Apple Music で再生出来る僕(か知り合い)が聴いた曲の直近50件の trackId を返すようにしています。(予告なく消す可能性あります)
Twitter にも一部流しています。Twitter にはアフィリエイトリンク張っていないので安心してクリックください。