spotify 12.3.0 → 12.4.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/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(' ')
|