dacpclient 0.2.11 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cf81a474e862f6f8c2bde6e929bc29a693b701df
4
- data.tar.gz: dfaa5045aded28a3ab95474c938759c1e371af78
3
+ metadata.gz: 0168ee84709980d66f477982404c0d5cddbb2f92
4
+ data.tar.gz: e105474bc17e7127c0d0f9427219152df7a83d0f
5
5
  SHA512:
6
- metadata.gz: af0b53555a31b9ad99cc4e3b334607520fa3a9225bd0da7c155abd9b919ba0fcea4cf43bc2949bf056b5969c77595eb14d8ca79ca9df557e3ffe2c998cce0cf8
7
- data.tar.gz: a16eb079d959501146eeb3aadb8607c1233ff6f9fa1e94df2455995a09796da1d1fd1a146372deed65047e158a076e44936fa47489b7244477e1043f4c786f62
6
+ metadata.gz: 508242daaf50dacc3c003d4e0d033ea0ed06df405dae77ce27d2ab34e3d0199e10a77143a1ab7b0fb1214b210873f5b1c29708616dcba7562109c984142064cc
7
+ data.tar.gz: f2f2dfd1c2238ae09d217ce4128032bbb174ef2e027042d687c5b8a6c8869ac1493002ee42f6df3dde07274f747fc450064f83da8b43155295d5c725c36e77ae
data/Gemfile CHANGED
@@ -3,4 +3,4 @@ gemspec
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gem 'minitest', '~> 5.3.1'
6
- gem 'rubocop', '~> 0.20.0'
6
+ gem 'rubocop', '~> 0.21.0'
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  A DACP (iTunes Remote protocol) client written in the wonderful Ruby language.
6
6
  You can use this for controlling iTunes. It uses the same protocol as the iTunes remote iOS app.
7
7
 
8
- You can control iTunes by connecting and entering a pin, or with Home Sharing. DACPClient supports both methods.
8
+ You can control iTunes by connecting and entering a pin.
9
9
 
10
10
  Look at the [bin/dacpclient](https://github.com/jurriaan/ruby-dacpclient/blob/master/bin/dacpclient) file for an example client.
11
11
 
@@ -29,33 +29,25 @@ Or install it yourself using:
29
29
 
30
30
  See [bin/dacpclient](https://github.com/jurriaan/ruby-dacpclient/blob/master/bin/dacpclient)
31
31
 
32
- Usage: dacpclient [command]
32
+ DACPClient v0.3.0
33
33
  (c) 2014 Jurriaan Pruis <email@jurriaanpruis.nl>
34
34
 
35
- Where command is one of the following:
36
- status
37
- status_ticker
38
- home_sharing
39
- play
40
- pause
41
- playpause
42
- next
43
- prev
44
- databases
45
- playqueue
46
- upnext
47
- stop
48
- debug
49
- usage
50
- previous
51
- help
52
-
53
- ## Todo
54
-
55
- - Use bonjour
56
- - Add tests
57
- - Add more tagdefinitions
58
- - Documentation
35
+ DACPClient commands:
36
+ dacpclient help # Display all possible commands
37
+ dacpclient help [COMMAND] # Describe available commands or one specific command
38
+ dacpclient hostname # Set the hostname
39
+ dacpclient next # Go to next item
40
+ dacpclient pause # Pause Playing
41
+ dacpclient play # Start playing
42
+ dacpclient play_playlist # Plays a playlist
43
+ dacpclient playlists # Show the playlists
44
+ dacpclient playpause # Toggle Playing
45
+ dacpclient prev # Go to previous item
46
+ dacpclient status # Shows the status of the DACP server
47
+ dacpclient stop # Stop playing
48
+ dacpclient upnext # Show what's up next
49
+ dacpclient version # Show DACPClient Version
50
+ dacpclient volume # Get or set volume
59
51
 
60
52
  ## Contributing
61
53
 
data/bin/dacpclient CHANGED
@@ -6,11 +6,13 @@ require 'socket'
6
6
  require 'yaml'
7
7
  require 'fileutils'
8
8
  require 'io/console'
9
+ require 'fuzzy_match'
9
10
  require 'thor'
10
11
 
11
12
  # This is the CLI DACP Client. Normally installed as `dacpclient`
12
13
  class CLIClient < Thor
13
- package_name :dacpclient
14
+ package_name 'DACPClient'
15
+
14
16
  include Thor::Actions
15
17
  def initialize(*)
16
18
  @config = {}
@@ -59,21 +61,6 @@ class CLIClient < Thor
59
61
  show_status
60
62
  end
61
63
 
62
- desc :home_sharing, 'Setup Home Sharing (Not fully functional)'
63
- def home_sharing
64
- puts 'Setting up Home Sharing. Saving Home Sharing GUID to ' + config_file
65
- puts "\nPlease enter your Apple ID credentials:"
66
- email = ask('Apple ID (e-mail address):').strip
67
- password = ask('Password:')
68
- guid = @client.setup_home_sharing(email, password)
69
- @config['appleid'] = email
70
- @config['hsgid'] = guid
71
- save_config
72
- puts "\n\n"
73
- puts "Got your Home Sharing GUID (#{guid}). Logging in.."
74
- login
75
- end
76
-
77
64
  desc :hostname, 'Set the hostname'
78
65
  def hostname
79
66
  @config['host'] = ask('Please enter a new hostname to connect to:').strip
@@ -125,13 +112,29 @@ class CLIClient < Thor
125
112
  playlist_items = @client.playlists
126
113
  puts 'Playlists:'
127
114
  puts '----------'
115
+ count = 0
116
+ num = Math.log10(playlist_items.length).floor + 1
128
117
  playlist_items.each do |playlist|
129
- print ' ' unless playlist.base_playlist?
118
+ printf("%#{num}d. ", count += 1) unless playlist.base_playlist?
119
+
130
120
  puts "#{playlist.name} (#{playlist.count})"
131
121
  end
132
122
  puts
133
123
  end
134
124
 
125
+ desc :play_playlist, 'Plays a playlist'
126
+ def play_playlist(playlist)
127
+ login
128
+ playlists = @client.playlists
129
+ if playlist =~ /^\d+$/
130
+ playlist = playlists[playlist.to_i]
131
+ else
132
+ fz = FuzzyMatch.new(playlists, read: :name)
133
+ playlist = fz.find(playlist)
134
+ end
135
+ @client.playspec(playlist)
136
+ end
137
+
135
138
  desc :upnext, 'Show what\'s up next'
136
139
  def upnext
137
140
  login
@@ -183,7 +186,17 @@ class CLIClient < Thor
183
186
  puts "DACPClient v#{DACPClient::VERSION}"
184
187
  puts "using DMAPParser v#{DMAPParser::VERSION}"
185
188
  print 'DACPClient and DMAPParser are Copyright (c) '
186
- puts "#{Time.now.year} Jurriaan Pruis"
189
+ puts "#{Time.now.year} Jurriaan Pruis <email@jurriaanpruis.nl>"
190
+ end
191
+
192
+ default_task :banner
193
+
194
+ desc :help, 'Display all possible commands'
195
+ def banner
196
+ puts "DACPClient v#{DACPClient::VERSION}"
197
+ puts "(c) #{Time.now.year} Jurriaan Pruis <email@jurriaanpruis.nl>"
198
+ puts
199
+ help
187
200
  end
188
201
 
189
202
  private
data/dacpclient.gemspec CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_runtime_dependency 'plist', '~> 3.1.0'
25
25
  spec.add_runtime_dependency 'dmapparser', '~> 0.1.0'
26
26
  spec.add_runtime_dependency 'thor', '~> 0.19.1'
27
+ spec.add_runtime_dependency 'fuzzy_match', '~> 2.0.4'
27
28
 
28
29
  spec.add_development_dependency 'yard'
29
30
  spec.add_development_dependency 'redcarpet'
@@ -39,7 +39,7 @@ module DACPClient
39
39
 
40
40
  private
41
41
 
42
- def node_resolver(node, resolved)
42
+ def node_resolver(_node, resolved)
43
43
  devices << Device.new(get_device_host(resolved), resolved.port,
44
44
  resolved.text_record)
45
45
 
@@ -11,6 +11,8 @@ require 'dacpclient/version'
11
11
  require 'dacpclient/model'
12
12
  require 'dacpclient/models/status'
13
13
  require 'dacpclient/models/pair_info'
14
+ require 'dacpclient/models/database'
15
+ require 'dacpclient/models/databases'
14
16
  require 'dacpclient/models/playlist'
15
17
  require 'dacpclient/models/playlists'
16
18
  require 'dacpclient/models/play_queue_item'
@@ -23,9 +25,6 @@ module DACPClient
23
25
  attr_writer :guid
24
26
  attr_reader :name, :host, :port, :session_id
25
27
 
26
- HOME_SHARING_HOST = 'https://homesharing.itunes.apple.com'.freeze
27
- HOME_SHARING_PATH = '/WebObjects/MZHomeSharing.woa/wa/getShareIdentifiers'.freeze
28
-
29
28
  DEFAULT_HEADERS = {
30
29
  'Viewer-Only-Client' => '1',
31
30
  'Accept-Encoding' => 'gzip',
@@ -56,19 +55,6 @@ module DACPClient
56
55
  alias_method :next, :nextitem
57
56
  alias_method :speakers, :getspeakers
58
57
 
59
- def setup_home_sharing(user, password)
60
- hs_client = Faraday.new(url: HOME_SHARING_HOST)
61
- result = hs_client.post do |request|
62
- request.url HOME_SHARING_PATH
63
- request.headers['Content-Type'] = 'text/xml'
64
- request.headers.merge!(DEFAULT_HEADERS)
65
- request.body = { 'appleId' => user, 'guid' => 'empty',
66
- 'password' => password }.to_plist
67
- end
68
- response = Plist.parse_xml(result.body)
69
- @hsgid = response['sgid']
70
- end
71
-
72
58
  def guid
73
59
  return @guid unless @guid.nil?
74
60
  d = Digest::SHA2.hexdigest(@name)
@@ -156,6 +142,19 @@ module DACPClient
156
142
  set_property('dmcp.volume', volume)
157
143
  end
158
144
 
145
+ def cue(command, query, index = 0)
146
+ do_action(:cue, command: command, query: query, index: index)
147
+ end
148
+
149
+ def playspec(playlist, db = default_db)
150
+ dbspec = "'dmap.persistentid:0x#{db.persistent_id.to_s(16)}'"
151
+ cspec = "'dmap.persistentid:0x#{playlist.persistent_id.to_s(16)}'"
152
+ # don't worry about playing item's from playlists yet..
153
+ # container-item-spec='dmap.containeritemid:%s'
154
+ do_action(:playspec, database_spec: dbspec,
155
+ container_spec: cspec)
156
+ end
157
+
159
158
  def repeat
160
159
  response = do_action(:getproperty, properties: 'dacp.repeatstate')
161
160
  response[:carp]
@@ -198,16 +197,25 @@ module DACPClient
198
197
  end
199
198
 
200
199
  def databases
201
- do_action('databases', clean_url: true)
200
+ meta = %w(dmap.itemname dmap.itemcount dmap.itemid dmap.persistentid
201
+ daap.baseplaylist com.apple.itunes.special-playlist
202
+ com.apple.itunes.smart-playlist com.apple.itunes.saved-genius
203
+ dmap.parentcontainerid dmap.editcommandssupported).join(',')
204
+ do_action('databases', clean_url: true, meta: meta, model: Databases)
202
205
  end
203
206
 
204
207
  def playlists(db = default_db)
205
- do_action("databases/#{db.miid}/containers", clean_url: true,
206
- model: Playlists).items
208
+ meta = %w(dmap.itemname dmap.itemcount dmap.itemid dmap.persistentid
209
+ daap.baseplaylist com.apple.itunes.special-playlist
210
+ com.apple.itunes.smart-playlist com.apple.itunes.saved-genius
211
+ dmap.parentcontainerid dmap.editcommandssupported).join(',')
212
+ do_action("databases/#{db.item_id}/containers", clean_url: true,
213
+ meta: meta,
214
+ model: Playlists).items
207
215
  end
208
216
 
209
217
  def default_db
210
- databases.mlcl.to_a.find { |item| item.mdbk == 1 }
218
+ databases.items.find { |item| item.default_db == 1 }
211
219
  end
212
220
 
213
221
  def default_playlist(db = default_db)
@@ -269,14 +277,17 @@ module DACPClient
269
277
  do_action(:setproperty, property => value)
270
278
  end
271
279
 
272
- def do_action(action, clean_url: false, model: nil, **params)
280
+ def do_action(action, params = {})
281
+ clean_url = params.delete(:clean_url) || false
282
+ model = params.delete(:model)
283
+
273
284
  action = '/' + action.to_s
274
285
  unless @session_id.nil?
275
286
  params['session-id'] = @session_id.to_s
276
287
  action = '/ctrl-int/1' + action unless clean_url
277
288
  end
278
289
  params['hsgid'] = @hsgid unless @hsgid.nil?
279
-
290
+ params = filter_param_keys(params)
280
291
  result = @client.get do |request|
281
292
  request.options.params_encoder = Faraday::FlatterParamsEncoder
282
293
  request.url action
@@ -287,6 +298,10 @@ module DACPClient
287
298
  parse_result result, model
288
299
  end
289
300
 
301
+ def filter_param_keys(params)
302
+ Hash[ params.map { |k, v| [k.to_s.tr('_', '-'), v] }]
303
+ end
304
+
290
305
  def parse_result(result, model)
291
306
  if !result.success?
292
307
  fail DACPForbiddenError, result
@@ -9,7 +9,7 @@ module FaradayMiddleware
9
9
  ACCEPT_ENCODING = 'Accept-Encoding'.freeze
10
10
  ENCODINGS = 'gzip,deflate'.freeze
11
11
 
12
- def initialize(app, options = nil)
12
+ def initialize(app)
13
13
  @app = app
14
14
  end
15
15
 
@@ -38,6 +38,10 @@ module DACPClient
38
38
  end.to_dmap
39
39
  end
40
40
 
41
+ def respond_to?(method)
42
+ dmap_attributes.key?(method)
43
+ end
44
+
41
45
  def method_missing(method, *args, &block)
42
46
  if method.to_s =~ /(.*)\=$/ &&
43
47
  dmap_attributes.key?(Regexp.last_match[1].to_sym)
@@ -0,0 +1,11 @@
1
+ module DACPClient
2
+ class Database < Model
3
+ dmap_tag :mlit
4
+ dmap_attribute :item_id, :miid
5
+ dmap_attribute :name, :minm
6
+ dmap_attribute :count, :mimc
7
+ dmap_attribute :container_count, :mctc
8
+ dmap_attribute :default_db, :mdbk
9
+ dmap_attribute :persistent_id, :mper
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module DACPClient
2
+ class Databases < Model
3
+ dmap_tag :avdb
4
+ dmap_attribute :status, :mstt
5
+ dmap_attribute :update_type, :myty
6
+ dmap_attribute :container_count, :mtco
7
+ dmap_attribute :returned_count, :mrco
8
+ dmap_container :items, :mlcl, DACPClient::Database
9
+ end
10
+ end
@@ -5,5 +5,6 @@ module DACPClient
5
5
  dmap_attribute :name, :minm
6
6
  dmap_attribute :base_playlist, :abpl
7
7
  dmap_attribute :count, :mimc
8
+ dmap_attribute :persistent_id, :mper
8
9
  end
9
10
  end
@@ -1,4 +1,4 @@
1
1
  # The DACPClient module
2
2
  module DACPClient
3
- VERSION = '0.2.11'
3
+ VERSION = '0.3.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dacpclient
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.11
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jurriaan Pruis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-03 00:00:00.000000000 Z
11
+ date: 2014-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.19.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: fuzzy_match
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.0.4
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.0.4
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: yard
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -160,6 +174,8 @@ files:
160
174
  - lib/dacpclient/faraday/flatter_params_encoder.rb
161
175
  - lib/dacpclient/faraday/gzip.rb
162
176
  - lib/dacpclient/model.rb
177
+ - lib/dacpclient/models/database.rb
178
+ - lib/dacpclient/models/databases.rb
163
179
  - lib/dacpclient/models/pair_info.rb
164
180
  - lib/dacpclient/models/play_queue.rb
165
181
  - lib/dacpclient/models/play_queue_item.rb