ruby-mpd 0.3.1 → 0.3.2

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/README.rdoc DELETED
@@ -1,296 +0,0 @@
1
- = ruby-mpd
2
-
3
- ruby-mpd is a powerful object-oriented Music Player Daemon library, forked from librmpd.
4
- librmpd is as of writing outdated by 6 years! This library tries to act as a successor,
5
- originally using librmpd as a base, however almost all of the codebase was rewritten.
6
- ruby-mpd supports all "modern" MPD features as well as callbacks.
7
-
8
- == MPD Protocol
9
-
10
- The Music Player Daemon protocol is implemented inside the library. The implementation
11
- brings the entire set of features to ruby, with support of the newest protocol commands.
12
- However some commands were remapped, some were converted to objects, as I felt they fit
13
- this way much more into ruby and are more intuitive.
14
-
15
- == Installation
16
-
17
- gem install ruby-mpd
18
-
19
- == Usage
20
-
21
- Require the library.
22
-
23
- require 'ruby-mpd'
24
-
25
- Then, make a new MPD instance:
26
-
27
- mpd = MPD.new 'localhost', 6600
28
-
29
- You can also omit the host and port, and it will use the defaults.
30
-
31
- mpd = MPD.new 'localhost'
32
- mpd = MPD.new
33
-
34
- Once you have an instance of the MPD class, connect to the server.
35
-
36
- mpd.connect
37
-
38
- When you are done, disconnect by calling disconnect.
39
-
40
- mpd.disconnect
41
-
42
- *Note*: In the past, one had to tackle the issue of the server possibly disconnecting
43
- the client at any time due to inactivity. Since 0.3.0, this is handled automatically
44
- via a reconnect mechanism.
45
-
46
- Once connected, you can issue commands to talk to the server.
47
-
48
- mpd.connect
49
-
50
- mpd.play if mpd.stopped?
51
-
52
- song = mpd.current_song
53
- puts "Current Song: #{song.artist} - #{song.title}"
54
-
55
- Command documentation can be found {here}[http://www.rubydoc.info/github/archSeer/ruby-mpd/master/MPD].
56
-
57
- == Commands
58
-
59
- Some commands require URI paths. ruby-mpd allows you to use MPD::Song objects directly
60
- and it extracts the file paths behind the scenes.
61
-
62
- song = mpd.songs_by_artist('Elvis Presley').first # => MPD::Song
63
- mpd.add song
64
-
65
- === Options
66
-
67
- Some commands accept "option hashes" besides their default values. For example, +#move+
68
- accepts an ID key instead of the position:
69
-
70
- mpd.move(1, 10) # => move first song to position 10.
71
- mpd.move({:id => 1}, 10) # => move the song with the ID of 1 to position 10.
72
-
73
- Commands that accept ID's: +#move+, +#delete+, +#play+, +#song_priority+. +#seek+
74
- accepts both +:pos+ and +:id+. *Note*: +#swap+ and +#swapid+ are still separate!
75
-
76
- === Ranges
77
-
78
- Some commands also allow ranges instead of numbers, specifying a range of songs.
79
- ruby-mpd correctly handles inclusive and exclusive ranges (1..10 vs 1...10). Negative
80
- range end means that we want the range to span until the end of the list.
81
-
82
- For example, +#queue+ allows us to return only a subset of the queue:
83
-
84
- mpd.queue.count # => 20
85
-
86
- mpd.queue(1..10).count # => 10
87
-
88
- mpd.queue(5..-1).count # => 15 (from 5 to the end of the range)
89
- mpd.queue(5...-1).count # => 15 (does the same)
90
-
91
- Move also allows specifying ranges to move a range of songs instead of just one.
92
-
93
- mpd.move 1, 10 # => move song 1 to position 10.
94
- mpd.move 1..3, 10 # => move songs 1, 2 and 3 to position 10 (and 11 and 12).
95
-
96
- Commands that support ranges: +#delete+, +#move+, +#queue+, +#song_priority+, +#shuffle+,
97
- +MPD::Playlist#load+.
98
-
99
- == Searching
100
-
101
- The MPD protocol supports two commands +find+ and +search+, where +find+ is strict
102
- and will be case sensitive, as well as return only full matches, while +search+ is
103
- "loose" -- case insensitive and allow partial matches.
104
-
105
- For ease of use, ruby-mpd encapsulates both +find+ and +search+ in one method,
106
- +MPD#where+.
107
-
108
- Searching is case *loose* by default, meaning it is case insensitive, and will do
109
- partial matching. To enable *strict* matching, enable the +strict+ option.
110
-
111
- This does not work for Playlist#searchadd.
112
-
113
- mpd.where({artist: 'MyArtiSt'}, {strict: true})
114
-
115
- Multiple query parameters can also be used:
116
-
117
- mpd.where(artist: 'Bonobo', album: 'Black Sands')
118
-
119
- Query keys can be any of of the tags supported by MPD (a list can be fetched via
120
- MPD#tags), or one of the two special parameters: +:file+ to search by full path
121
- (relative to database root), and +:any+ to match against all available tags.
122
-
123
- While searching, one can also enable the +add+ option, which will automatically add
124
- the songs the query returned to the queue. In that case, the response will only return
125
- +true+, stating that the operation was successful (instead of returning an array).
126
-
127
- mpd.where({artist: 'MyArtiSt'}, {strict: true, add: true})
128
-
129
- === Queue searching
130
-
131
- Queue searching works the same way (except by using +MPD#queue_where+), and it also
132
- accepts multiple search parameters (which seems to be undocumented in the MPD protocol
133
- specification).
134
-
135
- Same as +#where+, it is "loose" by default, and it supports a +:strict+ option.
136
-
137
- mpd.queue_where(artist: 'James Brown', genre: 'Funk')
138
-
139
- mpd.queue_where({artist: 'James Brown', genre: 'Funk'}, {strict: true})
140
-
141
- == Playlists
142
-
143
- Playlists are one of the objects that map the MPD commands onto a simple to use
144
- object. Instead of going trough all those function calls, passing data along to
145
- get your results, you simply use the object in an object-oriented way:
146
-
147
- mpd.playlists # => [MPD::Playlist, MPD::Playlist...]
148
-
149
- playlist = mpd.playlists.first
150
-
151
- p playlist.name # => "My playlist"
152
-
153
- playlist.songs # => [MPD::Song, MPD::Song...]
154
-
155
- playlist.rename('Awesomelist')
156
- p playlist.name # => "Awesomelist"
157
-
158
- playlist.add('awesome_track.mp3')
159
-
160
- To create a new playlist, simply create a new object. The playlist will be created
161
- in the daemon's library automatically as soon as you use +#add+ or +#searchadd+. There
162
- is also no save method, as playlists get 'saved' by the daemon any time you do an
163
- action on them (add, delete, rename).
164
-
165
- MPD::Playlist.new(mpd, 'name')
166
-
167
- Currently, one also has to pass in the MPD instance, as playlists are tied to a
168
- certain connection.
169
-
170
- == Callbacks
171
-
172
- Callbacks are a simple way to make your client respond to events, rather that
173
- have to continuously ask the server for updates. This allows you to focus on
174
- displaying the data, rather that working overly hard to get it. This is done
175
- by having a background thread continuously check the server for changes.
176
-
177
- To make use of callbacks, we need to:
178
-
179
- 1. Setup a callback to be called when something happens.
180
- 2. Create a MPD client instance with callbacks enabled.
181
-
182
- Firstly, we need to create a callback block and subscribe it, so that will get
183
- triggered whenever a specific event happens. When the callback is triggered,
184
- it will also recieve the new values of the event that happened.
185
-
186
- So how do we do this? We use the +MPD#on+ method, which sets it all up for us. The
187
- argument takes a symbol with the name of the event. The function also requires a block,
188
- which is our actual callback that will get called.
189
-
190
- mpd.on :volume do |volume|
191
- puts "Volume was set to #{volume}!"
192
- end
193
-
194
- One can also use separate methods or Procs and whatnot, just pass them in as a parameter.
195
-
196
- # Proc
197
- proc = Proc.new {|volume| puts "Volume was set to #{volume}!" }
198
- mpd.on :volume, &proc
199
-
200
- # Method
201
- def volume_change(value)
202
- puts "Volume changed to #{value}!"
203
- end
204
-
205
- method = self.method(:volume_change)
206
- mpd.on :volume, &method
207
-
208
- ruby-mpd supports callbacks for any of the keys returned by +MPD#status+, as well as +:connection+.
209
- Here's the full list of events, along with the variables it will return:
210
-
211
- * *volume*: The volume level as an Integer between 0-100.
212
- * *repeat*: true or false
213
- * *random*: true or false
214
- * *single*: true or false
215
- * *consume*: true or false
216
- * *playlist*: 31-bit unsigned Integer, the playlist version number.
217
- * *playlistlength*: Integer, the length of the playlist
218
- * *state*: :play, :stop, or :pause, state of the playback.
219
- * *song*: An MPD::Song object, representing the current song.
220
- * *songid*: playlist songid of the current song stopped on or playing.
221
- * *nextsong*: playlist song number of the next song to be played.
222
- * *nextsongid*: playlist songid of the next song to be played.
223
- * *time*: Returns two variables, *+total+* and *+elapsed+*, Integers representing seconds.
224
- * *elapsed*: Float, representing total time elapsed within the current song, but with higher accuracy.
225
- * *bitrate*: instantaneous bitrate in kbps.
226
- * *xfade*: crossfade in seconds
227
- * *mixrampdb*: mixramp threshold in dB (Float)
228
- * *mixrampdelay*: mixrampdelay in seconds
229
- * *audio*: Returns three variables: sampleRate, bits and channels.
230
- * *updating_db*: job id
231
- * *error*: if there is an error, returns message here
232
-
233
- * *connection*: Are we connected to the daemon? true or false
234
-
235
- Note that if the callback returns more than one value, the callback needs more arguments
236
- in order to recieve those values:
237
-
238
- mpd.on :audio do |sampleRate, bits, channels|
239
- puts bits
240
- end
241
-
242
- # or
243
- mpd.on :audio do |*args|
244
- puts args.join(',')
245
- end
246
-
247
- Finally, the easiest step. In order for callbacks to work, create a MPD instance
248
- with callbacks enabled:
249
-
250
- MPD.new('localhost', 6600, {callbacks: true})
251
-
252
- Easy as pie. The above will connect to the server like normal, but this time it will
253
- create a new thread that loops until you issue a `disconnect`. This loop checks the
254
- server, then sleeps for two tenths of a second, then loops.
255
-
256
- == Not yet implemented
257
-
258
- This section documents the features that are missing in this library at the moment.
259
-
260
- === Command lists
261
-
262
- Command lists are not implemented yet. The proposed API would look like:
263
-
264
- mpd.command_list do
265
- volume 80
266
- repeat true
267
- status
268
- end
269
-
270
- What makes me not so eager to implement this is that MPD returns all values one after
271
- another. This gets fixed with +command_list_ok_begin+, which returns +list_OK+ for every
272
- command used, however then we still get more than one response, and I can't think of a
273
- reasonable way to retun all of them back to the user. Maybe just ignore the return values?
274
-
275
- === Idle
276
-
277
- To implement idle, what is needed is a lock that prevents sending commands to the daemon
278
- while waiting for the response (except +noidle+). An intermediate solution would be to
279
- queue the commands to send them later, when idle has returned the response.
280
-
281
- Idle seems like a possible way to reimplement callbacks; make a separate connection
282
- and just use idle and when it returns, simply use idle again and again.
283
-
284
- === Tests
285
-
286
- Tests fail at the moment, as they are 6 years old. The entire MPD server mock class
287
- either needs to be rewritten, or a mpd.conf along with a sample database and instructions
288
- for a controlled environment needs to be written.
289
-
290
- == TODO list
291
-
292
- * MPD::Song, MPD::Directory.
293
- * Make stickers a mixin for Playlist, Song, Directory...
294
- * Namespace queue
295
-
296
- * Merge +where+ and +queue_where+, by doing +where(..., {in_queue: true})+?
data/test/test_parser.rb DELETED
@@ -1,52 +0,0 @@
1
- require_relative '../lib/ruby-mpd'
2
- require 'minitest/autorun'
3
-
4
- Parser = Class.new do
5
- include MPD::Parser
6
- end
7
- Parser.send(:public, *MPD::Parser.private_instance_methods)
8
-
9
- class TestParser < MiniTest::Test
10
-
11
- def setup
12
- @parser = Parser.new
13
- end
14
-
15
- def teardown
16
-
17
- end
18
-
19
- # Conversions for commands to the server
20
- def test_convert_bool
21
- assert_equal @parser.convert_command(:pause, true), 'pause 1'
22
- assert_equal @parser.convert_command(:pause, false), 'pause 0'
23
- end
24
-
25
- def test_convert_range
26
- # inclusive range
27
- assert_equal @parser.convert_command(:playlistinfo, 1..10), 'playlistinfo 1:11'
28
- # exclusive range
29
- assert_equal @parser.convert_command(:playlistinfo, 2...5), 'playlistinfo 2:5'
30
-
31
- # negative means "till end of range"
32
- assert_equal @parser.convert_command(:playlistinfo, 2...-1), 'playlistinfo 2:'
33
- end
34
-
35
- def test_convert_escape_whitespace
36
- assert_equal @parser.convert_command(:lsinfo, '/media/Storage/epic music'), 'lsinfo "/media/Storage/epic music"'
37
- end
38
-
39
- # Parse replies from server
40
- def test_parse_empty_listall_command
41
- assert_equal @parser.parse_response(:listall, ''), {}
42
- end
43
-
44
- def test_parse_playlist_uint
45
- assert_equal @parser.parse_key(:playlist, '31'), 31
46
- end
47
-
48
- def test_parse_playlist_name
49
- assert_equal @parser.parse_key(:playlist, 'leftover/classics.m3u'), 'leftover/classics.m3u'
50
- end
51
-
52
- end