mpc 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/mpc.rb +264 -0
- data/test/mpc_test.rb +124 -0
- metadata +67 -0
data/lib/mpc.rb
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
class Mpc
|
2
|
+
@@regexps = {
|
3
|
+
"ACK" => /\AACK \[(\d+)\@(\d+)\] \{(.*)\} (.+)\Z/,
|
4
|
+
"OK" => /\AOK\n\Z/,
|
5
|
+
"FILE" => /\Afile\:(.*)\Z/,
|
6
|
+
}
|
7
|
+
|
8
|
+
def initialize(host = "127.0.0.1", port = 6600)
|
9
|
+
@socket = TCPSocket.new(host, port)
|
10
|
+
@socket.gets
|
11
|
+
end
|
12
|
+
|
13
|
+
def play(song = nil)
|
14
|
+
song.nil? ? command = "play" : command = "play #{song.to_s}"
|
15
|
+
puts(command)
|
16
|
+
end
|
17
|
+
|
18
|
+
def pause
|
19
|
+
puts("pause 1")
|
20
|
+
end
|
21
|
+
|
22
|
+
def paused?
|
23
|
+
status_hash = status
|
24
|
+
status_hash[:state] == "pause"
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop
|
28
|
+
puts("stop")
|
29
|
+
end
|
30
|
+
|
31
|
+
def stopped?
|
32
|
+
status_hash = status
|
33
|
+
status_hash[:state] == "stop"
|
34
|
+
end
|
35
|
+
|
36
|
+
def next
|
37
|
+
puts("next")
|
38
|
+
end
|
39
|
+
|
40
|
+
def previous
|
41
|
+
puts("previous")
|
42
|
+
end
|
43
|
+
|
44
|
+
def random(state = nil)
|
45
|
+
if state.nil?
|
46
|
+
random? ? random_state = 0 : random_state = 1
|
47
|
+
else
|
48
|
+
random_state = state
|
49
|
+
end
|
50
|
+
puts("random #{random_state}")
|
51
|
+
end
|
52
|
+
|
53
|
+
def random?
|
54
|
+
status_hash = status
|
55
|
+
status_hash[:random] == "1"
|
56
|
+
end
|
57
|
+
|
58
|
+
def repeat(state = nil)
|
59
|
+
if state.nil?
|
60
|
+
repeat? ? repeat_state = 0 : repeat_state = 1
|
61
|
+
else
|
62
|
+
repeat_state = state
|
63
|
+
end
|
64
|
+
puts("repeat #{repeat_state}")
|
65
|
+
end
|
66
|
+
|
67
|
+
def repeat?
|
68
|
+
status_hash = status
|
69
|
+
status_hash[:repeat] == "1"
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_volume(volume)
|
73
|
+
begin
|
74
|
+
unless (0..100).include?(volume)
|
75
|
+
raise Exception.new("Volume should be between 0 (minimum) and 100 (maximum)")
|
76
|
+
end
|
77
|
+
puts("setvol #{volume.to_s}")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def volume
|
82
|
+
status_hash = status
|
83
|
+
status_hash[:volume]
|
84
|
+
end
|
85
|
+
|
86
|
+
def volume_up
|
87
|
+
setvol(volume.to_i + 20)
|
88
|
+
end
|
89
|
+
|
90
|
+
def volume_down
|
91
|
+
setvol(volume.to_i - 20)
|
92
|
+
end
|
93
|
+
|
94
|
+
def seek(time, song = nil)
|
95
|
+
if song.nil?
|
96
|
+
song = current_song[:pos]
|
97
|
+
end
|
98
|
+
puts("seek #{song.to_s} #{time.to_s}")
|
99
|
+
end
|
100
|
+
|
101
|
+
def find(type, what = "")
|
102
|
+
unless type.match(/\A(title|artist|album|filename)\Z/)
|
103
|
+
raise Exception.new("Wrong type: #{type}")
|
104
|
+
end
|
105
|
+
if what == ""
|
106
|
+
raise Exception.new(" \"What\" can\"t be an empty string")
|
107
|
+
end
|
108
|
+
parse_song_list(puts("search #{type} #{what}"))
|
109
|
+
end
|
110
|
+
|
111
|
+
def current_playlist_songs
|
112
|
+
parse_song_list(puts("playlistid"))
|
113
|
+
end
|
114
|
+
|
115
|
+
def list_all_songs
|
116
|
+
parse_song_list(puts("listallinfo"))
|
117
|
+
end
|
118
|
+
|
119
|
+
def current_song
|
120
|
+
parse_song_list(puts("currentsong"))
|
121
|
+
end
|
122
|
+
|
123
|
+
def stats
|
124
|
+
to_hash(puts("stats"))
|
125
|
+
end
|
126
|
+
|
127
|
+
def ping
|
128
|
+
song = current_song[0]
|
129
|
+
unless status[:state] == "stop"
|
130
|
+
output = {:song_time=>song[:time],:time=>status[:time].split(":").first,:artist=>song[:artist],:title=>song[:title],:file=>song[:file],:album=>song[:album],:id=>song[:id]}
|
131
|
+
else
|
132
|
+
output = {:song_time=>0,:time=>0,:artist=>nil,:title=>nil,:file=>nil,:album=>nil,:id=>nil}
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def list_playlists
|
137
|
+
to_hash(puts("listplaylists"))
|
138
|
+
end
|
139
|
+
|
140
|
+
def list_playlist_info(name)
|
141
|
+
parse_song_list(puts("listplaylistinfo #{name}"))
|
142
|
+
end
|
143
|
+
|
144
|
+
def add_to_playlist(uri, name = nil)
|
145
|
+
if name.nil?
|
146
|
+
puts("add \"#{uri}\"")
|
147
|
+
else
|
148
|
+
puts("playlistadd \"#{name}\" \"#{uri}\"")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def rename_playlist(original_name, name)
|
153
|
+
puts("rename \"#{original_name}\" \"#{name}\"")
|
154
|
+
end
|
155
|
+
|
156
|
+
def create_playlist(name)
|
157
|
+
puts("save \"#{name}\"")
|
158
|
+
end
|
159
|
+
|
160
|
+
def destroy_playlist(name)
|
161
|
+
puts("rm \"#{name}\"")
|
162
|
+
end
|
163
|
+
|
164
|
+
def clear!(name = nil)
|
165
|
+
if name.nil?
|
166
|
+
puts("clear")
|
167
|
+
else
|
168
|
+
puts("playlistclear \"#{name}\"")
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def get_paths
|
173
|
+
song_list(puts("listall"))
|
174
|
+
end
|
175
|
+
|
176
|
+
def list_library
|
177
|
+
root = Tree::TreeNode.new("/")
|
178
|
+
# root = Hash.new
|
179
|
+
get_paths.each do |path|
|
180
|
+
segments = path.split("/")
|
181
|
+
if root[segments.first].nil?
|
182
|
+
root << Tree::TreeNode.new(segments.first,segments.first)
|
183
|
+
# root[segments.first] = {}
|
184
|
+
end
|
185
|
+
last_element = root[segments.first]
|
186
|
+
first_element = segments.delete_at(0)
|
187
|
+
segments.each_with_index do |element, index|
|
188
|
+
if last_element[element].nil?
|
189
|
+
last_element << Tree::TreeNode.new(element, first_element + "/" + segments[0...index+1].join("/") )
|
190
|
+
# last_element[element] = {}
|
191
|
+
end
|
192
|
+
last_element = last_element[element]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
root
|
196
|
+
end
|
197
|
+
|
198
|
+
def delete_song(song)
|
199
|
+
puts("delete #{song.to_s}")
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
def puts(command)
|
205
|
+
@socket.puts(command)
|
206
|
+
gets
|
207
|
+
end
|
208
|
+
|
209
|
+
def gets
|
210
|
+
response = ""
|
211
|
+
while line = @socket.gets do
|
212
|
+
if @@regexps["OK"].match(line)
|
213
|
+
return response
|
214
|
+
elsif error = @@regexps["ACK"].match(line)
|
215
|
+
raise Exception.new(line)
|
216
|
+
else
|
217
|
+
response << line
|
218
|
+
end
|
219
|
+
end
|
220
|
+
response
|
221
|
+
end
|
222
|
+
|
223
|
+
def status
|
224
|
+
output = puts("status")
|
225
|
+
to_hash(output)
|
226
|
+
end
|
227
|
+
|
228
|
+
def to_hash(string)
|
229
|
+
status_hash = Hash.new
|
230
|
+
string.each do |line|
|
231
|
+
key, value = line.chomp.split(": ", 2)
|
232
|
+
status_hash[key.parameterize.underscore.to_sym] = value
|
233
|
+
end
|
234
|
+
status_hash
|
235
|
+
end
|
236
|
+
|
237
|
+
def parse_song_list(song_list)
|
238
|
+
output = Array.new
|
239
|
+
song_hash = Hash.new
|
240
|
+
song_list.each do |song|
|
241
|
+
if song.match(@@regexps["FILE"])
|
242
|
+
output << song_hash
|
243
|
+
song_hash = Hash.new
|
244
|
+
end
|
245
|
+
song_hash.merge!(to_hash(song))
|
246
|
+
end
|
247
|
+
output << song_hash
|
248
|
+
output.delete_at(0)
|
249
|
+
output
|
250
|
+
end
|
251
|
+
|
252
|
+
def song_list(list)
|
253
|
+
output = Array.new
|
254
|
+
list.each do |song|
|
255
|
+
if song.match(@@regexps["FILE"])
|
256
|
+
output << song.split(": ",2).second.gsub!("\n","")
|
257
|
+
end
|
258
|
+
end
|
259
|
+
output
|
260
|
+
end
|
261
|
+
|
262
|
+
class Exception < StandardError
|
263
|
+
end
|
264
|
+
end
|
data/test/mpc_test.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
class MpcTest < Test::Unit::TestCase
|
3
|
+
|
4
|
+
def setup
|
5
|
+
@mpc = Mpc.new
|
6
|
+
TCPSocket.any_instance.stubs(:puts).returns(nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
test "gets raises an exception on ACK response" do
|
10
|
+
TCPSocket.any_instance.stubs(:gets).returns("ACK [5@0] {} unknown command \"asd\"\n")
|
11
|
+
assert_raise(Mpc::Exception) do
|
12
|
+
@mpc.send(:puts,'asd')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
test "gets outputs empty string on OK response " do
|
17
|
+
TCPSocket.any_instance.stubs(:gets).returns("OK\n")
|
18
|
+
assert_equal("",@mpc.stop )
|
19
|
+
end
|
20
|
+
|
21
|
+
test "status outputs propper hash" do
|
22
|
+
@mpc.stubs(:gets).returns("volume: -1\nrepeat: 0\nrandom: 0\nsingle: 0\nconsume: 0\nplaylist: 43\nplaylistlength: 41\nxfade: 0\nstate: stop\nsong: 17\nsongid: 17\nnextsong: 18\nnextsongid: 18\n")
|
23
|
+
assert_equal({:songid=>"17", :state=>"stop", :single=>"0", :volume=>"-1", :nextsong=>"18", :consume=>"0", :nextsongid=>"18", :playlist=>"43", :repeat=>"0", :song=>"17", :playlistlength=>"41", :random=>"0", :xfade=>"0"},@mpc.send(:status) )
|
24
|
+
end
|
25
|
+
|
26
|
+
test "random without state should send request with opposite value" do
|
27
|
+
@mpc.stubs(:status).returns({:songid=>"17", :state=>"stop", :single=>"0", :volume=>"-1", :nextsong=>"18", :consume=>"0", :nextsongid=>"18", :playlist=>"43", :repeat=>"0", :song=>"17", :playlistlength=>"41", :random=>"0", :xfade=>"0"})
|
28
|
+
@mpc.expects(:puts).with('random 1')
|
29
|
+
@mpc.random
|
30
|
+
end
|
31
|
+
|
32
|
+
test "random with state should send request with given value" do
|
33
|
+
@mpc.stubs(:status).returns({:songid=>"17", :state=>"stop", :single=>"0", :volume=>"-1", :nextsong=>"18", :consume=>"0", :nextsongid=>"18", :playlist=>"43", :repeat=>"0", :song=>"17", :playlistlength=>"41", :random=>"0", :xfade=>"0"})
|
34
|
+
@mpc.expects(:puts).with('random 0')
|
35
|
+
@mpc.random(0)
|
36
|
+
end
|
37
|
+
|
38
|
+
test "repeat without state should send request with opposite value" do
|
39
|
+
@mpc.stubs(:status).returns({:songid=>"17", :state=>"stop", :single=>"0", :volume=>"-1", :nextsong=>"18", :consume=>"0", :nextsongid=>"18", :playlist=>"43", :repeat=>"0", :song=>"17", :playlistlength=>"41", :random=>"0", :xfade=>"0"})
|
40
|
+
@mpc.expects(:puts).with('repeat 1')
|
41
|
+
@mpc.repeat
|
42
|
+
end
|
43
|
+
|
44
|
+
test "repeat with state should send request with given value" do
|
45
|
+
@mpc.stubs(:status).returns({:songid=>"17", :state=>"stop", :single=>"0", :volume=>"-1", :nextsong=>"18", :consume=>"0", :nextsongid=>"18", :playlist=>"43", :repeat=>"0", :song=>"17", :playlistlength=>"41", :random=>"0", :xfade=>"0"})
|
46
|
+
@mpc.expects(:puts).with('repeat 0')
|
47
|
+
@mpc.repeat(0)
|
48
|
+
end
|
49
|
+
|
50
|
+
test "set_volume with volume in propper range should not raise exception" do
|
51
|
+
@mpc.expects(:puts).with('setvol 100')
|
52
|
+
@mpc.set_volume(100)
|
53
|
+
end
|
54
|
+
|
55
|
+
test "setvol with volume out of range should raise exception" do
|
56
|
+
assert_raise(Mpc::Exception) do
|
57
|
+
@mpc.set_volume(200)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
test "seek without song_position seeks current song" do
|
62
|
+
@mpc.stubs(:current_song).returns({:date=>"2008", :track=>"4", :album=>"One Kind Favor", :genre=>"Blues", :time=>"190", :file=>"Kuba's Music/B.B. King - One Kind Favor/04. B.B. King - How Many More Years.mp3", :pos=>"3", :title=>"How Many More Years", :id=>"3", :albumartist=>"B.B. King", :artist=>"B.B. King"})
|
63
|
+
@mpc.expects(:puts).with('seek 3 130')
|
64
|
+
@mpc.seek(130)
|
65
|
+
end
|
66
|
+
|
67
|
+
test "seek with song_position seeks given song" do
|
68
|
+
@mpc.stubs(:current_song).returns({:date=>"2008", :track=>"4", :album=>"One Kind Favor", :genre=>"Blues", :time=>"190", :file=>"Kuba's Music/B.B. King - One Kind Favor/04. B.B. King - How Many More Years.mp3", :pos=>"3", :title=>"How Many More Years", :id=>"3", :albumartist=>"B.B. King", :artist=>"B.B. King"})
|
69
|
+
@mpc.expects(:puts).with('seek 11 20')
|
70
|
+
@mpc.seek(20,11)
|
71
|
+
end
|
72
|
+
|
73
|
+
test "find with wrong type should raise exception" do
|
74
|
+
assert_raise(Mpc::Exception) do
|
75
|
+
@mpc.find('wrong_type')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
test "find with correct type but with empty string should raise exception" do
|
80
|
+
assert_raise(Mpc::Exception) do
|
81
|
+
@mpc.find('artist',"")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
test "find with correct type and with string should return hash with songs" do
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
test "list_library should return tree" do
|
90
|
+
@mpc.stubs(:get_paths).returns(["Abra Dab/Miasto Jest Nasze/ABRADAB - Bezposrednio.mp3",
|
91
|
+
"Abra Dab/Miasto Jest Nasze/miasto jest nasze (3).mp3",
|
92
|
+
"iTunes/iTunes Music/02. Aaliyah/Romeo must die/06 Are You Feelin Me.mp3",
|
93
|
+
"iTunes/iTunes Music/07. Dave Bing ft. Lil' Mo/Romeo must die/12 Someone Gonna Die Tonight.mp3"])
|
94
|
+
@root = Tree::TreeNode.new('/')
|
95
|
+
@first_node = Tree::TreeNode.new('Abra Dab')
|
96
|
+
|
97
|
+
@folder = Tree::TreeNode.new('Miasto Jest Nasze')
|
98
|
+
@folder << Tree::TreeNode.new('ABRADAB - Bezposrednio.mp3')
|
99
|
+
@folder << Tree::TreeNode.new('miasto jest nasze (3).mp3')
|
100
|
+
|
101
|
+
@first_node << @folder
|
102
|
+
@root << @first_node
|
103
|
+
|
104
|
+
@second_node = Tree::TreeNode.new('iTunes')
|
105
|
+
@folder = Tree::TreeNode.new('iTunes Music')
|
106
|
+
@first_subfolder = Tree::TreeNode.new('02. Aaliyah')
|
107
|
+
@element = Tree::TreeNode.new('Romeo must die')
|
108
|
+
@mp3 = Tree::TreeNode.new('06 Are You Feelin Me.mp3')
|
109
|
+
@element << @mp3
|
110
|
+
@first_subfolder << @element
|
111
|
+
@second_subfolder = Tree::TreeNode.new("07. Dave Bing ft Lil' Mo")
|
112
|
+
@element = Tree::TreeNode.new('Romeo must die')
|
113
|
+
@mp3 = Tree::TreeNode.new('12 Someone Gonna Die Tonight.mp3')
|
114
|
+
@element << @mp3
|
115
|
+
@second_subfolder << @element
|
116
|
+
|
117
|
+
@folder << @first_subfolder
|
118
|
+
@folder << @second_subfolder
|
119
|
+
@second_node << @folder
|
120
|
+
@root << @second_node
|
121
|
+
|
122
|
+
assert_equal(@root.to_s,@mpc.list_library.to_s)
|
123
|
+
end
|
124
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mpc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- "Micha\xC5\x82 Krzy\xC5\xBCanowski"
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-08-09 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Ruby MPD client gem
|
22
|
+
email: michal.krzyzanowski+mpc@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- lib/mpc.rb
|
31
|
+
- test/mpc_test.rb
|
32
|
+
has_rdoc: true
|
33
|
+
homepage:
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options: []
|
38
|
+
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 3
|
47
|
+
segments:
|
48
|
+
- 0
|
49
|
+
version: "0"
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.3.7
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: MPD client gem
|
66
|
+
test_files:
|
67
|
+
- test/mpc_test.rb
|