mpd_client 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mpd_client.gemspec
4
+ gemspec
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
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ class MPDClient
4
+ VERSION = "0.0.1"
5
+ end
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
+
@@ -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: []