hallon 0.8.0 → 0.9.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/.travis.yml +2 -0
- data/CHANGELOG +43 -0
- data/Gemfile +2 -0
- data/README.markdown +21 -13
- data/Rakefile +84 -23
- data/dev/login.rb +16 -0
- data/examples/adding_tracks_to_playlist.rb +49 -0
- data/examples/logging_in.rb +1 -6
- data/examples/show_published_playlists_of_user.rb +9 -19
- data/hallon.gemspec +1 -1
- data/lib/hallon.rb +3 -2
- data/lib/hallon/album.rb +55 -41
- data/lib/hallon/album_browse.rb +41 -37
- data/lib/hallon/artist.rb +30 -21
- data/lib/hallon/artist_browse.rb +59 -41
- data/lib/hallon/base.rb +68 -5
- data/lib/hallon/enumerator.rb +1 -0
- data/lib/hallon/error.rb +3 -0
- data/lib/hallon/ext/spotify.rb +169 -36
- data/lib/hallon/image.rb +30 -44
- data/lib/hallon/link.rb +29 -43
- data/lib/hallon/linkable.rb +68 -20
- data/lib/hallon/observable.rb +0 -1
- data/lib/hallon/player.rb +21 -7
- data/lib/hallon/playlist.rb +291 -0
- data/lib/hallon/playlist_container.rb +27 -0
- data/lib/hallon/search.rb +52 -45
- data/lib/hallon/session.rb +129 -81
- data/lib/hallon/toplist.rb +37 -19
- data/lib/hallon/track.rb +68 -45
- data/lib/hallon/user.rb +69 -33
- data/lib/hallon/version.rb +1 -1
- data/spec/hallon/album_browse_spec.rb +15 -9
- data/spec/hallon/album_spec.rb +15 -15
- data/spec/hallon/artist_browse_spec.rb +28 -9
- data/spec/hallon/artist_spec.rb +30 -14
- data/spec/hallon/enumerator_spec.rb +0 -1
- data/spec/hallon/hallon_spec.rb +20 -1
- data/spec/hallon/image_spec.rb +18 -41
- data/spec/hallon/link_spec.rb +10 -12
- data/spec/hallon/linkable_spec.rb +37 -18
- data/spec/hallon/player_spec.rb +8 -0
- data/spec/hallon/playlist_container_spec.rb +75 -0
- data/spec/hallon/playlist_spec.rb +204 -0
- data/spec/hallon/search_spec.rb +19 -16
- data/spec/hallon/session_spec.rb +61 -29
- data/spec/hallon/spotify_spec.rb +30 -0
- data/spec/hallon/toplist_spec.rb +22 -14
- data/spec/hallon/track_spec.rb +62 -21
- data/spec/hallon/user_spec.rb +47 -36
- data/spec/mockspotify.rb +35 -10
- data/spec/mockspotify/mockspotify_spec.rb +22 -0
- data/spec/spec_helper.rb +7 -3
- data/spec/support/common_objects.rb +91 -16
- data/spec/support/shared_for_linkable_objects.rb +39 -0
- metadata +30 -20
- data/Termfile +0 -7
- data/lib/hallon/synchronizable.rb +0 -32
- data/spec/hallon/synchronizable_spec.rb +0 -19
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Hallon
|
3
|
+
class PlaylistContainer < Base
|
4
|
+
class Folder
|
5
|
+
end
|
6
|
+
|
7
|
+
include Observable
|
8
|
+
|
9
|
+
# Wrap an existing PlaylistContainer pointer in an object.
|
10
|
+
#
|
11
|
+
# @param [Spotify::Pointer] pointer
|
12
|
+
def initialize(pointer)
|
13
|
+
@pointer = to_pointer(pointer, :playlistcontainer)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Boolean] true if the container is loaded
|
17
|
+
def loaded?
|
18
|
+
Spotify.playlistcontainer_is_loaded(pointer)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [User, nil] owner of the container (nil if unknown or no owner)
|
22
|
+
def owner
|
23
|
+
owner = Spotify.playlistcontainer_owner!(pointer)
|
24
|
+
User.new(owner) unless owner.null?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/hallon/search.rb
CHANGED
@@ -12,23 +12,36 @@ module Hallon
|
|
12
12
|
Spotify.enum_type(:radio_genre).symbols
|
13
13
|
end
|
14
14
|
|
15
|
+
# @return [Hash] default search parameters
|
16
|
+
def self.defaults
|
17
|
+
@defaults ||= {
|
18
|
+
:tracks => 25,
|
19
|
+
:albums => 25,
|
20
|
+
:artists => 25,
|
21
|
+
:tracks_offset => 0,
|
22
|
+
:albums_offset => 0,
|
23
|
+
:artists_offset => 0
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
15
27
|
# @param [Range<Integer>] range (from_year..to_year)
|
16
28
|
# @param [Symbol, …] genres
|
17
29
|
# @return [Search] radio search in given period and genres
|
18
30
|
def self.radio(range, *genres)
|
19
31
|
from_year, to_year = range.begin, range.end
|
20
32
|
genres = genres.reduce(0) do |mask, genre|
|
21
|
-
mask |
|
33
|
+
mask | Spotify.enum_value!(genre, "genre")
|
22
34
|
end
|
23
35
|
|
24
36
|
search = allocate
|
25
37
|
search.instance_eval do
|
26
38
|
@callback = proc { search.trigger(:load) }
|
27
|
-
pointer = Spotify.radio_search_create(session.pointer, from_year, to_year, genres, @callback, nil)
|
28
|
-
@pointer = Spotify::Pointer.new(pointer, :search, false)
|
39
|
+
@pointer = Spotify.radio_search_create!(session.pointer, from_year, to_year, genres, @callback, nil)
|
29
40
|
|
30
|
-
|
41
|
+
raise FFI::NullPointerError, "radio search failed" if @pointer.null?
|
31
42
|
end
|
43
|
+
|
44
|
+
search
|
32
45
|
end
|
33
46
|
|
34
47
|
# Construct a new search with given query.
|
@@ -43,86 +56,80 @@ module Hallon
|
|
43
56
|
# @option options [#to_i] :artists_offset (0) offset of artists in search result
|
44
57
|
# @see http://developer.spotify.com/en/libspotify/docs/group__search.html#gacf0b5e902e27d46ef8b1f40e332766df
|
45
58
|
def initialize(query, options = {})
|
46
|
-
o =
|
47
|
-
:tracks => 25,
|
48
|
-
:albums => 25,
|
49
|
-
:artists => 25,
|
50
|
-
:tracks_offset => 0,
|
51
|
-
:albums_offset => 0,
|
52
|
-
:artists_offset => 0
|
53
|
-
}.merge(options)
|
54
|
-
|
59
|
+
o = Search.defaults.merge(options)
|
55
60
|
@callback = proc { trigger(:load) }
|
56
|
-
pointer
|
57
|
-
|
61
|
+
@pointer = Spotify.search_create!(session.pointer, query, o[:tracks_offset].to_i, o[:tracks].to_i, o[:albums_offset].to_i, o[:albums].to_i, o[:artists_offset].to_i, o[:artists].to_i, @callback, nil)
|
62
|
+
|
63
|
+
raise FFI::NullPointerError, "search for “#{query}” failed" if @pointer.null?
|
58
64
|
end
|
59
65
|
|
60
|
-
# @return [Boolean] true if the search has been fully loaded
|
66
|
+
# @return [Boolean] true if the search has been fully loaded.
|
61
67
|
def loaded?
|
62
|
-
Spotify.search_is_loaded(
|
68
|
+
Spotify.search_is_loaded(pointer)
|
63
69
|
end
|
64
70
|
|
65
|
-
# @
|
66
|
-
|
67
|
-
|
71
|
+
# @see Error.explain
|
72
|
+
# @return [Symbol] search error status.
|
73
|
+
def status
|
74
|
+
Spotify.search_error(pointer)
|
68
75
|
end
|
69
76
|
|
70
|
-
# @return [String] search query this search was created with
|
77
|
+
# @return [String] search query this search was created with.
|
71
78
|
def query
|
72
|
-
Spotify.search_query(
|
79
|
+
Spotify.search_query(pointer)
|
73
80
|
end
|
74
81
|
|
75
|
-
# @return [String] “did you mean?” suggestion for current search
|
82
|
+
# @return [String] “did you mean?” suggestion for current search.
|
76
83
|
def did_you_mean
|
77
|
-
Spotify.search_did_you_mean(
|
84
|
+
Spotify.search_did_you_mean(pointer)
|
78
85
|
end
|
79
86
|
|
80
|
-
# @return [Enumerator<Track>]
|
87
|
+
# @return [Enumerator<Track>] list of all tracks in the search result.
|
81
88
|
def tracks
|
82
|
-
size = Spotify.search_num_tracks(
|
89
|
+
size = Spotify.search_num_tracks(pointer)
|
83
90
|
Enumerator.new(size) do |i|
|
84
|
-
track = Spotify.search_track(
|
85
|
-
Track.new(track)
|
91
|
+
track = Spotify.search_track!(pointer, i)
|
92
|
+
Track.new(track)
|
86
93
|
end
|
87
94
|
end
|
88
95
|
|
89
|
-
# @return [Integer] total tracks available for this search query
|
96
|
+
# @return [Integer] total tracks available for this search query.
|
90
97
|
def total_tracks
|
91
|
-
Spotify.search_total_tracks(
|
98
|
+
Spotify.search_total_tracks(pointer)
|
92
99
|
end
|
93
100
|
|
94
|
-
# @return [Enumerator<Album>]
|
101
|
+
# @return [Enumerator<Album>] list of all albums in the search result.
|
95
102
|
def albums
|
96
|
-
size = Spotify.search_num_albums(
|
103
|
+
size = Spotify.search_num_albums(pointer)
|
97
104
|
Enumerator.new(size) do |i|
|
98
|
-
album = Spotify.search_album(
|
99
|
-
Album.new(album)
|
105
|
+
album = Spotify.search_album!(pointer, i)
|
106
|
+
Album.new(album)
|
100
107
|
end
|
101
108
|
end
|
102
109
|
|
103
|
-
# @return [Integer] total tracks available for this search query
|
110
|
+
# @return [Integer] total tracks available for this search query.
|
104
111
|
def total_albums
|
105
|
-
Spotify.search_total_albums(
|
112
|
+
Spotify.search_total_albums(pointer)
|
106
113
|
end
|
107
114
|
|
108
|
-
# @return [Enumerator<Artist>]
|
115
|
+
# @return [Enumerator<Artist>] list of all artists in the search result.
|
109
116
|
def artists
|
110
|
-
size = Spotify.search_num_artists(
|
117
|
+
size = Spotify.search_num_artists(pointer)
|
111
118
|
Enumerator.new(size) do |i|
|
112
|
-
artist = Spotify.search_artist(
|
113
|
-
Artist.new(artist)
|
119
|
+
artist = Spotify.search_artist!(pointer, i)
|
120
|
+
Artist.new(artist)
|
114
121
|
end
|
115
122
|
end
|
116
123
|
|
117
|
-
# @return [Integer] total tracks available for this search query
|
124
|
+
# @return [Integer] total tracks available for this search query.
|
118
125
|
def total_artists
|
119
|
-
Spotify.search_total_artists(
|
126
|
+
Spotify.search_total_artists(pointer)
|
120
127
|
end
|
121
128
|
|
122
|
-
# @return [Link] link for this search query
|
129
|
+
# @return [Link] link for this search query.
|
123
130
|
def to_link
|
124
|
-
|
125
|
-
Link.new(
|
131
|
+
link = Spotify.link_create_from_search!(pointer)
|
132
|
+
Link.new(link) unless link.null?
|
126
133
|
end
|
127
134
|
end
|
128
135
|
end
|
data/lib/hallon/session.rb
CHANGED
@@ -37,30 +37,38 @@ module Hallon
|
|
37
37
|
# instance at a later time, you can use {instance}.
|
38
38
|
#
|
39
39
|
# @see Session.instance
|
40
|
-
# @see Session#initialize
|
41
40
|
#
|
42
41
|
# @param (see Session#initialize)
|
42
|
+
# @option (see Session#initialize)
|
43
|
+
# @yield (see Session#initialize)
|
44
|
+
# @raise (see Session#initialize)
|
45
|
+
# @see (see Session#initialize)
|
43
46
|
# @return [Session]
|
44
|
-
def Session.initialize(
|
47
|
+
def Session.initialize(appkey, options = {}, &block)
|
45
48
|
raise "Session has already been initialized" if @__instance__
|
46
|
-
@__instance__ = new(
|
49
|
+
@__instance__ = new(appkey, options, &block)
|
47
50
|
end
|
48
51
|
|
49
52
|
# Returns the previously initialized Session.
|
50
53
|
#
|
51
|
-
# @see Session.
|
54
|
+
# @see Session.initialize
|
52
55
|
#
|
53
56
|
# @return [Session]
|
54
57
|
def Session.instance
|
55
58
|
@__instance__ or raise "Session has not been initialized"
|
56
59
|
end
|
57
60
|
|
61
|
+
# @return [Boolean] true if a Session instance exists.
|
62
|
+
def Session.instance?
|
63
|
+
!! @__instance__
|
64
|
+
end
|
65
|
+
|
58
66
|
# @return [Array<Symbol>] list of available connection types.
|
59
67
|
def self.connection_types
|
60
68
|
Spotify.enum_type(:connection_type).symbols
|
61
69
|
end
|
62
70
|
|
63
|
-
# @return [Array<Symbol>] list of available connection rules
|
71
|
+
# @return [Array<Symbol>] list of available connection rules.
|
64
72
|
def self.connection_rules
|
65
73
|
Spotify.enum_type(:connection_rules).symbols
|
66
74
|
end
|
@@ -69,13 +77,15 @@ module Hallon
|
|
69
77
|
#
|
70
78
|
# @param [#to_s] appkey
|
71
79
|
# @param [Hash] options
|
72
|
-
# @option options [String] :user_agent ("Hallon") User-Agent to use (length < 256)
|
80
|
+
# @option options [String] :user_agent ("Hallon") User-Agent to use (length < `256`)
|
73
81
|
# @option options [String] :settings_path ("tmp") where to save settings and user-specific cache
|
74
|
-
# @option options [String] :cache_path ("") where to save cache files (
|
82
|
+
# @option options [String] :cache_path ("") where to save cache files (`""` to disable)
|
83
|
+
# @option options [String] :tracefile (nil) path to libspotify API tracefile (`nil` to disable)
|
84
|
+
# @option options [String] :device_id (nil) device ID for offline synchronization (`nil` to disable)
|
75
85
|
# @option options [Bool] :load_playlists (true) load playlists into RAM on startup
|
76
86
|
# @option options [Bool] :compress_playlists (true) compress local copies of playlists
|
77
87
|
# @option options [Bool] :cache_playlist_metadata (true) cache metadata for playlists locally
|
78
|
-
# @yield allows you to define handlers for events (see {
|
88
|
+
# @yield allows you to define handlers for events (see {Observable#on})
|
79
89
|
# @raise [ArgumentError] if `options[:user_agent]` is more than 256 characters long
|
80
90
|
# @raise [Hallon::Error] if `sp_session_create` fails
|
81
91
|
# @see http://developer.spotify.com/en/libspotify/docs/structsp__session__config.html
|
@@ -86,7 +96,9 @@ module Hallon
|
|
86
96
|
:cache_path => "",
|
87
97
|
:load_playlists => true,
|
88
98
|
:compress_playlists => true,
|
89
|
-
:cache_playlist_metadata => true
|
99
|
+
:cache_playlist_metadata => true,
|
100
|
+
:device_id => nil,
|
101
|
+
:tracefile => nil,
|
90
102
|
}.merge(options)
|
91
103
|
|
92
104
|
if @options[:user_agent].bytesize > 255
|
@@ -94,7 +106,7 @@ module Hallon
|
|
94
106
|
end
|
95
107
|
|
96
108
|
# Set configuration, as well as callbacks
|
97
|
-
config
|
109
|
+
config = Spotify::SessionConfig.new
|
98
110
|
config[:api_version] = Hallon::API_VERSION
|
99
111
|
config.application_key = appkey
|
100
112
|
@options.each { |(key, value)| config.send(:"#{key}=", value) }
|
@@ -112,12 +124,21 @@ module Hallon
|
|
112
124
|
end
|
113
125
|
end
|
114
126
|
|
127
|
+
# PlaylistContainer for the currently logged in session.
|
128
|
+
#
|
129
|
+
# @note returns nil if the session is not logged in.
|
130
|
+
# @return [PlaylistContainer, nil]
|
131
|
+
def container
|
132
|
+
container = Spotify.session_playlistcontainer!(pointer)
|
133
|
+
PlaylistContainer.new(container) unless container.null?
|
134
|
+
end
|
135
|
+
|
115
136
|
# Process pending Spotify events (might fire callbacks).
|
116
137
|
#
|
117
|
-
# @return [Fixnum]
|
138
|
+
# @return [Fixnum] time (in milliseconds) until it should be called again
|
118
139
|
def process_events
|
119
140
|
FFI::MemoryPointer.new(:int) do |p|
|
120
|
-
Spotify.session_process_events(
|
141
|
+
Spotify.session_process_events(pointer, p)
|
121
142
|
return p.read_int
|
122
143
|
end
|
123
144
|
end
|
@@ -138,8 +159,8 @@ module Hallon
|
|
138
159
|
|
139
160
|
loop do
|
140
161
|
begin
|
141
|
-
process_events
|
142
|
-
params = Timeout::timeout(
|
162
|
+
timeout = [process_events.fdiv(1000), 5].min # scope to five seconds
|
163
|
+
params = Timeout::timeout(timeout) { channel.pop }
|
143
164
|
redo if params == :notify
|
144
165
|
rescue Timeout::Error
|
145
166
|
params = :timeout
|
@@ -158,25 +179,57 @@ module Hallon
|
|
158
179
|
# @param [String] username
|
159
180
|
# @param [String] password
|
160
181
|
# @param [Boolean] remember_me have libspotify remember credentials for {#relogin}
|
161
|
-
# @return [
|
182
|
+
# @return [Session]
|
183
|
+
# @see login!
|
162
184
|
def login(username, password, remember_me = false)
|
163
|
-
tap { Spotify.session_login(
|
185
|
+
tap { Spotify.session_login(pointer, username, password, remember_me) }
|
164
186
|
end
|
165
187
|
|
166
188
|
# Login the remembered user (see {#login}).
|
167
189
|
#
|
168
190
|
# @raise [Hallon::Error] if no credentials are stored in libspotify
|
191
|
+
# @see #relogin!
|
169
192
|
def relogin
|
170
|
-
Error.maybe_raise Spotify.session_relogin(
|
193
|
+
Error.maybe_raise Spotify.session_relogin(pointer)
|
171
194
|
end
|
172
195
|
|
173
|
-
#
|
196
|
+
# Log in to Spotify using the given credentials.
|
174
197
|
#
|
175
|
-
# @return
|
198
|
+
# @note This function will not return until you’ve either logged in successfully,
|
199
|
+
# or until an error is raised.
|
200
|
+
# @param (see #login)
|
201
|
+
# @return [Session]
|
202
|
+
# @raise [Error] if failed to log in
|
203
|
+
# @see #login
|
204
|
+
def login!(username, password, remember_me = false)
|
205
|
+
login(username, password, remember_me)
|
206
|
+
tap { wait_until_logged_in }
|
207
|
+
end
|
208
|
+
|
209
|
+
# Log in the remembered user.
|
210
|
+
#
|
211
|
+
# @note This method will not return until you’ve either logged in successfully
|
212
|
+
# or until an error is raised.
|
213
|
+
# @return [Session]
|
214
|
+
# @raise [Error] if failed to log in
|
215
|
+
# @see #relogin
|
216
|
+
def relogin!
|
217
|
+
tap { relogin; wait_until_logged_in }
|
218
|
+
end
|
219
|
+
|
220
|
+
# Log out the current user.
|
221
|
+
#
|
222
|
+
# @note This method will not return until you’ve logged out successfully.
|
223
|
+
# @return [Session]
|
224
|
+
def logout!
|
225
|
+
tap { logout; wait_for(:logged_out) { logged_out? } }
|
226
|
+
end
|
227
|
+
|
228
|
+
# @return [String] username of the user stored in libspotify-remembered credentials.
|
176
229
|
def remembered_user
|
177
|
-
bufflen = Spotify.session_remembered_user(
|
230
|
+
bufflen = Spotify.session_remembered_user(pointer, nil, 0)
|
178
231
|
FFI::Buffer.alloc_out(bufflen + 1) do |b|
|
179
|
-
Spotify.session_remembered_user(
|
232
|
+
Spotify.session_remembered_user(pointer, b, b.size)
|
180
233
|
return b.get_string(0)
|
181
234
|
end if bufflen > 0
|
182
235
|
end
|
@@ -186,35 +239,25 @@ module Hallon
|
|
186
239
|
# @note If no credentials are stored nothing’ll happen.
|
187
240
|
# @return [self]
|
188
241
|
def forget_me!
|
189
|
-
tap { Spotify.session_forget_me(
|
242
|
+
tap { Spotify.session_forget_me(pointer) }
|
190
243
|
end
|
191
244
|
|
192
245
|
# Logs out of Spotify. Does nothing if not logged in.
|
193
246
|
#
|
194
247
|
# @return [self]
|
195
248
|
def logout
|
196
|
-
tap { Spotify.session_logout(
|
249
|
+
tap { Spotify.session_logout(pointer) if logged_in? }
|
197
250
|
end
|
198
251
|
|
199
|
-
#
|
200
|
-
#
|
201
|
-
# @return [User]
|
252
|
+
# @return [User] the User currently logged in.
|
202
253
|
def user
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
# Retrieve the relation type between logged in {User} and `user`.
|
207
|
-
#
|
208
|
-
# @return [Symbol] :unknown, :none, :unidirectional or :bidirectional
|
209
|
-
def relation_type?(user)
|
210
|
-
Spotify.user_relation_type(@pointer, user.pointer)
|
254
|
+
user = Spotify.session_user!(pointer)
|
255
|
+
User.new(user) unless user.null?
|
211
256
|
end
|
212
257
|
|
213
|
-
#
|
214
|
-
#
|
215
|
-
# @return [Symbol]
|
258
|
+
# @return [Symbol] current connection status.
|
216
259
|
def status
|
217
|
-
Spotify.session_connectionstate(
|
260
|
+
Spotify.session_connectionstate(pointer)
|
218
261
|
end
|
219
262
|
|
220
263
|
# Set session cache size in megabytes.
|
@@ -222,12 +265,12 @@ module Hallon
|
|
222
265
|
# @param [Integer]
|
223
266
|
# @return [Integer]
|
224
267
|
def cache_size=(size)
|
225
|
-
Spotify.session_set_cache_size(
|
268
|
+
Spotify.session_set_cache_size(pointer, @cache_size = size)
|
226
269
|
end
|
227
270
|
|
228
|
-
# @return [String]
|
271
|
+
# @return [String] currently logged in users’ country.
|
229
272
|
def country
|
230
|
-
coded = Spotify.session_user_country(
|
273
|
+
coded = Spotify.session_user_country(pointer)
|
231
274
|
country = ((coded >> 8) & 0xFF).chr
|
232
275
|
country << (coded & 0xFF).chr
|
233
276
|
end
|
@@ -256,35 +299,16 @@ module Hallon
|
|
256
299
|
tap { tracks_starred(tracks, false) }
|
257
300
|
end
|
258
301
|
|
259
|
-
# @note This will be 0 if not logged in.
|
260
|
-
# @note As of current writing, I am unsure if there’s a good way to find out
|
261
|
-
# when this enumerator will be populated. No callbacks or other status
|
262
|
-
# field can tell you when the current sessions’ friends are available.
|
263
|
-
# @return [Enumerator<User>] friends of currently logged in user
|
264
|
-
def friends
|
265
|
-
size = if logged_in?
|
266
|
-
# segfaults unless logged in
|
267
|
-
Spotify.session_num_friends(@pointer)
|
268
|
-
else
|
269
|
-
0
|
270
|
-
end
|
271
|
-
|
272
|
-
Enumerator.new(size) do |i|
|
273
|
-
friend = Spotify.session_friend(@pointer, i)
|
274
|
-
User.new(friend)
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
302
|
# Set the connection rules for this session.
|
279
303
|
#
|
280
304
|
# @param [Symbol, …] connection_rules
|
281
305
|
# @see Session.connection_rules
|
282
306
|
def connection_rules=(connection_rules)
|
283
307
|
rules = Array(connection_rules).reduce(0) do |mask, rule|
|
284
|
-
mask |
|
308
|
+
mask | Spotify.enum_value!(rule, "connection rule")
|
285
309
|
end
|
286
310
|
|
287
|
-
Spotify.session_set_connection_rules(
|
311
|
+
Spotify.session_set_connection_rules(pointer, rules)
|
288
312
|
end
|
289
313
|
|
290
314
|
# Set the connection type for this session.
|
@@ -292,14 +316,14 @@ module Hallon
|
|
292
316
|
# @param [Symbol] connection_type
|
293
317
|
# @see Session.connection_types
|
294
318
|
def connection_type=(connection_type)
|
295
|
-
Spotify.session_set_connection_type(
|
319
|
+
Spotify.session_set_connection_type(pointer, connection_type)
|
296
320
|
end
|
297
321
|
|
298
322
|
# Remaining time left you can stay offline before needing to relogin.
|
299
323
|
#
|
300
324
|
# @return [Integer] offline time left in seconds
|
301
325
|
def offline_time_left
|
302
|
-
Spotify.offline_time_left(
|
326
|
+
Spotify.offline_time_left(pointer)
|
303
327
|
end
|
304
328
|
|
305
329
|
# Offline synchronization status.
|
@@ -308,23 +332,19 @@ module Hallon
|
|
308
332
|
# @see http://developer.spotify.com/en/libspotify/docs/structsp__offline__sync__status.html
|
309
333
|
def offline_sync_status
|
310
334
|
struct = Spotify::OfflineSyncStatus.new
|
311
|
-
if Spotify.offline_sync_get_status(
|
335
|
+
if Spotify.offline_sync_get_status(pointer, struct.pointer)
|
312
336
|
Hash[struct.members.zip(struct.values)]
|
313
337
|
end
|
314
338
|
end
|
315
339
|
|
316
|
-
#
|
317
|
-
#
|
318
|
-
# @return [Integer]
|
340
|
+
# @return [Integer] number of playlists marked for offline sync.
|
319
341
|
def offline_playlists_count
|
320
|
-
Spotify.offline_num_playlists(
|
342
|
+
Spotify.offline_num_playlists(pointer)
|
321
343
|
end
|
322
344
|
|
323
|
-
#
|
324
|
-
#
|
325
|
-
# @return [Integer]
|
345
|
+
# @return [Integer] number of offline tracks left to sync for offline mode.
|
326
346
|
def offline_tracks_to_sync
|
327
|
-
Spotify.offline_tracks_to_sync(
|
347
|
+
Spotify.offline_tracks_to_sync(pointer)
|
328
348
|
end
|
329
349
|
|
330
350
|
# Set preferred offline bitrate.
|
@@ -337,36 +357,48 @@ module Hallon
|
|
337
357
|
# @see Player.bitrates
|
338
358
|
def offline_bitrate=(bitrate)
|
339
359
|
bitrate, resync = Array(bitrate)
|
340
|
-
Spotify.session_preferred_offline_bitrate(
|
360
|
+
Spotify.session_preferred_offline_bitrate(pointer, bitrate, !! resync)
|
361
|
+
end
|
362
|
+
|
363
|
+
# @note Returns nil when no user is logged in.
|
364
|
+
# @return [Playlist, nil] currently logged in user’s starred playlist.
|
365
|
+
def starred
|
366
|
+
playlist = Spotify.session_starred_create!(pointer)
|
367
|
+
Playlist.new(playlist) unless playlist.null?
|
368
|
+
end
|
369
|
+
|
370
|
+
# @note Returns nil when no user is logged in.
|
371
|
+
# @return [Playlist, nil] currently logged in user’s inbox playlist.
|
372
|
+
def inbox
|
373
|
+
playlist = Spotify.session_inbox_create!(pointer)
|
374
|
+
Playlist.new(playlist) unless playlist.null?
|
341
375
|
end
|
342
376
|
|
343
|
-
# True if currently logged in.
|
344
377
|
# @see #status
|
378
|
+
# @return [Boolean] true if logged in.
|
345
379
|
def logged_in?
|
346
380
|
status == :logged_in
|
347
381
|
end
|
348
382
|
|
349
|
-
# True if logged out.
|
350
383
|
# @see #status
|
384
|
+
# @return [Boolean] true if logged out.
|
351
385
|
def logged_out?
|
352
386
|
status == :logged_out
|
353
387
|
end
|
354
388
|
|
355
|
-
# True if session has been disconnected.
|
356
389
|
# @see #status
|
390
|
+
# @return [Boolean] true if session has been disconnected.
|
357
391
|
def disconnected?
|
358
392
|
status == :disconnected
|
359
393
|
end
|
360
394
|
|
361
|
-
# True if offline.
|
362
395
|
# @see #status
|
396
|
+
# @return [Boolean] true if offline.
|
363
397
|
def offline?
|
364
398
|
status == :offline
|
365
399
|
end
|
366
400
|
|
367
|
-
# String representation of the Session.
|
368
|
-
#
|
369
|
-
# @return [String]
|
401
|
+
# @return [String] string representation of the Session.
|
370
402
|
def to_s
|
371
403
|
"<#{self.class.name}:0x#{object_id.to_s(16)} status=#{status} @options=#{options.inspect}>"
|
372
404
|
end
|
@@ -382,5 +414,21 @@ module Hallon
|
|
382
414
|
Spotify.track_set_starred(pointer, ptr, tracks.size, starred)
|
383
415
|
end
|
384
416
|
end
|
417
|
+
|
418
|
+
# Waits until we’re either logged in or a failure occurs.
|
419
|
+
#
|
420
|
+
# @note You must call {#login} or {#relogin} before you call this method, or
|
421
|
+
# it will hang forever!
|
422
|
+
# @see login!
|
423
|
+
# @see relogin!
|
424
|
+
def wait_until_logged_in
|
425
|
+
# if the user does not have premium, libspotify will still fire logged_in as :ok,
|
426
|
+
# but a few moments later it fires connection_error; waiting for both and checking
|
427
|
+
# for errors on both hopefully circumvents this!
|
428
|
+
wait_for(:logged_in, :connection_error) do |_, error|
|
429
|
+
Error.maybe_raise(error)
|
430
|
+
session.logged_in?
|
431
|
+
end
|
432
|
+
end
|
385
433
|
end
|
386
434
|
end
|