mpd_client 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bbeae5055e98a65a3f47b86aaf40d85dbe4d1447dbabe1efffa70c801efb6b9
4
- data.tar.gz: ba2f70d5db12c266c6bf70d714867dcdbd51a2845b4906f79689e6d724fc1dee
3
+ metadata.gz: 6f6e3b14a951fdfbd9467155757083aff2fa275945363cf137e813dfce3e80ab
4
+ data.tar.gz: ce2ae51be7986266b0ad1978e76e5714ca6a4937d29bbeec905c295fedaa8960
5
5
  SHA512:
6
- metadata.gz: d4d105484473b078f546e9d961289552f09336e491fed04aa8213271127fcc6f725d2973746a77731fa0f505d9bf3656338b8ca40b5f908963c7dafa2daf66c4
7
- data.tar.gz: 3d9829735ff01c8b5fc27d9c3e9d9dce06dad9504a80423bb6b938a849797ecd9443a842d0fecd37a7355d1062231df74238f7c30ee49d1fbfdc99e1d6a704df
6
+ metadata.gz: e0b9ab125796173047fe37d4ddcd1885f1381bb3b9723da38c6999e385a457d72f064d3ec400ac62c5a58c2674386a872da392b997688402a662ddbad4cf638a
7
+ data.tar.gz: 7d690dfbb506809875c63b534431bbb5658acf3b48855357087350d1856ddd21f0e21475697790917998d7b0e99d24fc588144172fa605a9d53829896f4d9f6e
data/.rubocop.yml CHANGED
@@ -1,4 +1,7 @@
1
- Metrics/LineLength:
1
+ AllCops:
2
+ NewCops: enable
3
+
4
+ Layout/LineLength:
2
5
  Max: 120
3
6
 
4
7
  Metrics/AbcSize:
data/.travis.yml CHANGED
@@ -1,4 +1,10 @@
1
1
  language: ruby
2
+
3
+ rvm:
4
+ - 3.1.2
5
+ - 3.0.4
6
+ - 2.7.6
7
+
2
8
  script:
3
9
  - rspec
4
10
  - rubocop
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # MPD::Client CHANGELOG
2
2
 
3
+ ## 0.2.0
4
+
5
+ * Tested with Ruby 3.1
6
+ * Add `albumart` command
7
+ * Add `readpicture` command
8
+ * Remove `playlist` command. Use `playlistinfo` instead
9
+
3
10
  ## 0.1.0
4
11
 
5
12
  * Rename `MPDClient` to `MPD::Client`
data/Gemfile CHANGED
@@ -8,6 +8,7 @@ gemspec
8
8
  group :development, :test do
9
9
  gem 'pry'
10
10
  gem 'rubocop'
11
+ gem 'solargraph'
11
12
  end
12
13
 
13
14
  group :test do
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Anton Maminov
1
+ Copyright (c) 2012-2022 Anton Maminov
2
2
 
3
3
  MIT License
4
4
 
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/MPD_COMMANDS.md CHANGED
@@ -102,7 +102,7 @@ If the optional `SUBSYSTEMS` argument is used, MPD will only send notifications
102
102
  ---
103
103
  `mixrampdb {deciBels} => fetch_nothing`
104
104
 
105
- > Sets the threshold at which songs will be overlapped. Like crossfading but doesn't fade the track volume, just overlaps. The songs need to have MixRamp tags added by an external tool. 0dB is the normalized maximum volume so use negative values, I prefer -17dB. In the absence of mixramp tags crossfading will be used. See [mixramp](https://sourceforge.net/projects/mixramp/)
105
+ > Sets the threshold at which songs will be overlapped. Like crossfading but doesn't fade the track volume, just overlaps. The songs need to have MixRamp tags added by an external tool. 0dB is the normalized maximum volume so use negative values, I prefer -17dB. In the absence of mixramp tags crossfading will be used. See [mixramp](https://mpd.readthedocs.io/en/latest/user.html?highlight=mixramp#mixramp)
106
106
 
107
107
  ---
108
108
  `mixrampdelay {SECONDS} => fetch_nothing`
data/README.md CHANGED
@@ -31,6 +31,8 @@ gem install mpd_client
31
31
  All functionality is contained in the `MPD::Client` class. Creating an instance of this class is as simple as:
32
32
 
33
33
  ```ruby
34
+ require 'mpd_client'
35
+
34
36
  client = MPD::Client.new
35
37
  ```
36
38
 
@@ -64,6 +66,26 @@ client.status # insert the status command into the list
64
66
  client.command_list_end # result will be a Array with the results
65
67
  ```
66
68
 
69
+ ### Binary responses
70
+
71
+ Some commands can return binary data.
72
+
73
+ ```ruby
74
+ require 'mpd_client'
75
+
76
+ client = MPD::Client.new
77
+ client.connect('localhost', 6600)
78
+
79
+ if (current_song = client.currentsong)
80
+ data, io = client.readpicture(current_song['file'])
81
+ io # StringIO
82
+ data # => {"size"=>"322860", "type"=>"image/jpeg", "binary"=>"3372"}
83
+ File.write('cover.jpg', io.string)
84
+ end
85
+ ```
86
+
87
+ The above will locate album art for the current song and save image to `cover.jpg` file.
88
+
67
89
  ### Ranges
68
90
 
69
91
  Some commands(e.g. `move`, `delete`, `load`, `shuffle`, `playlistinfo`) support integer ranges(`[START:END]`) as argument. This is done in `mpd_client` by using two element array:
@@ -103,7 +125,7 @@ client = MPD::Client.new
103
125
  client.log = Logger.new($stderr)
104
126
  ```
105
127
 
106
- For more information about logging configuration, see [Logger](https://ruby-doc.org/stdlib-2.5.1/libdoc/logger/rdoc/Logger.html)
128
+ For more information about logging configuration, see [Logger](https://ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html)
107
129
 
108
130
  ## Development
109
131
 
@@ -121,6 +143,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
121
143
 
122
144
  ## License and Author
123
145
 
124
- Copyright (c) 2013-2018 by Anton Maminov
146
+ Copyright (c) 2012-2022 by Anton Maminov
125
147
 
126
148
  This library is distributed under the MIT license. Please see the LICENSE file.
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler'
4
+ Bundler.setup :default
5
+
6
+ require 'logger'
7
+ require 'mpd_client'
8
+
9
+ # MPD::Client.log = Logger.new($stderr)
10
+
11
+ client = MPD::Client.new
12
+ client.connect('localhost', 6600)
13
+
14
+ if (current_song = client.currentsong)
15
+ data, io = client.readpicture(current_song['file'])
16
+ puts data
17
+ File.write('cover.jpg', io.string)
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler'
4
+ Bundler.setup :default
5
+
6
+ require 'logger'
7
+ require 'mpd_client'
8
+
9
+ MPD::Client.log = Logger.new($stderr)
10
+
11
+ client = MPD::Client.new
12
+ client.connect('localhost', 6600)
13
+
14
+ puts client.stats
15
+ puts client.status
16
+ puts client.currentsong
17
+ puts client.playlistinfo
data/examples/stickers.rb CHANGED
@@ -9,7 +9,7 @@ require 'mpd_client'
9
9
  MPD::Client.log = Logger.new($stderr)
10
10
 
11
11
  # Stickers
12
- # http://www.musicpd.org/doc/protocol/ch03s07.html
12
+ # https://mpd.readthedocs.io/en/latest/protocol.html#stickers
13
13
 
14
14
  client = MPD::Client.new
15
15
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module MPD
4
4
  class Client
5
- VERSION = '0.1.0'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
data/lib/mpd_client.rb CHANGED
@@ -1,131 +1,130 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'socket'
4
+ require 'stringio'
4
5
  require 'mpd_client/version'
5
6
 
6
7
  module MPD
7
8
  HELLO_PREFIX = 'OK MPD '
8
9
  ERROR_PREFIX = 'ACK '
9
- SUCCESS = 'OK'
10
- NEXT = 'list_OK'
10
+ SUCCESS = "OK\n"
11
+ NEXT = "list_OK\n"
11
12
 
12
- # MPD changelog: http://git.musicpd.org/cgit/master/mpd.git/plain/NEWS
13
- # http://www.musicpd.org/doc/protocol/command_reference.html
14
- # http://git.musicpd.org/cgit/cirrus/mpd.git/plain/doc/protocol.xml
13
+ # MPD changelog: https://github.com/MusicPlayerDaemon/MPD/blob/master/NEWS
14
+ # Protocol: https://mpd.readthedocs.io/en/latest/protocol.html
15
15
  COMMANDS = {
16
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',
17
+ 'clearerror' => 'fetch_nothing',
18
+ 'currentsong' => 'fetch_object',
19
+ 'idle' => 'fetch_list',
20
+ 'noidle' => '',
21
+ 'status' => 'fetch_object',
22
+ 'stats' => 'fetch_object',
23
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',
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
33
  'replay_gain_status' => 'fetch_item',
34
- 'volume' => 'fetch_nothing',
34
+ 'volume' => 'fetch_nothing',
35
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',
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
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',
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',
68
67
  # 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',
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',
80
79
  # 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_object',
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',
95
94
  # Mounts and neighbors
96
- 'mount' => 'fetch_nothing',
97
- 'unmount' => 'fetch_nothing',
98
- 'listmounts' => 'fetch_mounts',
99
- 'listneighbors' => 'fetch_neighbors',
95
+ 'mount' => 'fetch_nothing',
96
+ 'unmount' => 'fetch_nothing',
97
+ 'listmounts' => 'fetch_mounts',
98
+ 'listneighbors' => 'fetch_neighbors',
100
99
  # 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',
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',
106
105
  # Connection Commands
107
- 'close' => '',
108
- 'kill' => '',
109
- 'password' => 'fetch_nothing',
110
- 'ping' => 'fetch_nothing',
106
+ 'close' => '',
107
+ 'kill' => '',
108
+ 'password' => 'fetch_nothing',
109
+ 'ping' => 'fetch_nothing',
111
110
  # Audio Output Commands
112
- 'disableoutput' => 'fetch_nothing',
113
- 'enableoutput' => 'fetch_nothing',
114
- 'outputs' => 'fetch_outputs',
115
- 'toggleoutput' => 'fetch_nothing',
111
+ 'disableoutput' => 'fetch_nothing',
112
+ 'enableoutput' => 'fetch_nothing',
113
+ 'outputs' => 'fetch_outputs',
114
+ 'toggleoutput' => 'fetch_nothing',
116
115
  # 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',
116
+ 'config' => 'fetch_item',
117
+ 'commands' => 'fetch_list',
118
+ 'notcommands' => 'fetch_list',
119
+ 'tagtypes' => 'fetch_list',
120
+ 'urlhandlers' => 'fetch_list',
121
+ 'decoders' => 'fetch_plugins',
123
122
  # Client To Client
124
- 'subscribe' => 'fetch_nothing',
125
- 'unsubscribe' => 'fetch_nothing',
126
- 'channels' => 'fetch_list',
127
- 'readmessages' => 'fetch_messages',
128
- 'sendmessage' => 'fetch_nothing'
123
+ 'subscribe' => 'fetch_nothing',
124
+ 'unsubscribe' => 'fetch_nothing',
125
+ 'channels' => 'fetch_list',
126
+ 'readmessages' => 'fetch_messages',
127
+ 'sendmessage' => 'fetch_nothing'
129
128
  }.freeze
130
129
 
131
130
  # The `MPD::Client` is used for interactions with a MPD server.
@@ -160,6 +159,7 @@ module MPD
160
159
 
161
160
  def add_command(name, retval)
162
161
  escaped_name = name.tr(' ', '_')
162
+
163
163
  define_method escaped_name.to_sym do |*args|
164
164
  ensure_connected
165
165
 
@@ -169,6 +169,7 @@ module MPD
169
169
 
170
170
  def remove_command(name)
171
171
  raise "Can't remove not existent '#{name}' command" unless method_defined? name.to_sym
172
+
172
173
  remove_method name.to_sym
173
174
  end
174
175
  end
@@ -188,11 +189,12 @@ module MPD
188
189
  def reconnect
189
190
  log&.info("MPD (re)connect #{@host}, #{@port}")
190
191
 
191
- @socket = if @host.start_with?('/')
192
- UNIXSocket.new(@host)
193
- else
194
- TCPSocket.new(@host, @port)
195
- end
192
+ @socket =
193
+ if @host.start_with?('/')
194
+ UNIXSocket.new(@host)
195
+ else
196
+ TCPSocket.new(@host, @port)
197
+ end
196
198
 
197
199
  hello
198
200
  @connected = true
@@ -216,15 +218,18 @@ module MPD
216
218
  @connected
217
219
  end
218
220
 
219
- # http://www.musicpd.org/doc/protocol/ch01s04.html
221
+ # https://www.musicpd.org/doc/protocol/command_lists.html
220
222
  def command_list_ok_begin
221
223
  raise 'Already in command list' unless @command_list.nil?
224
+
222
225
  write_command('command_list_ok_begin')
226
+
223
227
  @command_list = []
224
228
  end
225
229
 
226
230
  def command_list_end
227
231
  raise 'Not in command list' if @command_list.nil?
232
+
228
233
  write_command('command_list_end')
229
234
 
230
235
  fetch_command_list
@@ -238,6 +243,14 @@ module MPD
238
243
  # Sets the +logger+ used by this instance of MPD::Client
239
244
  attr_writer :log
240
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
+
241
254
  private
242
255
 
243
256
  def ensure_connected
@@ -248,10 +261,10 @@ module MPD
248
261
  @mutex.synchronize do
249
262
  write_command(command, *args)
250
263
 
251
- if !@command_list.nil?
252
- @command_list << retval
253
- else
264
+ if @command_list.nil?
254
265
  eval retval
266
+ else
267
+ @command_list << retval
255
268
  end
256
269
  end
257
270
  end
@@ -263,29 +276,34 @@ module MPD
263
276
  reconnect
264
277
  @socket.puts line
265
278
  end
279
+
266
280
  @socket.flush
267
281
  end
268
282
 
269
283
  def write_command(command, *args)
270
284
  parts = [command]
285
+
271
286
  args.each do |arg|
272
- line = if arg.is_a?(Array)
273
- arg.size == 1 ? "\"#{arg[0].to_i}:\"" : "\"#{arg[0].to_i}:#{arg[1].to_i}\""
274
- else
275
- "\"#{escape(arg)}\""
276
- end
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
277
293
 
278
294
  parts << line
279
295
  end
296
+
280
297
  # log.debug("Calling MPD: #{command}#{args}") if log
281
298
  log&.debug("Calling MPD: #{parts.join(' ')}")
282
299
  write_line(parts.join(' '))
283
300
  end
284
301
 
285
302
  def read_line
286
- line = @socket.gets.force_encoding('utf-8')
303
+ line = @socket.gets
304
+
287
305
  raise 'Connection lost while reading line' unless line.end_with?("\n")
288
- line.chomp!
306
+
289
307
  if line.start_with?(ERROR_PREFIX)
290
308
  error = line[/#{ERROR_PREFIX}(.*)/, 1].strip
291
309
  raise error
@@ -301,21 +319,22 @@ module MPD
301
319
  line
302
320
  end
303
321
 
304
- def read_pair(separator)
322
+ def read_pair
305
323
  line = read_line
324
+
306
325
  return if line.nil?
307
- pair = line.split(separator, 2)
308
- raise "Could now parse pair: '#{line}'" if pair.size < 2
309
326
 
310
- pair # Array
327
+ line.split(': ', 2)
311
328
  end
312
329
 
313
- def read_pairs(separator = ': ')
330
+ def read_pairs
314
331
  result = []
315
- pair = read_pair(separator)
332
+
333
+ pair = read_pair
334
+
316
335
  while pair
317
336
  result << pair
318
- pair = read_pair(separator)
337
+ pair = read_pair
319
338
  end
320
339
 
321
340
  result
@@ -323,6 +342,7 @@ module MPD
323
342
 
324
343
  def fetch_item
325
344
  pairs = read_pairs
345
+
326
346
  return nil if pairs.size != 1
327
347
 
328
348
  pairs[0][1]
@@ -330,6 +350,7 @@ module MPD
330
350
 
331
351
  def fetch_nothing
332
352
  line = read_line
353
+
333
354
  raise "Got unexpected value: #{line}" unless line.nil?
334
355
  end
335
356
 
@@ -338,8 +359,11 @@ module MPD
338
359
  seen = nil
339
360
 
340
361
  read_pairs.each do |key, value|
362
+ value = value.chomp.force_encoding('utf-8')
363
+
341
364
  if key != seen
342
365
  raise "Expected key '#{seen}', got '#{key}'" unless seen.nil?
366
+
343
367
  seen = key
344
368
  end
345
369
 
@@ -352,14 +376,18 @@ module MPD
352
376
  def fetch_objects(delimeters = [])
353
377
  result = []
354
378
  obj = {}
379
+
355
380
  read_pairs.each do |key, value|
356
381
  key = key.downcase
382
+ value = value.chomp.force_encoding('utf-8')
383
+
357
384
  if delimeters.include?(key)
358
385
  result << obj unless obj.empty?
359
386
  obj = {}
360
387
  elsif obj.include?(key)
361
388
  obj[key] << value
362
389
  end
390
+
363
391
  obj[key] = value
364
392
  end
365
393
 
@@ -374,6 +402,41 @@ module MPD
374
402
  objs ? objs[0] : {}
375
403
  end
376
404
 
405
+ def fetch_binary(io = StringIO.new, offset = 0, *args)
406
+ data = {}
407
+
408
+ @mutex.synchronize do
409
+ write_command(*args, offset)
410
+
411
+ binary = false
412
+
413
+ read_pairs.each do |item|
414
+ if binary
415
+ io << item.join(': ')
416
+ next
417
+ end
418
+
419
+ key = item[0]
420
+ value = item[1].chomp
421
+
422
+ binary = (key == 'binary')
423
+
424
+ data[key] = value
425
+ end
426
+ end
427
+
428
+ size = data['size'].to_i
429
+ binary = data['binary'].to_i
430
+
431
+ next_offset = offset + binary
432
+
433
+ return [data, io] if next_offset >= size
434
+
435
+ io.seek(-1, IO::SEEK_CUR)
436
+
437
+ fetch_binary(io, next_offset, *args)
438
+ end
439
+
377
440
  def fetch_changes
378
441
  fetch_objects(['cpos'])
379
442
  end
@@ -410,20 +473,13 @@ module MPD
410
473
  fetch_objects(['playlist'])
411
474
  end
412
475
 
413
- def fetch_playlist
414
- result = []
415
- read_pairs(':').each do |_key, value|
416
- result << value
417
- end
418
-
419
- result
420
- end
421
-
422
476
  def fetch_stickers
423
477
  result = []
478
+
424
479
  read_pairs.each do |_key, sticker|
425
480
  value = sticker.split('=', 2)
426
481
  raise "Could now parse sticker: #{sticker}" if value.size < 2
482
+
427
483
  result << Hash[*value]
428
484
  end
429
485
 
@@ -436,6 +492,7 @@ module MPD
436
492
 
437
493
  def fetch_command_list
438
494
  result = []
495
+
439
496
  begin
440
497
  @command_list.each do |retval|
441
498
  result << (eval retval)
@@ -449,9 +506,13 @@ module MPD
449
506
 
450
507
  def hello
451
508
  line = @socket.gets
509
+
452
510
  raise 'Connection lost while reading MPD hello' unless line.end_with?("\n")
511
+
453
512
  line.chomp!
513
+
454
514
  raise "Got invalid MPD hello: #{line}" unless line.start_with?(HELLO_PREFIX)
515
+
455
516
  @mpd_version = line[/#{HELLO_PREFIX}(.*)/, 1]
456
517
  end
457
518
 
data/mpd_client.gemspec CHANGED
@@ -14,10 +14,12 @@ Gem::Specification.new do |gem|
14
14
  gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
15
15
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
16
16
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.required_ruby_version = '>= 2.6.6'
17
18
  gem.name = 'mpd_client'
18
19
  gem.require_paths = ['lib']
19
20
  gem.version = MPD::Client::VERSION
20
21
  gem.license = 'MIT'
21
22
 
22
- gem.add_development_dependency 'bundler', '~> 1.16'
23
+ gem.add_development_dependency 'bundler'
24
+ gem.metadata['rubygems_mfa_required'] = 'true'
23
25
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mpd_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Maminov
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-31 00:00:00.000000000 Z
11
+ date: 2022-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.16'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.16'
26
+ version: '0'
27
27
  description: Yet another Ruby MPD client library
28
28
  email:
29
29
  - anton.linux@gmail.com
@@ -36,7 +36,6 @@ files:
36
36
  - ".gitignore"
37
37
  - ".hound.yml"
38
38
  - ".rubocop.yml"
39
- - ".ruby-version"
40
39
  - ".travis.yml"
41
40
  - CHANGELOG.md
42
41
  - Gemfile
@@ -46,6 +45,8 @@ files:
46
45
  - bin/console
47
46
  - bin/setup
48
47
  - examples/Gemfile
48
+ - examples/albumart.rb
49
+ - examples/client.rb
49
50
  - examples/idle.rb
50
51
  - examples/range.rb
51
52
  - examples/rangeid.rb
@@ -59,8 +60,9 @@ files:
59
60
  homepage: https://github.com/mamantoha/mpd_client
60
61
  licenses:
61
62
  - MIT
62
- metadata: {}
63
- post_install_message:
63
+ metadata:
64
+ rubygems_mfa_required: 'true'
65
+ post_install_message:
64
66
  rdoc_options: []
65
67
  require_paths:
66
68
  - lib
@@ -68,16 +70,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
68
70
  requirements:
69
71
  - - ">="
70
72
  - !ruby/object:Gem::Version
71
- version: '0'
73
+ version: 2.6.6
72
74
  required_rubygems_version: !ruby/object:Gem::Requirement
73
75
  requirements:
74
76
  - - ">="
75
77
  - !ruby/object:Gem::Version
76
78
  version: '0'
77
79
  requirements: []
78
- rubyforge_project:
79
- rubygems_version: 2.7.6
80
- signing_key:
80
+ rubygems_version: 3.3.7
81
+ signing_key:
81
82
  specification_version: 4
82
83
  summary: Simple Music Player Daemon library written entirely in Ruby
83
84
  test_files:
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.5.1