dacpclient 0.1.1 → 0.2.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: 1a91f6ef4321ce83e0cabc85493552ebf7961756
4
- data.tar.gz: c032e8102b42801e7fa220861ecefb5b306f0550
3
+ metadata.gz: ef5304a1a170b2d08364485ca254ff1f2607fba3
4
+ data.tar.gz: f7369e5cc191eae00af1a9b02e4e276069385496
5
5
  SHA512:
6
- metadata.gz: 352cf3561c859d1bf52deb7a3fb33d036245282e6610e72871f11863055afebd3fb41ff630fdb887c3f52573bf3ead3fbcf9fc7163ca0b6882391d9addf65ec6
7
- data.tar.gz: e78e5bad668a556c7d583702c23798a75b2df288bfb8358c9e750047685963bfbbc3380f9c2df6c12fb9236a3b5c2bb65f9b8bd5ebc6950a9c3801754cac72f6
6
+ metadata.gz: 6c8b74aac51cfa281f8201e52b49044ec1009bfca8fed4252c5d2da9dc9abeaba0bf3e755605900f30f1f521885d54c9e4614741fcaee4dffdcbc73b4165331e
7
+ data.tar.gz: e4204cd25fd0223c49b59dddfc032f91714827478ba3c6ca6692037b0ba83e15c771ea03aea0d96546704f34889230a44ef583b244d727f49d1426ba7d4094be
data/.rubocop.yml CHANGED
@@ -3,5 +3,7 @@ MethodLength:
3
3
  Enabled: false
4
4
  Documentation:
5
5
  Enabled: false
6
- ConstantName:
6
+ ClassVars:
7
+ Enabled: false
8
+ ClassLength:
7
9
  Enabled: false
data/Gemfile CHANGED
@@ -2,12 +2,3 @@
2
2
  gemspec
3
3
 
4
4
  source 'https://rubygems.org'
5
-
6
- group :test do
7
- gem 'pry'
8
- gem 'minitest', '~> 5.0.6'
9
- # gem 'coveralls', require: false
10
- gem 'rake'
11
- gem 'rubocop', '~> 0.11.1'
12
- gem 'simplecov', :require => false
13
- end
data/README.md CHANGED
@@ -5,6 +5,8 @@
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.
9
+
8
10
  Look at the [bin/dacpclient](https://github.com/jurriaan/ruby-dacpclient/blob/master/bin/dacpclient) file for an example client.
9
11
 
10
12
  ## Installation
@@ -25,8 +27,30 @@ Or install it yourself using:
25
27
 
26
28
  See [bin/dacpclient](https://github.com/jurriaan/ruby-dacpclient/blob/master/bin/dacpclient)
27
29
 
30
+ Usage: dacpclient [command]
31
+ (c) 2013 Jurriaan Pruis <email@jurriaanpruis.nl>
32
+
33
+ Where command is one of the following:
34
+ status
35
+ status_ticker
36
+ home_sharing
37
+ play
38
+ pause
39
+ playpause
40
+ next
41
+ prev
42
+ databases
43
+ playqueue
44
+ upnext
45
+ stop
46
+ debug
47
+ usage
48
+ previous
49
+ help
50
+
28
51
  ## Todo
29
52
 
53
+ - Use bonjour
30
54
  - Add tests
31
55
  - Add more tagdefinitions
32
56
  - Documentation
data/Rakefile CHANGED
@@ -19,4 +19,4 @@ task :rubocop do
19
19
  fail unless cli.run(args) == 0
20
20
  end
21
21
 
22
- task default: :test
22
+ task default: :test
data/bin/dacpclient CHANGED
@@ -1,13 +1,25 @@
1
1
  #!/usr/bin/env ruby
2
-
3
2
  require 'dacpclient'
4
- require 'english'
3
+ require 'English'
5
4
  require 'socket'
5
+ require 'yaml'
6
+ require 'yaml/dbm'
7
+ require 'fileutils'
8
+ require 'io/console'
6
9
 
7
10
  # This is the CLI DACP Client. Normally installed as `dacpclient`
8
11
  class CLIClient
9
12
  def initialize
10
- @client = DACPClient::Client.new("CLIClient (#{Socket.gethostname})", 'localhost', 3689)
13
+ @config = {}
14
+ @config['client_name'] ||= "DACPClient (#{Socket.gethostname})"
15
+ load_config
16
+
17
+ @client = DACPClient::Client.new(@config['client_name'], 'localhost', 3689)
18
+ if @config['guid'].nil? || @config['guid'] !~ /^[a-f0-9]{16}$/
19
+ @config['guid'] = @client.get_guid
20
+ save_config
21
+ end
22
+ @client.guid = @config['guid']
11
23
  @login = false
12
24
  end
13
25
 
@@ -64,6 +76,23 @@ class CLIClient
64
76
  end
65
77
  end
66
78
 
79
+ def home_sharing
80
+ puts "Setting up Home Sharing. Saving Home Sharing GUID to ~/.dacpclient/config.yml"
81
+ puts "\nPlease enter your Apple ID credentials:"
82
+ print "Apple ID (e-mail address): "
83
+ email = $stdin.gets.strip
84
+ print "Password: "
85
+ password = $stdin.noecho(&:gets).chomp
86
+ guid = @client.setup_home_sharing(email, password)
87
+ password = nil
88
+ @config['appleid'] = email
89
+ @config['hsgid'] = guid
90
+ save_config
91
+ puts "\n\n"
92
+ puts "Got your Home Sharing GUID (#{guid}). Logging in.."
93
+ login
94
+ end
95
+
67
96
  def play
68
97
  login
69
98
  @client.play
@@ -101,7 +130,7 @@ class CLIClient
101
130
 
102
131
  def upnext
103
132
  login
104
- items = @client.list_queue.mlcl.select { |item| item.type.tag == 'mlit' }
133
+ items = @client.list_queue.mlcl.to_a.select {|i| i.type.tag == 'mlit' }
105
134
  puts 'Up next:'
106
135
  puts '--------'
107
136
  puts
@@ -140,7 +169,13 @@ class CLIClient
140
169
  private
141
170
 
142
171
  def login
143
- @client.login unless @login
172
+ return if @login
173
+ @client.hsgid = @config['hsgid']
174
+ if @client.hsgid.nil?
175
+ @client.pair_and_login
176
+ else
177
+ @client.login
178
+ end
144
179
  @login = true
145
180
  end
146
181
 
@@ -165,7 +200,27 @@ class CLIClient
165
200
  end
166
201
  end
167
202
  end
203
+
204
+ def config_dir
205
+ File.join(ENV['HOME'], '.dacpclient')
206
+ end
207
+
208
+ def load_config
209
+ FileUtils.mkdir_p(config_dir)
210
+ config_file = File.join(config_dir,'config.yml')
211
+ if File.exists? config_file
212
+ @config.merge! YAML.load_file(config_file)
213
+ else
214
+ save_config
215
+ end
216
+ end
217
+
218
+ def save_config
219
+ File.open(File.join(config_dir,'config.yml'), 'w') do |out|
220
+ YAML.dump(@config, out)
221
+ end
222
+ end
168
223
  end
169
224
 
170
225
  cli = CLIClient.new
171
- cli.parse_arguments(Array(ARGV))
226
+ cli.parse_arguments(Array(ARGV))
data/dacpclient.gemspec CHANGED
@@ -20,10 +20,15 @@ Gem::Specification.new do |spec|
20
20
  spec.extra_rdoc_files = ['README.md', 'LICENSE']
21
21
 
22
22
  spec.add_runtime_dependency 'dnssd', '~> 2.0'
23
+ spec.add_runtime_dependency 'faraday', '~> 0.8.8'
24
+ spec.add_runtime_dependency 'plist', '~> 3.1.0'
23
25
 
24
26
  spec.add_development_dependency 'yard'
25
27
  spec.add_development_dependency 'redcarpet'
26
28
  spec.add_development_dependency 'github-markup'
29
+ spec.add_development_dependency 'minitest', '~> 5.2.0'
30
+ spec.add_development_dependency 'rubocop', '~> 0.15.0'
31
+ spec.add_development_dependency 'rake'
27
32
 
28
- spec.required_ruby_version = '>= 1.9.3'
33
+ spec.required_ruby_version = '>= 2.0.0'
29
34
  end
@@ -0,0 +1,43 @@
1
+ module DACPClient
2
+ # The Client class handles communication with the server
3
+ class Bonjour
4
+ SERVICE_NAME = '_daap._tcp'.freeze
5
+ DOMAIN = 'local'.freeze
6
+
7
+ def browse
8
+ servers = []
9
+
10
+ begin
11
+ timeout(3) do
12
+ DNSSD.browse!(SERVICE_NAME, DOMAIN) do |node|
13
+ ip, port = nil
14
+
15
+ resolver = DNSSD::Service.new
16
+ resolver.resolve(node) do |resolved|
17
+ ip = get_ip(resolved.target)
18
+ port = resolved.port
19
+
20
+ break unless resolved.flags.more_coming?
21
+ end
22
+
23
+ servers << { name: node.name, ip: ip, port: port, node: node }
24
+
25
+ break unless node.flags.more_coming?
26
+ end
27
+ end
28
+
29
+ rescue Timeout::Error
30
+ return []
31
+ end
32
+
33
+ servers
34
+ end
35
+
36
+ private
37
+
38
+ def get_ip(target)
39
+ info = Socket.getaddrinfo(target, nil, Socket::AF_INET)
40
+ info[0][2]
41
+ end
42
+ end
43
+ end
@@ -1,51 +1,89 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- Bundler.setup(:default)
1
+ require 'faraday'
4
2
  require 'digest'
5
3
  require 'net/http'
4
+ require 'uri'
5
+ require 'cgi'
6
+ require 'plist'
6
7
  require 'dacpclient/pairingserver'
7
8
  require 'dacpclient/dmapparser'
8
9
  require 'dacpclient/dmapbuilder'
9
- require 'uri'
10
- require 'cgi'
10
+ require 'dacpclient/bonjour'
11
11
 
12
12
  module DACPClient
13
13
  # The Client class handles communication with the server
14
14
  class Client
15
+ attr_accessor :guid, :hsgid
16
+ attr_reader :name, :host, :port, :session_id
17
+
18
+ HOME_SHARING_HOST = 'https://homesharing.itunes.apple.com'
19
+ HOME_SHARING_PATH = '/WebObjects/MZHomeSharing.woa/wa/getShareIdentifiers'
20
+
21
+ DEFAULT_HEADERS = {
22
+ 'Viewer-Only-Client' => '1',
23
+ # 'Accept-Encoding' => 'gzip',
24
+ 'Connection' => 'keep-alive',
25
+ 'User-Agent' => 'Remote/2.0'
26
+ }.freeze
15
27
 
16
28
  def initialize(name, host = 'localhost', port = 3689)
17
29
  @client = Net::HTTP.new(host, port)
18
30
  @name = name
19
31
  @host = host
20
32
  @port = port
21
- @service = nil
33
+
22
34
  @session_id = nil
35
+ @hsgid = nil
23
36
  @mediarevision = 1
37
+ @uri = URI::HTTP.build(host: @host, port: @port)
38
+ @client = Faraday.new(url: @uri.to_s)
39
+ end
40
+
41
+ def setup_home_sharing(user, password)
42
+ hs_client = Faraday.new(url: HOME_SHARING_HOST)
43
+ result = hs_client.post do |request|
44
+ request.url HOME_SHARING_PATH
45
+ request.headers['Content-Type'] = 'text/xml'
46
+ request.headers.merge!(DEFAULT_HEADERS)
47
+ request.body = { 'appleId' => user, 'guid' => 'empty',
48
+ 'password' => password }.to_plist
49
+ end
50
+ response = Plist.parse_xml(result.body)
51
+ @hsgid = response['sgid']
52
+ end
53
+
54
+ def get_guid
55
+ return @guid unless @guid.nil?
56
+ d = Digest::SHA2.hexdigest(@name)
57
+ d[0..15]
24
58
  end
25
59
 
26
60
  def pair(pin)
27
- pairingserver = PairingServer.new(@name, '0.0.0.0', 1024)
61
+ pairingserver = PairingServer.new(self, '0.0.0.0', 1024)
28
62
  pairingserver.pin = pin
29
63
  pairingserver.start
30
64
  end
31
65
 
32
- def self.get_guid(name)
33
- d = Digest::SHA2.hexdigest(name)
34
- d[0..15]
35
- end
36
-
37
66
  def serverinfo
38
- do_action('server-info')
67
+ do_action('server-info', {}, true)
39
68
  end
40
69
 
41
- def login(pin = nil)
42
- pairing_guid = '0x' + Client.get_guid(@name)
43
- response = do_action(:login, { 'pairing-guid' => pairing_guid })
70
+ def login
71
+ response = nil
72
+ if @hsgid.nil?
73
+ pairing_guid = '0x' + get_guid
74
+ response = do_action(:login, 'pairing-guid' => pairing_guid)
75
+ else
76
+ response = do_action(:login, 'hasFP' => '1')
77
+ end
44
78
  @session_id = response[:mlid]
45
79
  response
80
+ end
81
+
82
+ def pair_and_login(pin = nil)
83
+ login
46
84
  rescue DACPForbiddenError => e
47
85
  pin = 4.times.map { Random.rand(10) } if pin.nil?
48
- warn "#{e.result.message} error: Cannot login, starting pairing process"
86
+ warn "#{e.result.status} error: Cannot login, starting pairing process"
49
87
  warn "Pincode: #{pin}"
50
88
  pair(pin)
51
89
  retry
@@ -71,16 +109,29 @@ module DACPClient
71
109
  do_action(:pause)
72
110
  end
73
111
 
112
+ def track_length
113
+ response = do_action(:getproperty, properties: 'dacp.playingtime')
114
+ response['cast']
115
+ end
116
+
74
117
  def seek(ms)
75
118
  do_action(:setproperty, 'dacp.playingtime' => ms)
76
119
  end
77
120
 
121
+ def get_position
122
+ response = do_action(:getproperty, properties: 'dacp.playingtime')
123
+ response['cast'] - response['cant']
124
+ end
125
+
126
+ alias_method :position, :get_position
127
+ alias_method :position=, :seek
128
+
78
129
  def status(wait = false)
79
130
  revision = wait ? @mediarevision : 1
80
131
  result = do_action(:playstatusupdate, 'revision-number' => revision)
81
132
  @mediarevision = result[:cmsr]
82
133
  result
83
- rescue Net::ReadTimeout => e
134
+ rescue Faraday::Error::TimeoutError => e
84
135
  if wait
85
136
  retry
86
137
  else
@@ -96,6 +147,8 @@ module DACPClient
96
147
  do_action(:previtem)
97
148
  end
98
149
 
150
+ alias_method :previous, :prev
151
+
99
152
  def get_volume
100
153
  response = do_action(:getproperty, properties: 'dmcp.volume')
101
154
  response[:cmvo]
@@ -105,6 +158,9 @@ module DACPClient
105
158
  do_action(:setproperty, 'dmcp.volume' => volume)
106
159
  end
107
160
 
161
+ alias_method :volume, :get_volume
162
+ alias_method :volume=, :set_volume
163
+
108
164
  def get_repeat
109
165
  response = do_action(:getproperty, properties: 'dacp.repeatstate')
110
166
  response[:carp]
@@ -123,25 +179,31 @@ module DACPClient
123
179
  do_action(:setproperty, 'dmcp.volume' => volume)
124
180
  end
125
181
 
182
+ def getspeakers
183
+ do_action(:getspeakers)
184
+ end
185
+
126
186
  def ctrl_int
127
- do_action('ctrl-int', {}, false)
187
+ do_action('ctrl-int', {}, true)
128
188
  end
129
189
 
130
190
  def logout
131
- do_action(:logout, {}, false)
191
+ do_action(:logout)
192
+ @mediarevision = 1
193
+ @session_id = nil
132
194
  end
133
195
 
134
196
  def queue(id)
135
- do_action('playqueue-edit', { command: 'add',
136
- query: "\'dmap.itemid:#{id}\'" })
197
+ do_action('playqueue-edit', command: 'add',
198
+ query: "\'dmap.itemid:#{id}\'")
137
199
  end
138
200
 
139
201
  def clear_queue
140
- do_action('playqueue-edit', { command: 'clear' })
202
+ do_action('playqueue-edit', command: 'clear')
141
203
  end
142
204
 
143
205
  def list_queue
144
- do_action('playqueue-contents', {})
206
+ do_action('playqueue-contents')
145
207
  end
146
208
 
147
209
  def databases
@@ -153,7 +215,7 @@ module DACPClient
153
215
  end
154
216
 
155
217
  def default_db
156
- databases[:mlcl].to_a.find {|item| item.mdbk == 1}
218
+ databases[:mlcl].to_a.find { |item| item.mdbk == 1 }
157
219
  end
158
220
 
159
221
  def default_playlist(db)
@@ -166,7 +228,7 @@ module DACPClient
166
228
  end
167
229
 
168
230
  def now_playing_artwork(width = 320, height = 320)
169
- do_action(:nowplayingartwork, { mw: width, mh: height })
231
+ do_action(:nowplayingartwork, mw: width, mh: height)
170
232
  end
171
233
 
172
234
  def search(db, container, search, type = nil)
@@ -180,17 +242,19 @@ module DACPClient
180
242
  }
181
243
  queries = []
182
244
  type = types.keys if type.nil?
183
- Array(type).each do |t|
245
+ Array(type).each do |t|
184
246
  queries << "'#{types[t]}:#{search}'"
185
247
  end
186
- # @http.get("/databases/1/containers/1/items?query='daap.songartist:#{escaped_pattern}','daap.songalbum:#{escaped_pattern}','dmap.itemname:#{escaped_pattern}','daap.songgenre:#{escaped_pattern}','daap.songcomposer:#{escaped_pattern}'").body
187
- #queries.push(words.map { |v| "\'dmap.itemname:*#{v}*\'" }.join('+'))
188
- # queries.push(words.map{|v| "\'daap.songartist:*#{v}*\'"}.join('+'))
248
+
189
249
  q = queries.join(',')
190
- meta = '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'
250
+ meta = %w(dmap.itemname dmap.itemid daap.songartist daap.songalbumartist
251
+ daap.songalbum com.apple.itunes.cloud-id dmap.containeritemid
252
+ com.apple.itunes.has-video com.apple.itunes.itms-songid
253
+ com.apple.itunes.extended-media-kind dmap.downloadstatus
254
+ daap.songdisabled).join(',')
191
255
 
192
256
  url = "databases/#{db}/containers/#{container}/items"
193
- do_action(url, { type: 'music', sort: 'album', query: q, meta: meta},
257
+ do_action(url, { type: 'music', sort: 'album', query: q, meta: meta },
194
258
  true)
195
259
  end
196
260
 
@@ -202,28 +266,23 @@ module DACPClient
202
266
  params['session-id'] = @session_id
203
267
  action = '/ctrl-int/1' + action unless cleanurl
204
268
  end
205
- params = params.map { |k,v| "#{k}=#{v}" }.join('&')
206
- uri = URI::HTTP.build({ host: @host, port: @port, path: action,
207
- query: params })
208
- req = Net::HTTP::Get.new(uri.request_uri)
209
- req.add_field('Viewer-Only-Client', '1')
210
- res = Net::HTTP.new(uri.host, uri.port).start do |http|
211
- http.read_timeout = 1000
212
- http.request(req)
213
- end
214
- if res.kind_of?(Net::HTTPServiceUnavailable) ||
215
- res.kind_of?(Net::HTTPForbidden)
216
- raise DACPForbiddenError.new(res)
217
- elsif !res.kind_of?(Net::HTTPSuccess)
218
- warn 'No succes!'
219
- warn res
220
- return nil
269
+ params['hsgid'] = @hsgid unless @hsgid.nil?
270
+ result = @client.get do |request|
271
+ request.url action
272
+ request.params = params
273
+ request.headers.merge!(DEFAULT_HEADERS)
221
274
  end
222
275
 
223
- if res['Content-Type'] == 'application/x-dmap-tagged'
224
- DMAPParser::Parser.parse(res.body)
276
+ parse_result result
277
+ end
278
+
279
+ def parse_result(result)
280
+ if !result.success?
281
+ fail DACPForbiddenError, result
282
+ elsif result.headers['Content-Type'] == 'application/x-dmap-tagged'
283
+ DMAPParser.parse(result.body)
225
284
  else
226
- res.body
285
+ result.body
227
286
  end
228
287
  end
229
288
  end
@@ -232,8 +291,8 @@ module DACPClient
232
291
  # service unavailable
233
292
  class DACPForbiddenError < StandardError
234
293
  attr_reader :result
235
- def initialize(res)
236
- @result = res
294
+ def initialize(result)
295
+ @result = result
237
296
  end
238
297
  end
239
298
  end
@@ -11,31 +11,33 @@ module DACPClient
11
11
  new.send(method, *args, &block)
12
12
  end
13
13
 
14
- def method_missing(method, *args, &block)
15
- if method.to_s.length != 4 ||
16
- (tag = DMAPParser::Types.find { |a| a.tag.to_s == method.to_s }).nil?
17
- return super
14
+ def build_container(tag , &block)
15
+ unless tag.type == :container
16
+ fail "Tag #{method} is not a container type"
17
+ end
18
+ @dmap_stack << TagContainer.new(tag)
19
+ instance_eval(&block)
20
+ if @dmap_stack.length > 1
21
+ @dmap_stack.last.value << @dmap_stack.pop
22
+ else
23
+ return @result = @dmap_stack.pop
18
24
  end
25
+ end
26
+
27
+ def method_missing(method, *args, &block)
28
+ tag = TagDefinition[method]
29
+ return super if tag.nil?
30
+
19
31
  if block_given?
20
- if tag.type == :container
21
- @dmap_stack << DMAPParser::TagContainer.new(tag)
22
- instance_eval(&block)
23
- if @dmap_stack.length > 1
24
- @dmap_stack.last.value << @dmap_stack.pop
25
- else
26
- return @result = @dmap_stack.pop
27
- end
28
- else
29
- raise "Tag #{method} is not a container type"
30
- end
32
+ build_container(tag, &block)
31
33
  else
32
34
  if @dmap_stack.length > 0
33
35
  args = args.size > 1 ? args : args.first
34
- @dmap_stack.last.value << DMAPParser::Tag.new(tag, args)
36
+ @dmap_stack.last.value << Tag.new(tag, args)
35
37
  else
36
- raise 'Cannot build DMAP without a valid container'
38
+ fail 'Cannot build DMAP without a valid container'
37
39
  end
38
40
  end
39
41
  end
40
42
  end
41
- end
43
+ end
@@ -2,6 +2,10 @@ module DACPClient
2
2
  # The DMAPConverter class converts between binary and ruby formats
3
3
  class DMAPConverter
4
4
  class << self
5
+ def date_to_bin(data)
6
+ int_to_bin(value.to_i)
7
+ end
8
+
5
9
  def bin_to_byte(data)
6
10
  data.unpack('C').first
7
11
  end
@@ -27,15 +31,15 @@ module DACPClient
27
31
  end
28
32
 
29
33
  def bin_to_hex(data)
30
- data.bytes.reduce('') { |a, e| a += sprintf('%02X', e) }
34
+ data.bytes.reduce('') { |a, e| a + sprintf('%02X', e) }
35
+ end
36
+
37
+ def bin_to_date(data)
38
+ Time.at(bin_to_int(data))
31
39
  end
32
40
 
33
41
  def bool_to_bin(data)
34
- if data.true?
35
- "\x01"
36
- else
37
- "\x00"
38
- end
42
+ (data ? 1 : 0).chr
39
43
  end
40
44
 
41
45
  def int_to_bin(data)
@@ -61,6 +65,55 @@ module DACPClient
61
65
  def hex_to_bin(data)
62
66
  [data].pack 'H*'
63
67
  end
68
+
69
+ def decode_unknown(data)
70
+ if data =~ /[^\x20-\x7e]/ # non-readable characters
71
+ if data.bytesize == 1
72
+ return DMAPConverter.bin_to_byte(data)
73
+ elsif data.bytesize == 2
74
+ return DMAPConverter.bin_to_short(data)
75
+ elsif data.bytesize == 4
76
+ return DMAPConverter.bin_to_int(data)
77
+ elsif data.bytesize == 8
78
+ return DMAPConverter.bin_to_long(data)
79
+ end
80
+ end
81
+ data
82
+ end
83
+
84
+ def bin_to_string(data)
85
+ data
86
+ end
87
+ alias_method :string_to_bin, :bin_to_string
88
+
89
+ alias_method :uint16_to_bin, :short_to_bin
90
+ alias_method :uint32_to_bin, :int_to_bin
91
+ alias_method :uint64_to_bin, :long_to_bin
92
+
93
+ alias_method :bin_to_uint16, :bin_to_short
94
+ alias_method :bin_to_uint32, :bin_to_int
95
+ alias_method :bin_to_uint64, :bin_to_long
96
+ alias_method :bin_to_unknown, :decode_unknown
97
+
98
+ def decode(type, data)
99
+ decode_method = ('bin_to_' + type.to_s).to_sym
100
+ if respond_to? decode_method
101
+ send(decode_method, data)
102
+ else
103
+ warn "Decoder: Unknown type #{type}"
104
+ decode_unknown(data)
105
+ end
106
+ end
107
+
108
+ def encode(type, data)
109
+ encode_method = (type.to_s + '_to_bin').to_sym
110
+ if respond_to? encode_method
111
+ send(encode_method, data)
112
+ else
113
+ warn "Encoder: Unknown type #{type}"
114
+ data
115
+ end
116
+ end
64
117
  end
65
118
  end
66
- end
119
+ end