squeezer-ruby 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/.autotest
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "autotest/timestamp"
|
2
|
+
require 'autotest/growl' if RUBY_PLATFORM.to_s.include?("darwin")
|
3
|
+
require 'autotest/fsevent' if RUBY_PLATFORM.to_s.include?("darwin")
|
4
|
+
|
5
|
+
# adds exceptions from .gitignore file, please modify exceptions there!
|
6
|
+
imported_exceptions = IO.readlines('.gitignore').inject([]) do |acc, line|
|
7
|
+
acc << line.strip if line.to_s[0] != '#' && line.strip != ''; acc
|
8
|
+
end
|
9
|
+
|
10
|
+
Autotest.add_hook :initialize do |autotest|
|
11
|
+
imported_exceptions.each do |exception|
|
12
|
+
autotest.add_exception(exception)
|
13
|
+
end
|
14
|
+
end
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/README.mkd
CHANGED
@@ -10,7 +10,7 @@ This project is in it's early stages, it is not finished by any means.
|
|
10
10
|
It also lacks proper error handling and documentation.
|
11
11
|
Most of the critical refactor points have been marked with a 'todo' comment.
|
12
12
|
|
13
|
-
- active record like search methods and model relations (database chained search engine?)
|
13
|
+
- active record like search methods and model relations (database, chained search engine?)
|
14
14
|
- playlist handling
|
15
15
|
- add convenience methods
|
16
16
|
- add some more sugar
|
@@ -30,10 +30,14 @@ General:
|
|
30
30
|
config.port = 9090
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
# execute block and close the connection when done
|
34
|
+
Squeezer.open do |client|
|
35
|
+
puts client.version
|
36
|
+
puts Track.total
|
37
|
+
end
|
36
38
|
|
39
|
+
# or start a client and call exit manually
|
40
|
+
client = Squeezer::Client.new
|
37
41
|
client.players.each do |id, player|
|
38
42
|
puts "Name: #{player.name}"
|
39
43
|
puts "Model: #{player.model}"
|
@@ -41,10 +45,9 @@ General:
|
|
41
45
|
puts "Volume: #{player.volume}"
|
42
46
|
|
43
47
|
player.on! if player.off?
|
48
|
+
|
44
49
|
player.volume = "+40"
|
45
50
|
end
|
46
|
-
|
47
|
-
puts client.total_artists
|
48
51
|
|
49
52
|
client.artists.each do |artist|
|
50
53
|
puts artist.name
|
@@ -59,8 +62,28 @@ Models:
|
|
59
62
|
|
60
63
|
puts Player.find_by_name("living room").ip
|
61
64
|
puts Player.find_by_ip("192.168.1.1").name
|
65
|
+
|
66
|
+
Artist.all.each do |artist|
|
67
|
+
puts artist.name
|
68
|
+
end
|
69
|
+
|
70
|
+
puts Album.total
|
62
71
|
end
|
63
72
|
|
73
|
+
Testing
|
74
|
+
-------
|
75
|
+
Run all tests:
|
76
|
+
|
77
|
+
rake tests:run
|
78
|
+
|
79
|
+
Start Spork DRb server, for faster testing:
|
80
|
+
|
81
|
+
rake tests:spork
|
82
|
+
|
83
|
+
Run a continuous testing environment with autotest (configure .autotest for your own needs):
|
84
|
+
|
85
|
+
rake tests:autotest
|
86
|
+
|
64
87
|
Copyright
|
65
88
|
---------
|
66
89
|
Copyright (c) 2011 Daniël van Hoesel.
|
data/Rakefile
CHANGED
@@ -17,3 +17,20 @@ namespace :doc do
|
|
17
17
|
]
|
18
18
|
end
|
19
19
|
end
|
20
|
+
|
21
|
+
namespace :tests do
|
22
|
+
desc "Run Spork DRb server, for faster tests"
|
23
|
+
task :spork do |task|
|
24
|
+
sh "spork"
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Run a continuous testing environment"
|
28
|
+
task :autotest do |task|
|
29
|
+
sh "autotest"
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Run all tests"
|
33
|
+
task :run do |task|
|
34
|
+
Rake::Task[:spec].execute
|
35
|
+
end
|
36
|
+
end
|
data/lib/squeezer.rb
CHANGED
@@ -3,12 +3,12 @@ require 'uri'
|
|
3
3
|
require File.expand_path('../squeezer/core_extentions', __FILE__)
|
4
4
|
require File.expand_path('../squeezer/configuration', __FILE__)
|
5
5
|
require File.expand_path('../squeezer/api', __FILE__)
|
6
|
-
require File.expand_path('../squeezer/client', __FILE__)
|
7
6
|
require File.expand_path('../squeezer/models', __FILE__)
|
7
|
+
require File.expand_path('../squeezer/client', __FILE__)
|
8
8
|
|
9
9
|
module Squeezer
|
10
10
|
extend Configuration
|
11
|
-
|
11
|
+
|
12
12
|
# Alias for Squeezer::Client.new
|
13
13
|
#
|
14
14
|
# @return [Squeezer::Client]
|
@@ -22,4 +22,13 @@ module Squeezer
|
|
22
22
|
client.send(method, *args, &block)
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
# Convenience method to execute client calls in a block.
|
26
|
+
# The connection closes automatically at the end of the block.
|
27
|
+
def self.open(&block)
|
28
|
+
client.instance_eval(&block)
|
29
|
+
Squeezer.exit
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
include Squeezer::Models if $DEBUG
|
data/lib/squeezer/client.rb
CHANGED
@@ -2,48 +2,69 @@ module Squeezer
|
|
2
2
|
class Client
|
3
3
|
module Database
|
4
4
|
|
5
|
+
# TODO do we really need this methods?
|
6
|
+
# can't we just use the model's class methods?
|
7
|
+
|
5
8
|
# doesn't include 'Various Artists'
|
6
9
|
def total_artists
|
7
|
-
|
10
|
+
Models::Artist.total
|
8
11
|
end
|
9
12
|
|
10
13
|
def total_albums
|
11
|
-
|
14
|
+
Models::Album.total
|
12
15
|
end
|
13
16
|
|
14
|
-
def
|
15
|
-
|
17
|
+
def total_tracks
|
18
|
+
Models::Track.total
|
16
19
|
end
|
17
20
|
|
18
21
|
def total_genres
|
19
|
-
|
22
|
+
Models::Genre.total
|
20
23
|
end
|
21
24
|
|
22
|
-
def
|
23
|
-
|
24
|
-
cmd("info total #{entity.to_s} ?").to_i
|
25
|
+
def artists
|
26
|
+
Models::Artist.all
|
25
27
|
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
def albums
|
30
|
+
Models::Album.all
|
31
|
+
end
|
32
|
+
|
33
|
+
def tracks
|
34
|
+
Models::Track.all
|
35
|
+
end
|
36
|
+
|
37
|
+
def genres
|
38
|
+
Models::Genre.all
|
39
|
+
end
|
40
|
+
|
41
|
+
def rescan!
|
42
|
+
cmd("rescan")
|
43
|
+
end
|
44
|
+
|
45
|
+
def scanning?
|
46
|
+
cmd("rescan ?").to_boolean
|
30
47
|
end
|
31
48
|
|
32
|
-
|
49
|
+
# "The "abortscan" command causes Squeezebox Server to cancel a running scan.
|
50
|
+
# Please note that after stopping a scan this way you'll have to fully rescan
|
51
|
+
# your music collection to get consistent data."
|
52
|
+
def abortscan!
|
53
|
+
cmd("abortscan")
|
54
|
+
end
|
33
55
|
|
34
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
record[attributes[index]] = URI.unescape(field)
|
41
|
-
end
|
42
|
-
result << record
|
43
|
-
end
|
44
|
-
result.size == 1 ? result.first : result
|
56
|
+
# The "wipecache" command allows the caller to have the Squeezebox Server rescan
|
57
|
+
# its music library, reloading the music file information. This differs from the
|
58
|
+
# "rescan!" command in that it first clears the tag database. During a rescan
|
59
|
+
# triggered by "wipecache!", "rescan?" returns true.
|
60
|
+
def wipecache!
|
61
|
+
cmd("wipecache")
|
45
62
|
end
|
46
63
|
|
64
|
+
# TODO format the return data better and make it useful
|
65
|
+
def rescan_progress
|
66
|
+
extract_hash_from_data(cmd("rescanprogress"))
|
67
|
+
end
|
47
68
|
end
|
48
69
|
end
|
49
70
|
end
|
@@ -1,7 +1,16 @@
|
|
1
1
|
module Squeezer
|
2
2
|
class Client
|
3
3
|
module Utils
|
4
|
+
private
|
4
5
|
|
6
|
+
# update regex to play nice with : chars
|
7
|
+
def extract_hash_from_data(data)
|
8
|
+
record = Hash.new
|
9
|
+
data.scan(/([^\s]+)%3A([^\s]+)/) do |match|
|
10
|
+
record[match[0].to_sym] = URI.unescape(match[1])
|
11
|
+
end
|
12
|
+
record
|
13
|
+
end
|
5
14
|
end
|
6
15
|
end
|
7
16
|
end
|
@@ -6,8 +6,8 @@ module Squeezer
|
|
6
6
|
# An array of valid keys in the options hash when configuring an {Squeezer::API}
|
7
7
|
VALID_OPTIONS_KEYS = [:server, :port].freeze
|
8
8
|
|
9
|
-
# By default,
|
10
|
-
DEFAULT_SERVER =
|
9
|
+
# By default, set localhost as the server
|
10
|
+
DEFAULT_SERVER = "127.0.0.1".freeze
|
11
11
|
|
12
12
|
# The port where the CLI interface is running on
|
13
13
|
#
|
data/lib/squeezer/connection.rb
CHANGED
@@ -14,18 +14,7 @@ module Squeezer
|
|
14
14
|
private
|
15
15
|
|
16
16
|
def cmd(command, options={})
|
17
|
-
|
18
|
-
response = connection.cmd(command)
|
19
|
-
puts response if options == :debug
|
20
|
-
return false if response.nil?
|
21
|
-
return true if response.strip.eql?(command)
|
22
|
-
result = response.gsub(command.gsub('?', '').strip, '').strip
|
23
|
-
result.force_encoding("UTF-8") unless /^1\.8/ === RUBY_VERSION
|
24
|
-
result
|
25
|
-
end
|
26
|
-
|
27
|
-
def connection
|
28
|
-
Connection.retrieve_connection
|
17
|
+
Connection.exec(command, options)
|
29
18
|
end
|
30
19
|
|
31
20
|
class << self
|
@@ -34,13 +23,30 @@ module Squeezer
|
|
34
23
|
@connection = nil
|
35
24
|
end
|
36
25
|
|
26
|
+
def exec(command, options={})
|
27
|
+
# TODO raise exceptions instead of returning false
|
28
|
+
response = retrieve_connection.cmd(command)
|
29
|
+
puts response if options == :debug
|
30
|
+
return false if response.nil?
|
31
|
+
return true if response.strip.eql?(command)
|
32
|
+
result = response.gsub(command.gsub('?', '').strip, '').strip
|
33
|
+
result.force_encoding("UTF-8") unless /^1\.8/ === RUBY_VERSION
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
37
|
def retrieve_connection
|
38
38
|
@connection ||= open_connection
|
39
39
|
end
|
40
40
|
|
41
|
-
def open_connection
|
42
|
-
# do authentication and stuff
|
43
|
-
|
41
|
+
def open_connection
|
42
|
+
# TODO do authentication and stuff
|
43
|
+
# TODO fix the timeout if we receive a large ammount of data
|
44
|
+
# TODO add error handling when the host is unavailable
|
45
|
+
begin
|
46
|
+
Net::Telnet.new("Host" => Squeezer.server, "Port" => Squeezer.port, "Telnetmode" => false, "Prompt" => /\n/)
|
47
|
+
rescue
|
48
|
+
raise "connection failed"
|
49
|
+
end
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
data/lib/squeezer/models.rb
CHANGED
@@ -1,21 +1,24 @@
|
|
1
1
|
module Squeezer
|
2
2
|
module Models
|
3
3
|
|
4
|
-
class Model < API
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
class Model < API
|
5
|
+
|
6
|
+
# update regex to play nice with : chars
|
7
|
+
def self.extract_records(data)
|
8
|
+
records = Array.new
|
9
|
+
record = Hash.new
|
10
|
+
data.scan(/([^\s]+)%3A([^\s]+)/) do |match|
|
11
|
+
if match[0] == "id"
|
12
|
+
records << record unless record.empty?
|
13
|
+
record = {:id => URI.unescape(match[1])}
|
13
14
|
end
|
14
|
-
|
15
|
+
record[match[0].to_sym] = URI.unescape(match[1]) unless record.empty?
|
15
16
|
end
|
16
|
-
|
17
|
+
record.delete(:count)
|
18
|
+
records << record unless record.empty?
|
19
|
+
records
|
17
20
|
end
|
18
|
-
|
21
|
+
|
19
22
|
end
|
20
23
|
|
21
24
|
Dir[File.expand_path('../models/*.rb', __FILE__)].each{|f| require f}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Squeezer
|
2
|
+
module Models
|
3
|
+
|
4
|
+
class Album < Model
|
5
|
+
attr_reader :id, :name
|
6
|
+
|
7
|
+
def initialize(record)
|
8
|
+
unless record.nil?
|
9
|
+
@id = record[:id] if record.key?(:id)
|
10
|
+
@name = record[:title] if record.key?(:title)
|
11
|
+
@discs = record[:disccount] if record.key?(:disccount)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def discs
|
16
|
+
@discs.nil? ? 1 : @discs.to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.total
|
20
|
+
Connection.exec("info total albums ?").to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.all
|
24
|
+
results = Array.new
|
25
|
+
Model.extract_records(Connection.exec("albums 0 #{total} charset:utf8 tags:tqs")).each do |record|
|
26
|
+
results << Album.new(record)
|
27
|
+
end
|
28
|
+
results
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -1,8 +1,28 @@
|
|
1
1
|
module Squeezer
|
2
2
|
module Models
|
3
|
+
|
3
4
|
class Artist < Model
|
4
|
-
attr_accessor :id, :
|
5
|
-
|
5
|
+
attr_accessor :id, :name
|
6
|
+
|
7
|
+
def initialize(record)
|
8
|
+
unless record.nil?
|
9
|
+
@id = record[:id] if record.key?(:id)
|
10
|
+
@name = record[:artist] if record.key?(:artist)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.total
|
15
|
+
Connection.exec("info total artists ?").to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.all
|
19
|
+
results = Array.new
|
20
|
+
Model.extract_records(Connection.exec("artists 0 #{total + 1} charset:utf8 tags:s")).each do |record|
|
21
|
+
results << Artist.new(record)
|
22
|
+
end
|
23
|
+
results
|
24
|
+
end
|
6
25
|
end
|
26
|
+
|
7
27
|
end
|
8
28
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Squeezer
|
2
|
+
module Models
|
3
|
+
|
4
|
+
class Genre < Model
|
5
|
+
attr_accessor :id, :name
|
6
|
+
|
7
|
+
def initialize(record)
|
8
|
+
unless record.nil?
|
9
|
+
@id = record[:id] if record.key?(:id)
|
10
|
+
@name = record[:genre] if record.key?(:genre)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.total
|
15
|
+
Connection.exec("info total genres ?").to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.all
|
19
|
+
results = Array.new
|
20
|
+
Model.extract_records(Connection.exec("genres 0 #{total} charset:utf8 tags:s")).each do |record|
|
21
|
+
results << Genre.new(record)
|
22
|
+
end
|
23
|
+
results
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|