lib/robut-rdio.rb in robut-rdio-0.1.0 vs lib/robut-rdio.rb in robut-rdio-0.1.2
- old
+ new
@@ -43,66 +43,190 @@
def self.start_server
@server = Thread.new { Server.run! :host => (host || "localhost"), :port => (port || 4567) }
Server.token = self.token || "GAlNi78J_____zlyYWs5ZG02N2pkaHlhcWsyOWJtYjkyN2xvY2FsaG9zdEbwl7EHvbylWSWFWYMZwfc="
Server.domain = self.domain || "localhost"
end
+
+ #
+ # Because an instance of this plugin is not created until the Robut client has
+ # recieved at least one message. The server callbacks need to be created during
+ # the #handle request. This allows for the server to communicate back through
+ # the Robut communication channel that it receives the messages.
+ #
+ def establish_server_callbacks!
+ Server.reply_callback ||= lambda{|message| reply(message, :room)}
+ Server.state_callback ||= lambda{|message| reply("/me #{message}", :room)}
+ end
# Returns a description of how to use this plugin
def usage
[
"#{at_nick} play <song> - queues <song> for playing",
"#{at_nick} play album <album> - queues <album> for playing",
"#{at_nick} play track <track> - queues <track> for playing",
"#{at_nick} play/unpause - unpauses the track that is currently playing",
"#{at_nick} next - move to the next track",
+ "#{at_nick} next|skip album - skip all tracks in the current album group",
"#{at_nick} restart - restart the current track"
]
end
+
+
+ #
+ # @param [String,Array] request that is being evaluated as a play request
+ # @return [Boolean]
+ #
+ def play_results_regex
+ /^(?:play)?\s?(?:result)?\s?((?:\d[\s,-]*)+|all)$/
+ end
+
+ #
+ # @param [String,Array] request that is being evaluated as a play request
+ # @return [Boolean]
+ #
+ def play?(request)
+ Array(request).join(' ') =~ play_results_regex
+ end
+ #
+ # @param [Array,String] track_request the play request that is going to be
+ # parsed for available tracks.
+ #
+ # @return [Array] track numbers that were identified.
+ #
+ # @example Requesting multiple tracks
+ #
+ # "play 1"
+ # "play 1 2"
+ # "play 1,2"
+ # "play 1-3"
+ # "play 1, 2 4-6"
+ # "play all"
+ #
+ def parse_tracks_to_play(track_request)
+ if Array(track_request).join(' ') =~ /play all/
+ [ 'all' ]
+ else
+ Array(track_request).join(' ')[play_results_regex,-1].to_s.split(/(?:\s|,\s?)/).map do |track|
+ tracks = track.split("-")
+ (tracks.first.to_i..tracks.last.to_i).to_a
+ end.flatten
+ end
+ end
+
+ #
+ # @return [Regex] that is used to match searches for their parameters
+ # @see http://rubular.com/?regex=(find%7Cdo%20you%20have(%5Csany)%3F)%5Cs%3F(.%2B%5B%5E%3F%5D)%5C%3F%3F
+ #
+ def search_regex
+ /(find|do you have(\sany)?)\s?(.+[^?])\??/
+ end
+
+ #
+ # @param [String,Array] request that is being evaluated as a search request
+ # @return [Boolean]
+ #
+ def search?(request)
+ Array(request).join(' ') =~ search_regex
+ end
+
+ #
+ # @param [String,Array] request that is being evaluated as a search and playback
+ # request
+ # @return [Boolean]
+ #
+ def search_and_play?(request)
+ Array(request).join(' ') =~ /^play\b[^\b]+/
+ end
+
+ #
+ # @param [String,Array] request that is being evaluated as a command request
+ # @return [Boolean]
+ #
+ def command?(request)
+ Array(request).join(' ') =~ /^(?:play|(?:un)?pause|next|restart|back|clear)$/
+ end
+
+ #
+ # @param [String,Array] request that is being evaluated as a skip album request
+ # @return [Boolean]
+ #
+ def skip_album?(message)
+ message =~ /(next|skip) album/
+ end
+
# Queues songs into the Rdio web player. @nick play search query
# will queue the first search result matching 'search query' into
# the web player. It can be an artist, album, or song.
def handle(time, sender_nick, message)
- ::Rdio.init(self.class.key, self.class.secret)
+
+ establish_server_callbacks!
+
words = words(message)
if sent_to_me?(message)
- if words.join(' ') =~ /^(play)?\s?(result)?\s?\d/
- play_result(words.last.to_i)
- elsif words.first == 'play' and words.length > 1
- results = search(words)
- result = results.first
- if result
- queue(result)
- else
- reply("I couldn't find #{words.join(" ")} on Rdio.")
+
+ if play?(words)
+
+ play_result *parse_tracks_to_play(words)
+
+ elsif search_and_play?(words)
+
+ search_and_play_criteria = words[1..-1].join(" ")
+
+ unless search_and_play_result search_and_play_criteria
+ reply("I couldn't find '#{search_and_play_criteria}' on Rdio.")
end
- elsif words.join(' ') =~ /(find|do you have(\sany)?)\s?(.+[^?])\??/
- find(['',Regexp.last_match[-1]])
- else words.first =~ /play|(?:un)?pause|next|restart|back|clear/
- run_command(words.first)
+
+ elsif search?(words)
+
+ find words.join(' ')[search_regex,-1]
+
+ elsif skip_album?(message)
+
+ run_command("next_album")
+
+ else command?(words)
+
+ run_command(words.join("_"))
+
end
end
+
end
+ #
+ # As the plugin is initialized each time a request is made, the plugin maintains
+ # the state of the results from the last search request to ensure that it will
+ # be available when someone makes another request.
+ #
def results
@@results
end
-
+
+
private
RESULT_DISPLAYER = {
- ::Rdio::Album => lambda{|album| "#{album.artist.name} - #{album.name}"},
- ::Rdio::Track => lambda{|track| "#{track.artist.name} - #{track.album.name} - #{track.name}"}
+ ::Rdio::Album => lambda{|album| "#{album.artist_name} - #{album.name}"},
+ ::Rdio::Track => lambda{|track| "#{track.artist_name} - #{track.album_name} - #{track.name}"},
+ ::Rdio::Artist => lambda do |artist|
+ tracks = artist.tracks(nil,0,1)
+ unless tracks.empty?
+ "#{artist.name} - #{tracks.first.album_name} - #{tracks.first.name}"
+ else
+ "#{artist.name}"
+ end
+ end
}
def run_command(command)
Server.command << command
end
def find(query)
- reply("Searching for: #{query[1..-1].join(' ')}...")
+ reply("Searching for: #{query}...")
@@results = search(query)
result_display = format_results(@@results)
reply(result_display)
end
@@ -113,21 +237,45 @@
result_display += format_result(result, index) + "\n"
end
result_display
end
- def play_result(number)
+ def play_result(*requests)
+
if !has_results?
reply("I don't have any search results") and return
end
-
- if !has_result?(number)
- reply("I don't have that result") and return
+
+ requests = requests.flatten.compact
+
+ # Queue all the songs when the request is 'all'
+
+ if requests.first == "all"
+ results.length.times {|index| queue result_at(index) }
+ return
end
-
- queue result_at(number)
+
+ requests.flatten.each do |request|
+
+ if has_result?(request.to_i)
+ queue result_at(request.to_i)
+ else
+ reply("I don't have that result")
+ end
+
+ end
+
end
+
+ def search_and_play_result(message)
+
+ if result = Array(search(message)).first
+ queue(result)
+ true
+ end
+
+ end
def has_results?
@@results && @@results.any?
end
@@ -151,24 +299,33 @@
response = RESULT_DISPLAYER[search_result.class].call(search_result)
puts response
"#{index}: #{response}"
end
- # Searches Rdio for sources matching +words+. If the first word is
- # 'track', it only searches tracks, same for 'album'. Otherwise,
- # matches both albums and tracks.
+ # Searches Rdio for sources matching +words+.
+ #
+ # Given an array of Strings, which are the search terms, use Rdio to find any
+ # tracks that match. If the first word happens be `album` then search for
+ # albums that match the criteria.
+ #
+ #
def search(words)
+ ::Rdio.init(self.class.key, self.class.secret)
api = ::Rdio::Api.new(self.class.key, self.class.secret)
-
- if words[1] == "album"
- query_string = words[2..-1].join(' ')
+ words = words.split(' ')
+
+ if words.first == "album"
+ query_string = words[1..-1].join(' ')
results = api.search(query_string, "Album")
- elsif words[1] == "track"
- query_string = words[2..-1].join(' ')
+ elsif words.first == "track"
+ query_string = words[1..-1].join(' ')
results = api.search(query_string, "Track")
- else
+ elsif words.first == "artist"
query_string = words[1..-1].join(' ')
- results = api.search(query_string, "Album,Track")
+ results = api.search(query_string, "Artist")
+ else
+ query_string = words.join(' ')
+ results = api.search(query_string, "Track")
end
end
end