mpd_client 0.1.0 → 0.3.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: ffc79b6fabba358c1dfb4009c4ae05d1dcf8e90aa2f197adc9ffe0eb539b5bba
4
+ data.tar.gz: 703ebecf1c53fb53e96ca24abcbe7ed7d17d4499973e860730ba621a8937f5ad
5
5
  SHA512:
6
- metadata.gz: d4d105484473b078f546e9d961289552f09336e491fed04aa8213271127fcc6f725d2973746a77731fa0f505d9bf3656338b8ca40b5f908963c7dafa2daf66c4
7
- data.tar.gz: 3d9829735ff01c8b5fc27d9c3e9d9dce06dad9504a80423bb6b938a849797ecd9443a842d0fecd37a7355d1062231df74238f7c30ee49d1fbfdc99e1d6a704df
6
+ metadata.gz: 153b319bc45d272750a6df581be17694d3dd9ca57f3ce735fc8ba9598d7beff5097e0d4e773e4368e63db877d162a1e61f095dd8d0f04e2ec2f9b7c6c4a3ed89
7
+ data.tar.gz: 181f0c28e26f096fccef58f3848b25de4e7a06f6414bb38faea5fef7712904321bc10ee32e7d48cabc39c7230df937c6ba2b4a0dd9d437ebdc3bec0fd48c39eb
data/.rubocop.yml CHANGED
@@ -1,4 +1,8 @@
1
- Metrics/LineLength:
1
+ AllCops:
2
+ TargetRubyVersion: 3.2
3
+ NewCops: enable
4
+
5
+ Layout/LineLength:
2
6
  Max: 120
3
7
 
4
8
  Metrics/AbcSize:
@@ -23,5 +27,3 @@ Metrics/PerceivedComplexity:
23
27
  Security/Eval:
24
28
  Exclude:
25
29
  - 'lib/mpd_client.rb'
26
-
27
-
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,37 +1,48 @@
1
1
  # MPD::Client CHANGELOG
2
2
 
3
+ ## 0.3.0
4
+
5
+ - Require Ruby >= 3.2
6
+
7
+ ## 0.2.0
8
+
9
+ - Tested with Ruby 3.1
10
+ - Add `albumart` command
11
+ - Add `readpicture` command
12
+ - Remove `playlist` command. Use `playlistinfo` instead
13
+
3
14
  ## 0.1.0
4
15
 
5
- * Rename `MPDClient` to `MPD::Client`
16
+ - Rename `MPDClient` to `MPD::Client`
6
17
 
7
18
  ## 0.0.6
8
19
 
9
- * Fixed readcomments command
20
+ - Fixed readcomments command
10
21
 
11
22
  ## 0.0.5
12
23
 
13
- * Support for mount, umount, listmounts, listneighbors
14
- * Support for listfiles
15
- * Support for rangeid, addtagid, cleartagid
24
+ - Support for mount, umount, listmounts, listneighbors
25
+ - Support for listfiles
26
+ - Support for rangeid, addtagid, cleartagid
16
27
 
17
28
  ## 0.0.4
18
29
 
19
- * Added support for readcomments, toggleoutput, volume
20
- * Added a mutex protecting execution of MPD commands
21
- * Automatic reconnect after the server dropped the connection
30
+ - Added support for readcomments, toggleoutput, volume
31
+ - Added a mutex protecting execution of MPD commands
32
+ - Automatic reconnect after the server dropped the connection
22
33
 
23
34
  ## 0.0.3
24
35
 
25
- * Support for logging
26
- * Better support for fetching stickers from MPD
27
- * Fixed sticker commands
28
- * Add support for ranges
36
+ - Support for logging
37
+ - Better support for fetching stickers from MPD
38
+ - Fixed sticker commands
39
+ - Add support for ranges
29
40
 
30
41
  ## 0.0.2
31
42
 
32
- * Support for connecting to unix domain sockets
33
- * Fixed some bugs
43
+ - Support for connecting to unix domain sockets
44
+ - Fixed some bugs
34
45
 
35
46
  ## 0.0.1
36
47
 
37
- * Porting code from mpd-python2
48
+ - Porting code from mpd-python2
data/Gemfile CHANGED
@@ -6,8 +6,10 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
 
8
8
  group :development, :test do
9
+ gem 'bundler'
9
10
  gem 'pry'
10
11
  gem 'rubocop'
12
+ gem 'solargraph'
11
13
  end
12
14
 
13
15
  group :test do
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Anton Maminov
1
+ Copyright (c) 2012-2023 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-2023 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/range.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require 'bundler'
4
4
  Bundler.setup :default
5
5
 
6
- require 'pp'
7
6
  require 'logger'
8
7
  require 'mpd_client'
9
8
 
data/examples/rangeid.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require 'bundler'
4
4
  Bundler.setup :default
5
5
 
6
- require 'pp'
7
6
  require 'logger'
8
7
  require 'mpd_client'
9
8
 
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.3.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,20 +243,28 @@ 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
244
257
  raise 'Please connect to MPD server' unless connected?
245
258
  end
246
259
 
247
- def execute(command, *args, retval)
260
+ def execute(command, *, retval)
248
261
  @mutex.synchronize do
249
- write_command(command, *args)
262
+ write_command(command, *)
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,33 @@ 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
280
- # log.debug("Calling MPD: #{command}#{args}") if log
296
+
281
297
  log&.debug("Calling MPD: #{parts.join(' ')}")
282
298
  write_line(parts.join(' '))
283
299
  end
284
300
 
285
301
  def read_line
286
- line = @socket.gets.force_encoding('utf-8')
302
+ line = @socket.gets
303
+
287
304
  raise 'Connection lost while reading line' unless line.end_with?("\n")
288
- line.chomp!
305
+
289
306
  if line.start_with?(ERROR_PREFIX)
290
307
  error = line[/#{ERROR_PREFIX}(.*)/, 1].strip
291
308
  raise error
@@ -301,21 +318,22 @@ module MPD
301
318
  line
302
319
  end
303
320
 
304
- def read_pair(separator)
321
+ def read_pair
305
322
  line = read_line
323
+
306
324
  return if line.nil?
307
- pair = line.split(separator, 2)
308
- raise "Could now parse pair: '#{line}'" if pair.size < 2
309
325
 
310
- pair # Array
326
+ line.split(': ', 2)
311
327
  end
312
328
 
313
- def read_pairs(separator = ': ')
329
+ def read_pairs
314
330
  result = []
315
- pair = read_pair(separator)
331
+
332
+ pair = read_pair
333
+
316
334
  while pair
317
335
  result << pair
318
- pair = read_pair(separator)
336
+ pair = read_pair
319
337
  end
320
338
 
321
339
  result
@@ -323,6 +341,7 @@ module MPD
323
341
 
324
342
  def fetch_item
325
343
  pairs = read_pairs
344
+
326
345
  return nil if pairs.size != 1
327
346
 
328
347
  pairs[0][1]
@@ -330,6 +349,7 @@ module MPD
330
349
 
331
350
  def fetch_nothing
332
351
  line = read_line
352
+
333
353
  raise "Got unexpected value: #{line}" unless line.nil?
334
354
  end
335
355
 
@@ -338,8 +358,11 @@ module MPD
338
358
  seen = nil
339
359
 
340
360
  read_pairs.each do |key, value|
361
+ value = value.chomp.force_encoding('utf-8')
362
+
341
363
  if key != seen
342
364
  raise "Expected key '#{seen}', got '#{key}'" unless seen.nil?
365
+
343
366
  seen = key
344
367
  end
345
368
 
@@ -352,14 +375,18 @@ module MPD
352
375
  def fetch_objects(delimeters = [])
353
376
  result = []
354
377
  obj = {}
378
+
355
379
  read_pairs.each do |key, value|
356
380
  key = key.downcase
381
+ value = value.chomp.force_encoding('utf-8')
382
+
357
383
  if delimeters.include?(key)
358
384
  result << obj unless obj.empty?
359
385
  obj = {}
360
386
  elsif obj.include?(key)
361
387
  obj[key] << value
362
388
  end
389
+
363
390
  obj[key] = value
364
391
  end
365
392
 
@@ -374,6 +401,41 @@ module MPD
374
401
  objs ? objs[0] : {}
375
402
  end
376
403
 
404
+ def fetch_binary(io = StringIO.new, offset = 0, *)
405
+ data = {}
406
+
407
+ @mutex.synchronize do
408
+ write_command(*, offset)
409
+
410
+ binary = false
411
+
412
+ read_pairs.each do |item|
413
+ if binary
414
+ io << item.join(': ')
415
+ next
416
+ end
417
+
418
+ key = item[0]
419
+ value = item[1].chomp
420
+
421
+ binary = (key == 'binary')
422
+
423
+ data[key] = value
424
+ end
425
+ end
426
+
427
+ size = data['size'].to_i
428
+ binary = data['binary'].to_i
429
+
430
+ next_offset = offset + binary
431
+
432
+ return [data, io] if next_offset >= size
433
+
434
+ io.seek(-1, IO::SEEK_CUR)
435
+
436
+ fetch_binary(io, next_offset, *)
437
+ end
438
+
377
439
  def fetch_changes
378
440
  fetch_objects(['cpos'])
379
441
  end
@@ -410,20 +472,13 @@ module MPD
410
472
  fetch_objects(['playlist'])
411
473
  end
412
474
 
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
475
  def fetch_stickers
423
476
  result = []
477
+
424
478
  read_pairs.each do |_key, sticker|
425
479
  value = sticker.split('=', 2)
426
480
  raise "Could now parse sticker: #{sticker}" if value.size < 2
481
+
427
482
  result << Hash[*value]
428
483
  end
429
484
 
@@ -436,6 +491,7 @@ module MPD
436
491
 
437
492
  def fetch_command_list
438
493
  result = []
494
+
439
495
  begin
440
496
  @command_list.each do |retval|
441
497
  result << (eval retval)
@@ -449,9 +505,13 @@ module MPD
449
505
 
450
506
  def hello
451
507
  line = @socket.gets
508
+
452
509
  raise 'Connection lost while reading MPD hello' unless line.end_with?("\n")
510
+
453
511
  line.chomp!
512
+
454
513
  raise "Got invalid MPD hello: #{line}" unless line.start_with?(HELLO_PREFIX)
514
+
455
515
  @mpd_version = line[/#{HELLO_PREFIX}(.*)/, 1]
456
516
  end
457
517
 
data/mpd_client.gemspec CHANGED
@@ -13,11 +13,11 @@ Gem::Specification.new do |gem|
13
13
 
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
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.required_ruby_version = '>= 3.2'
17
17
  gem.name = 'mpd_client'
18
18
  gem.require_paths = ['lib']
19
19
  gem.version = MPD::Client::VERSION
20
20
  gem.license = 'MIT'
21
21
 
22
- gem.add_development_dependency 'bundler', '~> 1.16'
22
+ gem.metadata['rubygems_mfa_required'] = 'true'
23
23
  end
metadata CHANGED
@@ -1,29 +1,15 @@
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.3.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
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.16'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.16'
11
+ date: 2023-10-29 00:00:00.000000000 Z
12
+ dependencies: []
27
13
  description: Yet another Ruby MPD client library
28
14
  email:
29
15
  - anton.linux@gmail.com
@@ -36,7 +22,6 @@ files:
36
22
  - ".gitignore"
37
23
  - ".hound.yml"
38
24
  - ".rubocop.yml"
39
- - ".ruby-version"
40
25
  - ".travis.yml"
41
26
  - CHANGELOG.md
42
27
  - Gemfile
@@ -46,6 +31,8 @@ files:
46
31
  - bin/console
47
32
  - bin/setup
48
33
  - examples/Gemfile
34
+ - examples/albumart.rb
35
+ - examples/client.rb
49
36
  - examples/idle.rb
50
37
  - examples/range.rb
51
38
  - examples/rangeid.rb
@@ -59,8 +46,9 @@ files:
59
46
  homepage: https://github.com/mamantoha/mpd_client
60
47
  licenses:
61
48
  - MIT
62
- metadata: {}
63
- post_install_message:
49
+ metadata:
50
+ rubygems_mfa_required: 'true'
51
+ post_install_message:
64
52
  rdoc_options: []
65
53
  require_paths:
66
54
  - lib
@@ -68,18 +56,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
68
56
  requirements:
69
57
  - - ">="
70
58
  - !ruby/object:Gem::Version
71
- version: '0'
59
+ version: '3.2'
72
60
  required_rubygems_version: !ruby/object:Gem::Requirement
73
61
  requirements:
74
62
  - - ">="
75
63
  - !ruby/object:Gem::Version
76
64
  version: '0'
77
65
  requirements: []
78
- rubyforge_project:
79
- rubygems_version: 2.7.6
80
- signing_key:
66
+ rubygems_version: 3.4.14
67
+ signing_key:
81
68
  specification_version: 4
82
69
  summary: Simple Music Player Daemon library written entirely in Ruby
83
- test_files:
84
- - spec/mpd_client/mpd_client_spec.rb
85
- - spec/spec_helper.rb
70
+ test_files: []
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.5.1