Class: Audioscrobbler
- Inherits:
-
Object
- Object
- Audioscrobbler
- Defined in:
- lib/audioscrobbler.rb
Overview
Audioscrobbler
Description
Queue music tracks as they are played and submit the track information to Last.fm (www.last.fm) using the Audioscrobbler plugin protocol (www.audioscrobbler.net/).
Version 1.2 of the plugin protocol (www.audioscrobbler.net/development/protocol/) is currently used.
Usage
require "audioscrobbler"
scrob = Audioscrobbler.new("user", # Audioscrobbler username
"pass", # Audioscrobbler password
"queue.txt", # file for storing queue
)
# Replace these with the client ID that's been assigned to your
# plugin by the Audioscrobbler folks and your plugin's version number.
scrob.client = "tst"
scrob.version = "1.0"
# If you don't start the submitter, tracks will just pile up in the
# submission queue.
scrob.start_submitter_thread
# Report the currently-playing song:
scrob.("Beach Boys", # artist
"God Only Knows", # title
175, # track length, in seconds
"Pet Sounds", # album (optional)
"", # MusicBrainzID (optional)
"8", # track number (optional)
)
# Now wait until the Audioscrobbler submission criteria has been met and
# the track has finished playing.
# And then queue the track for submission:
scrob.enqueue("Beach Boys", # artist
"God Only Knows", # title
175, # track length, in seconds
1125378558, # track start time
"Pet Sounds", # album (optional)
"", # MusicBrainzID (optional)
"8", # track number (optional)
)
Defined Under Namespace
Classes: SubmissionQueue
Constant Summary collapse
- DEFAULT_HANDSHAKE_URL =
Default URL to connect to for the handshake.
"http://post.audioscrobbler.com/"
- DEFAULT_SUBMIT_INTERVAL_SEC =
Default minimum interval to wait between successful submissions.
5
- DEFAULT_CLIENT =
Default plugin name and version to report to the server. You MUST set these to the values that you’ve registered before you can distribute your plugin to anyone (including beta testers).
"tst"
- DEFAULT_VERSION =
"1.0"
- MAX_TRACKS_IN_SUBMISSION =
Maximum number of tracks that will be accepted in a single submission. This is a server-imposed limit.
10
Instance Attribute Summary collapse
-
#client ⇒ Object
Returns the value of attribute client.
-
#handshake_url ⇒ Object
Returns the value of attribute handshake_url.
-
#logger ⇒ Object
Returns the value of attribute logger.
-
#password ⇒ Object
Returns the value of attribute password.
-
#username ⇒ Object
Returns the value of attribute username.
-
#version ⇒ Object
Returns the value of attribute version.
Instance Method Summary collapse
-
#enqueue(artist, title, length, start_time, album = "", mbid = "", track_num = nil) ⇒ Object
Enqueue a track for submission.
-
#initialize(username, password, filename = nil, proxy = nil) ⇒ Audioscrobbler
constructor
Create a new Audioscrobbler object.
-
#report_now_playing(artist, title, length, album = "", mbid = "", track_num = '') ⇒ Object
Report the track that is currently playing.
-
#start_submitter_thread ⇒ Object
Start the submitter thread and return.
- #verbose ⇒ Object
-
#verbose=(v) ⇒ Object
Backwards-compatability methods to control the logging level.
Constructor Details
#initialize(username, password, filename = nil, proxy = nil) ⇒ Audioscrobbler
Create a new Audioscrobbler object.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/audioscrobbler.rb', line 124 def initialize(username, password, filename=nil, proxy=nil) @username = username @password = password @queue = SubmissionQueue.new(filename) @logger = Logger.new($stdout) @client = DEFAULT_CLIENT @version = DEFAULT_VERSION @handshake_url = DEFAULT_HANDSHAKE_URL @last_handshake_time = nil @handshake_backoff_sec = 0 @hard_failures = 0 @session_id = nil @submit_url = nil @now_playing_url = nil @submit_interval_sec = DEFAULT_SUBMIT_INTERVAL_SEC @proxy = {} if proxy uri = URI.parse(proxy) @proxy[:host], @proxy[:port] = uri.host, uri.port if uri.userinfo @proxy[:username], @proxy[:password] = uri.userinfo.split(':', 1) end end end |
Instance Attribute Details
#client ⇒ Object
Returns the value of attribute client.
152 153 154 |
# File 'lib/audioscrobbler.rb', line 152 def client @client end |
#handshake_url ⇒ Object
Returns the value of attribute handshake_url.
152 153 154 |
# File 'lib/audioscrobbler.rb', line 152 def handshake_url @handshake_url end |
#logger ⇒ Object
Returns the value of attribute logger.
151 152 153 |
# File 'lib/audioscrobbler.rb', line 151 def logger @logger end |
#password ⇒ Object
Returns the value of attribute password.
151 152 153 |
# File 'lib/audioscrobbler.rb', line 151 def password @password end |
#username ⇒ Object
Returns the value of attribute username.
151 152 153 |
# File 'lib/audioscrobbler.rb', line 151 def username @username end |
#version ⇒ Object
Returns the value of attribute version.
152 153 154 |
# File 'lib/audioscrobbler.rb', line 152 def version @version end |
Instance Method Details
#enqueue(artist, title, length, start_time, album = "", mbid = "", track_num = nil) ⇒ Object
Enqueue a track for submission. Returns true if track was successfully queued and false otherwise (currently, it only fails if the track’s metadata didn’t meet the Audioscrobbler submission rules).
456 457 458 459 460 461 462 463 464 |
# File 'lib/audioscrobbler.rb', line 456 def enqueue(artist, title, length, start_time, album="", mbid="", track_num=nil) if not (artist, title, length, start_time) @logger.warn("Ignoring track with invalid metadata for submission") return false end @queue.append(artist, title, length, start_time, album, mbid, track_num) true end |
#report_now_playing(artist, title, length, album = "", mbid = "", track_num = '') ⇒ Object
Report the track that is currently playing. Returns true if the report was successful and false otherwise.
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
# File 'lib/audioscrobbler.rb', line 384 def (artist, title, length, album="", mbid="", track_num='') @logger.debug("Reporting \"#{artist} - #{title}\" as now-playing") if not (artist, title, length, Time.now.to_i) @logger.warn("Ignoring track with invalid metadata for now-playing") return false end # FIXME(derat): Huge race condition here between us and the submission # thread, but I am to lazy to fix it right now. if not @session_id do_handshake(false) end # Construct our argument list. args = { 's' => @session_id, 'a' => artist, 't' => title, 'b' => album, 'l' => length.to_i, 'n' => track_num, 'm' => mbid, } # Convert it into a single escaped string for the body. body = args.collect {|k, v| "#{k}=" + CGI.escape(v.to_s) }.join('&') success = false begin url = URI.parse(@now_playing_url) rescue Exception @logger.warn("Submission failed -- couldn't parse now-playing " + "URL \"#@now_playing_url\"") else headers = { 'Content-Type' => 'application/x-www-form-urlencoded' } begin data = Net::HTTP.start(url.host, url.port, @proxy[:host], @proxy[:port], @proxy[:username], @proxy[:password]) do |http| http.post(url.path, body, headers).body end rescue Exception @logger.warn("Submission failed -- couldn't read " \ "#@now_playing_url: #{$!}") else data.chomp! if data == "OK" @logger.debug("Now-playing report was successful") success = true else @logger.warn("Now-playing report failed -- got \"#{data}\"") end end end success end |
#start_submitter_thread ⇒ Object
Start the submitter thread and return.
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/audioscrobbler.rb', line 299 def start_submitter_thread @submit_thread = Thread.new do while true # Wait until there are some tracks in the queue. tracks = @queue.peek(MAX_TRACKS_IN_SUBMISSION) # Keep trying to handshake until we're successful. do_handshake while not @session_id # Might as well re-check in case more tracks have shown up # during the handshake. tracks = @queue.peek(MAX_TRACKS_IN_SUBMISSION) @logger.debug("Submitting #{tracks.length} track(s)") # Construct our argument list. args = { "s" => @session_id } for i in 0..tracks.length-1 args.update({ "a[#{i}]" => tracks[i].artist, "t[#{i}]" => tracks[i].title, "i[#{i}]" => Time.at(tracks[i].start_time).to_i, "o[#{i}]" => 'P', "r[#{i}]" => '', "l[#{i}]" => tracks[i].length.to_s, "b[#{i}]" => tracks[i].album, "n[#{i}]" => tracks[i].track_num, "m[#{i}]" => tracks[i].mbid, }) end # Convert it into a single escaped string for the body. body = args.collect {|k, v| "#{k}=" + CGI.escape(v.to_s) }.join('&') begin url = URI.parse(@submit_url) headers = { 'Content-Type' => 'application/x-www-form-urlencoded' } data = Net::HTTP.start(url.host, url.port, @proxy[:host], @proxy[:port], @proxy[:username], @proxy[:password]) do |http| http.post(url.path, body, headers).body end rescue Exception @logger.warn("Submission failed -- couldn't read " \ "#@submit_url: #{$!}") else # Check whether the submission was successful. lines = data.split("\n") if not lines[0] @logger.warn("Submission failed -- got empty response") elsif lines[0] == "OK" @logger.debug("Submission was successful") @queue.delete(tracks.length) elsif lines[0] == "BADSESSION" @logger.warn("Submission failed -- session is invalid") # Unset the session ID so we'll re-handshake. @session_id = nil else @logger.warn("Submission failed -- got unknown response " \ "\"#{lines[0]}\"") @hard_failures += 1 end end if @hard_failures >= 3 @logger.warn("Got #@hard_failures failures; re-handshaking") @session_id = nil end @logger.debug("Sleeping #@submit_interval_sec sec before checking " \ "for more tracks") sleep(@submit_interval_sec) end end end |
#verbose ⇒ Object
159 160 161 |
# File 'lib/audioscrobbler.rb', line 159 def verbose @logger.sev_threshold == Logger::DEBUG end |
#verbose=(v) ⇒ Object
Backwards-compatability methods to control the logging level.
156 157 158 |
# File 'lib/audioscrobbler.rb', line 156 def verbose=(v) @logger.sev_threshold = v ? Logger::DEBUG : Logger::WARN end |