Class: DACPClient::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/dacpclient/client.rb

Overview

The Client class handles communication with the server

Constant Summary collapse

HOME_SHARING_HOST =
'https://homesharing.itunes.apple.com'
HOME_SHARING_PATH =
'/WebObjects/MZHomeSharing.woa/wa/getShareIdentifiers'
DEFAULT_HEADERS =
{
  'Viewer-Only-Client' => '1',
  # 'Accept-Encoding' => 'gzip',
  'Connection' => 'keep-alive',
  'User-Agent' => 'RubyDACPClient/' + VERSION
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, host = 'localhost', port = 3689) ⇒ Client

Returns a new instance of Client.



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/dacpclient/client.rb', line 28

def initialize(name, host = 'localhost', port = 3689)
  @client = Net::HTTP.new(host, port)
  @name = name
  @host = host
  @port = port

  @session_id = nil
  @hsgid = nil
  @mediarevision = 1
  @uri = URI::HTTP.build(host: @host, port: @port)
  @client = Faraday.new(url: @uri.to_s)
end

Instance Attribute Details

#guidObject

Returns the value of attribute guid.



15
16
17
# File 'lib/dacpclient/client.rb', line 15

def guid
  @guid
end

#hostObject (readonly)

Returns the value of attribute host.



16
17
18
# File 'lib/dacpclient/client.rb', line 16

def host
  @host
end

#hsgidObject

Returns the value of attribute hsgid.



15
16
17
# File 'lib/dacpclient/client.rb', line 15

def hsgid
  @hsgid
end

#nameObject (readonly)

Returns the value of attribute name.



16
17
18
# File 'lib/dacpclient/client.rb', line 16

def name
  @name
end

#portObject (readonly)

Returns the value of attribute port.



16
17
18
# File 'lib/dacpclient/client.rb', line 16

def port
  @port
end

#session_idObject (readonly)

Returns the value of attribute session_id.



16
17
18
# File 'lib/dacpclient/client.rb', line 16

def session_id
  @session_id
end

Instance Method Details

#artwork(database, id, width = 320, height = 320) ⇒ Object



203
204
205
206
# File 'lib/dacpclient/client.rb', line 203

def artwork(database, id, width = 320, height = 320)
  url = "databases/#{database}/items/#{id}/extra_data/artwork"
  do_action(url, { mw: width, mh: height }, true)
end

#clear_queueObject



179
180
181
# File 'lib/dacpclient/client.rb', line 179

def clear_queue
  do_action('playqueue-edit', command: 'clear')
end

#content_codesObject



104
105
106
# File 'lib/dacpclient/client.rb', line 104

def content_codes
  do_action('content-codes', {}, true)
end

#ctrl_intObject



164
165
166
# File 'lib/dacpclient/client.rb', line 164

def ctrl_int
  do_action('ctrl-int', {}, true)
end

#databasesObject



187
188
189
# File 'lib/dacpclient/client.rb', line 187

def databases
  do_action('databases', {}, true)
end

#default_dbObject



195
196
197
# File 'lib/dacpclient/client.rb', line 195

def default_db
  databases[:mlcl].to_a.find { |item| item.mdbk == 1 }
end

#default_playlist(db) ⇒ Object



199
200
201
# File 'lib/dacpclient/client.rb', line 199

def default_playlist(db)
  @client.playlists(72).mlcl.to_a.find { |item| item.abpl }
end

#list_queueObject



183
184
185
# File 'lib/dacpclient/client.rb', line 183

def list_queue
  do_action('playqueue-contents')
end

#loginObject



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/dacpclient/client.rb', line 82

def 
  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

#logoutObject



168
169
170
171
172
# File 'lib/dacpclient/client.rb', line 168

def logout
  do_action(:logout)
  @mediarevision = 1
  @session_id = nil
end

#now_playing_artwork(width = 320, height = 320) ⇒ Object



208
209
210
# File 'lib/dacpclient/client.rb', line 208

def now_playing_artwork(width = 320, height = 320)
  do_action(:nowplayingartwork, mw: width, mh: height)
end

#pair(pin) ⇒ Object



72
73
74
75
76
# File 'lib/dacpclient/client.rb', line 72

def pair(pin)
  pairingserver = PairingServer.new(self, '0.0.0.0', 1024)
  pairingserver.pin = pin
  pairingserver.start
end

#pair_and_login(pin = nil) ⇒ Object



94
95
96
97
98
99
100
101
102
# File 'lib/dacpclient/client.rb', line 94

def (pin = nil)
  
rescue DACPForbiddenError => e
  pin = 4.times.map { Random.rand(10) } if pin.nil?
  warn "#{e.result.status} error: Cannot login, starting pairing process"
  warn "Pincode: #{pin}"
  pair(pin)
  retry
end

#playlists(db) ⇒ Object



191
192
193
# File 'lib/dacpclient/client.rb', line 191

def playlists(db)
  do_action("databases/#{db}/containers", {}, true)
end

#positionObject



117
118
119
120
# File 'lib/dacpclient/client.rb', line 117

def position
  response = do_action(:getproperty, properties: 'dacp.playingtime')
  response.cast? ? (response['cast'] - response['cant']) : 0
end

#queue(id) ⇒ Object



174
175
176
177
# File 'lib/dacpclient/client.rb', line 174

def queue(id)
  do_action('playqueue-edit', command: 'add',
                              query: "\'dmap.itemid:#{id}\'")
end

#repeatObject



146
147
148
149
# File 'lib/dacpclient/client.rb', line 146

def repeat
  response = do_action(:getproperty, properties: 'dacp.repeatstate')
  response[:carp]
end

#repeat=(repeatstate) ⇒ Object



151
152
153
# File 'lib/dacpclient/client.rb', line 151

def repeat=(repeatstate)
  do_action(:setproperty, 'dacp.repeatstate' => repeatstate)
end

#search(db, container, search, type = nil) ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/dacpclient/client.rb', line 212

def search(db, container, search, type = nil)
  search = URI.escape(search)
  types = {
    title: 'dmap.itemname',
    artist: 'daap.songartist',
    album: 'daap.songalbum',
    genre: 'daap.songgenre',
    composer: 'daap.songcomposer'
  }
  queries = []
  type = types.keys if type.nil?
  Array(type).each do |t|
    queries << "'#{types[t]}:#{search}'"
  end

  q = queries.join(',')
  meta  = %w(dmap.itemname dmap.itemid daap.songartist daap.songalbumartist
             daap.songalbum com.apple.itunes.cloud-id dmap.containeritemid
             com.apple.itunes.has-video com.apple.itunes.itms-songid
             com.apple.itunes.extended-media-kind dmap.downloadstatus
             daap.songdisabled).join(',')

  url = "databases/#{db}/containers/#{container}/items"
  do_action(url, { type: 'music', sort: 'album', query: q, meta: meta },
            true)
end

#seek(ms) ⇒ Object Also known as: position=



113
114
115
# File 'lib/dacpclient/client.rb', line 113

def seek(ms)
  do_action(:setproperty, 'dacp.playingtime' => ms)
end

#serverinfoObject



78
79
80
# File 'lib/dacpclient/client.rb', line 78

def serverinfo
  do_action('server-info', {}, true)
end

#setup_home_sharing(user, password) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/dacpclient/client.rb', line 53

def setup_home_sharing(user, password)
  hs_client = Faraday.new(url: HOME_SHARING_HOST)
  result = hs_client.post do |request|
    request.url HOME_SHARING_PATH
    request.headers['Content-Type'] = 'text/xml'
    request.headers.merge!(DEFAULT_HEADERS)
    request.body = { 'appleId' => user, 'guid' => 'empty',
                     'password' => password }.to_plist
  end
  response = Plist.parse_xml(result.body)
  @hsgid = response['sgid']
end

#shuffleObject



155
156
157
158
# File 'lib/dacpclient/client.rb', line 155

def shuffle
  response = do_action(:getproperty, properties: 'dmcp.shufflestate')
  response[:cash]
end

#shuffle=(shufflestate) ⇒ Object



160
161
162
# File 'lib/dacpclient/client.rb', line 160

def shuffle=(shufflestate)
  do_action(:setproperty, 'dmcp.shufflestate' => shufflestate)
end

#status(wait = false) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/dacpclient/client.rb', line 124

def status(wait = false)
  revision = wait ? @mediarevision : 1
  result = do_action(:playstatusupdate, 'revision-number' => revision)
  @mediarevision = result[:cmsr]
  result
rescue Faraday::Error::TimeoutError => e
  if wait
    retry
  else
    raise e
  end
end

#track_lengthObject



108
109
110
111
# File 'lib/dacpclient/client.rb', line 108

def track_length
  response = do_action(:getproperty, properties: 'dacp.playingtime')
  response.cast? ? response['cast'] : 0
end

#volumeObject



137
138
139
140
# File 'lib/dacpclient/client.rb', line 137

def volume
  response = do_action(:getproperty, properties: 'dmcp.volume')
  response[:cmvo]
end

#volume=(volume) ⇒ Object



142
143
144
# File 'lib/dacpclient/client.rb', line 142

def volume=(volume)
  do_action(:setproperty, 'dmcp.volume' => volume)
end