ruby-mpd 0.1.5 → 0.1.7
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 +100 -45
- data/lib/ruby-mpd.rb +42 -83
- data/lib/ruby-mpd/exceptions.rb +33 -0
- data/lib/ruby-mpd/parser.rb +24 -7
- data/lib/ruby-mpd/playlist.rb +5 -6
- data/lib/ruby-mpd/plugins/channels.rb +2 -5
- data/lib/ruby-mpd/plugins/controls.rb +2 -5
- data/lib/ruby-mpd/plugins/database.rb +44 -6
- data/lib/ruby-mpd/plugins/information.rb +7 -4
- data/lib/ruby-mpd/plugins/playback_options.rb +3 -3
- data/lib/ruby-mpd/plugins/playlists.rb +1 -3
- data/lib/ruby-mpd/plugins/queue.rb +3 -5
- data/lib/ruby-mpd/plugins/reflection.rb +3 -3
- data/lib/ruby-mpd/song.rb +9 -3
- data/ruby-mpd.gemspec +1 -1
- data/{tests → test}/libtests.rb +78 -89
- metadata +6 -11
- data/AUTHORS +0 -1
- data/DOC.rdoc +0 -78
- data/data/database.yaml +0 -347
- data/examples/rmpc.rb +0 -67
- data/examples/tailmpc.rb +0 -115
- data/lib/mpdserver.rb +0 -1206
- data/tests/servertests.rb +0 -3405
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
|