ruby-mpd 0.1.5 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/examples/rmpc.rb DELETED
@@ -1,67 +0,0 @@
1
- #!/usr/bin/ruby -w
2
-
3
- # This is a very simple MPD client that just sends commands to the server
4
- # from the command line
5
- #
6
- # Copyright 2006 Andrew Rader ( bitwise_mcgee AT yahoo.com | http://nymb.us )
7
- #
8
-
9
- require 'librmpd'
10
-
11
- if ARGV.length == 0
12
- puts "Usage: rmpc.rb <command> <command options>"
13
- puts "\tUse --help for commands / command options"
14
-
15
- else
16
- if ARGV.include?( '--help' ) or ARGV.include?( '-h' )
17
- puts "Usage: rmpc.rb <command> <command options>"
18
- puts "\tAvailable Commands / Command Options:\n\n"
19
- puts "\tcmd\topts\tdescription"
20
- puts "\tplay\t[pos]\tbegin playback, optionally play song at position pos"
21
- puts "\tpause\tnone\ttoggle the pause flag"
22
- puts "\tstop\tnone\tstop playback"
23
- puts "\tnext\tnone\tplay next in playlist"
24
- puts "\tprev\tnone\tplay previous in playlist"
25
- puts "\tvolume\t[vol]\tprint the current volume, or, sets the volume to vol"
26
- puts "\trepeat\tnone\ttoggle the repeat flag"
27
- puts "\trandom\tnone\ttoggle the random flag"
28
- puts "\tstats\tnone\tprint the server stats"
29
- else
30
- mpd = MPD.new
31
- mpd.connect
32
- case ARGV[0]
33
- when 'play'
34
- mpd.play ARGV[1].to_i - 1
35
- when 'pause'
36
- mpd.pause = !mpd.paused?
37
- when 'stop'
38
- mpd.stop
39
- when 'next'
40
- mpd.next
41
- when 'prev'
42
- mpd.previous
43
- when 'volume'
44
- if ARGV[1].nil?
45
- puts "Volume: #{mpd.volume}"
46
- else
47
- mpd.volume = ARGV[1].to_i
48
- end
49
- when 'consume'
50
- mpd.consume = !mpd.consume?
51
- when 'single'
52
- mpd.single = !mpd.single?
53
- when 'repeat'
54
- mpd.repeat = !mpd.repeat?
55
- when 'random'
56
- mpd.random = !mpd.random?
57
- when 'stats'
58
- hash = mpd.stats
59
- hash.each_pair do |key, value|
60
- puts "#{key} => #{value}"
61
- end
62
- else
63
- puts "Unknown Command #{ARGV[0]}"
64
- end
65
- mpd.disconnect
66
- end
67
- end
data/examples/tailmpc.rb DELETED
@@ -1,115 +0,0 @@
1
- #!/usr/bin/ruby -w
2
-
3
- #
4
- # This is a very simple MPD client that just spews changes on the server
5
- # out to the console (much like tail works on normal files)
6
- #
7
- # Copyright 2006 Andrew Rader ( bitwise_mcgee AT yahoo.com | http://nymb.us )
8
- #
9
-
10
- require 'librmpd'
11
-
12
- class TailMPC
13
-
14
- def initialize( time_cb = false )
15
- @mpd = MPD.new
16
-
17
- @mpd.register_callback( self.method('playlist_cb'), MPD::PLAYLIST_CALLBACK )
18
- @mpd.register_callback( self.method('song_cb'), MPD::CURRENT_SONG_CALLBACK )
19
- @mpd.register_callback( self.method('state_cb'), MPD::STATE_CALLBACK )
20
- @mpd.register_callback( self.method('time_cb'), MPD::TIME_CALLBACK ) if time_cb
21
- @mpd.register_callback( self.method('vol_cb'), MPD::VOLUME_CALLBACK )
22
- @mpd.register_callback( self.method('repeat_cb'), MPD::REPEAT_CALLBACK )
23
- @mpd.register_callback( self.method('random_cb'), MPD::RANDOM_CALLBACK )
24
- @mpd.register_callback( self.method('pls_length_cb'), MPD::PLAYLIST_LENGTH_CALLBACK )
25
- @mpd.register_callback( self.method('xfade_cb'), MPD::CROSSFADE_CALLBACK )
26
- @mpd.register_callback( self.method('songid_cb'), MPD::CURRENT_SONGID_CALLBACK )
27
- @mpd.register_callback( self.method('bitrate_cb'), MPD::BITRATE_CALLBACK )
28
- @mpd.register_callback( self.method('audio_cb'), MPD::AUDIO_CALLBACK )
29
- @mpd.register_callback( self.method('connection_cb'), MPD::CONNECTION_CALLBACK )
30
- end
31
-
32
- def start
33
- puts "Starting TailMPC - Press Ctrl-D to quit\n\n"
34
- @mpd.connect true
35
- t = Thread.new do
36
- gets
37
- end
38
-
39
- t.join
40
- end
41
-
42
- def stop
43
- puts "Shutting Down TailMPC"
44
- @mpd.disconnect true
45
- end
46
-
47
- def time_cb( elapsed, total )
48
- el_min = elapsed / 60
49
- el_sec = elapsed % 60
50
-
51
- elapsed = "#{el_min}:#{el_sec}"
52
-
53
- tot_min = total / 60
54
- tot_sec = total % 60
55
-
56
- total = "#{tot_min}:#{tot_sec}"
57
- puts "Time: #{elapsed} / #{total}"
58
- end
59
-
60
- def state_cb( newstate )
61
- puts "State: #{newstate}"
62
- end
63
-
64
- def song_cb( current )
65
- if not current.nil?
66
- puts "Current Song: \n\tID: #{current.songid}\n\tPosition: #{current.pos}\n\tFile: #{current.file}\n\tArtist: #{current.artist}\n\tTitle: #{current.title}"
67
- else
68
- puts "Curent Song: nil"
69
- end
70
- end
71
-
72
- def playlist_cb( pls )
73
- puts "Playlist: Version ##{pls}"
74
- end
75
-
76
- def vol_cb( vol )
77
- puts "Volume: #{vol}%"
78
- end
79
-
80
- def repeat_cb( rep )
81
- puts(rep ? 'Repeat: On' : 'Repeat: Off')
82
- end
83
-
84
- def random_cb( ran )
85
- puts(ran ? 'Random: On' : 'Random: Off')
86
- end
87
-
88
- def pls_length_cb( len )
89
- puts "Playlist Length: #{len}"
90
- end
91
-
92
- def xfade_cb( xfade )
93
- puts "Crossfade: #{xfade}"
94
- end
95
-
96
- def songid_cb( id )
97
- puts "Current Song ID: #{id}"
98
- end
99
-
100
- def bitrate_cb( rate )
101
- puts "Bitrate: #{rate}"
102
- end
103
-
104
- def audio_cb( sample, bits, channels )
105
- puts "Audio:\n\tSample Rate: #{sample}\n\tBits: #{bits}\n\tChannels: #{channels}"
106
- end
107
-
108
- def connection_cb( connected )
109
- puts( connected ? 'Connected' : 'Disconnected' )
110
- end
111
- end
112
-
113
- client = TailMPC.new #true # Uncomment the true to enable the time callback
114
-
115
- client.start
data/lib/mpdserver.rb DELETED
@@ -1,1206 +0,0 @@
1
- #
2
- #== mpdserver.rb
3
- #
4
- # This is the test server for librmpd. It is a 'shallow' server,
5
- # it implements only the client/server protocol in a highly
6
- # scriptable manner. This means you can set up your own simple
7
- # test music database for testing an mpd client. You can now
8
- # distribute your unit tests (you do have unit tests, yes?) along
9
- # with a test database (a YAML file), and anyone can check that
10
- # your client is in working order.
11
- #
12
- #== Usage
13
- #
14
- # The MPD Server is a subclass of GServer, so you have a lot of
15
- # flexibility at your disposal. The constructor of the server object
16
- # takes the port, an optional YAML database file, and any args for GServer.
17
- #
18
- # The YAML database file can be one of your own creation, or you can use
19
- # one supplied by librmpd (default)
20
- #
21
- # Example:
22
- #
23
- # require 'rubygems'
24
- # require 'librmpd'
25
- # require 'mpdserver'
26
- #
27
- # server = MPDTestServer.new 7700
28
- # server.start
29
- #
30
- # You can then enable auditing to see what commands are run by a client:
31
- #
32
- # server.audit = true
33
- #
34
- # This will print any commands from a client to stdout
35
- #
36
- #=== Unit Testing
37
- #
38
- # For unit testing a client using the test server, I recommend using the
39
- # set up and tear down methods to initialize and destroy a test server.
40
- #
41
- # def setup
42
- # @server = MPDTestServer.new 7700
43
- # @server.start
44
- # end
45
- #
46
- # def teardown
47
- # @server.stop
48
- # end
49
- #
50
- # This will ensure you are using a clean server instance for each test.
51
-
52
- require 'gserver'
53
- require 'yaml'
54
-
55
- class MPDTestServer < GServer
56
-
57
- def initialize( port, db_file = nil, *args )
58
- super port, *args
59
-
60
- if db_file.nil?
61
- db_file = __FILE__.gsub(/\/[^\/]*$/, '') + '/../data/database.yaml'
62
- end
63
-
64
- @status = {
65
- :volume => 0,
66
- :repeat => 0,
67
- :random => 0,
68
- :playlist => 1,
69
- :state => 'stop',
70
- :xfade => 0
71
- }
72
- @elapsed_time = 0
73
- @current_song = nil
74
- @database = YAML::load( File.open( db_file ) )
75
- @songs = @database[0]
76
- @playlists = @database[1]
77
- @artists = []
78
- @albums = []
79
- @titles = []
80
- @the_playlist = []
81
- @playback_thread = nil
82
- @filetree = {:name =>'', :dirs =>[], :songs =>[]}
83
- @songs.each_with_index do |song,i|
84
- song['id'] = i
85
- if !song['artist'].nil? and !@artists.include? song['artist']
86
- @artists << song['artist']
87
- end
88
- if !song['album'].nil? and !@albums.include? song['album']
89
- @albums << song['album']
90
- end
91
- if !song['title'].nil?
92
- @titles << song['title']
93
- end
94
- if !song['file'].nil?
95
- dirs = song['file'].split '/'
96
- dirs.pop
97
- the_dir = @filetree
98
- dirs.each do |d|
99
- found = nil
100
- the_dir[:dirs].each do |sub|
101
- if sub[:name] == d
102
- found = sub
103
- break
104
- end
105
- end
106
- if found.nil?
107
- found = {:name => d, :dirs =>[], :songs =>[]}
108
- the_dir[:dirs] << found
109
- end
110
- the_dir = found
111
- end # End dirs.each
112
- the_dir[:songs] << song
113
- end # End if !song['file'].nil?
114
- end # End @songs.each
115
-
116
- sort_dir @filetree
117
- @artists.sort!
118
- @albums.sort!
119
- @titles.sort!
120
- end
121
-
122
- def start
123
- super
124
-
125
- @playback_thread = Thread.new(@status, self) do |status, server|
126
- while not server.stopped?
127
- if status[:state] == 'play'
128
- song = server.get_current_song
129
- if song.nil?
130
- server.elapsed_time = 0
131
- status[:state] = 'stop'
132
- next
133
- end
134
-
135
- status[:time] = "#{server.elapsed_time}:#{song['time']}"
136
- status[:bitrate] = 192
137
- status[:audio] = '44100:16:2'
138
-
139
- if server.elapsed_time >= song['time'].to_i
140
- server.elapsed_time = 0
141
- server.next_song
142
- end
143
-
144
- server.elapsed_time = server.elapsed_time + 1
145
- elsif status[:state] == 'pause'
146
- song = server.get_current_song
147
- if song.nil?
148
- server.elapsed_time = 0
149
- status[:state] = 'stop'
150
- next
151
- end
152
- status[:time] = "#{server.elapsed_time}:#{song['time']}"
153
- status[:bitrate] = 192
154
- status[:audio] = '44100:16:2'
155
- else
156
- status[:time] = nil
157
- status[:bitrate] = nil
158
- status[:audio] = nil
159
- server.elapsed_time = 0
160
- end
161
- sleep 1
162
- end
163
- end
164
- end
165
-
166
- def serve( sock )
167
- command_list = []
168
- in_cmd_list = false
169
- in_ok_list = false
170
- the_error = nil
171
- sock.puts 'OK MPD 0.11.5'
172
- begin
173
- while line = sock.gets
174
-
175
- args = build_args line
176
-
177
- cmd = args.shift
178
-
179
- if cmd == 'command_list_begin' and args.length == 0 and !in_cmd_list
180
- in_cmd_list = true
181
- log 'MPD: Starting Command List' if audit
182
- elsif cmd == 'command_list_ok_begin' and args.length == 0 and !in_cmd_list
183
- in_cmd_list = true
184
- in_ok_list = true
185
- log 'MPD: Starting Command OK List' if audit
186
- elsif cmd == 'command_list_end' and in_cmd_list
187
- log 'MPD: Running Command List' if audit
188
-
189
- the_ret = true
190
- command_list.each_with_index do |set,i|
191
- the_ret = do_cmd sock, set[0], set[1]
192
-
193
- if audit
194
- log "MPD Command List: CMD ##{i}: \"#{set[0]}(#{set[1].join(', ')})\": " + (the_ret ? 'successful' : 'failed')
195
- end
196
-
197
- break unless the_ret
198
-
199
- sock.puts 'list_OK' if in_ok_list
200
-
201
- end
202
-
203
- sock.puts 'OK' if the_ret
204
-
205
- command_list.clear
206
- in_cmd_list = false
207
- in_ok_list = false
208
- else
209
- if in_cmd_list
210
- command_list << [cmd, args]
211
- else
212
- ret = do_cmd sock, cmd, args
213
- sock.puts 'OK' if ret
214
- if audit
215
- log "MPD Command \"#{cmd}(#{args.join(', ')})\": " + (ret ? 'successful' : 'failed')
216
- end # End if audit
217
- end # End if in_cmd_list
218
- end # End if cmd == 'comand_list_begin' ...
219
- end # End while line = sock.gets
220
- rescue
221
- end
222
- end
223
-
224
- def do_cmd( sock, cmd, args )
225
- case cmd
226
- when 'add'
227
- if args.length == 0
228
- # Add the entire database
229
- @songs.each do |s|
230
- s['_mod_ver'] = @status[:playlist]
231
- incr_version
232
- @the_playlist << s
233
- end
234
- return true
235
- else
236
- # Add a single entry
237
- the_song = nil
238
- @songs.each do |s|
239
- if s['file'] == args[0]
240
- the_song = s
241
- break
242
- end
243
- end
244
-
245
- if the_song.nil?
246
- dir = locate_dir(args[0])
247
- if not dir.nil?
248
- # Add the dir
249
- add_dir_to_pls dir
250
- return true
251
- else
252
- return(cmd_fail(sock,'ACK [50@0] {add} directory or file not found'))
253
- end
254
- else
255
- the_song['_mod_ver'] = @status[:playlist]
256
- incr_version
257
- @the_playlist << the_song
258
- return true
259
- end
260
- end
261
- when 'clear'
262
- args_check( sock, cmd, args, 0 ) do
263
- incr_version
264
- @the_playlist = []
265
- @current_song = nil
266
- return true
267
- end
268
- when 'clearerror'
269
- args_check( sock, cmd, args, 0 ) do
270
- the_error = nil
271
- return true
272
- end
273
- when 'close'
274
- sock.close
275
- return true
276
- when 'crossfade'
277
- args_check( sock, cmd, args, 1 ) do |args|
278
- if is_int(args[0]) and args[0].to_i >= 0
279
- @status[:xfade] = args[0].to_i
280
- return true
281
- else
282
- return(cmd_fail(sock,"ACK [2@0] {crossfade} \"#{args[0]}\" is not a integer >= 0"))
283
- end
284
- end
285
- when 'currentsong'
286
- args_check( sock, cmd, args, 0 ) do
287
- if @current_song != nil and @current_song < @the_playlist.length
288
- send_song sock, @the_playlist[@current_song]
289
- end
290
- return true
291
- end
292
- when 'delete'
293
- args_check( sock, cmd, args, 1 ) do |args|
294
- if is_int args[0]
295
- if args[0].to_i < 0 or args[0].to_i >= @the_playlist.length
296
- return(cmd_fail(sock,"ACK [50@0] {delete} song doesn't exist: \"#{args[0]}\""))
297
- else
298
- @the_playlist.delete_at args[0].to_i
299
- args[0].to_i.upto @the_playlist.length - 1 do |i|
300
- @the_playlist[i]['_mod_ver'] = @status[:playlist]
301
- end
302
- incr_version
303
- return true
304
- end
305
- else
306
- return(cmd_fail('ACK [2@0] {delete} need a positive integer'))
307
- end
308
- end
309
- when 'deleteid'
310
- args_check( sock, cmd, args, 1 ) do |args|
311
- if is_int args[0]
312
- the_song = nil
313
- @the_playlist.each do |song|
314
- if song['id'] == args[0].to_i
315
- the_song = song
316
- break
317
- end
318
- end
319
-
320
- if not the_song.nil?
321
- index = @the_playlist.index the_song
322
- @the_playlist.delete the_song
323
- index.upto @the_playlist.length - 1 do |i|
324
- @the_playlist[i]['_mod_ver'] = @status[:playlist]
325
- end
326
- incr_version
327
- return true
328
- else
329
- return(cmd_fail(sock,"ACK [50@0] {deleteid} song id doesn't exist: \"#{args[0]}\""))
330
- end
331
- else
332
- return(cmd_fail(sock,'ACK [2@0] {deleteid} need a positive integer'))
333
- end
334
- end
335
- when 'find'
336
- args_check( sock, cmd, args, 2 ) do |args|
337
- if args[0] != 'album' and args[0] != 'artist' and args[0] != 'title'
338
- return(cmd_fail(sock,'ACK [2@0] {find} incorrect arguments'))
339
- else
340
- if args[0] == 'album'
341
- @songs.each do |song|
342
- if song['album'] == args[1]
343
- send_song sock, song
344
- end
345
- end
346
- elsif args[0] == 'artist'
347
- @songs.each do |song|
348
- if song['artist'] == args[1]
349
- send_song sock, song
350
- end
351
- end
352
- elsif args[0] == 'title'
353
- @songs.each do |song|
354
- if song['title'] == args[1]
355
- send_song sock, song
356
- end
357
- end
358
- end
359
- return true
360
- end
361
- end
362
- when 'kill'
363
- args_check( sock, cmd, args, 0 ) do
364
- sock.close
365
- return true
366
- end
367
- when 'list'
368
- args_check( sock, cmd, args, 1..2 ) do |args|
369
- if args[0] != 'album' and args[0] != 'artist' and args[0] != 'title'
370
- return(cmd_fail(sock,"ACK [2@0] {list} \"#{args[0]}\" is not known"))
371
- elsif args[0] == 'artist' and args.length > 1
372
- return(cmd_fail(sock,'ACK [2@0] {list} should be "Album" for 3 arguments'))
373
- else
374
- if args[0] == 'artist'
375
- # List all Artists
376
- @artists.each do |artist|
377
- sock.puts "Artist: #{artist}"
378
- end
379
- return true
380
- elsif args[0] == 'title'
381
- # List all Titles
382
- @titles.each do |title|
383
- sock.puts "Title: #{title}"
384
- end
385
- return true
386
- else
387
- if args.length == 2
388
- # List all Albums by Artist
389
- # artist == args[1]
390
- listed = []
391
- @songs.each do |song|
392
- if song['artist'] == args[1]
393
- if not song['album'].nil? and !listed.include? song['album']
394
- sock.puts "Album: #{song['album']}"
395
- listed << song['album']
396
- end
397
- end
398
- end
399
- return true
400
- else
401
- # List all Albums
402
- @albums.each do |album|
403
- sock.puts "Album: #{album}"
404
- end
405
- return true
406
- end
407
- end
408
- end
409
- end
410
- when 'listall'
411
- args_check( sock, cmd, args, 0..1 ) do |args|
412
- if args.length == 0
413
- @filetree[:dirs].each do |d|
414
- send_dir sock, d, false
415
- end
416
- else
417
- was_song = false
418
- @songs.each do |song|
419
- if song['file'] == args[0]
420
- sock.puts "file: #{song['file']}"
421
- was_song = true
422
- break
423
- end
424
- end
425
-
426
- if was_song
427
- return true
428
- end
429
-
430
- dir = locate_dir args[0]
431
- if not dir.nil?
432
- parents = args[0].split '/'
433
- parents.pop
434
- parents = parents.join '/'
435
- parents += '/' unless parents.length == 0
436
- send_dir sock, dir, false, parents
437
- else
438
- return(cmd_fail(sock,'ACK [50@0] {listall} directory or file not found'))
439
- end
440
- end
441
- return true
442
- end
443
- when 'listallinfo'
444
- args_check( sock, cmd, args, 0..1 ) do |args|
445
- if args.length == 0
446
- @filetree[:dirs].each do |d|
447
- send_dir sock, d, true
448
- end
449
- else
450
- was_song = false
451
- @songs.each do |song|
452
- if song['file'] == args[0]
453
- send_song song
454
- was_song = true
455
- break
456
- end
457
- end
458
-
459
- if was_song
460
- return true
461
- end
462
-
463
- dir = locate_dir args[0]
464
- if not dir.nil?
465
- parents = args[0].split '/'
466
- parents.pop
467
- parents = parents.join '/'
468
- parents += '/' unless parents.length == 0
469
- send_dir sock, dir, true, parents
470
- else
471
- return(cmd_fail(sock,'ACK [50@0] {listallinfo} directory or file not found'))
472
- end
473
- end
474
- return true
475
- end
476
- when 'load'
477
- args_check( sock, cmd, args, 1 ) do
478
- # incr_version for each song loaded
479
- pls = args[0] + '.m3u'
480
- the_pls = nil
481
- @playlists.each do |p|
482
- if p['file'] == pls
483
- the_pls = p
484
- break
485
- end
486
- end
487
-
488
- unless the_pls.nil?
489
- the_pls['songs'].each do |song|
490
- song['_mod_ver'] = @status[:playlist]
491
- @the_playlist << song
492
- incr_version
493
- end
494
- else
495
- return(cmd_fail(sock,"ACK [50@0] {load} playlist \"#{args[0]}\" not found"))
496
- end
497
- end
498
- when 'lsinfo'
499
- args_check( sock, cmd, args, 0..1 ) do
500
- if args.length == 0
501
- @filetree[:dirs].each do |d|
502
- sock.puts "directory: #{d[:name]}"
503
- d[:songs].each do |s|
504
- send_song sock, s
505
- end
506
- end
507
- @playlists.each do |pls|
508
- sock.puts "playlist: #{pls['file'].gsub( /\.m3u$/, '' )}"
509
- end
510
- else
511
- dir = locate_dir args[0]
512
- if dir.nil?
513
- return(cmd_fail(sock,"ACK [50@0] {lsinfo} directory not found"))
514
- else
515
- dir[:dirs].each do |d|
516
- sock.puts "directory: #{args[0] + '/' + d[:name]}"
517
- end
518
- dir[:songs].each do |s|
519
- send_song sock, s
520
- end
521
- end
522
- end
523
- return true
524
- end
525
- when 'move'
526
- args_check( sock, cmd, args, 2 ) do |args|
527
- if !is_int args[0]
528
- return(cmd_fail(sock,"ACK [2@0] {move} \"#{args[0]}\" is not a integer"))
529
- elsif !is_int args[1]
530
- return(cmd_fail(sock,"ACK [2@0] {move} \"#{args[1]}\" is not a integer"))
531
- elsif args[0].to_i < 0 or args[0].to_i >= @the_playlist.length
532
- return(cmd_fail(sock,"ACK [50@0] {move} song doesn't exist: \"#{args[0]}\""))
533
- elsif args[1].to_i < 0 or args[1].to_i >= @the_playlist.length
534
- return(cmd_fail(sock,"ACK [50@0] {move} song doesn't exist: \"#{args[1]}\""))
535
- else
536
- tmp = @the_playlist.delete_at args[0].to_i
537
- @the_playlist.insert args[1].to_i, tmp
538
- if args[0].to_i < args[1].to_i
539
- args[0].to_i.upto args[1].to_i do |i|
540
- @the_playlist[i]['_mod_ver'] = @status[:playlist]
541
- end
542
- else
543
- args[1].to_i.upto args[0].to_i do |i|
544
- @the_playlist[i]['_mod_ver'] = @status[:playlist]
545
- end
546
- end
547
- incr_version
548
- return true
549
- end
550
- end
551
- when 'moveid'
552
- args_check( sock, cmd, args, 2 ) do |args|
553
- if !is_int args[0]
554
- return(cmd_fail(sock,"ACK [2@0] {moveid} \"#{args[0]}\" is not a integer"))
555
- elsif !is_int args[1]
556
- return(cmd_fail(sock,"ACK [2@0] {moveid} \"#{args[1]}\" is not a integer"))
557
- elsif args[1].to_i < 0 or args[1].to_i >= @the_playlist.length
558
- return(cmd_fail(sock,"ACK [50@0] {moveid} song doesn't exist: \"#{args[1]}\""))
559
- else
560
- # Note: negative args should be checked
561
- the_song = nil
562
- index = -1
563
- @the_playlist.each_with_index do |song,i|
564
- if song['id'] == args[0].to_i
565
- the_song = song
566
- index = i
567
- end
568
- end
569
- if the_song.nil?
570
- return(cmd_fail(sock,"ACK [50@0] {moveid} song id doesn't exist: \"#{args[0]}\""))
571
- end
572
- tmp = @the_playlist.delete_at index
573
- @the_playlist.insert args[1].to_i, tmp
574
- if index < args[1].to_i
575
- index.upto args[1].to_i do |i|
576
- @the_playlist[i]['_mod_ver'] = @status[:playlist]
577
- end
578
- else
579
- args[1].to_i.upto index do |i|
580
- @the_playlist[i]['_mod_ver'] = @status[:playlist]
581
- end
582
- end
583
- incr_version
584
- return true
585
- end
586
- end
587
- when 'next'
588
- args_check( sock, cmd, args, 0 ) do
589
- if @status[:state] != 'stop'
590
- next_song
591
- @elapsed_time = 0
592
- @status[:state] = 'play'
593
- end
594
- return true
595
- end
596
- when 'pause'
597
- args_check( sock, cmd, args, 0..1 ) do |args|
598
- if args.length > 0 and not is_bool args[0]
599
- return(cmd_fail(sock,"ACK [2@0] {pause} \"#{args[0]}\" is not 0 or 1"))
600
- end
601
-
602
- if @status[:state] != 'stop'
603
- if args.length == 1
604
- @status[:state] = ( args[0] == '1' ? 'pause' : 'play' )
605
- else
606
- @status[:state] = ( @status[:state] == 'pause' ? 'play' : 'pause' )
607
- end
608
- end
609
-
610
- return true
611
- end
612
- when 'password'
613
- args_check( sock, cmd, args, 1 ) do |args|
614
- return true if args[0] == 'test'
615
- return(cmd_fail(sock,"ACK [3@0] {password} incorrect password"))
616
- end
617
- when 'ping'
618
- args_check( sock, cmd, args, 0 ) do
619
- return true
620
- end
621
- when 'play'
622
- args_check( sock, cmd, args, 0..1 ) do |args|
623
- if args.length > 0 and !is_int(args[0])
624
- return(cmd_fail(sock,'ACK [2@0] {play} need a positive integer'))
625
- else
626
- args.clear if args[0] == '-1'
627
- if args.length == 0
628
- if @the_playlist.length > 0 and @status[:state] != 'play'
629
- @current_song = 0 if @current_song.nil?
630
- @elapsed_time = 0
631
- @status[:state] = 'play'
632
- end
633
- else
634
- if args[0].to_i < 0 or args[0].to_i >= @the_playlist.length
635
- return(cmd_fail(sock,"ACK [50@0] {play} song doesn't exist: \"#{args[0]}\""))
636
- end
637
-
638
- @current_song = args[0].to_i
639
- @elapsed_time = 0
640
- @status[:state] = 'play'
641
- end
642
- return true
643
- end
644
- end
645
- when 'playid'
646
- args_check( sock, cmd, args, 0..1 ) do |args|
647
- if args.length > 0 and !is_int(args[0])
648
- return(cmd_fail(sock,'ACK [2@0] {playid} need a positive integer'))
649
- else
650
- args.clear if args[0] == '-1'
651
- if args.length == 0
652
- if @the_playlist.length > 0 and @status[:state] != 'play'
653
- @current_song = 0 if @current_song.nil?
654
- @elapsed_time = 0
655
- @status[:state] = 'play'
656
- end
657
- else
658
- index = nil
659
- @the_playlist.each_with_index do |s,i|
660
- if s['id'] == args[0].to_i
661
- index = i
662
- break;
663
- end
664
- end
665
-
666
- return(cmd_fail(sock,"ACK [50@0] {playid} song id doesn't exist: \"#{args[0]}\"")) if index.nil?
667
-
668
- @current_song = index
669
- @elapsed_time = 0
670
- @status[:state] = 'play'
671
- end
672
- return true
673
- end
674
- end
675
- when 'playlist'
676
- log 'MPD Warning: Call to Deprecated API: "playlist"' if audit
677
- args_check( sock, cmd, args, 0 ) do
678
- @the_playlist.each_with_index do |v,i|
679
- sock.puts "#{i}:#{v['file']}"
680
- end
681
- return true
682
- end
683
- when 'playlistinfo'
684
- args_check( sock, cmd, args, 0..1 ) do |args|
685
- if args.length > 0 and !is_int(args[0])
686
- return(cmd_fail(sock,'ACK [2@0] {playlistinfo} need a positive integer'))
687
- else
688
- args.clear if args.length > 0 and args[0].to_i < 0
689
- if args.length != 0
690
- if args[0].to_i >= @the_playlist.length
691
- return(cmd_fail(sock,"ACK [50@0] {playlistinfo} song doesn't exist: \"#{args[0]}\""))
692
- else
693
- song = @the_playlist[args[0].to_i]
694
- send_song sock, song
695
- sock.puts "Pos: #{args[0].to_i}"
696
- sock.puts "Id: #{song['id']}"
697
- return true
698
- end
699
- else
700
- @the_playlist.each_with_index do |song,i|
701
- send_song sock, song
702
- sock.puts "Pos: #{i}"
703
- sock.puts "Id: #{song['id']}"
704
- end
705
- return true
706
- end
707
- end
708
- end
709
- when 'playlistid'
710
- args_check( sock, cmd, args, 0..1 ) do |args|
711
- if args.length > 0 and !is_int(args[0])
712
- return(cmd_fail(sock,'ACK [2@0] {playlistid} need a positive integer'))
713
- else
714
- song = nil
715
- pos = nil
716
- args.clear if args[0].to_i < 0
717
- if args.length != 0
718
- @the_playlist.each_with_index do |s,i|
719
- if s['id'] == args[0].to_i
720
- song = s
721
- pos = i
722
- break;
723
- end
724
- end
725
-
726
- return(cmd_fail(sock,"ACK [50@0] {playlistid} song id doesn't exist: \"#{args[0]}\"")) if song.nil?
727
-
728
- send_song sock, song
729
- sock.puts "Pos: #{pos}"
730
- return true
731
- else
732
- @the_playlist.each_with_index do |song,i|
733
- send_song sock, song
734
- sock.puts "Pos: #{i}"
735
- end
736
- return true
737
- end
738
- end
739
- end
740
- when 'plchanges'
741
- args_check( sock, cmd, args, 1 ) do |args|
742
- if args.length > 0 and !is_int(args[0])
743
- return(cmd_fail(sock,'ACK [2@0] {plchanges} need a positive integer'))
744
- else
745
- # Note: args[0] < 0 just return OK...
746
- @the_playlist.each_with_index do |song,i|
747
- if args[0].to_i > @status[:playlist] or song['_mod_ver'] >= args[0].to_i or song['_mod_ver'] == 0
748
- send_song sock, song
749
- sock.puts "Pos: #{i}"
750
- end
751
- end
752
- return true
753
- end
754
- end
755
- when 'plchangesposid'
756
- args_check( sock, cmd, args, 1 ) do |args|
757
- if args.length > 0 and !is_int(args[0])
758
- return(cmd_fail(sock,'ACK [2@0] {plchangesposid} need a positive integer'))
759
- else
760
- # Note: args[0] < 0 just return OK...
761
- @the_playlist.each_with_index do |song,i|
762
- if args[0].to_i > @status[:playlist] or song['_mod_ver'] >= args[0].to_i or song['_mod_ver'] == 0
763
- sock.puts "cpos: #{i}"
764
- sock.puts "Id: #{song['id']}"
765
- end
766
- end
767
- return true
768
- end
769
- end
770
- when 'previous'
771
- args_check( sock, cmd, args, 0 ) do
772
- return true if @status[:state] == 'stop'
773
- prev_song
774
- @elapsed_time = 0
775
- @status[:state] = 'play'
776
- return true
777
- end
778
- when 'random'
779
- args_check( sock, cmd, args, 1 ) do |args|
780
- if is_bool args[0]
781
- @status[:random] = args[0].to_i
782
- return true
783
- elsif is_int args[0]
784
- return(cmd_fail(sock,"ACK [2@0] {random} \"#{args[0]}\" is not 0 or 1"))
785
- else
786
- return(cmd_fail(sock,'ACK [2@0] {random} need an integer'))
787
- end
788
- end
789
- when 'repeat'
790
- args_check( sock, cmd, args, 1 ) do |args|
791
- if is_bool args[0]
792
- @status[:repeat] = args[0].to_i
793
- return true
794
- elsif is_int args[0]
795
- return(cmd_fail(sock,"ACK [2@0] {repeat} \"#{args[0]}\" is not 0 or 1"))
796
- else
797
- return(cmd_fail(sock,'ACK [2@0] {repeat} need an integer'))
798
- end
799
- end
800
- when 'rm'
801
- args_check( sock, cmd, args, 1 ) do |args|
802
- rm_pls = args[0] + '.m3u'
803
- the_pls = -1
804
- @playlists.each_with_index do |pls,i|
805
- the_pls = i if pls['file'] == rm_pls
806
- end
807
-
808
- if the_pls != -1
809
- @playlists.delete_at the_pls
810
- return true
811
- else
812
- return(cmd_fail(sock,"ACK [50@0] {rm} playlist \"#{args[0]}\" not found"))
813
- end
814
- end
815
- when 'save'
816
- args_check( sock, cmd, args, 1 ) do |args|
817
- new_playlist = {'file' => args[0]+'.m3u', 'songs' => @the_playlist}
818
- @playlists << new_playlist
819
- return true
820
- end
821
- when 'search'
822
- args_check( sock, cmd, args, 2 ) do |args|
823
- if args[0] != 'title' and args[0] != 'artist' and args[0] != 'album' and args[0] != 'filename'
824
- return(cmd_fail(sock,'ACK [2@0] {search} incorrect arguments'))
825
- end
826
- args[0] = 'file' if args[0] == 'filename'
827
- @songs.each do |song|
828
- data = song[args[0]]
829
- if not data.nil? and data.downcase.include? args[1]
830
- send_song sock, song
831
- end
832
- end
833
- return true
834
- end
835
- when 'seek'
836
- args_check( sock, cmd, args, 2 ) do |args|
837
- if !is_int args[0]
838
- return(cmd_fail(sock,"ACK [2@0] {seek} \"#{args[0]}\" is not a integer"))
839
- elsif !is_int args[1]
840
- return(cmd_fail(sock,"ACK [2@0] {seek} \"#{args[1]}\" is not a integer"))
841
- else
842
- if args[0].to_i > @the_playlist.length or args[0].to_i < 0
843
- return(cmd_fail(sock,"ACK [50@0] {seek} song doesn't exist: \"#{args[0]}\""))
844
- end
845
- args[1] = '0' if args[1].to_i < 0
846
- song = @the_playlist[args[0].to_i]
847
- if args[1].to_i >= song['time'].to_i
848
- if args[0].to_i + 1 < @the_playlist.length
849
- @current_song = args[0].to_i + 1
850
- @elapsed_time = 0
851
- @status[:state] = 'play' unless @status[:state] == 'pause'
852
- else
853
- @current_song = nil
854
- @elapsed_time = 0
855
- @status[:state] = 'stop'
856
- end
857
- else
858
- @current_song = args[0].to_i
859
- @elapsed_time = args[1].to_i
860
- @status[:state] = 'play' unless @status[:state] == 'pause'
861
- end
862
- return true
863
- end
864
- end
865
- when 'seekid'
866
- args_check( sock, cmd, args, 2 ) do |args|
867
- if !is_int args[0]
868
- return(cmd_fail(sock,"ACK [2@0] {seekid} \"#{args[0]}\" is not a integer"))
869
- elsif !is_int args[1]
870
- return(cmd_fail(sock,"ACK [2@0] {seekid} \"#{args[1]}\" is not a integer"))
871
- else
872
- pos = nil
873
- song = nil
874
- @the_playlist.each_with_index do |s,i|
875
- if s['id'] == args[0].to_i
876
- song = s
877
- pos = i
878
- break;
879
- end
880
- end
881
-
882
- if song.nil?
883
- return(cmd_fail(sock,"ACK [50@0] {seekid} song id doesn't exist: \"#{args[0]}\""))
884
- end
885
-
886
- args[1] = '0' if args[1].to_i < 0
887
- if args[1].to_i >= song['time'].to_i
888
- if pos + 1 < @the_playlist.length
889
- @current_song = pos + 1
890
- @elapsed_time = 0
891
- @status[:state] = 'play' unless @status[:state] == 'pause'
892
- else
893
- @current_song = nil
894
- @elapsed_time = 0
895
- @status[:state] = 'stop'
896
- end
897
- else
898
- @current_song = pos
899
- @elapsed_time = args[1].to_i
900
- @status[:state] = 'play' unless @status[:state] == 'pause'
901
- end
902
- return true
903
- end
904
- end
905
- when 'setvol'
906
- args_check( sock, cmd, args, 1 ) do |args|
907
- if !is_int args[0]
908
- return(cmd_fail(sock,'ACK [2@0] {setvol} need an integer'))
909
- else
910
- # Note: args[0] < 0 actually sets the vol val to < 0
911
- @status[:volume] = args[0].to_i
912
- return true
913
- end
914
- end
915
- when 'shuffle'
916
- args_check( sock, cmd, args, 0 ) do
917
- @the_playlist.each do |s|
918
- s['_mod_ver'] = @status[:playlist]
919
- end
920
- incr_version
921
- @the_playlist.reverse!
922
- return true
923
- end
924
- when 'stats'
925
- args_check( sock, cmd, args, 0 ) do
926
- # artists
927
- sock.puts "artists: #{@artists.size}"
928
- # albums
929
- sock.puts "albums: #{@albums.size}"
930
- # songs
931
- sock.puts "songs: #{@songs.size}"
932
- # uptime
933
- sock.puts "uptime: 500"
934
- # db_playtime
935
- time = 0
936
- @songs.each do |s|
937
- time += s['time'].to_i
938
- end
939
- sock.puts "db_playtime: #{time}"
940
- # db_update
941
- sock.puts "db_update: 1159418502"
942
- # playtime
943
- sock.puts "playtime: 10"
944
- return true
945
- end
946
- when 'status'
947
- args_check( sock, cmd, args, 0 ) do
948
- @status.each_pair do |key,val|
949
- sock.puts "#{key}: #{val}" unless val.nil?
950
- end
951
- sock.puts "playlistlength: #{@the_playlist.length}"
952
-
953
- if @current_song != nil and @the_playlist.length > @current_song
954
- sock.puts "song: #{@current_song}"
955
- sock.puts "songid: #{@the_playlist[@current_song]['id']}"
956
- end
957
-
958
- @status[:updating_db] = nil
959
- return true
960
- end
961
- when 'stop'
962
- args_check( sock, cmd, args, 0 ) do
963
- @status[:state] = 'stop'
964
- @status[:time] = nil
965
- @status[:bitrate] = nil
966
- @status[:audio] = nil
967
- return true
968
- end
969
- when 'swap'
970
- args_check( sock, cmd, args, 2 ) do |args|
971
- if !is_int args[0]
972
- return(cmd_fail(sock,"ACK [2@0] {swap} \"#{args[0]}\" is not a integer"))
973
- elsif !is_int args[1]
974
- return(cmd_fail(sock,"ACK [2@0] {swap} \"#{args[1]}\" is not a integer"))
975
- elsif args[0].to_i >= @the_playlist.length or args[0].to_i < 0
976
- return(cmd_fail(sock,"ACK [50@0] {swap} song doesn't exist: \"#{args[0]}\""))
977
- elsif args[1].to_i >= @the_playlist.length or args[1].to_i < 0
978
- return(cmd_fail(sock,"ACK [50@0] {swap} song doesn't exist: \"#{args[1]}\""))
979
- else
980
- tmp = @the_playlist[args[1].to_i]
981
- @the_playlist[args[1].to_i] = @the_playlist[args[0].to_i]
982
- @the_playlist[args[0].to_i] = tmp
983
- @the_playlist[args[0].to_i]['_mod_ver'] = @status[:playlist]
984
- @the_playlist[args[1].to_i]['_mod_ver'] = @status[:playlist]
985
- incr_version
986
- return true
987
- end
988
- end
989
- when 'swapid'
990
- args_check( sock, cmd, args, 2 ) do |args|
991
- if !is_int args[0]
992
- return(cmd_fail(sock,"ACK [2@0] {swapid} \"#{args[0]}\" is not a integer"))
993
- elsif !is_int args[1]
994
- return(cmd_fail(sock,"ACK [2@0] {swapid} \"#{args[1]}\" is not a integer"))
995
- else
996
- from = nil
997
- to = nil
998
- @the_playlist.each_with_index do |song,i|
999
- if song['id'] == args[0].to_i
1000
- from = i
1001
- elsif song['id'] == args[1].to_i
1002
- to = i
1003
- end
1004
- end
1005
- if from.nil?
1006
- return(cmd_fail(sock,"ACK [50@0] {swapid} song id doesn't exist: \"#{args[0]}\""))
1007
- elsif to.nil?
1008
- return(cmd_fail(sock,"ACK [50@0] {swapid} song id doesn't exist: \"#{args[1]}\""))
1009
- end
1010
- tmp = @the_playlist[to]
1011
- @the_playlist[to] = @the_playlist[from]
1012
- @the_playlist[from] = tmp
1013
- @the_playlist[to]['_mod_ver'] = @status[:playlist]
1014
- @the_playlist[from]['_mod_ver'] = @status[:playlist]
1015
-
1016
- incr_version
1017
- return true
1018
- end
1019
- end
1020
- when 'update'
1021
- args_check( sock, cmd, args, 0..1 ) do |args|
1022
- incr_version
1023
- sock.puts 'updating_db: 1'
1024
- @status[:updating_db] = '1'
1025
- return true
1026
- end
1027
- when 'volume'
1028
- log 'MPD Warning: Call to Deprecated API: "volume"' if audit
1029
- args_check( sock, cmd, args, 1 ) do |args|
1030
- if !is_int args[0]
1031
- return(cmd_fail(sock,'ACK [2@0] {volume} need an integer'))
1032
- else
1033
- # Note: args[0] < 0 subtract from the volume
1034
- @status[:volume] += args[0].to_i
1035
- return true
1036
- end
1037
- end
1038
- else
1039
- return(cmd_fail(sock,"ACK [5@0] {} unknown command #{cmd}"))
1040
- end # End Case cmd
1041
- end
1042
-
1043
- def get_current_song
1044
- if @current_song != nil and @current_song < @the_playlist.length
1045
- return @the_playlist[@current_song]
1046
- else
1047
- return nil
1048
- end
1049
- end
1050
-
1051
- def prev_song
1052
- return if @current_song.nil?
1053
- if @current_song == 0
1054
- @elapsed_time = 0
1055
- else
1056
- @current_song -= 1
1057
- end
1058
- end
1059
-
1060
- def next_song
1061
- return if @current_song.nil?
1062
- @current_song = (@current_song +1 < @the_playlist.length ? @current_song +1 : nil)
1063
- end
1064
-
1065
- def elapsed_time=( new_time )
1066
- @elapsed_time = new_time
1067
- end
1068
-
1069
- def elapsed_time
1070
- @elapsed_time
1071
- end
1072
-
1073
- def incr_version
1074
- if @status[:playlist] == 2147483647
1075
- @status[:playlist] = 1
1076
- @the_playlist.each do |song|
1077
- song['_mod_ver'] = 0
1078
- end
1079
- else
1080
- @status[:playlist] += 1
1081
- end
1082
- end
1083
-
1084
- def cmd_fail( sock, msg )
1085
- sock.puts msg
1086
- return false
1087
- end
1088
-
1089
- def build_args( line )
1090
- ret = []
1091
- word = ''
1092
- escaped = false
1093
- in_quote = false
1094
-
1095
- line.strip!
1096
-
1097
- line.each_byte do |c|
1098
- c = c.chr
1099
- if c == ' ' and !in_quote
1100
- ret << word unless word.empty?
1101
- word = ''
1102
- elsif c == '"' and !escaped
1103
- if in_quote
1104
- in_quote = false
1105
- else
1106
- in_quote = true
1107
- end
1108
- ret << word unless word.empty?
1109
- word = ''
1110
- else
1111
- escaped = (c == '\\')
1112
- word += c
1113
- end
1114
- end
1115
-
1116
- ret << word unless word.empty?
1117
-
1118
- return ret
1119
- end
1120
-
1121
- def args_check( sock, cmd, argv, argc )
1122
- if (argc.kind_of? Range and argc.include?(argv.length)) or
1123
- (argv.length == argc)
1124
- yield argv
1125
- else
1126
- sock.puts "ACK [2@0] {#{cmd}} wrong number of arguments for \"#{cmd}\""
1127
- end
1128
- end
1129
-
1130
- def is_int( val )
1131
- val =~ /^[-+]?[0-9]*$/
1132
- end
1133
-
1134
- def is_bool( val )
1135
- val == '0' or val == '1'
1136
- end
1137
-
1138
- def locate_dir( path )
1139
- dirs = path.split '/'
1140
-
1141
- the_dir = @filetree
1142
- dirs.each do |d|
1143
- found = nil
1144
- the_dir[:dirs].each do |sub|
1145
- if sub[:name] == d
1146
- found = sub
1147
- break
1148
- end
1149
- end
1150
- if found.nil?
1151
- return nil
1152
- else
1153
- the_dir = found
1154
- end
1155
- end
1156
-
1157
- return the_dir
1158
- end
1159
-
1160
- def send_song( sock, song )
1161
- return if song.nil?
1162
- sock.puts "file: #{song['file']}"
1163
- song.each_pair do |key,val|
1164
- sock.puts "#{key.capitalize}: #{val}" unless key == 'file' or key == '_mod_ver'
1165
- end
1166
- end
1167
-
1168
- def send_dir( sock, dir, allinfo, path = '' )
1169
- sock.puts "directory: #{path}#{dir[:name]}"
1170
-
1171
- dir[:songs].each do |song|
1172
- if allinfo
1173
- send_song sock, song
1174
- else
1175
- sock.puts "file: #{song['file']}"
1176
- end
1177
- end
1178
-
1179
- dir[:dirs].each do |d|
1180
- send_dir(sock, d, allinfo, dir[:name] + '/')
1181
- end
1182
- end
1183
-
1184
- def add_dir_to_pls( dir )
1185
- dir[:songs].each do |song|
1186
- song['_mod_ver'] = @status[:playlist]
1187
- incr_version
1188
- @the_playlist << song
1189
- end
1190
-
1191
- dir[:dirs].each do |d|
1192
- add_dir_to_pls d
1193
- end
1194
- end
1195
-
1196
- def sort_dir( dir )
1197
- dir[:dirs].sort! do |x,y|
1198
- x[:name] <=> y[:name]
1199
- end
1200
-
1201
- dir[:dirs].each do |d|
1202
- sort_dir d
1203
- end
1204
- end
1205
-
1206
- end