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 +4 -4
- data/.rubocop.yml +4 -1
- data/.travis.yml +6 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +1 -0
- data/LICENSE +2 -2
- data/MPD_COMMANDS.md +1 -1
- data/README.md +24 -2
- data/examples/albumart.rb +18 -0
- data/examples/client.rb +17 -0
- data/examples/stickers.rb +1 -1
- data/lib/mpd_client/version.rb +1 -1
- data/lib/mpd_client.rb +198 -137
- data/mpd_client.gemspec +3 -1
- metadata +15 -14
- data/.ruby-version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f6e3b14a951fdfbd9467155757083aff2fa275945363cf137e813dfce3e80ab
|
4
|
+
data.tar.gz: ce2ae51be7986266b0ad1978e76e5714ca6a4937d29bbeec905c295fedaa8960
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e0b9ab125796173047fe37d4ddcd1885f1381bb3b9723da38c6999e385a457d72f064d3ec400ac62c5a58c2674386a872da392b997688402a662ddbad4cf638a
|
7
|
+
data.tar.gz: 7d690dfbb506809875c63b534431bbb5658acf3b48855357087350d1856ddd21f0e21475697790917998d7b0e99d24fc588144172fa605a9d53829896f4d9f6e
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
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://
|
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
|
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)
|
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
|
data/examples/client.rb
ADDED
@@ -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
data/lib/mpd_client/version.rb
CHANGED
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 =
|
10
|
-
NEXT =
|
10
|
+
SUCCESS = "OK\n"
|
11
|
+
NEXT = "list_OK\n"
|
11
12
|
|
12
|
-
# MPD changelog:
|
13
|
-
#
|
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'
|
18
|
-
'currentsong'
|
19
|
-
'idle'
|
20
|
-
'noidle'
|
21
|
-
'status'
|
22
|
-
'stats'
|
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'
|
25
|
-
'crossfade'
|
26
|
-
'mixrampdb'
|
27
|
-
'mixrampdelay'
|
28
|
-
'random'
|
29
|
-
'repeat'
|
30
|
-
'setvol'
|
31
|
-
'single'
|
32
|
-
'replay_gain_mode'
|
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'
|
34
|
+
'volume' => 'fetch_nothing',
|
35
35
|
# Playback Control Commands
|
36
|
-
'next'
|
37
|
-
'pause'
|
38
|
-
'play'
|
39
|
-
'playid'
|
40
|
-
'previous'
|
41
|
-
'seek'
|
42
|
-
'seekid'
|
43
|
-
'seekcur'
|
44
|
-
'stop'
|
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'
|
47
|
-
'addid'
|
48
|
-
'addtagid'
|
49
|
-
'cleartagid'
|
50
|
-
'clear'
|
51
|
-
'delete'
|
52
|
-
'deleteid'
|
53
|
-
'move'
|
54
|
-
'moveid'
|
55
|
-
'
|
56
|
-
'
|
57
|
-
'
|
58
|
-
'
|
59
|
-
'
|
60
|
-
'
|
61
|
-
'
|
62
|
-
'
|
63
|
-
'
|
64
|
-
'
|
65
|
-
'
|
66
|
-
'
|
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'
|
70
|
-
'listplaylistinfo'
|
71
|
-
'listplaylists'
|
72
|
-
'load'
|
73
|
-
'playlistadd'
|
74
|
-
'playlistclear'
|
75
|
-
'playlistdelete'
|
76
|
-
'playlistmove'
|
77
|
-
'rename'
|
78
|
-
'rm'
|
79
|
-
'save'
|
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'
|
82
|
-
'find'
|
83
|
-
'findadd'
|
84
|
-
'list'
|
85
|
-
'listall'
|
86
|
-
'listallinfo'
|
87
|
-
'listfiles'
|
88
|
-
'lsinfo'
|
89
|
-
'search'
|
90
|
-
'searchadd'
|
91
|
-
'searchaddp1'
|
92
|
-
'update'
|
93
|
-
'rescan'
|
94
|
-
'readcomments'
|
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'
|
97
|
-
'unmount'
|
98
|
-
'listmounts'
|
99
|
-
'listneighbors'
|
95
|
+
'mount' => 'fetch_nothing',
|
96
|
+
'unmount' => 'fetch_nothing',
|
97
|
+
'listmounts' => 'fetch_mounts',
|
98
|
+
'listneighbors' => 'fetch_neighbors',
|
100
99
|
# Sticker Commands
|
101
|
-
'sticker get'
|
102
|
-
'sticker set'
|
103
|
-
'sticker delete'
|
104
|
-
'sticker list'
|
105
|
-
'sticker find'
|
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'
|
110
|
-
'ping'
|
106
|
+
'close' => '',
|
107
|
+
'kill' => '',
|
108
|
+
'password' => 'fetch_nothing',
|
109
|
+
'ping' => 'fetch_nothing',
|
111
110
|
# Audio Output Commands
|
112
|
-
'disableoutput'
|
113
|
-
'enableoutput'
|
114
|
-
'outputs'
|
115
|
-
'toggleoutput'
|
111
|
+
'disableoutput' => 'fetch_nothing',
|
112
|
+
'enableoutput' => 'fetch_nothing',
|
113
|
+
'outputs' => 'fetch_outputs',
|
114
|
+
'toggleoutput' => 'fetch_nothing',
|
116
115
|
# Reflection Commands
|
117
|
-
'config'
|
118
|
-
'commands'
|
119
|
-
'notcommands'
|
120
|
-
'tagtypes'
|
121
|
-
'urlhandlers'
|
122
|
-
'decoders'
|
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'
|
125
|
-
'unsubscribe'
|
126
|
-
'channels'
|
127
|
-
'readmessages'
|
128
|
-
'sendmessage'
|
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 =
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
-
#
|
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
|
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 =
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
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
|
303
|
+
line = @socket.gets
|
304
|
+
|
287
305
|
raise 'Connection lost while reading line' unless line.end_with?("\n")
|
288
|
-
|
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
|
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
|
-
|
327
|
+
line.split(': ', 2)
|
311
328
|
end
|
312
329
|
|
313
|
-
def read_pairs
|
330
|
+
def read_pairs
|
314
331
|
result = []
|
315
|
-
|
332
|
+
|
333
|
+
pair = read_pair
|
334
|
+
|
316
335
|
while pair
|
317
336
|
result << pair
|
318
|
-
pair = read_pair
|
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'
|
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.
|
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:
|
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: '
|
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: '
|
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
|
-
|
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:
|
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
|
-
|
79
|
-
|
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
|