Last.fmラジオの自作
http://code.google.com/p/thelastripper/wiki/LastFM12UnofficialDocumentation
Last.fm のラジオを自分で作る際に必要な WebAPI のドキュメント.ただ...これをみて堂々とプレイヤーを公開するのは気が引けますね.再生までの簡単な手順はこんな感じ*1.最後の方に "まとめ" として簡単なRubyスクリプトも載せてみた.これくらいなら大丈夫だよね...きっと..
手順
ラジオとして機能させるにはざっくり分けると四つのステップを踏むことになります.具体的には,
- Handshake
- セッションIDの取得,リクエスト先のホスト名の取得
- Adjusting Radio Station
- ラジオの選択
- Requesting an XSPF
- プレイリストの取得
- Play
- 音楽再生
となります.しかし,Handshake で取得する情報は,2回目以降でも使い回しができるみたいです.
Handshake
セッションを張る
要求
以下の URL に GET リクエストを送る.
http://ws.audioscrobbler.com/radio/handshake.php?version=1.3.1.1&username=[USERNAME]&passwordmd5=[PASSWORDMD5_HASH]
応答
すると,ws.audioscrobbler.com からはこんな感じのが返ってくる
session=ae1eb54a11615e605d61d6e83dde71bc stream_url=http://87.117.229.85:80/last.mp3?Session=ae1eb54a11615e605d61d6e83dde71bc subscriber=0 framehack=0 base_url=ws.audioscrobbler.com base_path=/radio info_message= fingerprint_upload_url=http://ws.audioscrobbler.com/fingerprint/upload.php
実際に必要なのは session, base_url, base_path の値だけ.
これら三つを保存しておく.
Adjusting Radio Station
ラジオ局の指定
要求
以下の URL に GET リクエストを送る.
http://[base_url][base_path]/adjust.php?session=[sessionID]&url=[LASTFMURI]
Last.fm独自URI
僕が良く使うやつだけ挙げておく.他のは元のを参照してください.
- lastfm://artist/$artistname
- $artistname で指定されるアーティストに似た楽曲が集まった局を指定
- lastfm://globaltags/$tag
- $tag で指定されるタグがついた楽曲が集まった局を指定
応答
例えば, lastfm://globaltags/8bit とラジオ局を選択した時に,選択が成功するとこんな感じの応答が返ってくる.
response=OK url=lastfm://globaltags/8bit stationname= 8bit Tag Radio discovery=true
ここで特に保存すべき内容は無い.
Requesting an XSPF
プレイリストを取得する.XSPF とはプレイリストのファイル形式の一つで,XML の形式をとっています.
要求
以下のように GET 要求を送る
http://[base_url][base_path]/xspf.php?sk=[SESSIONID]&desktop=1.3.1.1
- [base_url],[base_path],[sessionID]
- 先ほどの Handshake で取得した値
応答
すると以下のXSPF形式で応答がある.(先ほどと同じ様に 8bit というタグで指定した場合)
<playlist version="1" xmlns:lastfm="http://www.audioscrobbler.net/dtd/xspf-lastfm"> <title>+8bit+Tag+Radio</title> <creator>Last.fm</creator> <link rel="http://www.last.fm/skipsLeft">6</link> <trackList> <track> <location>http://play.last.fm/user/da4d7abd06d00e21b9b387b491cbf1fe.mp3</locatio n> <title>Marbles</title> <id>36732</id> <album>Swarm & Dither</album> <creator>Hrvatski</creator> <duration>227000</duration> <image>http://cdn.last.fm/coverart/130x130/7099.jpg</image> <lastfm:trackauth>92c11</lastfm:trackauth> <lastfm:albumId>7099</lastfm:albumId> <lastfm:artistId>2308</lastfm:artistId> <link rel="http://www.last.fm/artistpage">http://www.last.fm/music/Hrvat ski</link> <link rel="http://www.last.fm/albumpage">http://www.last.fm/music/Hrvatski/Swarm %2B%2526%2BDither</link> <link rel="http://www.last.fm/trackpage">http://www.last.fm/music/Hrvatski/_/Mar bles</link> <link rel="http://www.last.fm/buyTrackURL"></link> <link rel="http://www.last.fm/buyAlbumURL">http://www.last.fm/affiliate_sendto.p hp?link=catch&prod=7099&pos=65633c2c6d40fbe9c8bf27ce82d2ca5a</link> <link rel="http://www.last.fm/freeTrackURL"></link> </track> <track> <location>http://play.last.fm/user/f1702fa3845bec7817a62749a1fbc49a.mp3</location> <title>Pioneer</title> <id>69590509</id> <album>Pioneer</album> <creator>she</creator> <duration>204000</duration> <image>http://cdn.last.fm/coverart/130x130/3287460.jpg</image> <lastfm:trackauth>35a7f</lastfm:trackauth> <lastfm:albumId>3287460</lastfm:albumId> <lastfm:artistId>1083333</lastfm:artistId> <link rel="http://www.last.fm/artistpage">http://www.last.fm/music/she</link> <link rel="http://www.last.fm/albumpage">http://www.last.fm/music/she/Pioneer</link> <link rel="http://www.last.fm/trackpage">http://www.last.fm/music/she/_/Pioneer</link> <link rel="http://www.last.fm/buyTrackURL"></link> <link rel="http://www.last.fm/buyAlbumURL"></link> <link rel="http://www.last.fm/freeTrackURL">http://freedownloads.last.fm/download/69590509/Pioneer.mp3</link> </track> </trackList> </playlist>
とこんな感じ.
Play
先のレスポンスの中の,"/playlist/trackList/track/location" で指定される文字列を,mpg123 などに渡す.
まとめ
再生までの最小限のコードはこんな感じ(?)になるのかな.
コピペして,@user, @password に,Last.fm のアカウント情報を代入すれば音楽の再生はできる.実際,ラジオとして機能させるには,UIをもっとシッカリしないとないといけないですが,それが目的ではないのでごく簡単な実装まで.
require "net/http" require "digest/md5" require "rexml/document" HANDSHAKE_HOST = 'ws.audioscrobbler.com' PORT = '80' HANDSHAKE = 'handshake.php' HANDSHAKE_PATH = '/radio' ADJUST = 'adjust.php' PLAYLIST = 'xspf.php' def main @user = 'ユーザ名' @password = 'パスワード' # タグ 8bit でラジオ局を指定 @lastfm_uri = 'lastfm://globaltags/8bit' #セッションIDの取得,リクエスト先のホストURIの取得 handshake #ラジオの選択 adjusting_radio_station #プレイリストの取得 requesting_an_XSPF #音楽再生 play end #セッションIDの取得,リクエスト先のホストURIの取得 def handshake handshake = HANDSHAKE_PATH + '/' + HANDSHAKE query = '?' + { 'version' => '1.3.1.1', 'username' => @user, 'passwordmd5' => Digest::MD5.hexdigest(@password), }.to_a.map{|a|a.join('=')}.join('&') Net::HTTP.new(HANDSHAKE_HOST,PORT).request_get(handshake+query) do |res| puts '--- Handshake Response ---', res.body @session = res.body.match(/^session=(.+)$/)[1] @base_url = res.body.match(/^base_url=(.+)$/)[1] @base_path = res.body.match(/^base_path=(.+)$/)[1] end end #ラジオの選択 def adjusting_radio_station adjust = @base_path+'/'+ADJUST query = '?' + { 'session' => @session, 'url' => @lastfm_uri, }.to_a.map{|a|a.join('=')}.join('&') Net::HTTP.new(@base_url,PORT).request_get(adjust+query) do |res| puts '--- Adjusting Radio Station Response ---', res.body end end #プレイリストの取得 def requesting_an_XSPF path = @base_path + '/' + PLAYLIST query = '?' + { 'sk' => @session, 'desktop' => '1.4.2.58376', }.map{|a|a.join('=')}.join('&') xpath = '/playlist/trackList/track/location' @tracks = Array.new # ここにMP3ファイルのURLをしまう Net::HTTP.new(@base_url,PORT).request_get(path+query) do |res| puts '--- Requesting An XSPF Response ---', res.body REXML::Document.new(res.body).elements.each(xpath) do |ele| @tracks << ele.text end end puts @tracks end def play cmd = "mpg123 #{@tracks.join(' ')}" puts "Execute '#{cmd}'" system cmd end main if $0 == __FILE__
ちなみに
上記 '/playlist/trackList/track/location' のURLに同時にアクセスすることができないようになってる.厳密に言うと,'/playlist/trackList/track/location' のURLへは複数のコネクションを張ることが不可能になっている.つまり「ダウンロードし放題だぜヒャッホーゥ!!」とはならないようになっている.