spotify 12.3.0 → 12.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +19 -0
- data/Gemfile +1 -0
- data/README.markdown +29 -10
- data/Rakefile +10 -3
- data/examples/.gitignore +4 -0
- data/examples/README.md +15 -0
- data/examples/audio-stream.rb +170 -0
- data/examples/logging-in.rb +95 -0
- data/lib/spotify.rb +2 -1
- data/lib/spotify/error.rb +4 -1
- data/lib/spotify/managed_pointer.rb +53 -23
- data/lib/spotify/monkey_patches/ffi_pointer.rb +18 -0
- data/lib/spotify/structs.rb +14 -0
- data/lib/spotify/structs/session_callbacks.rb +2 -2
- data/lib/spotify/structs/session_config.rb +1 -1
- data/lib/spotify/types/image_id.rb +33 -26
- data/lib/spotify/types/nul_string.rb +23 -34
- data/lib/spotify/types/utf8_string.rb +19 -25
- data/lib/spotify/util.rb +1 -0
- data/lib/spotify/version.rb +1 -2
- data/spec/bench_helper.rb +29 -0
- data/spec/benchmarks/managed_pointer_bench.rb +32 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/spotify/api/functions_spec.rb +67 -0
- data/spec/spotify/api_spec.rb +14 -58
- data/spec/spotify/defines_spec.rb +1 -1
- data/spec/spotify/managed_pointer_spec.rb +60 -11
- data/spec/spotify/monkey_patches/ffi_pointer_spec.rb +9 -0
- data/spec/spotify/types/nul_string_spec.rb +3 -3
- data/spec/{api-linux.h → support/api-linux.h} +0 -0
- data/spec/support/api-linux.xml +1893 -0
- data/spec/{api-mac.h → support/api-mac.h} +0 -0
- data/spec/{api-mac.xml → support/api-mac.xml} +0 -0
- data/spec/support/hook_spotify.rb +1 -1
- data/spec/{linux-platform.rb → support/linux-platform.rb} +0 -0
- data/spec/{mac-platform.rb → support/mac-platform.rb} +0 -0
- metadata +58 -46
- data/LICENSE +0 -19
- data/spec/api-linux.xml +0 -1887
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
[HEAD][]
|
2
|
+
-----------
|
3
|
+
This release breaks backwards-compatibility, as functions will no
|
4
|
+
longer accept pointers of any other type of what they expect. This
|
5
|
+
means that you must wrap any pointers in a Spotify::ManagedPointer
|
6
|
+
of the appropriate type before using them to call API functions.
|
7
|
+
|
8
|
+
Callbacks now receive actual structs (by reference) in their params,
|
9
|
+
instead of a pointer which needed to be manually casted.
|
10
|
+
|
11
|
+
- [1fe1abed6] refactor retaining class for all managed pointer
|
12
|
+
- [af54c02e1] **naive type-checking for all spotify objects**
|
13
|
+
- [1d6939cd8] monkeypatch-add FFI::AbstractMemory#read_size_t
|
14
|
+
- [b9ce8941c] unlock mutex for Spotify::Error.explain (considered thread-safe)
|
15
|
+
- [fd2490728] add Spotify::Struct#to_s/to_h
|
16
|
+
- [1cbaf4a64] **callbacks now receive structs by reference instead of raw pointer**
|
17
|
+
|
1
18
|
[v12.3.0][]
|
2
19
|
-----------
|
3
20
|
Lots of internal and external changes. You could almost say it’s a rewrite.
|
@@ -147,6 +164,8 @@ v0.0.0
|
|
147
164
|
------
|
148
165
|
- release to register rubygems.org name
|
149
166
|
|
167
|
+
[HEAD]: https://github.com/Burgestrand/spotify/compare/v12.3.0...HEAD
|
168
|
+
|
150
169
|
[v12.3.0]: https://github.com/Burgestrand/spotify/compare/v12.2.0...v12.3.0
|
151
170
|
[v12.2.0]: https://github.com/Burgestrand/spotify/compare/v12.0.3...v12.2.0
|
152
171
|
[v12.0.3]: https://github.com/Burgestrand/spotify/compare/v12.0.2...v12.0.3
|
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -17,12 +17,12 @@ The Spotify gem has:
|
|
17
17
|
- [100% API coverage][], including callback support. You’ll be able to use any function from the libspotify library.
|
18
18
|
- [Automatic garbage collection][]. Piggybacking on Ruby’s GC to manage pointer lifecycle.
|
19
19
|
- [Parallell function call protection][]. libspotify is not thread-safe, but Spotify protects you.
|
20
|
-
- [Type conversion][]. Special pointers for every Spotify type.
|
20
|
+
- [Type conversion and type safety][]. Special pointers for every Spotify type, protecting you from accidental mix-ups.
|
21
21
|
|
22
22
|
[100% API coverage]: http://rdoc.info/github/Burgestrand/spotify/master/Spotify/API
|
23
23
|
[Automatic garbage collection]: http://rdoc.info/github/Burgestrand/spotify/master/Spotify/ManagedPointer
|
24
24
|
[Parallell function call protection]: http://rdoc.info/github/Burgestrand/spotify/master/Spotify#method_missing-class_method
|
25
|
-
[Type conversion]: http://rdoc.info/github/Burgestrand/spotify/master/Spotify/ManagedPointer
|
25
|
+
[Type conversion and type safety]: http://rdoc.info/github/Burgestrand/spotify/master/Spotify/ManagedPointer
|
26
26
|
|
27
27
|
The Spotify gem is aimed at experienced developers
|
28
28
|
--------------------------------------------------
|
@@ -32,14 +32,15 @@ you can write using it. The Spotify gem itself, however, has very few opinions.
|
|
32
32
|
|
33
33
|
Known supporting libraries:
|
34
34
|
|
35
|
-
- [Hallon](https://github.com/Burgestrand/Hallon)
|
36
|
-
a
|
37
|
-
|
38
|
-
|
35
|
+
- [Hallon](https://github.com/Burgestrand/Hallon). Hallon attempted to be a one-gem-does-all,
|
36
|
+
at a higher abstraction level than the Spotify gem. It features a slightly more convenient
|
37
|
+
API for certain things, and requires no ruby FFI knowledge, but it is not really an expert
|
38
|
+
on any use case.
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
The Spotify API is well suited for experimentation, and it can be awfully fun to play with.
|
41
|
+
The examples/ directory contains example code for achieving certain tasks with the Spotify gem.
|
42
|
+
|
43
|
+
If you need any assitance feel free to post a message on the mailing list: [ruby-hallon@googlegroups.com][].
|
43
44
|
|
44
45
|
Manually installing libspotify
|
45
46
|
------------------------------
|
@@ -63,7 +64,25 @@ Given a version `X.Y.Z`, each segment corresponds to:
|
|
63
64
|
|
64
65
|
License
|
65
66
|
-------
|
66
|
-
|
67
|
+
Copyright (c) 2012 Kim Burgestrand <kim@burgestrand.se>
|
68
|
+
|
69
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
70
|
+
of this software and associated documentation files (the "Software"), to deal
|
71
|
+
in the Software without restriction, including without limitation the rights
|
72
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
73
|
+
copies of the Software, and to permit persons to whom the Software is
|
74
|
+
furnished to do so, subject to the following conditions:
|
75
|
+
|
76
|
+
The above copyright notice and this permission notice shall be included in
|
77
|
+
all copies or substantial portions of the Software.
|
78
|
+
|
79
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
80
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
81
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
82
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
83
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
84
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
85
|
+
THE SOFTWARE.
|
67
86
|
|
68
87
|
[semantic versioning (semver.org)]: http://semver.org/
|
69
88
|
[ruby-hallon@googlegroups.com]: mailto:ruby-hallon@googlegroups.com
|
data/Rakefile
CHANGED
@@ -20,9 +20,16 @@ rescue LoadError
|
|
20
20
|
puts "WARN: YARD not available. You may install documentation dependencies via bundler."
|
21
21
|
end
|
22
22
|
|
23
|
+
desc "Run code benchmarks"
|
24
|
+
task :bench do
|
25
|
+
sh "ruby", "spec/bench_helper.rb"
|
26
|
+
end
|
27
|
+
|
23
28
|
desc "re-generate spec/api.h.xml"
|
24
29
|
task :gen do
|
25
|
-
|
30
|
+
Dir["spec/support/api-*.h"].each do |header|
|
31
|
+
sh "gccxml", header, "-fxml=#{header.sub('.h', '.xml')}"
|
32
|
+
end
|
26
33
|
end
|
27
34
|
|
28
35
|
task :console do
|
@@ -31,11 +38,11 @@ end
|
|
31
38
|
|
32
39
|
require 'rspec/core/rake_task'
|
33
40
|
RSpec::Core::RakeTask.new(:test_mac) do |spec|
|
34
|
-
spec.ruby_opts = ['-r ./spec/mac-platform']
|
41
|
+
spec.ruby_opts = ['-r ./spec/support/mac-platform', '-W']
|
35
42
|
end
|
36
43
|
|
37
44
|
RSpec::Core::RakeTask.new(:test_linux) do |spec|
|
38
|
-
spec.ruby_opts = ['-r ./spec/linux-platform']
|
45
|
+
spec.ruby_opts = ['-r ./spec/support/linux-platform', '-W']
|
39
46
|
end
|
40
47
|
|
41
48
|
desc "Run the tests for both Linux and Mac OS"
|
data/examples/.gitignore
ADDED
data/examples/README.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# How to run the examples:
|
2
|
+
|
3
|
+
1. Install the dependencies. Preferrably via bundler with `bundle install`.
|
4
|
+
2. Configure your environment variables SPOTIFY\_USERNAME, SPOTIFY\_PASSWORD.
|
5
|
+
3. Download your binary application key from <https://developer.spotify.com/technologies/libspotify/keys/>.
|
6
|
+
4. Put your `spotify_appkey.key` application key in the example directory.
|
7
|
+
5. Run your specific example, e.g. `bundle exec ruby logging-in.rb`.
|
8
|
+
|
9
|
+
## Available examples and what they do
|
10
|
+
|
11
|
+
- **logging-in.rb**
|
12
|
+
|
13
|
+
a fairly small example on how you could login with the spotify API. It
|
14
|
+
has some callbacks to provide more information on what happens during
|
15
|
+
the process.
|
@@ -0,0 +1,170 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "spotify"
|
6
|
+
require "logger"
|
7
|
+
require "json"
|
8
|
+
require "pry"
|
9
|
+
require "plaything"
|
10
|
+
|
11
|
+
Thread.abort_on_exception = true
|
12
|
+
|
13
|
+
# We use a logger to print some information on when things are happening.
|
14
|
+
$logger = Logger.new($stderr)
|
15
|
+
$logger.level = Logger::INFO
|
16
|
+
|
17
|
+
# libspotify supports callbacks, but they are not useful for waiting on
|
18
|
+
# operations (how they fire can be strange at times, and sometimes they
|
19
|
+
# might not fire at all). As a result, polling is the way to go.
|
20
|
+
def poll(session)
|
21
|
+
until yield
|
22
|
+
FFI::MemoryPointer.new(:int) do |ptr|
|
23
|
+
Spotify.session_process_events(session, ptr)
|
24
|
+
end
|
25
|
+
sleep(0.1)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# For making sure fetching configuration options fail with a useful error
|
30
|
+
# message when running the examples.
|
31
|
+
def env(name)
|
32
|
+
ENV.fetch(name) do
|
33
|
+
raise "Missing ENV[#{name}]. Please: export #{name}=\"your value\""
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def play_track(uri)
|
38
|
+
link = Spotify.link_create_from_string(uri)
|
39
|
+
track = Spotify.link_as_track(link)
|
40
|
+
poll($session) { Spotify.track_is_loaded(track) }
|
41
|
+
Spotify.try(:session_player_play, $session, false)
|
42
|
+
Spotify.try(:session_player_load, $session, track)
|
43
|
+
Spotify.try(:session_player_play, $session, true)
|
44
|
+
end
|
45
|
+
|
46
|
+
class FrameReader
|
47
|
+
include Enumerable
|
48
|
+
|
49
|
+
def initialize(channels, sample_type, frames_count, frames_ptr)
|
50
|
+
@channels = channels
|
51
|
+
@sample_type = sample_type
|
52
|
+
@size = frames_count * @channels
|
53
|
+
# strip type information, we work with bytes
|
54
|
+
@pointer = FFI::Pointer.new(frames_ptr)
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_reader :size
|
58
|
+
|
59
|
+
def each
|
60
|
+
return enum_for(__method__) unless block_given?
|
61
|
+
|
62
|
+
ffi_read = :"get_#{@sample_type}"
|
63
|
+
ffi_size = FFI.type_size(@sample_type)
|
64
|
+
|
65
|
+
(0...size).each do |index|
|
66
|
+
yield @pointer.public_send(ffi_read, index * ffi_size)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
plaything = Plaything.new
|
72
|
+
|
73
|
+
#
|
74
|
+
# Global callback procs.
|
75
|
+
#
|
76
|
+
# They are global variables to protect from ever being garbage collected.
|
77
|
+
#
|
78
|
+
# You must not allow the callbacks to ever be garbage collected, or libspotify
|
79
|
+
# will hold information about callbacks that no longer exist, and crash upon
|
80
|
+
# calling the first missing callback. This is *very* important!
|
81
|
+
|
82
|
+
$session_callbacks = {
|
83
|
+
log_message: proc do |session, message|
|
84
|
+
$logger.info("session (log message)") { message }
|
85
|
+
end,
|
86
|
+
|
87
|
+
logged_in: proc do |session, error|
|
88
|
+
$logger.debug("session (logged in)") { Spotify::Error.explain(error) }
|
89
|
+
end,
|
90
|
+
|
91
|
+
logged_out: proc do |session|
|
92
|
+
$logger.debug("session (logged out)") { "logged out!" }
|
93
|
+
end,
|
94
|
+
|
95
|
+
streaming_error: proc do |session, error|
|
96
|
+
$logger.error("session (player)") { "streaming error %s" % Spotify::Error.explain(error) }
|
97
|
+
end,
|
98
|
+
|
99
|
+
start_playback: proc do |session|
|
100
|
+
$logger.debug("session (player)") { "start playback" }
|
101
|
+
plaything.play
|
102
|
+
end,
|
103
|
+
|
104
|
+
stop_playback: proc do |session|
|
105
|
+
$logger.debug("session (player)") { "stop playback" }
|
106
|
+
plaything.stop
|
107
|
+
end,
|
108
|
+
|
109
|
+
get_audio_buffer_stats: proc do |session, stats|
|
110
|
+
stats[:samples] = plaything.queue_size
|
111
|
+
stats[:stutter] = plaything.drops
|
112
|
+
$logger.debug("session (player)") { "queue size [#{stats[:samples]}, #{stats[:stutter]}]" }
|
113
|
+
end,
|
114
|
+
|
115
|
+
music_delivery: proc do |session, format, frames, num_frames|
|
116
|
+
if num_frames == 0
|
117
|
+
plaything.stop
|
118
|
+
$logger.debug("session (player)") { "music delivery audio discontuity" }
|
119
|
+
else
|
120
|
+
frames = FrameReader.new(format[:channels], format[:sample_type], num_frames, frames)
|
121
|
+
consumed_samples = plaything << frames
|
122
|
+
consumed_frames = consumed_samples / format[:channels]
|
123
|
+
$logger.debug("session (player)") { "music delivery #{consumed_frames} of #{num_frames}" }
|
124
|
+
consumed_frames
|
125
|
+
end
|
126
|
+
end,
|
127
|
+
|
128
|
+
end_of_track: proc do |session|
|
129
|
+
$end_of_track = true
|
130
|
+
$logger.debug("session (player)") { "end of track" }
|
131
|
+
plaything.stop
|
132
|
+
end,
|
133
|
+
}
|
134
|
+
|
135
|
+
#
|
136
|
+
# Main work code.
|
137
|
+
#
|
138
|
+
|
139
|
+
# You can read about what these session configuration options do in the
|
140
|
+
# libspotify documentation:
|
141
|
+
# https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__config.html
|
142
|
+
config = Spotify::SessionConfig.new({
|
143
|
+
api_version: Spotify::API_VERSION.to_i,
|
144
|
+
application_key: IO.read("./spotify_appkey.key"),
|
145
|
+
cache_location: ".spotify/",
|
146
|
+
settings_location: ".spotify/",
|
147
|
+
tracefile: "spotify_tracefile.txt",
|
148
|
+
user_agent: "spotify for ruby",
|
149
|
+
callbacks: Spotify::SessionCallbacks.new($session_callbacks),
|
150
|
+
})
|
151
|
+
|
152
|
+
$logger.info "Creating session."
|
153
|
+
FFI::MemoryPointer.new(Spotify::Session) do |ptr|
|
154
|
+
Spotify.try(:session_create, config, ptr)
|
155
|
+
$session = Spotify::Session.new(ptr.read_pointer)
|
156
|
+
end
|
157
|
+
|
158
|
+
$logger.info "Created! Logging in."
|
159
|
+
Spotify.session_login($session, env("SPOTIFY_USERNAME"), env("SPOTIFY_PASSWORD"), false, nil)
|
160
|
+
|
161
|
+
$logger.info "Log in requested. Waiting forever until logged in."
|
162
|
+
poll($session) { Spotify.session_connectionstate($session) == :logged_in }
|
163
|
+
|
164
|
+
$logger.info "Logged in as #{Spotify.session_user_name($session)}."
|
165
|
+
|
166
|
+
print "Spotify track URI: "
|
167
|
+
play_track gets.chomp
|
168
|
+
|
169
|
+
$logger.info "Playing track until end. Use ^C to exit."
|
170
|
+
poll($session) { $end_of_track }
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'spotify'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
# We use a logger to print some information on when things are happening.
|
8
|
+
$logger = Logger.new($stderr)
|
9
|
+
|
10
|
+
#
|
11
|
+
# Some utility.
|
12
|
+
#
|
13
|
+
|
14
|
+
# This method is just for convenience. Calling the process_events function
|
15
|
+
# is slightly cumbersome.
|
16
|
+
def process_events(session)
|
17
|
+
FFI::MemoryPointer.new(:int) do |ptr|
|
18
|
+
Spotify.session_process_events(session, ptr)
|
19
|
+
return Rational(ptr.read_int, 1000)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# libspotify supports callbacks, but they are not useful for waiting on
|
24
|
+
# operations (how they fire can be strange at times, and sometimes they
|
25
|
+
# might not fire at all). As a result, polling is the way to go.
|
26
|
+
def poll(session)
|
27
|
+
until yield
|
28
|
+
process_events(session)
|
29
|
+
sleep(0.01)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# For making sure fetching configuration options fail with a useful error
|
34
|
+
# message when running the examples.
|
35
|
+
def env(name)
|
36
|
+
ENV.fetch(name) do
|
37
|
+
raise "Missing ENV['#{name}']. Please: export #{name}='your value'"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Global callback procs.
|
43
|
+
#
|
44
|
+
# They are global variables to protect from ever being garbage collected.
|
45
|
+
#
|
46
|
+
# You must not allow the callbacks to ever be garbage collected, or libspotify
|
47
|
+
# will hold information about callbacks that no longer exist, and crash upon
|
48
|
+
# calling the first missing callback. This is *very* important!
|
49
|
+
|
50
|
+
$session_callbacks = {
|
51
|
+
log_message: lambda do |session, message|
|
52
|
+
$logger.info('session (log message)') { message }
|
53
|
+
end,
|
54
|
+
|
55
|
+
logged_in: lambda do |session, error|
|
56
|
+
$logger.info('session (logged in)') { Spotify::Error.explain(error) }
|
57
|
+
end,
|
58
|
+
|
59
|
+
logged_out: lambda do |session|
|
60
|
+
$logger.info('session (logged out)') { 'logged out!' }
|
61
|
+
end,
|
62
|
+
}
|
63
|
+
|
64
|
+
#
|
65
|
+
# Main work code.
|
66
|
+
#
|
67
|
+
|
68
|
+
# You can read about what these session configuration options do in the
|
69
|
+
# libspotify documentation:
|
70
|
+
# https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__config.html
|
71
|
+
config = Spotify::SessionConfig.new({
|
72
|
+
api_version: Spotify::API_VERSION.to_i,
|
73
|
+
application_key: IO.read('./spotify_appkey.key'),
|
74
|
+
cache_location: ".spotify/",
|
75
|
+
settings_location: ".spotify/",
|
76
|
+
tracefile: "spotify_tracefile.txt",
|
77
|
+
user_agent: "spotify for ruby",
|
78
|
+
callbacks: Spotify::SessionCallbacks.new($session_callbacks),
|
79
|
+
})
|
80
|
+
|
81
|
+
$logger.info "Creating session."
|
82
|
+
FFI::MemoryPointer.new(Spotify::Session) do |ptr|
|
83
|
+
# Spotify.try is a special method. It raises a ruby exception if the returned spotify
|
84
|
+
# error code is an error.
|
85
|
+
Spotify.try(:session_create, config, ptr)
|
86
|
+
$session = Spotify::Session.new(ptr.read_pointer)
|
87
|
+
end
|
88
|
+
|
89
|
+
$logger.info "Created! Logging in."
|
90
|
+
Spotify.session_login($session, env('SPOTIFY_USERNAME'), env('SPOTIFY_PASSWORD'), false, nil)
|
91
|
+
|
92
|
+
$logger.info "Log in requested. Waiting forever until logged in."
|
93
|
+
poll($session) { Spotify.session_connectionstate($session) == :logged_in }
|
94
|
+
|
95
|
+
$logger.info "Logged in as #{Spotify.session_user_name($session)}."
|
data/lib/spotify.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'ffi'
|
3
|
+
require 'spotify/monkey_patches/ffi_pointer'
|
3
4
|
require 'libspotify'
|
4
5
|
require 'monitor'
|
5
6
|
|
@@ -28,7 +29,7 @@ module Spotify
|
|
28
29
|
extend FFI::Library
|
29
30
|
|
30
31
|
begin
|
31
|
-
ffi_lib [LIBSPOTIFY_BIN, 'libspotify', '/Library/Frameworks/libspotify.framework/libspotify']
|
32
|
+
ffi_lib [LIBSPOTIFY_BIN, 'spotify', 'libspotify', '/Library/Frameworks/libspotify.framework/libspotify']
|
32
33
|
rescue LoadError
|
33
34
|
puts <<-ERROR.gsub(/^ */, '')
|
34
35
|
Failed to load the `libspotify` library. It is possible that the libspotify gem
|
data/lib/spotify/error.rb
CHANGED
@@ -4,6 +4,9 @@ module Spotify
|
|
4
4
|
class << self
|
5
5
|
# Explain a Spotify error with a descriptive message.
|
6
6
|
#
|
7
|
+
# @note this method calls the API directly, since the
|
8
|
+
# underlying API call is considered thread-safe.
|
9
|
+
#
|
7
10
|
# @param [Symbol, Integer] error
|
8
11
|
# @return [String] a decriptive string of the error
|
9
12
|
def explain(error)
|
@@ -11,7 +14,7 @@ module Spotify
|
|
11
14
|
|
12
15
|
message = []
|
13
16
|
message << "[#{symbol.to_s.upcase}]"
|
14
|
-
message << Spotify.error_message(error)
|
17
|
+
message << Spotify::API.error_message(error)
|
15
18
|
message << "(#{error})"
|
16
19
|
|
17
20
|
message.join(' ')
|