class PartyGoerClient

Connects to a partygoer instance, performs an initial poll, and then polls every 5 minutes. In between polls the client stays in sync using faye to recieve push updates

Attributes

played[R]
playing[R]
queued[R]

Public Class Methods

new(server) click to toggle source
# File lib/partygoer-client.rb, line 21
def initialize(server)
  @server = server

  @played = []
  @queued = []
  @playing = nil

  @played_lock = Mutex.new
  @queued_lock = Mutex.new
  @playing_lock = Mutex.new

  @event_loop = nil

  @api = RestClient::Resource.new("#{@server}/api/queue", headers:
             { content_type: 'application/json', accept: 'application/json' })
  @client = Faye::Client.new("#{@server}:9292/faye")
  logger.info { "Client initialized with server: #{server}" }
  @is_playing = false
  start
end

Public Instance Methods

currently_queued() click to toggle source
# File lib/partygoer-client.rb, line 105
def currently_queued
  queued
end
logger() click to toggle source
# File lib/partygoer-client.rb, line 113
def logger
  @@logger
end
now_playing() click to toggle source

Aliases

# File lib/partygoer-client.rb, line 101
def now_playing
  playing
end
playing=(track) click to toggle source
# File lib/partygoer-client.rb, line 42
def playing=(track)
  payload = { track: track }
  @api['/playing'].put(payload)
  logger.info { "Set now playing to #{track['name']}" }
rescue
  logger.error {
    "Could not set now playing to #{track.inspect}:\n\t#{$!}\n\t#{$@.join("\n\t")}"
  }
end
recently_played() click to toggle source
# File lib/partygoer-client.rb, line 109
def recently_played
  played
end
skip?() click to toggle source
# File lib/partygoer-client.rb, line 65
def skip?
  if @playing['num_upvotes'] == 0
    @playing['num_downvotes'] >= 2
  else
    @playing['num_downvotes'] >= (2 * @playing['num_upvotes'])
  end
rescue
  logger.error("Failed to check skip criteria:\n#{$!}\n#{$@.join("\n\t")}")
end
start() click to toggle source
# File lib/partygoer-client.rb, line 81
def start
  pull_update
  return if @event_loop
  logger.info { 'Starting event loop' }
  start_event_loop

  at_exit do
    stop
  end
end
stop() click to toggle source
# File lib/partygoer-client.rb, line 92
def stop
  return unless @event_loop
  logger.info { 'Stopping event loop' }
  EM.stop_event_loop
  @event_loop.kill
  @event_loop = nil
end
suggest(track) click to toggle source
# File lib/partygoer-client.rb, line 52
def suggest(track)
  case track
  when String
    payload = { track: { spotify_uri: track } }
  when Hash
    payload = { track: track }
  end
  EM.defer(proc { @api['/tracks'].post(payload) })
  logger.info { "Suggested #{track.inspect}" }
rescue
  logger.error("Failed to suggest track: #{track.inspect}\n#{$!}\n#{$@.join("\n\t")}")
end
up_next() click to toggle source
# File lib/partygoer-client.rb, line 75
def up_next
  @queued[1]
rescue
  logger.error("Failed to check up next:\n#{$!}\n#{$@.join("\n\t")}")
end

Private Instance Methods

on_created(track) click to toggle source
# File lib/partygoer-client.rb, line 167
def on_created(track)
  proc {
    logger.debug { "Track created: #{track.inspect}" }
    begin
      @queued_lock.synchronize{
        @queued << track
      }
    rescue
      logger.error { "Error in on_created:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
    ensure
      sort!
    end
  }
end
on_next(track) click to toggle source
# File lib/partygoer-client.rb, line 182
def on_next(track)
  proc {
    logger.debug { "Next track called: #{track.inspect}" }
    begin
      @playing_lock.synchronize {
        @playing['playing'] = false
        @played_lock.synchronize { @played << @playing }
        @queued_lock.synchronize {
          @queued.delete_if {
             |item| item['id'] == @playing['id']
          }
          index = @queued.find_index {
            |item| item['id'] == track['id']
          }
          @queued[index] = track
        }
        @playing = track
      }
    rescue
      logger.error { "Error in on_next:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
    ensure
      sort!
    end
  }
end
on_voted(track) click to toggle source
# File lib/partygoer-client.rb, line 149
def on_voted(track)
  proc {
    logger.debug { "Track voted on: #{track.inspect}" }
    begin
      @queued_lock.synchronize{
        index = @queued.find_index {
          |item| item['id'] == track['id']
        }
        @queued[index] = track
      }
    rescue
      logger.error { "Error in on_voted:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
    ensure
      sort!
    end
  }
end
pull_update() click to toggle source
# File lib/partygoer-client.rb, line 208
def pull_update
  logger.info { "Polling for update" }
  begin
    body = JSON.parse(@api['/recently_played'].get.body)
    @played_lock.synchronize { @played = body }
  rescue
    logger.error { "Could not fetch recently played:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
  end
  begin
    body = JSON.parse(@api['/tracks'].get.body)
    @queued_lock.synchronize { @queued = body }
  rescue
    logger.error { "Could not fetch queue:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
  end
  begin
    body = JSON.parse(@api['/playing'].get.body)
    @playing_lock.synchronize { @playing = body }
  rescue
    logger.error { "Could not fetch now playing:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
  end
  sort!
end
sort!() click to toggle source
# File lib/partygoer-client.rb, line 231
def sort!
  begin
    @played_lock.synchronize{
      @played.sort_by! { |track| track['id'] }
    }
    logger.debug { 'Sorted recently played' }
  rescue
    logger.error { "Could not sort recently played:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
  end
  begin
    @queued_lock.synchronize{
      @queued.sort_by! { |track|
        i = track['playing'] ? 0 : 1
        [i, -1 * track['score'], track['id']]
      }
    }
    logger.debug { 'Sorted queued' }
  rescue
    logger.error { "Could not sort the queue:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
  end
  begin
    @playing_lock.synchronize{
      @playing = @queued.select { |track| track['playing'] }.first
    }
    logger.debug { 'Updated now playing' }
  rescue
    logger.error { "Could not update now playing:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
  end
end
start_event_loop() click to toggle source
# File lib/partygoer-client.rb, line 118
def start_event_loop
  @event_loop = Thread.new do
    begin
      EM.run do
        EM.add_periodic_timer(300) { EM.defer { pull_update } }

        @client.subscribe('/track/vote') do |message|
          EM.defer(on_voted(message))
        end

        @client.subscribe('/track/create') do |message|
          EM.defer(on_created(message))
        end

        @client.subscribe('/queue/next') do |message|
          EM.defer(on_next(message))
        end
      end
    rescue
      logger.critical { "Could not start event loop:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
      stop
      raise $!
    end
  end
rescue
  logger.critical { "Could not start event loop:\n\t#{$!}\n\t#{$@.join("\n\t")}" }
  stop
  raise $!
end