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
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
|