#!ruby # Author:: cho45(砂糖) # Copyright:: Copyright (c) 2005 cho45(砂糖) # License:: Ruby's require "net/http" require "uri" require "digest/md5" # This is Class for control LastFM ( http://www.last.fm/ ) # Usage sample: # lastfm = LastFM.new(USER, PASS) # puts lastfm.stream_url class LastFM class LastFMError < StandardError; end class HTTPError < LastFMError; end class SessionError < LastFMError; end class InvalidModeError < LastFMError; end VERSION = "0.1" GET_SESSION_URI = "http://wsdev.audioscrobbler.com/radio/getsession.php" NOW_PLAYING_URI = "http://wsdev.audioscrobbler.com/radio/np.php" CONTROL_URI = "http://wsdev.audioscrobbler.com/radio/control.php" TUNE_URI = "http://wsdev.audioscrobbler.com/radio/tune.php" MODE = { "1" => "random", "3" => "profile", "4" => "personal", } attr_reader :stream_url, :mode, :subject # Create new instance of LastFM. # _user_ and _pass_ is required. # mode and subject is initializing mode. def initialize(user, pass, mode="random", subject=nil) @user, @mode, @subject = user, mode, subject @pass = Digest::MD5.hexdigest(pass) @session, @stream_url = get_session(@user, @pass, @mode, @subject) now_playing # for already existing stream. end # Get now playing infomation # # example: # lastfm.now_playing #=> {"artist"=>"broken social scene", # "stationfeed"=>"foomfoom", # "albumcover_medium"=>"http://coverart.last.fm/130x130/1416353.jpg", # "artist_url"=>"http://test.audioscrobbler.com/music/broken+social+scene", # "trackduration"=>"263", # "track"=>"looks just like the sun", # "station_url"=>"http://test.audioscrobbler.com/user/typester/", # "trackprogress"=>"5", # "stationfeed_url"=>"http://test.audioscrobbler.com/user/foomfoom/", # "streaming"=>"true", # "recordtoprofile"=>"1", # "radiomode"=>"3", # "albumcover_large"=>"http://coverart.last.fm/300x300/1416353.jpg", # "album"=>"You Forgot It In People", # "albumcover_small"=>"http://coverart.last.fm/50x50/1416353.jpg", # "album_url"=> "http://test.audioscrobbler.com/music/broken%20social%20scene/You%20Forgot%20It%20In%20People", # "track_url"=> "http://test.audioscrobbler.com/music/broken+social+scene/_/looks+just+like+the+sun", # "station"=>"cho45"} def now_playing uri = URI.parse(NOW_PLAYING_URI + "?session=#{@session}") ret = request(uri) if ret["station"] @subject = ret["station"] end @mode = MODE[ret["radiomode"]] ret end # Return true if it's streaming def streaming? if now_playing["streaming"] == "true" true else false end end # Send love command def love command("love") end # Send Skip command def skip command("skip") end # Send Ban command def ban command("ban") end # Change mode. _mode_ must be one of MODE. # # example: # # change mode to random # lastfm.change("random") #=> true # # change mode to profile and set target user as cho45 # lastfm.change("profile", "cho45") #=> true def change(mode, user=nil) if MODE.values.include?(mode) @mode = mode @subject = user uri = URI.parse(TUNE_URI + "?session=#{@session}&mode=#{mode}&subject=#{user}") res = request(uri) if res["response"] == "OK" true else false end else InvalidModeError.new("Mode can be one of #{MODE.values.join(", ")}.") end end private # Get session and stream url from user and pass # Raise SessionError if the process is failed. def get_session(user, pass, mode="random", subject=nil) query = "?username=#{user}&passwordmd5=#{pass}" query += "&mode=#{mode}" if MODE.values.include?(mode) query += "&subject=#{subject}" if subject uri = URI.parse(GET_SESSION_URI + query) res = request(uri) if res["session"] == "FAILED" raise SessionError.new("Getting session was failed") end [res["session"], res["stream_url"]] end # Send command def command(com) uri = URI.parse(CONTROL_URI + "?session=#{@session}&command=#{com}") res = request(uri) if res["response"] == "OK" true else false end end # HTTP request and parse response to Hash. # Raise HTTPError if HTTP request is failed. def request(uri) ret = nil res = nil Net::HTTP.start(uri.host, uri.port) do |http| res = http.get(uri.request_uri) end if res.code == "200" ret = parse_lastfm_response(res.body) else raise HTTPError.new("HTTP Request was failed.") end ret end # parse and return as Hash. def parse_lastfm_response(str) ret = {} str.scan(/^(.*?)=(.*)$/n).each do |key, val| ret[key] = val end ret end end if $0 == __FILE__ require 'optparse' require "ostruct" require "readline" def parse(args) options = OpenStruct.new options.user = nil options.pass = nil options.mode = "random" options.subject = nil opts = OptionParser.new do |opts| opts.banner = "Usage: lastfm.rb [options]" opts.separator "" opts.separator "Specific options:" opts.on("-u", "--user USER", "LastFM user name.") do |user| options.user = user end opts.on("-p", "--pass PASS", "LastFM password.") do |pass| options.pass = pass end opts.separator "" opts.on("--mode [MODE]", LastFM::MODE.values, "Initializing mode.") do |mode| options.mode = mode end opts.on("--subject USER", "Target username") do |subj| options.subject = subj end opts.separator "" opts.on_tail("-h", "--help", "Show this message") do puts opts exit end opts.on_tail("-v", "--version", "Show version") do puts "LastFM::VERSION : #{LastFM::VERSION}" exit end end opts.parse!(args) options end options =parse(ARGV) unless options.user && options.pass puts "user and pass is required." exit end lastfm = LastFM.new(options.user, options.pass, options.mode, options.subject) puts lastfm.stream_url require "pp" prompt = "#{lastfm.subject} > " while buf = Readline.readline(prompt, true) case buf when "np", "now playing" np = lastfm.now_playing puts "Title: #{np["track"]}" puts "Album: #{np["album"]}" puts "Artist: #{np["artist"]}" when "love", "skip", "ban" if lastfm.send(buf) puts "Ok" else puts "Error" end when "random" lastfm.change("random") when /^profile\s+(.*)$/ lastfm.change("profile", Regexp.last_match[1]) when /^debug (.*)$/ begin pp eval("lastfm.#{Regexp.last_match[1]}") rescue Exception => e puts e.inspect, e.backtrace[0..5].join("\n\t") end when "help" puts "np, now playing:" puts "\tShow current playing." puts "love, skip, ban" puts "\tSend command." puts "random, profile USER" puts "\tSet target user USER." puts "exit, quit" puts "\tQuit." when "exit", "quit" break end prompt = "#{lastfm.subject} > " end end