squeezer-ruby 0.1.1 → 0.2.0
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/.autotest +14 -0
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/README.mkd +29 -6
- data/Rakefile +17 -0
- data/lib/squeezer.rb +12 -3
- data/lib/squeezer/client.rb +3 -1
- data/lib/squeezer/client/database.rb +44 -23
- data/lib/squeezer/client/utils.rb +9 -0
- data/lib/squeezer/configuration.rb +2 -2
- data/lib/squeezer/connection.rb +21 -15
- data/lib/squeezer/models.rb +15 -12
- data/lib/squeezer/models/album.rb +34 -0
- data/lib/squeezer/models/artist.rb +22 -2
- data/lib/squeezer/models/genre.rb +28 -0
- data/lib/squeezer/models/player.rb +65 -37
- data/lib/squeezer/models/playlist.rb +43 -0
- data/lib/squeezer/models/track.rb +28 -0
- data/lib/squeezer/version.rb +1 -1
- data/script/console +16 -0
- data/spec/spec_helper.rb +37 -30
- data/spec/squeezer/client/database_spec.rb +97 -6
- data/spec/squeezer/client/players_spec.rb +2 -2
- data/spec/squeezer/models/player_spec.rb +268 -0
- data/spec/squeezer/models/playlist_spec.rb +34 -0
- data/spec/squeezer_spec.rb +10 -2
- data/squeezer.gemspec +2 -0
- metadata +51 -6
@@ -120,58 +120,90 @@ module Squeezer
|
|
120
120
|
power(:off)
|
121
121
|
end
|
122
122
|
|
123
|
-
|
124
|
-
|
123
|
+
# fade in seconds
|
124
|
+
def play(fade=0)
|
125
|
+
cmd("#{id} play #{fade}")
|
126
|
+
end
|
127
|
+
|
128
|
+
def stop
|
129
|
+
cmd("#{id} stop")
|
125
130
|
end
|
126
131
|
|
127
|
-
|
132
|
+
def pause
|
133
|
+
cmd("#{id} pause")
|
134
|
+
end
|
128
135
|
|
129
|
-
|
130
|
-
|
131
|
-
def players
|
132
|
-
player_map = Hash.new
|
133
|
-
count = cmd("player count ?").to_i
|
134
|
-
count.to_i.times do |index|
|
135
|
-
id = cmd("player id #{index} ?")
|
136
|
-
player_map[id] = Models::Player.new(id)
|
137
|
-
end
|
138
|
-
player_map
|
136
|
+
def playing?
|
137
|
+
mode == :play
|
139
138
|
end
|
140
139
|
|
141
|
-
def
|
142
|
-
|
140
|
+
def stopped?
|
141
|
+
mode == :stop
|
143
142
|
end
|
144
143
|
|
145
|
-
|
146
|
-
|
147
|
-
find(:name => name)
|
144
|
+
def paused?
|
145
|
+
mode == :pause
|
148
146
|
end
|
149
147
|
|
150
|
-
def
|
151
|
-
|
148
|
+
def mode
|
149
|
+
cmd("#{id} mode ?").to_sym
|
150
|
+
end
|
151
|
+
|
152
|
+
def show(options)
|
153
|
+
cmd("#{id} show font:#{options[:font]} line2:#{options[:line2]} centered:#{options[:centered]} duration:#{options[:duration]}")
|
152
154
|
end
|
153
155
|
|
154
|
-
def
|
155
|
-
|
156
|
+
def alert(message, options={})
|
157
|
+
options = {:line2 => URI.escape(message), :centered => true, :duration => 3, :font => :huge}.merge(options)
|
158
|
+
show(options)
|
156
159
|
end
|
157
160
|
|
158
|
-
def
|
159
|
-
|
160
|
-
|
161
|
-
|
161
|
+
def blink(message, times, duration=1, options={})
|
162
|
+
times.times do
|
163
|
+
alert(message, :duration => duration)
|
164
|
+
sleep duration
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def playlist
|
169
|
+
Models::Playlist.new(id)
|
170
|
+
end
|
171
|
+
|
172
|
+
def <=>(target)
|
173
|
+
self.name <=> target.name
|
174
|
+
end
|
175
|
+
|
176
|
+
# beware of offline players, they still show up on the list
|
177
|
+
# test if those players are connected with Player#connected?
|
178
|
+
def players
|
179
|
+
Player.all
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.all
|
183
|
+
result = Array.new
|
184
|
+
count = Connection.exec("player count ?").to_i
|
185
|
+
count.to_i.times do |index|
|
186
|
+
id = Connection.exec("player id #{index} ?")
|
187
|
+
result << Models::Player.new(id)
|
188
|
+
end
|
162
189
|
result
|
163
190
|
end
|
164
191
|
|
165
|
-
|
166
|
-
|
192
|
+
# handle duplicates in these find methods, specs expect only one result
|
193
|
+
def self.find_by_name(name)
|
194
|
+
find(:name => name)
|
167
195
|
end
|
168
196
|
|
197
|
+
def self.find_by_ip(ip)
|
198
|
+
find(:ip => ip)
|
199
|
+
end
|
200
|
+
|
169
201
|
def self.find_by_id(id)
|
170
|
-
|
202
|
+
find(:id => id)
|
171
203
|
end
|
172
|
-
|
173
|
-
def find(values)
|
174
|
-
|
204
|
+
|
205
|
+
def self.find(values)
|
206
|
+
all.each do |player|
|
175
207
|
match = true
|
176
208
|
values.each do |property,value|
|
177
209
|
match = false unless player.send(property.to_sym) == value
|
@@ -180,11 +212,7 @@ module Squeezer
|
|
180
212
|
end
|
181
213
|
return nil
|
182
214
|
end
|
183
|
-
|
184
|
-
def self.find(values)
|
185
|
-
self.new(nil).find(values)
|
186
|
-
end
|
187
|
-
|
215
|
+
|
188
216
|
end
|
189
217
|
end
|
190
218
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Squeezer
|
2
|
+
module Models
|
3
|
+
|
4
|
+
# initial playlist support
|
5
|
+
class Playlist < Model
|
6
|
+
|
7
|
+
attr_reader :id
|
8
|
+
|
9
|
+
def initialize(id, options={})
|
10
|
+
super options
|
11
|
+
@id = id
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(entity)
|
15
|
+
playlistcontrol(:cmd => :add, :entity => entity_name(entity), :id => entity.id)
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
# starts playing directly
|
20
|
+
def load(entity)
|
21
|
+
playlistcontrol(:cmd => :load, :entity => entity_name(entity), :id => entity.id)
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear
|
26
|
+
cmd "#{id} playlist clear"
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def entity_name(entity)
|
32
|
+
entity.class.to_s.downcase.split('::').last
|
33
|
+
end
|
34
|
+
|
35
|
+
def playlistcontrol(options)
|
36
|
+
# TODO make better use of these options
|
37
|
+
# .. and what about entity lists?
|
38
|
+
cmd "#{id} playlistcontrol cmd:#{options[:cmd]} #{options[:entity]}_id:#{options[:id]}"
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Squeezer
|
2
|
+
module Models
|
3
|
+
|
4
|
+
class Track < Model
|
5
|
+
attr_accessor :id, :name
|
6
|
+
|
7
|
+
def initialize(record=nil)
|
8
|
+
unless record.nil?
|
9
|
+
@id = record[:id] if record.key?(:id)
|
10
|
+
@name = record[:title] if record.key?(:title)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.total
|
15
|
+
Connection.exec("info total songs ?").to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.all
|
19
|
+
results = Array.new
|
20
|
+
Model.extract_records(Connection.exec("tracks 0 #{total} charset:utf8 tags:s")).each do |record|
|
21
|
+
results << Track.new(record)
|
22
|
+
end
|
23
|
+
results
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/squeezer/version.rb
CHANGED
data/script/console
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
libs = []
|
4
|
+
libs << "irb/completion"
|
5
|
+
libs << File.expand_path("../../lib/squeezer.rb", __FILE__)
|
6
|
+
|
7
|
+
command_line = []
|
8
|
+
command_line << "irb"
|
9
|
+
command_line << libs.inject("") { |acc, lib| acc + %( -r "#{lib}") }
|
10
|
+
command_line << "--simple-prompt"
|
11
|
+
command_line << "-d"
|
12
|
+
command_line << "-W1"
|
13
|
+
command = command_line.join(" ")
|
14
|
+
|
15
|
+
puts "Welcome to the Squeezer console interface."
|
16
|
+
exec command
|
data/spec/spec_helper.rb
CHANGED
@@ -1,41 +1,48 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
add_group 'Squeezer', 'lib/squeezer'
|
4
|
-
add_group 'Specs', 'spec'
|
5
|
-
end
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spork'
|
6
3
|
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
Spork.prefork do
|
5
|
+
# Loading more in this block will cause your tests to run faster. However,
|
6
|
+
# if you change any configuration or code from libraries loaded here, you'll
|
7
|
+
# need to restart spork for it take effect.
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
@connection.stubs(:cmd).with("exit")
|
18
|
-
|
19
|
-
# Net::Telnet.stubs(:new).returns(@connection)
|
20
|
-
|
21
|
-
# maybe find a better way to mock the connection? Because the mock
|
22
|
-
# stays in memory of the API's eigenclass if we mocked the telnet
|
23
|
-
# connection directly. So we mock the connection getter instead.
|
24
|
-
# But since we didn't find a way to mock a module method, we mock the
|
25
|
-
# getter per class.
|
26
|
-
Squeezer::Models::Player.any_instance.stubs(:connection).returns(@connection)
|
27
|
-
Squeezer::Client.any_instance.stubs(:connection).returns(@connection)
|
9
|
+
require 'rspec'
|
10
|
+
require 'simplecov'
|
11
|
+
SimpleCov.start do
|
12
|
+
add_group 'Squeezer', 'lib/squeezer'
|
13
|
+
add_group 'Specs', 'spec'
|
14
|
+
add_filter __FILE__
|
28
15
|
end
|
29
16
|
|
30
|
-
|
31
|
-
|
17
|
+
RSpec.configure do |config|
|
18
|
+
config.mock_with :mocha
|
19
|
+
|
20
|
+
config.before(:each) do
|
21
|
+
# TODO add authentication stuff to the mock
|
22
|
+
@connection = mock("connection")
|
23
|
+
sock = mock("sock")
|
24
|
+
sock.stubs(:close).returns(true)
|
25
|
+
@connection.stubs(:sock).returns(sock)
|
26
|
+
@connection.stubs(:cmd).with("exit")
|
27
|
+
|
28
|
+
Net::Telnet.stubs(:new).returns(@connection)
|
29
|
+
end
|
30
|
+
|
31
|
+
config.after(:each) do
|
32
|
+
Squeezer.exit
|
33
|
+
end
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
|
-
|
37
|
+
Spork.each_run do
|
38
|
+
# This code will be run each time you run your specs.
|
39
|
+
|
40
|
+
load File.expand_path('../../lib/squeezer.rb', __FILE__)
|
41
|
+
end
|
36
42
|
|
37
|
-
require 'rspec'
|
38
43
|
|
39
44
|
def stub_connection
|
40
45
|
@connection.stubs(:cmd)
|
41
|
-
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
@@ -6,31 +6,31 @@ describe Squeezer::Client do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
describe ".total_*" do
|
9
|
-
it "should return the
|
9
|
+
it "should return the total artists" do
|
10
10
|
stub_connection.with("info total artists ?").returns("info total artists 2\n")
|
11
11
|
@client.total_artists.should == 2
|
12
12
|
end
|
13
13
|
|
14
|
-
it "should return the
|
14
|
+
it "should return the total albums" do
|
15
15
|
stub_connection.with("info total albums ?").returns("info total albums 4\n")
|
16
16
|
@client.total_albums.should == 4
|
17
17
|
end
|
18
18
|
|
19
|
-
it "should return the
|
19
|
+
it "should return the total genres" do
|
20
20
|
stub_connection.with("info total genres ?").returns("info total genres 6\n")
|
21
21
|
@client.total_genres.should == 6
|
22
22
|
end
|
23
23
|
|
24
|
-
it "should return the
|
24
|
+
it "should return the total songs" do
|
25
25
|
stub_connection.with("info total songs ?").returns("info total songs 8\n")
|
26
|
-
@client.
|
26
|
+
@client.total_tracks.should == 8
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
describe ".artists" do
|
31
31
|
before do
|
32
32
|
stub_connection.with("info total artists ?").returns("info total artists 1\n")
|
33
|
-
stub_connection.with("artists 0 2 charset:utf8 tags:s").returns("artists 0 2 charset%3Autf8 tags%3As id%3A4631 artist%3AVarious%20Artists textkey%3A%20 id%3A3998 artist%
|
33
|
+
stub_connection.with("artists 0 2 charset:utf8 tags:s").returns("artists 0 2 charset%3Autf8 tags%3As id%3A4631 artist%3AVarious%20Artists textkey%3A%20 id%3A3998 artist%3AAwesome%20Artist textkey%3AA count%3A434\n")
|
34
34
|
end
|
35
35
|
|
36
36
|
it "should return a list with Artist models" do
|
@@ -44,5 +44,96 @@ describe Squeezer::Client do
|
|
44
44
|
artists.first.name.should == "Various Artists"
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
48
|
+
describe ".albums" do
|
49
|
+
before do
|
50
|
+
stub_connection.with("info total albums ?").returns("info total albums 2\n")
|
51
|
+
stub_connection.with("albums 0 2 charset:utf8 tags:tqs").returns("albums 0 2 charset%3Autf8 tags%3Atqs id%3A4631 title%3AAwesome%20Album disccount%3A1 textkey%3AA id%3A3998 title%3AAnother%20Album textkey%3AA count%3A434\n")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should return a list with Album models" do
|
55
|
+
albums = @client.albums
|
56
|
+
albums.first.should be_a Squeezer::Models::Album
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should return a list with all the albums" do
|
60
|
+
albums = @client.albums
|
61
|
+
albums.should have(2).things
|
62
|
+
albums.first.name.should == "Awesome Album"
|
63
|
+
albums.first.discs.should == 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ".tracks" do
|
68
|
+
before do
|
69
|
+
stub_connection.with("info total songs ?").returns("info total songs 2\n")
|
70
|
+
stub_connection.with("tracks 0 2 charset:utf8 tags:s").returns("albums 0 2 charset%3Autf8 tags%3As id%3A4631 title%3AAwesome%20Track textkey%3AA id%3A3998 title%3AAnother%20Track textkey%3AA count%3A434\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should return a list with Track models" do
|
74
|
+
tracks = @client.tracks
|
75
|
+
tracks.first.should be_a Squeezer::Models::Track
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should return a list with all the tracks" do
|
79
|
+
tracks = @client.tracks
|
80
|
+
tracks.should have(2).things
|
81
|
+
tracks.first.name.should == "Awesome Track"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe ".genres" do
|
86
|
+
before do
|
87
|
+
stub_connection.with("info total genres ?").returns("info total genres 2\n")
|
88
|
+
stub_connection.with("genres 0 2 charset:utf8 tags:s").returns("genres 0 2 charset%3Autf8 tags%3As id%3A4631 genre%3AAwesome%20Genre textkey%3AA id%3A3998 genre%3AAnother%20Genre textkey%3AA count%3A434\n")
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should return a list with Genre models" do
|
92
|
+
genres = @client.genres
|
93
|
+
genres.first.should be_a Squeezer::Models::Genre
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should return a list with all the genres" do
|
97
|
+
genres = @client.genres
|
98
|
+
genres.should have(2).things
|
99
|
+
genres.first.name.should == "Awesome Genre"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "database scanning" do
|
104
|
+
describe ".rescan!" do
|
105
|
+
it "should initiate a rescan of the music database" do
|
106
|
+
stub_connection.with("rescan").returns("rescan")
|
107
|
+
@client.rescan!.should be true
|
108
|
+
end
|
109
|
+
end
|
47
110
|
|
111
|
+
describe ".scanning?" do
|
112
|
+
it "should indicate whether or not the database is scanning" do
|
113
|
+
stub_connection.with("rescan ?").returns("rescan 1")
|
114
|
+
@client.scanning?.should be true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe ".abortscan!" do
|
119
|
+
it "should abort a scan" do
|
120
|
+
stub_connection.with("abortscan").returns("abortscan")
|
121
|
+
@client.abortscan!.should be true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe ".wipecache!" do
|
126
|
+
it "should wipe the database" do
|
127
|
+
stub_connection.with("wipecache").returns("wipecache")
|
128
|
+
@client.wipecache!.should be true
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe ".rescan_progress" do
|
133
|
+
it "should return the rescan progress" do
|
134
|
+
stub_connection.with("rescanprogress").returns("rescan%3A1 directory%3A100 musicip%3A100 mergeva%3A100 cleanup1%3A100 cleanup2%3A100 steps%3Adirectory%2Cmusicip%2Cmergeva%2Ccleanup1%2Ccleanup2 totaltime%3A00%3A00%3A38")
|
135
|
+
@client.rescan_progress[:rescan].to_boolean.should be true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
48
139
|
end
|