class DACPClient::Client

The Client class handles communication with the server

Constants

DEFAULT_HEADERS

Attributes

guid[W]
host[R]
hsgid[RW]
name[R]
port[R]
session_id[R]

Public Class Methods

new(name, host = 'localhost', port = 3689) click to toggle source
# File lib/dacpclient/client.rb, line 39
def initialize(name, host = 'localhost', port = 3689)
  @name = name
  @host = host
  @port = port

  @session_id = nil
  @hsgid = nil
  @media_revision = 1
  setup_connection
end

Public Instance Methods

albums(db = default_db) click to toggle source
# File lib/dacpclient/client.rb, line 278
def albums(db = default_db)
  url = "databases/#{db.item_id}/groups"
  meta = 'dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist,daap.songalbumartist'
  query = "('daap.songartist!:'+('com.apple.itunes.extended-media-kind:1','com.apple.itunes.extended-media-kind:32'))"
  do_action(url, meta: meta, type: 'music', :'group-type' => 'albums', sort: 'artist', :'include-sort-headers' => 1, query: query, clean_url: true, model: Albums).items
end
artists(db = default_db) click to toggle source
# File lib/dacpclient/client.rb, line 271
def artists(db = default_db)
  url = "databases/#{db.item_id}/groups"
  meta = 'dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist,daap.groupalbumcount,daap.songartistid'
  query = "('daap.songartist!:'+('com.apple.itunes.extended-media-kind:1','com.apple.itunes.extended-media-kind:32'))"
  do_action(url, meta: meta, type: 'music', :'group-type' => 'artists', sort: 'album', :'include-sort-headers' => 1, query: query, clean_url: true, model: Artists).items
end
artwork(database, id, width = 320, height = 320) click to toggle source
# File lib/dacpclient/client.rb, line 229
def artwork(database, id, width = 320, height = 320)
  url = "databases/#{database}/items/#{id}/extra_data/artwork"
  do_action(url, { mw: width, mh: height }, clean_url: true)
end
clear_queue() click to toggle source
# File lib/dacpclient/client.rb, line 195
def clear_queue
  do_action('playqueue-edit', command: 'clear')
end
content_codes() click to toggle source
# File lib/dacpclient/client.rb, line 106
def content_codes
  do_action('content-codes', clean_url: true)
end
ctrl_int() click to toggle source
# File lib/dacpclient/client.rb, line 180
def ctrl_int
  do_action('ctrl-int', clean_url: true)
end
cue(command, query, index = 0) click to toggle source
# File lib/dacpclient/client.rb, line 149
def cue(command, query, index = 0)
  do_action(:cue, command: command, query: query, index: index)
end
databases() click to toggle source
# File lib/dacpclient/client.rb, line 203
def databases
  meta = %w(dmap.itemname dmap.itemcount dmap.itemid dmap.persistentid
            daap.baseplaylist com.apple.itunes.special-playlist
            com.apple.itunes.smart-playlist com.apple.itunes.saved-genius
            dmap.parentcontainerid dmap.editcommandssupported).join(',')
  do_action('databases', clean_url: true, meta: meta, model: Databases)
end
default_db() click to toggle source
# File lib/dacpclient/client.rb, line 221
def default_db
  databases.items.find { |item| item.default_db == 1 }
end
default_playlist(db = default_db) click to toggle source
# File lib/dacpclient/client.rb, line 225
def default_playlist(db = default_db)
  playlists(db).find(&:base_playlist?)
end
guid() click to toggle source
# File lib/dacpclient/client.rb, line 62
def guid
  return @guid unless @guid.nil?
  d = Digest::SHA2.hexdigest(@name)
  d[0..15].upcase
end
list_queue() click to toggle source
# File lib/dacpclient/client.rb, line 199
def list_queue
  do_action('playqueue-contents', model: PlayQueue)
end
login() click to toggle source
# File lib/dacpclient/client.rb, line 78
def login
  response = nil
  if @hsgid.nil?
    pairing_guid = '0x' + guid
    response = do_action(:login,  :'pairing-guid' => pairing_guid)
  else
    response = do_action(:login, hasFP: 1)
  end
  @session_id = response[:mlid]
  response
end
logout() click to toggle source
# File lib/dacpclient/client.rb, line 184
def logout
  do_action(:logout)
  @media_revision = 1
  @session_id = nil
end
now_playing_artwork(width = 320, height = 320) click to toggle source
# File lib/dacpclient/client.rb, line 234
def now_playing_artwork(width = 320, height = 320)
  do_action(:nowplayingartwork, mw: width, mh: height)
end
pair(pin) click to toggle source
# File lib/dacpclient/client.rb, line 68
def pair(pin)
  pairingserver = PairingServer.new(name, guid)
  pairingserver.pin = pin
  pairingserver.start
end
pair_and_login(pin = nil) click to toggle source
# File lib/dacpclient/client.rb, line 90
def pair_and_login(pin = nil)
  login
rescue DACPForbiddenError, Faraday::ConnectionFailed => e
  pin = 4.times.map { Random.rand(10) } if pin.nil?
  if e.instance_of? DACPForbiddenError
    message = e.result.status
  else
    message = e
  end
  warn "#{message} error: Cannot login, starting pairing process"
  warn "Pincode: #{pin}"
  @host = pair(pin).host
  setup_connection
  retry
end
playlists(db = default_db) click to toggle source
# File lib/dacpclient/client.rb, line 211
def playlists(db = default_db)
  meta = %w(dmap.itemname dmap.itemcount dmap.itemid dmap.persistentid
            daap.baseplaylist com.apple.itunes.special-playlist
            com.apple.itunes.smart-playlist com.apple.itunes.saved-genius
            dmap.parentcontainerid dmap.editcommandssupported).join(',')
  do_action("databases/#{db.item_id}/containers", clean_url: true,
                                                  meta: meta,
                                                  model: Playlists).items
end
playspec(playlist, db = default_db) click to toggle source
# File lib/dacpclient/client.rb, line 153
def playspec(playlist, db = default_db)
  dbspec = "'dmap.persistentid:0x#{db.persistent_id.to_s(16)}'"
  cspec = "'dmap.persistentid:0x#{playlist.persistent_id.to_s(16)}'"
  # don't worry about playing item's from playlists yet..
  # container-item-spec='dmap.containeritemid:%s'
  do_action(:playspec, database_spec: dbspec,
                       container_spec: cspec)
end
position() click to toggle source
# File lib/dacpclient/client.rb, line 119
def position
  response = do_action(:getproperty, properties: 'dacp.playingtime')
  response.cast? ? (response['cast'] - response['cant']) : 0
end
position=(ms)
Alias for: seek
queue(id) click to toggle source
# File lib/dacpclient/client.rb, line 190
def queue(id)
  do_action('playqueue-edit', command: 'add',
                              query: "\'dmap.itemid:#{id}\'")
end
repeat() click to toggle source
# File lib/dacpclient/client.rb, line 162
def repeat
  response = do_action(:getproperty, properties: 'dacp.repeatstate')
  response[:carp]
end
repeat=(repeatstate) click to toggle source
# File lib/dacpclient/client.rb, line 167
def repeat=(repeatstate)
  set_property('dacp.repeatstate', repeatstate)
end
seek(ms) click to toggle source
# File lib/dacpclient/client.rb, line 115
def seek(ms)
  set_property('dacp.playingtime', ms)
end
Also aliased as: position=
serverinfo() click to toggle source
# File lib/dacpclient/client.rb, line 74
def serverinfo
  do_action('server-info', clean_url: true)
end
shuffle() click to toggle source
# File lib/dacpclient/client.rb, line 171
def shuffle
  response = do_action(:getproperty, properties: 'dmcp.shufflestate')
  response[:cash]
end
shuffle=(shufflestate) click to toggle source
# File lib/dacpclient/client.rb, line 176
def shuffle=(shufflestate)
  set_property('dmcp.shufflestate', shufflestate)
end
status(wait = false) click to toggle source
# File lib/dacpclient/client.rb, line 126
def status(wait = false)
  revision = wait ? @media_revision : 1
  result = do_action(:playstatusupdate, :'revision-number' => revision,
                                        model: Status)
  @media_revision = result.media_revision
  result
rescue Faraday::Error::TimeoutError => e
  if wait
    retry
  else
    raise e
  end
end
track_length() click to toggle source
# File lib/dacpclient/client.rb, line 110
def track_length
  response = do_action(:getproperty, properties: 'dacp.playingtime')
  response.cast? ? response['cast'] : 0
end
volume() click to toggle source
# File lib/dacpclient/client.rb, line 140
def volume
  response = do_action(:getproperty, properties: 'dmcp.volume')
  response[:cmvo]
end
volume=(volume) click to toggle source
# File lib/dacpclient/client.rb, line 145
def volume=(volume)
  set_property('dmcp.volume', volume)
end

Private Instance Methods

do_action(action, params = {}) click to toggle source
# File lib/dacpclient/client.rb, line 298
def do_action(action, params = {})
  clean_url = params.delete(:clean_url) || false
  model = params.delete(:model)

  action = '/' + action.to_s
  unless @session_id.nil?
    params['session-id'] = @session_id.to_s
    action = '/ctrl-int/1' + action unless clean_url
  end

  params['hsgid'] = @hsgid unless @hsgid.nil?
  params = filter_param_keys(params)
  result = @client.get do |request|
    request.options.params_encoder = Faraday::FlatterParamsEncoder
    request.url action
    request.params = params
    request.headers.merge!(DEFAULT_HEADERS)
  end

  parse_result result, model
end
filter_param_keys(params) click to toggle source
# File lib/dacpclient/client.rb, line 320
def filter_param_keys(params)
  Hash[params.map { |k, v| [k.to_s.tr('_', '-'), v] }]
end
parse_result(result, model) click to toggle source
# File lib/dacpclient/client.rb, line 324
def parse_result(result, model)
  if !result.success?
    fail DACPForbiddenError, result
  elsif result.headers['Content-Type'] == 'application/x-dmap-tagged' && result.body.length > 0
    res = DMAPParser::Parser.parse(result.body)
    model ? model.new(res) : res
  else
    result.body
  end
end
set_property(property, value) click to toggle source
# File lib/dacpclient/client.rb, line 294
def set_property(property, value)
  do_action(:setproperty, property => value)
end
setup_connection() click to toggle source
# File lib/dacpclient/client.rb, line 287
def setup_connection
  @uri = URI::HTTP.build(host: @host, port: @port)
  Faraday::Utils.default_params_encoder = Faraday::FlatterParamsEncoder
  @client = Faraday.new(@uri.to_s)
  @client.use FaradayMiddleware::Gzip
end