mpd_client 0.0.1
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/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +63 -0
- data/Rakefile +2 -0
- data/lib/mpd_client/version.rb +5 -0
- data/lib/mpd_client.rb +329 -0
- data/mpd_client.gemspec +17 -0
- metadata +53 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Anton Maminov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# MPDClient
|
2
|
+
|
3
|
+
Yet another Music Player Daemon (MPD) client library written entirely in Ruby.
|
4
|
+
`mpd_client` is a Ruby port of the [python-mpd](https://github.com/Mic92/python-mpd2) library.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application `Gemfile`:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'mpd_client'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
```
|
17
|
+
$ bundle
|
18
|
+
```
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
```
|
23
|
+
$ gem install mpd_client
|
24
|
+
```
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
All functionality is contained in the `MPDClient` class. Creating an instance of this class is as simple as:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
client = MPDClient.new
|
31
|
+
```
|
32
|
+
|
33
|
+
Once you have an instance of the `MPDClient` class, start by connecting to the server:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
client.connect('localhost', 6600)
|
37
|
+
```
|
38
|
+
|
39
|
+
The client library can be used as follows:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
puts client.mpd_version # print the mpd version
|
43
|
+
puts client.search('title', 'ruby') # print the result of the command 'search title ruby'
|
44
|
+
client.close # send the close command
|
45
|
+
client.disconect # disconnect from the server
|
46
|
+
```
|
47
|
+
|
48
|
+
Command lists are also supported using `command_list_ok_begin` and `command_list_end`:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
client.command_list_ok_begin # start a command list
|
52
|
+
client.update # insert the update command into the list
|
53
|
+
client.status # insert the status command into the list
|
54
|
+
client.command_list_end # result will be a Array with the results
|
55
|
+
```
|
56
|
+
|
57
|
+
## Contributing
|
58
|
+
|
59
|
+
1. Fork it
|
60
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
61
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
62
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
63
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/mpd_client.rb
ADDED
@@ -0,0 +1,329 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
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
|
+
# http://mpd.wikia.com/wiki/MusicPlayerDaemonCommands
|
12
|
+
COMMANDS = {
|
13
|
+
# Status Commands
|
14
|
+
"clearerror" => "fetch_nothing",
|
15
|
+
"currentsong" => "fetch_object",
|
16
|
+
"idle" => "fetch_list",
|
17
|
+
"noidle" => "",
|
18
|
+
"status" => "fetch_object",
|
19
|
+
"stats" => "fetch_object",
|
20
|
+
# Playback Option Commands
|
21
|
+
"consume" => "fetch_nothing",
|
22
|
+
"crossfade" => "fetch_nothing",
|
23
|
+
"mixrampdb" => "fetch_nothing",
|
24
|
+
"mixrampdelay" => "fetch_nothing",
|
25
|
+
"random" => "fetch_nothing",
|
26
|
+
"repeat" => "fetch_nothing",
|
27
|
+
"setvol" => "fetch_nothing",
|
28
|
+
"single" => "fetch_nothing",
|
29
|
+
"replay_gain_mode" => "fetch_nothing",
|
30
|
+
"replay_gain_status" => "fetch_item",
|
31
|
+
# Playback Control Commands
|
32
|
+
"next" => "fetch_nothing",
|
33
|
+
"pause" => "fetch_nothing",
|
34
|
+
"play" => "fetch_nothing",
|
35
|
+
"playid" => "fetch_nothing",
|
36
|
+
"previous" => "fetch_nothing",
|
37
|
+
"seek" => "fetch_nothing",
|
38
|
+
"seekid" => "fetch_nothing",
|
39
|
+
"seekcur" => "fetch_nothing",
|
40
|
+
"stop" => "fetch_nothing",
|
41
|
+
# Playlist Commands
|
42
|
+
"add" => "fetch_nothing",
|
43
|
+
"addid" => "fetch_item",
|
44
|
+
"clear" => "fetch_nothing",
|
45
|
+
"delete" => "fetch_nothing",
|
46
|
+
"deleteid" => "fetch_nothing",
|
47
|
+
"move" => "fetch_nothing",
|
48
|
+
"moveid" => "fetch_nothing",
|
49
|
+
"playlist" => "fetch_playlist",
|
50
|
+
"playlistfind" => "fetch_songs",
|
51
|
+
"playlistid" => "fetch_songs",
|
52
|
+
"playlistinfo" => "fetch_songs",
|
53
|
+
"playlistsearch" => "fetch_songs",
|
54
|
+
"plchanges" => "fetch_songs",
|
55
|
+
"plchangesposid" => "fetch_changes",
|
56
|
+
"prio" => "fetch_nothing",
|
57
|
+
"prioid" => "fetch_nothing",
|
58
|
+
"shuffle" => "fetch_nothing",
|
59
|
+
"swap" => "fetch_nothing",
|
60
|
+
"swapid" => "fetch_nothing",
|
61
|
+
# Stored Playlist Commands
|
62
|
+
"listplaylist" => "fetch_list",
|
63
|
+
"listplaylistinfo" => "fetch_songs",
|
64
|
+
"listplaylists" => "fetch_playlists",
|
65
|
+
"load" => "fetch_nothing",
|
66
|
+
"playlistadd" => "fetch_nothing",
|
67
|
+
"playlistclear" => "fetch_nothing",
|
68
|
+
"playlistdelete" => "fetch_nothing",
|
69
|
+
"playlistmove" => "fetch_nothing",
|
70
|
+
"rename" => "fetch_nothing",
|
71
|
+
"rm" => "fetch_nothing",
|
72
|
+
"save" => "fetch_nothing",
|
73
|
+
# Database Commands
|
74
|
+
"count" => "fetch_object",
|
75
|
+
"find" => "fetch_songs",
|
76
|
+
"findadd" => "fetch_nothing",
|
77
|
+
"list" => "fetch_list",
|
78
|
+
"listall" => "fetch_database",
|
79
|
+
"listallinfo" => "fetch_database",
|
80
|
+
"lsinfo" => "fetch_database",
|
81
|
+
"search" => "fetch_songs",
|
82
|
+
"update" => "fetch_item",
|
83
|
+
"rescan" => "fetch_item",
|
84
|
+
# Sticker Commands
|
85
|
+
"sticker get" => "fetch_item",
|
86
|
+
"sticker set" => "fetch_nothing",
|
87
|
+
"sticker delete" => "fetch_nothing",
|
88
|
+
"sticker list" => "fetch_list",
|
89
|
+
"sticker find" => "fetch_songs",
|
90
|
+
# Connection Commands
|
91
|
+
"close" => "",
|
92
|
+
"kill" => "",
|
93
|
+
"password" => "fetch_nothing",
|
94
|
+
"ping" => "fetch_nothing",
|
95
|
+
# Audio Output Commands
|
96
|
+
"disableoutput" => "fetch_nothing",
|
97
|
+
"enableoutput" => "fetch_nothing",
|
98
|
+
"outputs" => "fetch_outputs",
|
99
|
+
# Reflection Commands
|
100
|
+
"config" => "fetch_item",
|
101
|
+
"commands" => "fetch_list",
|
102
|
+
"notcommands" => "fetch_list",
|
103
|
+
"tagtypes" => "fetch_list",
|
104
|
+
"urlhandlers" => "fetch_list",
|
105
|
+
"decoders" => "fetch_plugins",
|
106
|
+
# Client To Client
|
107
|
+
"subscribe" => "fetch_nothing",
|
108
|
+
"unsubscribe" => "fetch_nothing",
|
109
|
+
"channels" => "fetch_list",
|
110
|
+
"readmessages" => "fetch_messages",
|
111
|
+
"sendmessage" => "fetch_nothing"
|
112
|
+
}
|
113
|
+
|
114
|
+
class MPDClient
|
115
|
+
attr_reader :mpd_version
|
116
|
+
|
117
|
+
def initialize
|
118
|
+
reset
|
119
|
+
end
|
120
|
+
|
121
|
+
def connect(host = 'localhost', port = 6600)
|
122
|
+
@socket = TCPSocket.new(host, port)
|
123
|
+
hello
|
124
|
+
end
|
125
|
+
|
126
|
+
def disconnect
|
127
|
+
@socket.close
|
128
|
+
reset
|
129
|
+
end
|
130
|
+
|
131
|
+
# http://www.musicpd.org/doc/protocol/ch01s04.html
|
132
|
+
def command_list_ok_begin
|
133
|
+
raise "Already in command list" unless @command_list.nil?
|
134
|
+
write_command('command_list_ok_begin')
|
135
|
+
@command_list = []
|
136
|
+
end
|
137
|
+
|
138
|
+
def command_list_end
|
139
|
+
raise "Not in command list" if @command_list.nil?
|
140
|
+
write_command('command_list_end')
|
141
|
+
|
142
|
+
return fetch_command_list
|
143
|
+
end
|
144
|
+
|
145
|
+
class << self
|
146
|
+
def add_command(name, retval)
|
147
|
+
define_method name.to_sym do |*args|
|
148
|
+
execute(name, *args, retval)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def remove_command(name)
|
153
|
+
raise "Can't remove not existent '#{name}' command" unless method_defined? name.to_sym
|
154
|
+
remove_method name.to_sym
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def execute(command, *args, retval)
|
161
|
+
if !@command_list.nil?
|
162
|
+
write_command(command, *args)
|
163
|
+
@command_list << retval
|
164
|
+
else
|
165
|
+
write_command(command, *args)
|
166
|
+
eval retval
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def write_line(line)
|
171
|
+
@socket.puts line
|
172
|
+
@socket.flush
|
173
|
+
end
|
174
|
+
|
175
|
+
def write_command(command, *args)
|
176
|
+
parts = [command]
|
177
|
+
args.each{|arg| parts << "\"#{escape(arg)}\""}
|
178
|
+
write_line(parts.join(' '))
|
179
|
+
end
|
180
|
+
|
181
|
+
def read_line
|
182
|
+
line = @socket.gets.force_encoding('utf-8')
|
183
|
+
raise "Connection lost while reading line" unless line.end_with?("\n")
|
184
|
+
line.chomp!
|
185
|
+
if line.start_with?(ERROR_PREFIX)
|
186
|
+
error = line[/#{ERROR_PREFIX}(.*)/, 1].strip
|
187
|
+
raise error
|
188
|
+
end
|
189
|
+
|
190
|
+
if !@command_list.nil?
|
191
|
+
return if line == NEXT
|
192
|
+
raise "Got unexpected '#{SUCCESS}'" if line == SUCCESS
|
193
|
+
elsif line == SUCCESS
|
194
|
+
return
|
195
|
+
end
|
196
|
+
|
197
|
+
return line
|
198
|
+
end
|
199
|
+
|
200
|
+
def read_pair(separator)
|
201
|
+
line = read_line
|
202
|
+
return if line.nil?
|
203
|
+
pair = line.split(separator)
|
204
|
+
raise "Could now parse pair: '#{line}'" if pair.size < 2
|
205
|
+
|
206
|
+
return pair #Array
|
207
|
+
end
|
208
|
+
|
209
|
+
def read_pairs(separator = ': ')
|
210
|
+
result = []
|
211
|
+
pair = read_pair(separator)
|
212
|
+
while pair
|
213
|
+
result << pair
|
214
|
+
pair = read_pair(separator)
|
215
|
+
end
|
216
|
+
|
217
|
+
return result
|
218
|
+
end
|
219
|
+
|
220
|
+
def fetch_item
|
221
|
+
pairs = read_pairs
|
222
|
+
return nil if pairs.size != 1
|
223
|
+
return pairs[0][1]
|
224
|
+
end
|
225
|
+
|
226
|
+
def fetch_nothing
|
227
|
+
line = read_line
|
228
|
+
raise "Got unexpected return value: #{line}" unless line.nil?
|
229
|
+
end
|
230
|
+
|
231
|
+
def fetch_list
|
232
|
+
result = []
|
233
|
+
seen = nil
|
234
|
+
read_pairs.each do |key, value|
|
235
|
+
if key != seen
|
236
|
+
if seen != nil
|
237
|
+
raise "Expected key '#{seen}', got '#{key}'"
|
238
|
+
end
|
239
|
+
seen = key
|
240
|
+
end
|
241
|
+
result << value
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def fetch_objects(delimeters = [])
|
246
|
+
result = []
|
247
|
+
obj = {}
|
248
|
+
read_pairs.each do |key, value|
|
249
|
+
key = key.downcase
|
250
|
+
if delimeters.include?(key)
|
251
|
+
result << obj unless obj.empty?
|
252
|
+
obj = {}
|
253
|
+
elsif obj.include?(key)
|
254
|
+
obj[key] << value
|
255
|
+
end
|
256
|
+
obj[key] = value
|
257
|
+
end
|
258
|
+
|
259
|
+
result << obj unless obj.empty?
|
260
|
+
|
261
|
+
return result
|
262
|
+
end
|
263
|
+
|
264
|
+
def fetch_object
|
265
|
+
objs = fetch_objects
|
266
|
+
return objs ? objs[0] : {}
|
267
|
+
end
|
268
|
+
|
269
|
+
def fetch_changes; fetch_objects(['cpos']); end
|
270
|
+
|
271
|
+
def fetch_songs; fetch_objects(['file']); end
|
272
|
+
|
273
|
+
def fetch_messages; fetch_objects('channel'); end
|
274
|
+
|
275
|
+
def fetch_outputs; fetch_objects(['outputid']); end
|
276
|
+
|
277
|
+
def fetch_plugins; fetch_objects(['plugin']); end
|
278
|
+
|
279
|
+
def fetch_database; fetch_objects(['file', 'directory', 'playlist']); end
|
280
|
+
|
281
|
+
def fetch_playlists; fetch_objects(['playlist']); end
|
282
|
+
|
283
|
+
def fetch_playlist
|
284
|
+
result = []
|
285
|
+
read_pairs(':').each do |key, value|
|
286
|
+
result << value
|
287
|
+
end
|
288
|
+
|
289
|
+
return result
|
290
|
+
end
|
291
|
+
|
292
|
+
def fetch_command_list
|
293
|
+
result = []
|
294
|
+
begin
|
295
|
+
@command_list.each do |retval|
|
296
|
+
result << (eval retval)
|
297
|
+
end
|
298
|
+
ensure
|
299
|
+
@command_list = nil
|
300
|
+
end
|
301
|
+
|
302
|
+
return result
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
def hello
|
307
|
+
line = @socket.gets
|
308
|
+
raise "Connection lost while reading MPD hello" unless line.end_with?("\n")
|
309
|
+
line.chomp!
|
310
|
+
raise "Got invalid MPD hello: #{line}" unless line.start_with?(HELLO_PREFIX)
|
311
|
+
@mpd_version = line[/#{HELLO_PREFIX}(.*)/, 1]
|
312
|
+
end
|
313
|
+
|
314
|
+
def reset
|
315
|
+
@mpd_version = nil
|
316
|
+
@command_list = nil
|
317
|
+
@socket = nil
|
318
|
+
end
|
319
|
+
|
320
|
+
def escape(text)
|
321
|
+
text.gsub("\\", "\\\\").gsub('"', '\\"')
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
COMMANDS.each_pair do |name, callback|
|
327
|
+
MPDClient.add_command(name, callback)
|
328
|
+
end
|
329
|
+
|
data/mpd_client.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/mpd_client/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Anton Maminov"]
|
6
|
+
gem.email = ["anton.linux@gmail.com"]
|
7
|
+
gem.description = %q{Yet another Ruby MPD client library}
|
8
|
+
gem.summary = %q{Simple Music Player Daemon library written entirely in Ruby}
|
9
|
+
gem.homepage = "https://github.com/mamantoha/mpd_client"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "mpd_client"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = MPDClient::VERSION
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mpd_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Anton Maminov
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-30 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Yet another Ruby MPD client library
|
15
|
+
email:
|
16
|
+
- anton.linux@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- Gemfile
|
23
|
+
- LICENSE
|
24
|
+
- README.md
|
25
|
+
- Rakefile
|
26
|
+
- lib/mpd_client.rb
|
27
|
+
- lib/mpd_client/version.rb
|
28
|
+
- mpd_client.gemspec
|
29
|
+
homepage: https://github.com/mamantoha/mpd_client
|
30
|
+
licenses: []
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 1.8.21
|
50
|
+
signing_key:
|
51
|
+
specification_version: 3
|
52
|
+
summary: Simple Music Player Daemon library written entirely in Ruby
|
53
|
+
test_files: []
|