mpc 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/mpc.rb +264 -0
  2. data/test/mpc_test.rb +124 -0
  3. 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