mpd_client 0.0.5 → 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.
data/lib/mpd_client.rb CHANGED
@@ -1,425 +1,527 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'socket'
4
- require "mpd_client/version"
5
-
6
- HELLO_PREFIX = "OK MPD "
7
- ERROR_PREFIX = "ACK "
8
- SUCCESS = "OK"
9
- NEXT = "list_OK"
10
-
11
- # MPD changelog: http://git.musicpd.org/cgit/master/mpd.git/plain/NEWS
12
- # http://www.musicpd.org/doc/protocol/command_reference.html
13
- # http://git.musicpd.org/cgit/cirrus/mpd.git/plain/doc/protocol.xml
14
- #
15
- COMMANDS = {
16
- # Status Commands
17
- "clearerror" => "fetch_nothing",
18
- "currentsong" => "fetch_object",
19
- "idle" => "fetch_list",
20
- "noidle" => "",
21
- "status" => "fetch_object",
22
- "stats" => "fetch_object",
23
- # Playback Option Commands
24
- "consume" => "fetch_nothing",
25
- "crossfade" => "fetch_nothing",
26
- "mixrampdb" => "fetch_nothing",
27
- "mixrampdelay" => "fetch_nothing",
28
- "random" => "fetch_nothing",
29
- "repeat" => "fetch_nothing",
30
- "setvol" => "fetch_nothing",
31
- "single" => "fetch_nothing",
32
- "replay_gain_mode" => "fetch_nothing",
33
- "replay_gain_status" => "fetch_item",
34
- "volume" => "fetch_nothing",
35
- # Playback Control Commands
36
- "next" => "fetch_nothing",
37
- "pause" => "fetch_nothing",
38
- "play" => "fetch_nothing",
39
- "playid" => "fetch_nothing",
40
- "previous" => "fetch_nothing",
41
- "seek" => "fetch_nothing",
42
- "seekid" => "fetch_nothing",
43
- "seekcur" => "fetch_nothing",
44
- "stop" => "fetch_nothing",
45
- # Playlist Commands
46
- "add" => "fetch_nothing",
47
- "addid" => "fetch_item",
48
- "addtagid" => "fetch_nothing",
49
- "cleartagid" => "fetch_nothing",
50
- "clear" => "fetch_nothing",
51
- "delete" => "fetch_nothing",
52
- "deleteid" => "fetch_nothing",
53
- "move" => "fetch_nothing",
54
- "moveid" => "fetch_nothing",
55
- "playlist" => "fetch_playlist",
56
- "playlistfind" => "fetch_songs",
57
- "playlistid" => "fetch_songs",
58
- "playlistinfo" => "fetch_songs",
59
- "playlistsearch" => "fetch_songs",
60
- "plchanges" => "fetch_songs",
61
- "plchangesposid" => "fetch_changes",
62
- "prio" => "fetch_nothing",
63
- "prioid" => "fetch_nothing",
64
- "rangeid" => "fetch_nothing",
65
- "shuffle" => "fetch_nothing",
66
- "swap" => "fetch_nothing",
67
- "swapid" => "fetch_nothing",
68
- # Stored Playlist Commands
69
- "listplaylist" => "fetch_list",
70
- "listplaylistinfo" => "fetch_songs",
71
- "listplaylists" => "fetch_playlists",
72
- "load" => "fetch_nothing",
73
- "playlistadd" => "fetch_nothing",
74
- "playlistclear" => "fetch_nothing",
75
- "playlistdelete" => "fetch_nothing",
76
- "playlistmove" => "fetch_nothing",
77
- "rename" => "fetch_nothing",
78
- "rm" => "fetch_nothing",
79
- "save" => "fetch_nothing",
80
- # Database Commands
81
- "count" => "fetch_object",
82
- "find" => "fetch_songs",
83
- "findadd" => "fetch_nothing",
84
- "list" => "fetch_list",
85
- "listall" => "fetch_database",
86
- "listallinfo" => "fetch_database",
87
- "listfiles" => "fetch_database",
88
- "lsinfo" => "fetch_database",
89
- "search" => "fetch_songs",
90
- "searchadd" => "fetch_nothing",
91
- "searchaddp1" => "fetch_nothing",
92
- "update" => "fetch_item",
93
- "rescan" => "fetch_item",
94
- "readcomments" => "fetch_item",
95
- # Mounts and neighbors
96
- "mount" => "fetch_nothing",
97
- "unmount" => "fetch_nothing",
98
- "listmounts" => "fetch_mounts",
99
- "listneighbors" => "fetch_neighbors",
100
- # Sticker Commands
101
- "sticker get" => "fetch_sticker",
102
- "sticker set" => "fetch_nothing",
103
- "sticker delete" => "fetch_nothing",
104
- "sticker list" => "fetch_stickers",
105
- "sticker find" => "fetch_songs",
106
- # Connection Commands
107
- "close" => "",
108
- "kill" => "",
109
- "password" => "fetch_nothing",
110
- "ping" => "fetch_nothing",
111
- # Audio Output Commands
112
- "disableoutput" => "fetch_nothing",
113
- "enableoutput" => "fetch_nothing",
114
- "outputs" => "fetch_outputs",
115
- "toggleoutput" => "fetch_nothing",
116
- # Reflection Commands
117
- "config" => "fetch_item",
118
- "commands" => "fetch_list",
119
- "notcommands" => "fetch_list",
120
- "tagtypes" => "fetch_list",
121
- "urlhandlers" => "fetch_list",
122
- "decoders" => "fetch_plugins",
123
- # Client To Client
124
- "subscribe" => "fetch_nothing",
125
- "unsubscribe" => "fetch_nothing",
126
- "channels" => "fetch_list",
127
- "readmessages" => "fetch_messages",
128
- "sendmessage" => "fetch_nothing"
129
- }
130
-
131
- # The MPDClient library is used for interactions with a MPD.
132
- #
133
- # == Example
134
- #
135
- # require 'mpd_client'
136
- # require 'logger'
137
- #
138
- # client = MPDClient.new
139
- # client.log = Logger.new($stderr)
140
- # client.connect('/var/run/mpd/socket')
141
- #
142
- class MPDClient
143
- attr_reader :mpd_version
144
-
145
- class << self
146
- # Default logger for all MPDClient instances
147
- #
148
- # MPDClient.log = Logger.new($stderr)
149
- #
150
- attr_accessor :log
151
-
152
- def add_command(name, retval)
153
- escaped_name = name.gsub(' ', '_')
154
- define_method escaped_name.to_sym do |*args|
155
- execute(name, *args, retval)
4
+ require 'stringio'
5
+ require 'mpd_client/version'
6
+
7
+ module MPD
8
+ HELLO_PREFIX = 'OK MPD '
9
+ ERROR_PREFIX = 'ACK '
10
+ SUCCESS = "OK\n"
11
+ NEXT = "list_OK\n"
12
+
13
+ # MPD changelog: https://github.com/MusicPlayerDaemon/MPD/blob/master/NEWS
14
+ # Protocol: https://mpd.readthedocs.io/en/latest/protocol.html
15
+ COMMANDS = {
16
+ # Status Commands
17
+ 'clearerror' => 'fetch_nothing',
18
+ 'currentsong' => 'fetch_object',
19
+ 'idle' => 'fetch_list',
20
+ 'noidle' => '',
21
+ 'status' => 'fetch_object',
22
+ 'stats' => 'fetch_object',
23
+ # Playback Option Commands
24
+ 'consume' => 'fetch_nothing',
25
+ 'crossfade' => 'fetch_nothing',
26
+ 'mixrampdb' => 'fetch_nothing',
27
+ 'mixrampdelay' => 'fetch_nothing',
28
+ 'random' => 'fetch_nothing',
29
+ 'repeat' => 'fetch_nothing',
30
+ 'setvol' => 'fetch_nothing',
31
+ 'single' => 'fetch_nothing',
32
+ 'replay_gain_mode' => 'fetch_nothing',
33
+ 'replay_gain_status' => 'fetch_item',
34
+ 'volume' => 'fetch_nothing',
35
+ # Playback Control Commands
36
+ 'next' => 'fetch_nothing',
37
+ 'pause' => 'fetch_nothing',
38
+ 'play' => 'fetch_nothing',
39
+ 'playid' => 'fetch_nothing',
40
+ 'previous' => 'fetch_nothing',
41
+ 'seek' => 'fetch_nothing',
42
+ 'seekid' => 'fetch_nothing',
43
+ 'seekcur' => 'fetch_nothing',
44
+ 'stop' => 'fetch_nothing',
45
+ # Playlist Commands
46
+ 'add' => 'fetch_nothing',
47
+ 'addid' => 'fetch_item',
48
+ 'addtagid' => 'fetch_nothing',
49
+ 'cleartagid' => 'fetch_nothing',
50
+ 'clear' => 'fetch_nothing',
51
+ 'delete' => 'fetch_nothing',
52
+ 'deleteid' => 'fetch_nothing',
53
+ 'move' => 'fetch_nothing',
54
+ 'moveid' => 'fetch_nothing',
55
+ 'playlistfind' => 'fetch_songs',
56
+ 'playlistid' => 'fetch_songs',
57
+ 'playlistinfo' => 'fetch_songs',
58
+ 'playlistsearch' => 'fetch_songs',
59
+ 'plchanges' => 'fetch_songs',
60
+ 'plchangesposid' => 'fetch_changes',
61
+ 'prio' => 'fetch_nothing',
62
+ 'prioid' => 'fetch_nothing',
63
+ 'rangeid' => 'fetch_nothing',
64
+ 'shuffle' => 'fetch_nothing',
65
+ 'swap' => 'fetch_nothing',
66
+ 'swapid' => 'fetch_nothing',
67
+ # Stored Playlist Commands
68
+ 'listplaylist' => 'fetch_list',
69
+ 'listplaylistinfo' => 'fetch_songs',
70
+ 'listplaylists' => 'fetch_playlists',
71
+ 'load' => 'fetch_nothing',
72
+ 'playlistadd' => 'fetch_nothing',
73
+ 'playlistclear' => 'fetch_nothing',
74
+ 'playlistdelete' => 'fetch_nothing',
75
+ 'playlistmove' => 'fetch_nothing',
76
+ 'rename' => 'fetch_nothing',
77
+ 'rm' => 'fetch_nothing',
78
+ 'save' => 'fetch_nothing',
79
+ # Database Commands
80
+ 'count' => 'fetch_object',
81
+ 'find' => 'fetch_songs',
82
+ 'findadd' => 'fetch_nothing',
83
+ 'list' => 'fetch_list',
84
+ 'listall' => 'fetch_database',
85
+ 'listallinfo' => 'fetch_database',
86
+ 'listfiles' => 'fetch_database',
87
+ 'lsinfo' => 'fetch_database',
88
+ 'search' => 'fetch_songs',
89
+ 'searchadd' => 'fetch_nothing',
90
+ 'searchaddp1' => 'fetch_nothing',
91
+ 'update' => 'fetch_item',
92
+ 'rescan' => 'fetch_item',
93
+ 'readcomments' => 'fetch_object',
94
+ # Mounts and neighbors
95
+ 'mount' => 'fetch_nothing',
96
+ 'unmount' => 'fetch_nothing',
97
+ 'listmounts' => 'fetch_mounts',
98
+ 'listneighbors' => 'fetch_neighbors',
99
+ # Sticker Commands
100
+ 'sticker get' => 'fetch_sticker',
101
+ 'sticker set' => 'fetch_nothing',
102
+ 'sticker delete' => 'fetch_nothing',
103
+ 'sticker list' => 'fetch_stickers',
104
+ 'sticker find' => 'fetch_songs',
105
+ # Connection Commands
106
+ 'close' => '',
107
+ 'kill' => '',
108
+ 'password' => 'fetch_nothing',
109
+ 'ping' => 'fetch_nothing',
110
+ # Audio Output Commands
111
+ 'disableoutput' => 'fetch_nothing',
112
+ 'enableoutput' => 'fetch_nothing',
113
+ 'outputs' => 'fetch_outputs',
114
+ 'toggleoutput' => 'fetch_nothing',
115
+ # Reflection Commands
116
+ 'config' => 'fetch_item',
117
+ 'commands' => 'fetch_list',
118
+ 'notcommands' => 'fetch_list',
119
+ 'tagtypes' => 'fetch_list',
120
+ 'urlhandlers' => 'fetch_list',
121
+ 'decoders' => 'fetch_plugins',
122
+ # Client To Client
123
+ 'subscribe' => 'fetch_nothing',
124
+ 'unsubscribe' => 'fetch_nothing',
125
+ 'channels' => 'fetch_list',
126
+ 'readmessages' => 'fetch_messages',
127
+ 'sendmessage' => 'fetch_nothing'
128
+ }.freeze
129
+
130
+ # The `MPD::Client` is used for interactions with a MPD server.
131
+ #
132
+ # Example:
133
+ #
134
+ # ```ruby
135
+ # require 'mpd_client'
136
+ # require 'logger'
137
+ #
138
+ # client = MPD::Client.new
139
+ # client.log = Logger.new($stderr)
140
+ # client.connect('/var/run/mpd/socket')
141
+ # ```
142
+ class Client
143
+ attr_reader :mpd_version
144
+
145
+ class << self
146
+ # Default logger for all `MPD::Client`` instances
147
+ #
148
+ # ```ruby
149
+ # MPD::Client.log = Logger.new($stderr)
150
+ # ```
151
+ attr_accessor :log
152
+
153
+ def connect(host = 'localhost', port = 6600)
154
+ client = MPD::Client.new
155
+ client.connect(host, port)
156
+
157
+ client
158
+ end
159
+
160
+ def add_command(name, retval)
161
+ escaped_name = name.tr(' ', '_')
162
+
163
+ define_method escaped_name.to_sym do |*args|
164
+ ensure_connected
165
+
166
+ execute(name, *args, retval)
167
+ end
168
+ end
169
+
170
+ def remove_command(name)
171
+ raise "Can't remove not existent '#{name}' command" unless method_defined? name.to_sym
172
+
173
+ remove_method name.to_sym
156
174
  end
157
175
  end
158
176
 
159
- def remove_command(name)
160
- raise "Can't remove not existent '#{name}' command" unless method_defined? name.to_sym
161
- remove_method name.to_sym
177
+ def initialize
178
+ @mutex = Mutex.new
179
+ reset
162
180
  end
163
- end
164
181
 
165
- def initialize
166
- @mutex = Mutex.new
167
- reset
168
- end
182
+ def connect(host = 'localhost', port = 6600)
183
+ @host = host
184
+ @port = port
169
185
 
170
- def connect(host = 'localhost', port = 6600)
171
- @host = host
172
- @port = port
173
- reconnect
174
- end
186
+ reconnect
187
+ end
188
+
189
+ def reconnect
190
+ log&.info("MPD (re)connect #{@host}, #{@port}")
191
+
192
+ @socket =
193
+ if @host.start_with?('/')
194
+ UNIXSocket.new(@host)
195
+ else
196
+ TCPSocket.new(@host, @port)
197
+ end
175
198
 
176
- def reconnect
177
- log.info("MPD (re)connect #{@host}, #{@port}") if log
178
- if @host.start_with?('/')
179
- @socket = UNIXSocket.new(@host)
180
- hello
181
- else
182
- @socket = TCPSocket.new(@host, @port)
183
199
  hello
200
+ @connected = true
184
201
  end
185
- end
186
202
 
187
- def disconnect
188
- log.info("MPD disconnect") if log
189
- @socket.close
190
- reset
191
- end
203
+ def disconnect
204
+ log&.info('MPD disconnect')
205
+ @socket.close
206
+ reset
207
+ end
192
208
 
193
- # http://www.musicpd.org/doc/protocol/ch01s04.html
194
- def command_list_ok_begin
195
- raise "Already in command list" unless @command_list.nil?
196
- write_command('command_list_ok_begin')
197
- @command_list = []
198
- end
209
+ def reset
210
+ @mpd_version = nil
211
+ @command_list = nil
212
+ @socket = nil
213
+ @log = nil
214
+ @connected = false
215
+ end
199
216
 
200
- def command_list_end
201
- raise "Not in command list" if @command_list.nil?
202
- write_command('command_list_end')
217
+ def connected?
218
+ @connected
219
+ end
203
220
 
204
- return fetch_command_list
205
- end
221
+ # https://www.musicpd.org/doc/protocol/command_lists.html
222
+ def command_list_ok_begin
223
+ raise 'Already in command list' unless @command_list.nil?
206
224
 
207
- # The current logger. If no logger has been set MPDClient.log is used
208
- #
209
- def log
210
- @log || MPDClient.log
211
- end
225
+ write_command('command_list_ok_begin')
212
226
 
213
- # Sets the +logger+ used by this instance of MPDClient
214
- #
215
- def log= logger
216
- @log = logger
217
- end
227
+ @command_list = []
228
+ end
218
229
 
219
- private
230
+ def command_list_end
231
+ raise 'Not in command list' if @command_list.nil?
220
232
 
221
- def execute(command, *args, retval)
222
- @mutex.synchronize do
223
- if !@command_list.nil?
224
- write_command(command, *args)
225
- @command_list << retval
226
- else
233
+ write_command('command_list_end')
234
+
235
+ fetch_command_list
236
+ end
237
+
238
+ # The current logger. If no logger has been set MPD::Client.log is used
239
+ def log
240
+ @log || MPD::Client.log
241
+ end
242
+
243
+ # Sets the +logger+ used by this instance of MPD::Client
244
+ attr_writer :log
245
+
246
+ def albumart(uri)
247
+ fetch_binary(StringIO.new, 0, 'albumart', uri)
248
+ end
249
+
250
+ def readpicture(uri)
251
+ fetch_binary(StringIO.new, 0, 'readpicture', uri)
252
+ end
253
+
254
+ private
255
+
256
+ def ensure_connected
257
+ raise 'Please connect to MPD server' unless connected?
258
+ end
259
+
260
+ def execute(command, *args, retval)
261
+ @mutex.synchronize do
227
262
  write_command(command, *args)
228
- eval retval
263
+
264
+ if @command_list.nil?
265
+ eval retval
266
+ else
267
+ @command_list << retval
268
+ end
229
269
  end
230
270
  end
231
- end
232
271
 
233
- def write_line(line)
234
- begin
235
- @socket.puts line
236
- rescue Errno::EPIPE
237
- reconnect
238
- @socket.puts line
272
+ def write_line(line)
273
+ begin
274
+ @socket.puts line
275
+ rescue Errno::EPIPE
276
+ reconnect
277
+ @socket.puts line
278
+ end
279
+
280
+ @socket.flush
239
281
  end
240
- @socket.flush
241
- end
242
282
 
243
- def write_command(command, *args)
244
- parts = [command]
245
- args.each do |arg|
246
- if arg.kind_of?(Array)
247
- parts << (arg.size == 1 ? "\"#{arg[0].to_i}:\"" : "\"#{arg[0].to_i}:#{arg[1].to_i}\"")
248
- else
249
- parts << "\"#{escape(arg)}\""
283
+ def write_command(command, *args)
284
+ parts = [command]
285
+
286
+ args.each do |arg|
287
+ line =
288
+ if arg.is_a?(Array)
289
+ arg.size == 1 ? "\"#{arg[0].to_i}:\"" : "\"#{arg[0].to_i}:#{arg[1].to_i}\""
290
+ else
291
+ "\"#{escape(arg)}\""
292
+ end
293
+
294
+ parts << line
250
295
  end
296
+
297
+ # log.debug("Calling MPD: #{command}#{args}") if log
298
+ log&.debug("Calling MPD: #{parts.join(' ')}")
299
+ write_line(parts.join(' '))
251
300
  end
252
- #log.debug("Calling MPD: #{command}#{args}") if log
253
- log.debug("Calling MPD: #{parts.join(' ')}") if log
254
- write_line(parts.join(' '))
255
- end
256
301
 
257
- def read_line
258
- line = @socket.gets.force_encoding('utf-8')
259
- raise "Connection lost while reading line" unless line.end_with?("\n")
260
- line.chomp!
261
- if line.start_with?(ERROR_PREFIX)
262
- error = line[/#{ERROR_PREFIX}(.*)/, 1].strip
263
- raise error
302
+ def read_line
303
+ line = @socket.gets
304
+
305
+ raise 'Connection lost while reading line' unless line.end_with?("\n")
306
+
307
+ if line.start_with?(ERROR_PREFIX)
308
+ error = line[/#{ERROR_PREFIX}(.*)/, 1].strip
309
+ raise error
310
+ end
311
+
312
+ if !@command_list.nil?
313
+ return if line == NEXT
314
+ raise "Got unexpected '#{SUCCESS}'" if line == SUCCESS
315
+ elsif line == SUCCESS
316
+ return
317
+ end
318
+
319
+ line
264
320
  end
265
321
 
266
- if !@command_list.nil?
267
- return if line == NEXT
268
- raise "Got unexpected '#{SUCCESS}'" if line == SUCCESS
269
- elsif line == SUCCESS
270
- return
322
+ def read_pair
323
+ line = read_line
324
+
325
+ return if line.nil?
326
+
327
+ line.split(': ', 2)
271
328
  end
272
329
 
273
- return line
274
- end
330
+ def read_pairs
331
+ result = []
275
332
 
276
- def read_pair(separator)
277
- line = read_line
278
- return if line.nil?
279
- pair = line.split(separator, 2)
280
- raise "Could now parse pair: '#{line}'" if pair.size < 2
333
+ pair = read_pair
281
334
 
282
- return pair #Array
283
- end
335
+ while pair
336
+ result << pair
337
+ pair = read_pair
338
+ end
284
339
 
285
- def read_pairs(separator = ': ')
286
- result = []
287
- pair = read_pair(separator)
288
- while pair
289
- result << pair
290
- pair = read_pair(separator)
340
+ result
291
341
  end
292
342
 
293
- return result
294
- end
343
+ def fetch_item
344
+ pairs = read_pairs
295
345
 
296
- def fetch_item
297
- pairs = read_pairs
298
- return nil if pairs.size != 1
299
- return pairs[0][1]
300
- end
346
+ return nil if pairs.size != 1
301
347
 
302
- def fetch_nothing
303
- line = read_line
304
- raise "Got unexpected return value: #{line}" unless line.nil?
305
- end
348
+ pairs[0][1]
349
+ end
350
+
351
+ def fetch_nothing
352
+ line = read_line
353
+
354
+ raise "Got unexpected value: #{line}" unless line.nil?
355
+ end
306
356
 
307
- def fetch_list
308
- result = []
309
- seen = nil
310
- read_pairs.each do |key, value|
311
- if key != seen
312
- if seen != nil
313
- raise "Expected key '#{seen}', got '#{key}'"
357
+ def fetch_list
358
+ result = []
359
+ seen = nil
360
+
361
+ read_pairs.each do |key, value|
362
+ value = value.chomp.force_encoding('utf-8')
363
+
364
+ if key != seen
365
+ raise "Expected key '#{seen}', got '#{key}'" unless seen.nil?
366
+
367
+ seen = key
314
368
  end
315
- seen = key
369
+
370
+ result << value
316
371
  end
317
- result << value
372
+
373
+ result
318
374
  end
319
375
 
320
- return result
321
- end
376
+ def fetch_objects(delimeters = [])
377
+ result = []
378
+ obj = {}
379
+
380
+ read_pairs.each do |key, value|
381
+ key = key.downcase
382
+ value = value.chomp.force_encoding('utf-8')
383
+
384
+ if delimeters.include?(key)
385
+ result << obj unless obj.empty?
386
+ obj = {}
387
+ elsif obj.include?(key)
388
+ obj[key] << value
389
+ end
322
390
 
323
- def fetch_objects(delimeters = [])
324
- result = []
325
- obj = {}
326
- read_pairs.each do |key, value|
327
- key = key.downcase
328
- if delimeters.include?(key)
329
- result << obj unless obj.empty?
330
- obj = {}
331
- elsif obj.include?(key)
332
- obj[key] << value
391
+ obj[key] = value
333
392
  end
334
- obj[key] = value
393
+
394
+ result << obj unless obj.empty?
395
+
396
+ result
335
397
  end
336
398
 
337
- result << obj unless obj.empty?
399
+ def fetch_object
400
+ objs = fetch_objects
338
401
 
339
- return result
340
- end
402
+ objs ? objs[0] : {}
403
+ end
341
404
 
342
- def fetch_object
343
- objs = fetch_objects
344
- return objs ? objs[0] : {}
345
- end
405
+ def fetch_binary(io = StringIO.new, offset = 0, *args)
406
+ data = {}
346
407
 
347
- def fetch_changes; fetch_objects(['cpos']); end
408
+ @mutex.synchronize do
409
+ write_command(*args, offset)
348
410
 
349
- def fetch_songs; fetch_objects(['file']); end
411
+ binary = false
350
412
 
351
- def fetch_mounts; fetch_objects(['mount']); end
413
+ read_pairs.each do |item|
414
+ if binary
415
+ io << item.join(': ')
416
+ next
417
+ end
352
418
 
353
- def fetch_neighbors; fetch_objects(['neighbor']); end
419
+ key = item[0]
420
+ value = item[1].chomp
354
421
 
355
- def fetch_messages; fetch_objects('channel'); end
422
+ binary = (key == 'binary')
423
+
424
+ data[key] = value
425
+ end
426
+ end
356
427
 
357
- def fetch_outputs; fetch_objects(['outputid']); end
428
+ size = data['size'].to_i
429
+ binary = data['binary'].to_i
358
430
 
359
- def fetch_plugins; fetch_objects(['plugin']); end
431
+ next_offset = offset + binary
360
432
 
361
- def fetch_database; fetch_objects(['file', 'directory', 'playlist']); end
433
+ return [data, io] if next_offset >= size
362
434
 
363
- def fetch_playlists; fetch_objects(['playlist']); end
435
+ io.seek(-1, IO::SEEK_CUR)
364
436
 
365
- def fetch_playlist
366
- result = []
367
- read_pairs(':').each do |key, value|
368
- result << value
437
+ fetch_binary(io, next_offset, *args)
369
438
  end
370
439
 
371
- return result
372
- end
440
+ def fetch_changes
441
+ fetch_objects(['cpos'])
442
+ end
373
443
 
374
- def fetch_stickers
375
- result = []
376
- read_pairs.each do |key, sticker|
377
- value = sticker.split('=', 2)
378
- raise "Could now parse sticker: #{sticker}" if value.size < 2
379
- result << Hash[*value]
444
+ def fetch_songs
445
+ fetch_objects(['file'])
380
446
  end
381
447
 
382
- return result
383
- end
448
+ def fetch_mounts
449
+ fetch_objects(['mount'])
450
+ end
451
+
452
+ def fetch_neighbors
453
+ fetch_objects(['neighbor'])
454
+ end
455
+
456
+ def fetch_messages
457
+ fetch_objects('channel')
458
+ end
459
+
460
+ def fetch_outputs
461
+ fetch_objects(['outputid'])
462
+ end
463
+
464
+ def fetch_plugins
465
+ fetch_objects(['plugin'])
466
+ end
467
+
468
+ def fetch_database
469
+ fetch_objects(%w[file directory playlist])
470
+ end
471
+
472
+ def fetch_playlists
473
+ fetch_objects(['playlist'])
474
+ end
475
+
476
+ def fetch_stickers
477
+ result = []
384
478
 
385
- def fetch_sticker; fetch_stickers[0]; end
479
+ read_pairs.each do |_key, sticker|
480
+ value = sticker.split('=', 2)
481
+ raise "Could now parse sticker: #{sticker}" if value.size < 2
386
482
 
387
- def fetch_command_list
388
- result = []
389
- begin
390
- @command_list.each do |retval|
391
- result << (eval retval)
483
+ result << Hash[*value]
392
484
  end
393
- ensure
394
- @command_list = nil
485
+
486
+ result
395
487
  end
396
488
 
397
- return result
398
- end
489
+ def fetch_sticker
490
+ fetch_stickers[0]
491
+ end
399
492
 
493
+ def fetch_command_list
494
+ result = []
400
495
 
401
- def hello
402
- line = @socket.gets
403
- raise "Connection lost while reading MPD hello" unless line.end_with?("\n")
404
- line.chomp!
405
- raise "Got invalid MPD hello: #{line}" unless line.start_with?(HELLO_PREFIX)
406
- @mpd_version = line[/#{HELLO_PREFIX}(.*)/, 1]
407
- end
496
+ begin
497
+ @command_list.each do |retval|
498
+ result << (eval retval)
499
+ end
500
+ ensure
501
+ @command_list = nil
502
+ end
408
503
 
409
- def reset
410
- @mpd_version = nil
411
- @command_list = nil
412
- @socket = nil
413
- @log = nil
414
- end
504
+ result
505
+ end
415
506
 
416
- def escape(text)
417
- text.to_s.gsub("\\", "\\\\").gsub('"', '\\"')
418
- end
507
+ def hello
508
+ line = @socket.gets
419
509
 
420
- end
510
+ raise 'Connection lost while reading MPD hello' unless line.end_with?("\n")
511
+
512
+ line.chomp!
513
+
514
+ raise "Got invalid MPD hello: #{line}" unless line.start_with?(HELLO_PREFIX)
515
+
516
+ @mpd_version = line[/#{HELLO_PREFIX}(.*)/, 1]
517
+ end
421
518
 
422
- COMMANDS.each_pair do |name, callback|
423
- MPDClient.add_command(name, callback)
519
+ def escape(text)
520
+ text.to_s.gsub('\\', '\\\\').gsub('"', '\\"')
521
+ end
522
+ end
424
523
  end
425
524
 
525
+ MPD::COMMANDS.each_pair do |name, callback|
526
+ MPD::Client.add_command(name, callback)
527
+ end