hallon 0.15.0 → 0.16.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 +40 -0
- data/README.markdown +1 -1
- data/dev/application_key_converter.rb +11 -0
- data/examples/example_support.rb +4 -0
- data/examples/playing_audio.rb +1 -1
- data/lib/hallon.rb +11 -0
- data/lib/hallon/album_browse.rb +3 -3
- data/lib/hallon/artist_browse.rb +20 -4
- data/lib/hallon/blob.rb +11 -0
- data/lib/hallon/enumerator.rb +5 -0
- data/lib/hallon/ext/spotify.rb +2 -0
- data/lib/hallon/image.rb +7 -1
- data/lib/hallon/link.rb +5 -2
- data/lib/hallon/observable.rb +48 -1
- data/lib/hallon/player.rb +15 -26
- data/lib/hallon/playlist.rb +16 -25
- data/lib/hallon/playlist_container.rb +38 -0
- data/lib/hallon/search.rb +75 -4
- data/lib/hallon/session.rb +31 -42
- data/lib/hallon/toplist.rb +3 -3
- data/lib/hallon/track.rb +28 -10
- data/lib/hallon/version.rb +1 -1
- data/spec/hallon/album_browse_spec.rb +68 -18
- data/spec/hallon/album_spec.rb +62 -27
- data/spec/hallon/artist_browse_spec.rb +106 -31
- data/spec/hallon/artist_spec.rb +32 -18
- data/spec/hallon/blob_spec.rb +6 -0
- data/spec/hallon/enumerator_spec.rb +10 -0
- data/spec/hallon/error_spec.rb +4 -4
- data/spec/hallon/hallon_spec.rb +1 -1
- data/spec/hallon/image_spec.rb +58 -47
- data/spec/hallon/link_spec.rb +51 -43
- data/spec/hallon/observable/album_browse_spec.rb +1 -1
- data/spec/hallon/observable/artist_browse_spec.rb +1 -1
- data/spec/hallon/observable/image_spec.rb +1 -1
- data/spec/hallon/observable/playlist_container_spec.rb +4 -4
- data/spec/hallon/observable/playlist_spec.rb +14 -14
- data/spec/hallon/observable/post_spec.rb +1 -1
- data/spec/hallon/observable/search_spec.rb +1 -1
- data/spec/hallon/observable/session_spec.rb +17 -17
- data/spec/hallon/observable/toplist_spec.rb +1 -1
- data/spec/hallon/observable_spec.rb +40 -6
- data/spec/hallon/player_spec.rb +1 -1
- data/spec/hallon/playlist_container_spec.rb +96 -13
- data/spec/hallon/playlist_spec.rb +180 -45
- data/spec/hallon/search_spec.rb +211 -28
- data/spec/hallon/session_spec.rb +44 -38
- data/spec/hallon/toplist_spec.rb +31 -14
- data/spec/hallon/track_spec.rb +159 -50
- data/spec/hallon/user_post_spec.rb +10 -5
- data/spec/hallon/user_spec.rb +60 -50
- data/spec/spec_helper.rb +40 -15
- data/spec/support/album_mocks.rb +30 -0
- data/spec/support/artist_mocks.rb +36 -0
- data/spec/support/common_objects.rb +0 -201
- data/spec/support/image_mocks.rb +34 -0
- data/spec/support/playlist_container_mocks.rb +36 -0
- data/spec/support/playlist_mocks.rb +70 -0
- data/spec/support/search_mocks.rb +23 -0
- data/spec/support/session_mocks.rb +33 -0
- data/spec/support/toplist_mocks.rb +19 -0
- data/spec/support/track_mocks.rb +28 -0
- data/spec/support/user_mocks.rb +20 -0
- metadata +40 -18
- data/spec/support/context_stub_session.rb +0 -5
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,46 @@ Hallon’s Changelog
|
|
4
4
|
[HEAD][]
|
5
5
|
------------------
|
6
6
|
|
7
|
+
[v0.16.0][]
|
8
|
+
------------------
|
9
|
+
This release brings a lot of changes to the Hallon test suite, mainly to make
|
10
|
+
it more readable and less of a mess.
|
11
|
+
|
12
|
+
__Added__
|
13
|
+
|
14
|
+
- ArtistBrowse#top_hits [5ef57a7]
|
15
|
+
- Session#flush_caches [693567]
|
16
|
+
- Track#playable_track [2d9cdfb]
|
17
|
+
- Support for playlists in search results [05f49e4]
|
18
|
+
- Support for suggestion search [1ae4b29]
|
19
|
+
- Support for unseen tracks for playlists in playlist containers [4bf8496]
|
20
|
+
- Support for login with credentials blob instead of password [61ffdf3]
|
21
|
+
|
22
|
+
__Changed__
|
23
|
+
|
24
|
+
- Removed the final `self` parameter of all libspotify events [d738a79]
|
25
|
+
- Renamed Playlist::Track#create_time to added_at [1adc2d7]
|
26
|
+
- Renamed Playlist::Track#creator to adder [1adc2d7]
|
27
|
+
- Session#offline_sync_status now returns a hash always [c14d42ae]
|
28
|
+
- Make AlbumBrowse#request_duration always return an integer [ee0697c2]
|
29
|
+
- Make ArtistBrowse#request_duration always return an integer [ee0697c2]
|
30
|
+
- Make Toplist#request_duration always return an integer [ebc64e1a]
|
31
|
+
- Track#popularity now returns a value between 0 and 100 [cd85ae7f]
|
32
|
+
- Move Session#wait_for onto Observable, now they all support it! [e14da3e]
|
33
|
+
- Make Playlist#update rely solely on callbacks [e703c132]
|
34
|
+
- Player.new no longer takes a session parameter [7508d18]
|
35
|
+
- Session.instance now raises NoSessionError on missing session [3b47b7]
|
36
|
+
- Observable#wait_for now calls original handler on events [be236bfd]
|
37
|
+
|
38
|
+
__Fixed__
|
39
|
+
|
40
|
+
- Have Playlist#subscribers always return an array [a8d26c9a]
|
41
|
+
- Fix Image#data for images with no data [e7d8627]
|
42
|
+
- Playlist::Track#message always return a string [72a644a]
|
43
|
+
- Session#login! now raises when given the wrong password [f650a36]
|
44
|
+
- Do not wrap the Session pointer in a Spotify pointer [9ae047d]
|
45
|
+
- Link.valid? segfaulting if having no session [84c9f69c]
|
46
|
+
|
7
47
|
[v0.15.0][]
|
8
48
|
------------------
|
9
49
|
Updated to libspotify v11.1.60 [3c810b0], and improved the examples provided within Hallon codebase significantly.
|
data/README.markdown
CHANGED
@@ -64,7 +64,7 @@ For more information about audio support in Hallon, see the section "Audio suppo
|
|
64
64
|
|
65
65
|
### Contact details
|
66
66
|
|
67
|
-
- __Got questions?__ Ask on the mailing list: <https://groups.google.com/d/forum/ruby-hallon>
|
67
|
+
- __Got questions?__ Ask on the mailing list: <mailto:ruby-hallon@googlegroups.com> (<https://groups.google.com/d/forum/ruby-hallon>)
|
68
68
|
- __Found a bug?__ Report an issue: <https://github.com/Burgestrand/Hallon/issues/new>
|
69
69
|
- __Have feedback?__ I ❤ feedback! Please send it to the mailing list.
|
70
70
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if ARGF.file == STDIN
|
4
|
+
puts "You forgot to give me a file!"
|
5
|
+
abort "Usage: ruby application_key_converter.rb spotify_appkey.key > new_spotify_appkey.key"
|
6
|
+
end
|
7
|
+
|
8
|
+
old_format = ARGF.read.split.join
|
9
|
+
new_format = [old_format].pack("H*")
|
10
|
+
|
11
|
+
print new_format
|
data/examples/example_support.rb
CHANGED
data/examples/playing_audio.rb
CHANGED
@@ -11,7 +11,7 @@ rescue LoadError => e
|
|
11
11
|
abort "[ERROR] Could not load gem 'hallon-openal', please install with 'gem install hallon-openal'"
|
12
12
|
end
|
13
13
|
|
14
|
-
player = Hallon::Player.new(
|
14
|
+
player = Hallon::Player.new(Hallon::OpenAL)
|
15
15
|
|
16
16
|
# Program flow.
|
17
17
|
|
data/lib/hallon.rb
CHANGED
@@ -12,6 +12,7 @@ require 'hallon/error'
|
|
12
12
|
require 'hallon/base'
|
13
13
|
require 'hallon/enumerator'
|
14
14
|
require 'hallon/audio_queue'
|
15
|
+
require 'hallon/blob'
|
15
16
|
|
16
17
|
require 'hallon/observable/album_browse'
|
17
18
|
require 'hallon/observable/artist_browse'
|
@@ -70,6 +71,16 @@ module Hallon
|
|
70
71
|
# Thrown by {Loadable#load} and {Playlist#update} on failure.
|
71
72
|
TimeoutError = Class.new(Hallon::Error)
|
72
73
|
|
74
|
+
# Raised by Session.instance
|
75
|
+
NoSessionError = Class.new(StandardError)
|
76
|
+
|
77
|
+
# Raised by Session#login! and Session#relogin!
|
78
|
+
LoginError = Class.new(StandardError)
|
79
|
+
|
80
|
+
# Raised by PlaylistContainer#num_unseen_tracks_for and PlaylistContainer#unseen_tracks_for.
|
81
|
+
# @note most likely raised because of the playlist not being in the playlist container.
|
82
|
+
OperationFailedError = Class.new(StandardError)
|
83
|
+
|
73
84
|
class << self
|
74
85
|
# @return [Numeric] default load timeout in seconds, used in {Loadable#load}.
|
75
86
|
attr_reader :load_timeout
|
data/lib/hallon/album_browse.rb
CHANGED
@@ -77,11 +77,11 @@ module Hallon
|
|
77
77
|
end
|
78
78
|
|
79
79
|
# @note If the object is not loaded, the result is undefined.
|
80
|
-
# @
|
81
|
-
# @return [Rational, nil] time it took for the albumbrowse request to complete (in seconds).
|
80
|
+
# @return [Rational] time it took for the albumbrowse request to complete (in seconds).
|
82
81
|
def request_duration
|
83
82
|
duration = Spotify.albumbrowse_backend_request_duration(pointer)
|
84
|
-
|
83
|
+
duration = 0 if duration < 0
|
84
|
+
Rational(duration, 1000)
|
85
85
|
end
|
86
86
|
|
87
87
|
# @return [Copyrights] enumerator of copyright notices.
|
data/lib/hallon/artist_browse.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
module Hallon
|
3
|
-
# ArtistBrowse is
|
3
|
+
# An ArtistBrowse object is for retrieving details about a given artist, such
|
4
|
+
# as it’s tracks, albums, similar artists and more.
|
4
5
|
#
|
5
6
|
# @see Artist
|
6
7
|
# @see http://developer.spotify.com/en/libspotify/docs/group__artistbrowse.html
|
@@ -55,6 +56,16 @@ module Hallon
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
59
|
+
# Enumerates through all tophit tracks of an album browsing object.
|
60
|
+
class TopHits < Enumerator
|
61
|
+
size :artistbrowse_num_tophit_tracks
|
62
|
+
|
63
|
+
# @return [Track, nil]
|
64
|
+
item :artistbrowse_tophit_track! do |track|
|
65
|
+
Track.from(track)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
58
69
|
extend Observable::ArtistBrowse
|
59
70
|
include Loadable
|
60
71
|
|
@@ -107,11 +118,11 @@ module Hallon
|
|
107
118
|
end
|
108
119
|
|
109
120
|
# @note If the object is not loaded, the result is undefined.
|
110
|
-
# @
|
111
|
-
# @return [Rational, nil] time it took for the albumbrowse request to complete (in seconds).
|
121
|
+
# @return [Rational] time it took for the albumbrowse request to complete (in seconds).
|
112
122
|
def request_duration
|
113
123
|
duration = Spotify.artistbrowse_backend_request_duration(pointer)
|
114
|
-
|
124
|
+
duration = 0 if duration < 0
|
125
|
+
Rational(duration, 1000)
|
115
126
|
end
|
116
127
|
|
117
128
|
# @return [Portraits] artist portraits as {Image}s.
|
@@ -138,5 +149,10 @@ module Hallon
|
|
138
149
|
def similar_artists
|
139
150
|
SimilarArtists.new(self)
|
140
151
|
end
|
152
|
+
|
153
|
+
# @return [TopHits] enumerator of the artist’s most popular tracks.
|
154
|
+
def top_hits
|
155
|
+
TopHits.new(self)
|
156
|
+
end
|
141
157
|
end
|
142
158
|
end
|
data/lib/hallon/blob.rb
ADDED
data/lib/hallon/enumerator.rb
CHANGED
data/lib/hallon/ext/spotify.rb
CHANGED
@@ -46,6 +46,7 @@ module Spotify
|
|
46
46
|
wrap_function :track_artist, :artist
|
47
47
|
wrap_function :track_album, :album
|
48
48
|
wrap_function :localtrack_create, :track
|
49
|
+
wrap_function :track_get_playable, :track
|
49
50
|
|
50
51
|
wrap_function :album_artist, :artist
|
51
52
|
|
@@ -59,6 +60,7 @@ module Spotify
|
|
59
60
|
wrap_function :artistbrowse_track, :track
|
60
61
|
wrap_function :artistbrowse_album, :album
|
61
62
|
wrap_function :artistbrowse_similar_artist, :artist
|
63
|
+
wrap_function :artistbrowse_tophit_track, :track
|
62
64
|
|
63
65
|
wrap_function :image_create, :image
|
64
66
|
wrap_function :image_create_from_link, :image
|
data/lib/hallon/image.rb
CHANGED
@@ -71,7 +71,13 @@ module Hallon
|
|
71
71
|
def data
|
72
72
|
FFI::MemoryPointer.new(:size_t) do |size|
|
73
73
|
data = Spotify.image_data(pointer, size)
|
74
|
-
|
74
|
+
size = size.read_size_t
|
75
|
+
|
76
|
+
if size > 0
|
77
|
+
return data.read_bytes(size)
|
78
|
+
else
|
79
|
+
return "".force_encoding("BINARY")
|
80
|
+
end
|
75
81
|
end
|
76
82
|
end
|
77
83
|
|
data/lib/hallon/link.rb
CHANGED
@@ -9,6 +9,10 @@ module Hallon
|
|
9
9
|
# @param [#to_s] spotify_uri
|
10
10
|
# @return [Boolean]
|
11
11
|
def self.valid?(spotify_uri)
|
12
|
+
unless Session.instance?
|
13
|
+
raise NoSessionError, "You must have initialized a session to create links"
|
14
|
+
end
|
15
|
+
|
12
16
|
if spotify_uri.is_a?(Link)
|
13
17
|
return true
|
14
18
|
elsif spotify_uri.to_s["\x00"] # image ids
|
@@ -25,9 +29,8 @@ module Hallon
|
|
25
29
|
# @param [#to_str] uri
|
26
30
|
# @raise [ArgumentError] link could not be parsed
|
27
31
|
def initialize(uri)
|
28
|
-
# if no session instance exists, libspotify segfaults, so assert that we have one
|
29
32
|
unless Session.instance?
|
30
|
-
raise "
|
33
|
+
raise NoSessionError, "You must have initialized a session to create links"
|
31
34
|
end
|
32
35
|
|
33
36
|
# we support any #to_link’able object
|
data/lib/hallon/observable.rb
CHANGED
@@ -111,6 +111,53 @@ module Hallon
|
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
|
+
# Wait for the given callbacks to fire until the block returns true
|
115
|
+
#
|
116
|
+
# @note Given block will be called once instantly without parameters.
|
117
|
+
# @note If no events happen for 0.25 seconds, the block will be called without parameters.
|
118
|
+
# @param [Symbol, ...] *events list of events to wait for
|
119
|
+
# @yield [Symbol, *args] name of the event that fired, and its’ arguments
|
120
|
+
# @return whatever the block returns
|
121
|
+
def wait_for(*events)
|
122
|
+
channel = SizedQueue.new(10) # sized just to be safe
|
123
|
+
|
124
|
+
old_handlers = events.each_with_object({}) do |event, hash|
|
125
|
+
hash[event] = on(event) do |*args|
|
126
|
+
channel << [event, *args]
|
127
|
+
hash[event].call(*args)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
old_notify = session.on(:notify_main_thread) do
|
132
|
+
channel << :notify
|
133
|
+
end
|
134
|
+
|
135
|
+
if result = yield
|
136
|
+
return result
|
137
|
+
end
|
138
|
+
|
139
|
+
loop do
|
140
|
+
begin
|
141
|
+
timeout = [session.process_events.fdiv(1000), 2].min # scope to two seconds
|
142
|
+
timeout = timeout + 0.010 # minimum of ten miliseconds timeout
|
143
|
+
params = Timeout::timeout(timeout) { channel.pop }
|
144
|
+
redo if params == :notify
|
145
|
+
rescue Timeout::Error
|
146
|
+
params = nil
|
147
|
+
end
|
148
|
+
|
149
|
+
if result = yield(*params)
|
150
|
+
return result
|
151
|
+
end
|
152
|
+
end
|
153
|
+
ensure
|
154
|
+
old_handlers.each_pair do |event, handler|
|
155
|
+
on(event, &handler)
|
156
|
+
end unless old_handlers.nil?
|
157
|
+
session.on(:notify_main_thread, &old_notify) unless old_notify.nil?
|
158
|
+
end
|
159
|
+
|
160
|
+
|
114
161
|
# @param [#to_s] name
|
115
162
|
# @return [Boolean] true if a callback with `name` exists.
|
116
163
|
def has_callback?(name)
|
@@ -149,7 +196,7 @@ module Hallon
|
|
149
196
|
# @return whatever the handler returns
|
150
197
|
def trigger(name, *arguments, &block)
|
151
198
|
if handler = handlers[name.to_s]
|
152
|
-
handler.call(*arguments,
|
199
|
+
handler.call(*arguments, &block)
|
153
200
|
end
|
154
201
|
end
|
155
202
|
|
data/lib/hallon/player.rb
CHANGED
@@ -6,7 +6,7 @@ module Hallon
|
|
6
6
|
# controlling the playback features of libspotify.
|
7
7
|
#
|
8
8
|
# @see Session
|
9
|
-
class Player
|
9
|
+
class Player < Base
|
10
10
|
# meep?
|
11
11
|
extend Observable::Player
|
12
12
|
|
@@ -24,18 +24,17 @@ module Hallon
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
# Constructs a Player, given
|
27
|
+
# Constructs a Player, given an audio driver.
|
28
28
|
#
|
29
29
|
# @example
|
30
|
-
# player = Hallon::Player.new(
|
30
|
+
# player = Hallon::Player.new(Hallon::OpenAL)
|
31
31
|
# player.play(track)
|
32
32
|
#
|
33
33
|
# @note for instructions on how to write your own audio driver, see Hallons’ README
|
34
|
-
# @param [Session] session
|
35
34
|
# @param [AudioDriver] driver
|
36
35
|
# @yield instance_evals itself, allowing you to define callbacks using `on`
|
37
|
-
def initialize(
|
38
|
-
@session =
|
36
|
+
def initialize(driver, &block)
|
37
|
+
@session = Hallon::Session.instance
|
39
38
|
@pointer = @session.pointer
|
40
39
|
|
41
40
|
# sample rate is often (if not always) 44.1KHz, so
|
@@ -85,20 +84,20 @@ module Hallon
|
|
85
84
|
#
|
86
85
|
# Will be called after calling our buffers are full enough to support
|
87
86
|
# continous playback.
|
88
|
-
def start_playback
|
87
|
+
def start_playback
|
89
88
|
self.status = :playing
|
90
89
|
end
|
91
90
|
|
92
91
|
# Called by libspotify when the driver should pause audio playback.
|
93
92
|
#
|
94
93
|
# Might happen if we’re playing audio faster than we can stream it.
|
95
|
-
def stop_playback
|
94
|
+
def stop_playback
|
96
95
|
self.status = :paused
|
97
96
|
end
|
98
97
|
|
99
98
|
# Called by libspotify on music delivery; format is
|
100
99
|
# a hash of (sample) rate, channels and (sample) type.
|
101
|
-
def music_delivery(format, frames
|
100
|
+
def music_delivery(format, frames)
|
102
101
|
@queue.synchronize do
|
103
102
|
if frames.none?
|
104
103
|
@queue.clear
|
@@ -113,7 +112,7 @@ module Hallon
|
|
113
112
|
# Called by libspotify to request information about our
|
114
113
|
# audio buffer. Required if we want libspotify to tell
|
115
114
|
# us when we should start and stop playback.
|
116
|
-
def get_audio_buffer_stats
|
115
|
+
def get_audio_buffer_stats
|
117
116
|
drops = @driver.drops if @driver.respond_to?(:drops)
|
118
117
|
[@queue.size, drops.to_i]
|
119
118
|
end
|
@@ -192,22 +191,12 @@ module Hallon
|
|
192
191
|
# @param (see #play)
|
193
192
|
# @return (see #play)
|
194
193
|
def play!(track = nil)
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
on(:end_of_track) do
|
202
|
-
monitor.synchronize do
|
203
|
-
end_of_track = true
|
204
|
-
condvar.signal
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
play(track)
|
209
|
-
condvar.wait_until { end_of_track }
|
210
|
-
end
|
194
|
+
end_of_track = false
|
195
|
+
old_callback = on(:end_of_track) { end_of_track = true }
|
196
|
+
play(track)
|
197
|
+
wait_for(:end_of_track) { end_of_track }
|
198
|
+
ensure
|
199
|
+
on(:end_of_track, &old_callback)
|
211
200
|
end
|
212
201
|
|
213
202
|
# Set preferred playback bitrate.
|
data/lib/hallon/playlist.rb
CHANGED
@@ -29,10 +29,10 @@ module Hallon
|
|
29
29
|
|
30
30
|
@index = index
|
31
31
|
@playlist_ptr = playlist_pointer
|
32
|
-
@
|
33
|
-
@message = Spotify.playlist_track_message(playlist_ptr, index)
|
32
|
+
@message = Spotify.playlist_track_message(playlist_ptr, index).to_s
|
34
33
|
@seen = Spotify.playlist_track_seen(playlist_ptr, index)
|
35
|
-
@
|
34
|
+
@added_at = Time.at(Spotify.playlist_track_create_time(playlist_ptr, index)).utc
|
35
|
+
@adder = begin
|
36
36
|
creator = Spotify.playlist_track_creator!(playlist_ptr, index)
|
37
37
|
User.from(creator)
|
38
38
|
end
|
@@ -46,11 +46,11 @@ module Hallon
|
|
46
46
|
# @return [Integer] index this track was created with.
|
47
47
|
attr_reader :index
|
48
48
|
|
49
|
-
# @return [Time] time when track at {#index} was added to playlist.
|
50
|
-
attr_reader :
|
49
|
+
# @return [Time, nil] time when track at {#index} was added to playlist.
|
50
|
+
attr_reader :added_at
|
51
51
|
|
52
52
|
# @return [User, nil] person who added track at {#index} to this playlist.
|
53
|
-
attr_reader :
|
53
|
+
attr_reader :adder
|
54
54
|
|
55
55
|
# @return [String] message attached to this track at {#index}.
|
56
56
|
attr_reader :message
|
@@ -150,24 +150,14 @@ module Hallon
|
|
150
150
|
Spotify.playlist_set_collaborative(pointer, !!collaborative)
|
151
151
|
end
|
152
152
|
|
153
|
-
#
|
154
|
-
# This method will block until libspotify says the playlist no longer
|
155
|
-
# has pending changes.
|
153
|
+
# Waits for the playlist to begin updating and blocks until it is done.
|
156
154
|
#
|
157
155
|
# @return [Playlist]
|
158
156
|
def upload(timeout = Hallon.load_timeout)
|
159
|
-
unless pending?
|
160
|
-
# libspotify has this bug where pending? returns false directly after
|
161
|
-
# adding tracks to a playlist; processing events once usually toggles
|
162
|
-
# the playlist into pending? mode
|
163
|
-
session.process_events
|
164
|
-
end
|
165
|
-
|
166
157
|
Timeout.timeout(timeout, Hallon::TimeoutError) do
|
167
|
-
|
158
|
+
wait_for(:playlist_update_in_progress) { |done| done }
|
159
|
+
self
|
168
160
|
end
|
169
|
-
|
170
|
-
self
|
171
161
|
end
|
172
162
|
|
173
163
|
# @return [Boolean] true if playlist has pending changes
|
@@ -215,7 +205,7 @@ module Hallon
|
|
215
205
|
Spotify.playlist_set_offline_mode(session.pointer, pointer, !! available_offline)
|
216
206
|
end
|
217
207
|
|
218
|
-
# @return [String]
|
208
|
+
# @return [String] playlist name, or an empty string if unavailable.
|
219
209
|
def name
|
220
210
|
Spotify.playlist_name(pointer)
|
221
211
|
end
|
@@ -258,17 +248,18 @@ module Hallon
|
|
258
248
|
def subscribers
|
259
249
|
ptr = Spotify.playlist_subscribers(pointer)
|
260
250
|
|
261
|
-
|
251
|
+
if ptr.null?
|
252
|
+
[]
|
253
|
+
else
|
262
254
|
struct = Spotify::Subscribers.new(ptr)
|
263
|
-
|
264
255
|
if struct[:count].zero?
|
265
256
|
[]
|
266
257
|
else
|
267
258
|
struct[:subscribers].map(&:read_string)
|
268
259
|
end
|
269
|
-
|
270
|
-
|
271
|
-
|
260
|
+
end
|
261
|
+
ensure
|
262
|
+
Spotify.playlist_subscribers_free(ptr) unless ptr.null?
|
272
263
|
end
|
273
264
|
|
274
265
|
# @return [Integer] total number of subscribers.
|