spotify 12.5.3 → 12.6.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +20 -5
- data/CHANGELOG.md +29 -1
- data/Gemfile +5 -0
- data/MIT-LICENSE +21 -0
- data/README.markdown +75 -50
- data/Rakefile +1 -1
- data/examples/example-audio_delivery_speed.rb +48 -0
- data/examples/{audio-stream_example.rb → example-audio_stream.rb} +14 -29
- data/examples/example-console.rb +9 -0
- data/examples/example-listing_playlists.rb +89 -0
- data/examples/example-loading_object.rb +25 -0
- data/examples/example-random_related_artists.rb +53 -0
- data/examples/support.rb +106 -0
- data/lib/spotify.rb +36 -55
- data/lib/spotify/api.rb +54 -26
- data/lib/spotify/api/album.rb +45 -2
- data/lib/spotify/api/album_browse.rb +81 -3
- data/lib/spotify/api/artist.rb +21 -2
- data/lib/spotify/api/artist_browse.rb +121 -3
- data/lib/spotify/api/error.rb +5 -1
- data/lib/spotify/api/image.rb +72 -6
- data/lib/spotify/api/inbox.rb +33 -4
- data/lib/spotify/api/link.rb +117 -4
- data/lib/spotify/api/miscellaneous.rb +12 -0
- data/lib/spotify/api/playlist.rb +321 -16
- data/lib/spotify/api/playlist_container.rb +168 -9
- data/lib/spotify/api/search.rb +156 -3
- data/lib/spotify/api/session.rb +390 -26
- data/lib/spotify/api/toplist_browse.rb +74 -3
- data/lib/spotify/api/track.rb +134 -4
- data/lib/spotify/api/user.rb +18 -2
- data/lib/spotify/api_helpers.rb +47 -0
- data/lib/spotify/data_converters.rb +7 -0
- data/lib/spotify/{types → data_converters}/best_effort_string.rb +1 -1
- data/lib/spotify/{types → data_converters}/byte_string.rb +0 -0
- data/lib/spotify/data_converters/country_code.rb +30 -0
- data/lib/spotify/{types → data_converters}/image_id.rb +1 -1
- data/lib/spotify/{type_safety.rb → data_converters/type_safety.rb} +0 -0
- data/lib/spotify/{types → data_converters}/utf8_string.rb +2 -2
- data/lib/spotify/{types → data_converters}/utf8_string_pointer.rb +2 -2
- data/lib/spotify/error.rb +180 -47
- data/lib/spotify/managed_pointer.rb +32 -12
- data/lib/spotify/monkey_patches/ffi_buffer.rb +11 -0
- data/lib/spotify/monkey_patches/ffi_enums.rb +4 -0
- data/lib/spotify/monkey_patches/ffi_pointer.rb +1 -0
- data/lib/spotify/structs.rb +4 -0
- data/lib/spotify/structs/session_callbacks.rb +97 -26
- data/lib/spotify/structs/session_config.rb +1 -1
- data/lib/spotify/structs/subscribers.rb +4 -3
- data/lib/spotify/types.rb +104 -5
- data/lib/spotify/{objects → types}/album.rb +0 -0
- data/lib/spotify/{objects → types}/album_browse.rb +0 -0
- data/lib/spotify/{objects → types}/artist.rb +0 -0
- data/lib/spotify/{objects → types}/artist_browse.rb +0 -0
- data/lib/spotify/{objects → types}/image.rb +0 -0
- data/lib/spotify/{objects → types}/inbox.rb +0 -0
- data/lib/spotify/{objects → types}/link.rb +0 -0
- data/lib/spotify/{objects → types}/playlist.rb +0 -0
- data/lib/spotify/{objects → types}/playlist_container.rb +0 -0
- data/lib/spotify/{objects → types}/search.rb +0 -0
- data/lib/spotify/{objects → types}/session.rb +0 -0
- data/lib/spotify/{objects → types}/toplist_browse.rb +0 -0
- data/lib/spotify/{objects → types}/track.rb +0 -0
- data/lib/spotify/{objects → types}/user.rb +0 -0
- data/lib/spotify/util.rb +38 -35
- data/lib/spotify/version.rb +1 -1
- data/spec/spec_helper.rb +24 -13
- data/spec/spotify/api/image_spec.rb +32 -0
- data/spec/spotify/api/inbox_spec.rb +34 -0
- data/spec/spotify/api/link_spec.rb +40 -0
- data/spec/spotify/api/playlist_spec.rb +99 -0
- data/spec/spotify/api/playlistcontainer_spec.rb +82 -0
- data/spec/spotify/api/session_spec.rb +97 -0
- data/spec/spotify/api/track_spec.rb +29 -0
- data/spec/spotify/api_error_spec.rb +55 -0
- data/spec/spotify/api_spec.rb +17 -11
- data/spec/spotify/{types → data_converters}/best_effort_string_spec.rb +4 -4
- data/spec/spotify/{types → data_converters}/byte_string_spec.rb +0 -0
- data/spec/spotify/data_converters/country_code_spec.rb +16 -0
- data/spec/spotify/{types → data_converters}/image_id_spec.rb +1 -1
- data/spec/spotify/{type_safety_spec.rb → data_converters/type_safety_spec.rb} +0 -0
- data/spec/spotify/{types → data_converters}/utf8_string_pointer_spec.rb +0 -0
- data/spec/spotify/{types → data_converters}/utf8_string_spec.rb +1 -1
- data/spec/spotify/{api/functions_spec.rb → functions_spec.rb} +2 -0
- data/spec/spotify/managed_pointer_spec.rb +13 -13
- data/spec/spotify/structs/subscribers_spec.rb +5 -3
- data/spec/spotify/{defines_spec.rb → types_spec.rb} +16 -3
- data/spec/spotify/util_spec.rb +24 -0
- data/spec/spotify_spec.rb +74 -0
- data/spec/support/spotify_util.rb +6 -2
- data/spec/support/spy_output.rb +16 -0
- data/spotify.gemspec +4 -2
- metadata +111 -71
- data/examples/README.md +0 -15
- data/examples/console_example.rb +0 -22
- data/examples/example_support.rb +0 -66
- data/examples/loading-object_example.rb +0 -43
- data/examples/logging-in_example.rb +0 -58
- data/lib/spotify/defines.rb +0 -109
- data/lib/spotify/objects.rb +0 -17
- data/spec/spotify/enums_spec.rb +0 -9
- data/spec/spotify/spotify_spec.rb +0 -69
|
@@ -1,17 +1,88 @@
|
|
|
1
1
|
module Spotify
|
|
2
2
|
class API
|
|
3
3
|
# @!group ToplistBrowse
|
|
4
|
+
|
|
5
|
+
# Initiate a request for browsing a toplist.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# callback = proc { |toplist_browse| puts "Toplist query finished!" }
|
|
9
|
+
# toplist_browse = Spotify.toplistbrowse_create(session, :tracks, :everywhere, nil, callback, nil)
|
|
10
|
+
#
|
|
11
|
+
# @example for sweden
|
|
12
|
+
# callback = proc { |toplist_browse| puts "Toplist query finished!" }
|
|
13
|
+
# toplist_browse = Spotify.toplistbrowse_create(session, :tracks, Spotify::CountryCode.to_native("SE", nil), nil, callback, nil)
|
|
14
|
+
#
|
|
15
|
+
# @note it is *very* important that the callback is not garbage collected before it is called!
|
|
16
|
+
# @param [Session] session
|
|
17
|
+
# @param [Symbol] type one of :artists, :albums, :tracks
|
|
18
|
+
# @param [Symbol] region :everywhere, :user, or a CountryCode
|
|
19
|
+
# @param [String, nil] username if region is :user this is the user to get toplists for, use nil for currently logged in user
|
|
20
|
+
# @param [Proc<ToplistBrowse, FFI::Pointer>] callback
|
|
21
|
+
# @param [FFI::Pointer] userdata
|
|
22
|
+
# @method toplistbrowse_create(session, type, region, username, callback, userdata)
|
|
4
23
|
attach_function :toplistbrowse_create, [ Session, :toplisttype, :toplistregion, UTF8String, :toplistbrowse_complete_cb, :userdata ], ToplistBrowse
|
|
24
|
+
|
|
25
|
+
# @param [ToplistBrowse] toplist_browse
|
|
26
|
+
# @return [Boolean] true if toplist request has finished loading
|
|
27
|
+
# @method toplistbrowse_is_loaded(toplist_browse)
|
|
5
28
|
attach_function :toplistbrowse_is_loaded, [ ToplistBrowse ], :bool
|
|
6
|
-
|
|
29
|
+
|
|
30
|
+
# @param [ToplistBrowse] toplist_browse
|
|
31
|
+
# @return [Symbol] error code
|
|
32
|
+
# @method toplistbrowse_error(toplist_browse)
|
|
33
|
+
attach_function :toplistbrowse_error, [ ToplistBrowse ], APIError
|
|
34
|
+
|
|
35
|
+
# @see #toplistbrowse_is_loaded
|
|
36
|
+
# @note if the toplist is not loaded, the function always return 0.
|
|
37
|
+
# @param [ToplistBrowse] toplist_browse
|
|
38
|
+
# @return [Integer] number of artists in the toplist result
|
|
39
|
+
# @method toplistbrowse_num_artists(toplist_browse)
|
|
7
40
|
attach_function :toplistbrowse_num_artists, [ ToplistBrowse ], :int
|
|
41
|
+
|
|
42
|
+
# @see #toplistbrowse_num_artists
|
|
43
|
+
# @note if index is out of range, the function always return nil.
|
|
44
|
+
# @param [ToplistBrowse] toplist_browse
|
|
45
|
+
# @param [Integer] index number between 0...{#toplistbrowse_num_artists}
|
|
46
|
+
# @return [Artist, nil] artist at index
|
|
47
|
+
# @method toplistbrowse_artist(toplist_browse, :int)
|
|
8
48
|
attach_function :toplistbrowse_artist, [ ToplistBrowse, :int ], Artist
|
|
49
|
+
|
|
50
|
+
# @see #toplistbrowse_is_loaded
|
|
51
|
+
# @note if the toplist is not loaded, the function always return 0.
|
|
52
|
+
# @param [ToplistBrowse] toplist_browse
|
|
53
|
+
# @return [Integer] number of albums in the toplist result
|
|
54
|
+
# @method toplistbrowse_num_albums(toplist_browse)
|
|
9
55
|
attach_function :toplistbrowse_num_albums, [ ToplistBrowse ], :int
|
|
56
|
+
|
|
57
|
+
# @see #toplistbrowse_num_albums
|
|
58
|
+
# @note if index is out of range, the function always return nil.
|
|
59
|
+
# @param [ToplistBrowse] toplist_browse
|
|
60
|
+
# @param [Integer] index number between 0...{#toplistbrowse_num_albums}
|
|
61
|
+
# @return [Album, nil] album at index
|
|
62
|
+
# @method toplistbrowse_album(toplist_browse, index)
|
|
10
63
|
attach_function :toplistbrowse_album, [ ToplistBrowse, :int ], Album
|
|
64
|
+
|
|
65
|
+
# @see #toplistbrowse_is_loaded
|
|
66
|
+
# @note if the toplist is not loaded, the function always return 0.
|
|
67
|
+
# @param [ToplistBrowse] toplist_browse
|
|
68
|
+
# @return [Integer] number of tracks in the toplist result
|
|
69
|
+
# @method toplistbrowse_num_tracks(toplist_browse)
|
|
11
70
|
attach_function :toplistbrowse_num_tracks, [ ToplistBrowse ], :int
|
|
71
|
+
|
|
72
|
+
# @see #toplistbrowse_num_tracks
|
|
73
|
+
# @note if index is out of range, the function always return nil.
|
|
74
|
+
# @param [ToplistBrowse] toplist_browse
|
|
75
|
+
# @param [Integer] index number between 0...{#toplistbrowse_num_tracks}
|
|
76
|
+
# @return [Track, nil] track at index
|
|
77
|
+
# @method toplistbrowse_track(toplist_browse, index)
|
|
12
78
|
attach_function :toplistbrowse_track, [ ToplistBrowse, :int ], Track
|
|
79
|
+
|
|
80
|
+
# @param [ToplistBrowse] toplist_browse
|
|
81
|
+
# @return [Integer] the time in ms that was spent waiting for Spotify backend to serve request, or -1 if served from cache
|
|
82
|
+
# @method toplistbrowse_backend_request_duration(toplist_browse)
|
|
13
83
|
attach_function :toplistbrowse_backend_request_duration, [ ToplistBrowse ], :int
|
|
14
|
-
|
|
15
|
-
attach_function :
|
|
84
|
+
|
|
85
|
+
attach_function :toplistbrowse_add_ref, [ ToplistBrowse ], APIError
|
|
86
|
+
attach_function :toplistbrowse_release, [ ToplistBrowse ], APIError
|
|
16
87
|
end
|
|
17
88
|
end
|
data/lib/spotify/api/track.rb
CHANGED
|
@@ -1,26 +1,156 @@
|
|
|
1
1
|
module Spotify
|
|
2
2
|
class API
|
|
3
3
|
# @!group Track
|
|
4
|
+
|
|
5
|
+
# @param [Track] track
|
|
6
|
+
# @return [Boolean] true if track has finished loading
|
|
7
|
+
# @method track_is_loaded(track)
|
|
4
8
|
attach_function :track_is_loaded, [ Track ], :bool
|
|
5
|
-
|
|
9
|
+
|
|
10
|
+
# @param [Track] track
|
|
11
|
+
# @return [Symbol] error code
|
|
12
|
+
# @method track_error(track)
|
|
13
|
+
attach_function :track_error, [ Track ], APIError
|
|
14
|
+
|
|
15
|
+
# @see #track_is_loaded
|
|
16
|
+
# @note if the track is not loaded, the function always return :unavailable.
|
|
17
|
+
# @param [Session] session
|
|
18
|
+
# @param [Track] track
|
|
19
|
+
# @return [Symbol] track availability, one of :unavailable, :available, :not_streamable, :banned_by_artist
|
|
20
|
+
# @method track_get_availability(session, track)
|
|
6
21
|
attach_function :track_get_availability, [ Session, Track ], :track_availability
|
|
22
|
+
|
|
23
|
+
# @note if the track is not loaded, the function always return false.
|
|
24
|
+
# @param [Session] session
|
|
25
|
+
# @param [Track] track
|
|
26
|
+
# @return [Boolean] true if the track is a local track
|
|
27
|
+
# @method track_is_local(session, track)
|
|
7
28
|
attach_function :track_is_local, [ Session, Track ], :bool
|
|
29
|
+
|
|
30
|
+
# @note if the track is not loaded, the function always return false.
|
|
31
|
+
# @param [Session] session
|
|
32
|
+
# @param [Track] track
|
|
33
|
+
# @return [Boolean] true if the track is automatically linked to another playable version of the track
|
|
34
|
+
# @method track_is_autolinked(session, track)
|
|
8
35
|
attach_function :track_is_autolinked, [ Session, Track ], :bool
|
|
36
|
+
|
|
37
|
+
# @note if the track is not loaded, the function always return false.
|
|
38
|
+
# @param [Session] session
|
|
39
|
+
# @param [Track] track
|
|
40
|
+
# @return [Boolean] true if the track is starred by the current user
|
|
41
|
+
# @method track_is_starred(session, track)
|
|
9
42
|
attach_function :track_is_starred, [ Session, Track ], :bool
|
|
10
|
-
|
|
43
|
+
|
|
44
|
+
# Star or unstar a list of tracks.
|
|
45
|
+
#
|
|
46
|
+
# @example
|
|
47
|
+
# Spotify.track_set_starred(session, [track_a, track_b], true) => :ok
|
|
48
|
+
#
|
|
49
|
+
# @param [Session] session
|
|
50
|
+
# @param [Array<Track>] tracks
|
|
51
|
+
# @param [Boolean] starred true if tracks should be starred, false if unstarred
|
|
52
|
+
# @return [Symbol] error code
|
|
53
|
+
# @method track_set_starred(session, tracks, starred)
|
|
54
|
+
attach_function :track_set_starred, [ Session, :array, :int, :bool ], APIError do |session, tracks, starred|
|
|
55
|
+
tracks = Array(tracks)
|
|
56
|
+
|
|
57
|
+
with_buffer(Spotify::Track, size: tracks.length) do |tracks_buffer|
|
|
58
|
+
tracks_buffer.write_array_of_pointer(tracks)
|
|
59
|
+
sp_track_set_starred(session, tracks_buffer, tracks.length, starred)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @note if the track is not loaded, the function always return 0.
|
|
64
|
+
# @param [Track] track
|
|
65
|
+
# @return [Integer] number of artists performing the track
|
|
66
|
+
# @method track_num_artists(track)
|
|
11
67
|
attach_function :track_num_artists, [ Track ], :int
|
|
68
|
+
|
|
69
|
+
# @see #track_num_artists
|
|
70
|
+
# @note if index is out of range, the function always return nil.
|
|
71
|
+
# @param [Track] track
|
|
72
|
+
# @param [Integer] index number between 0...{#track_num_artists}
|
|
73
|
+
# @return [Artist, nil] artist at index
|
|
74
|
+
# @method track_artist(track, index)
|
|
12
75
|
attach_function :track_artist, [ Track, :int ], Artist
|
|
76
|
+
|
|
77
|
+
# @note if the track is not loaded, the function always return nil.
|
|
78
|
+
# @param [Track] track
|
|
79
|
+
# @return [Album, nil] album of the track
|
|
80
|
+
# @method track_album(track)
|
|
13
81
|
attach_function :track_album, [ Track ], Album
|
|
82
|
+
|
|
83
|
+
# @note if the track is not loaded, the function always return an empty string.
|
|
84
|
+
# @param [Track] track
|
|
85
|
+
# @return [String] track name
|
|
86
|
+
# @method track_name(track)
|
|
14
87
|
attach_function :track_name, [ Track ], UTF8String
|
|
88
|
+
|
|
89
|
+
# @note if the track is not loaded, the function always return 0.
|
|
90
|
+
# @param [Track] track
|
|
91
|
+
# @return [Integer] duration of the track in milliseconds
|
|
92
|
+
# @method track_duration(track)
|
|
15
93
|
attach_function :track_duration, [ Track ], :int
|
|
94
|
+
|
|
95
|
+
# @note if the track is not loaded, the function always return 0.
|
|
96
|
+
# @param [Track] track
|
|
97
|
+
# @return [Integer] popularity of the track, 0..100
|
|
98
|
+
# @method track_popularity(track)
|
|
16
99
|
attach_function :track_popularity, [ Track ], :int
|
|
100
|
+
|
|
101
|
+
# @note The function only returns valid data for tracks appearing in an ArtistBrowse or AlbumBrowse result.
|
|
102
|
+
# @note if the disc is not available, the function always return 0.
|
|
103
|
+
# @param [Track] track
|
|
104
|
+
# @return [Integer] disc number for the track, 1..(total number of discs on album)
|
|
105
|
+
# @method track_disc(track)
|
|
17
106
|
attach_function :track_disc, [ Track ], :int
|
|
107
|
+
|
|
108
|
+
# @note The function only returns valid data for tracks appearing in an ArtistBrowse or AlbumBrowse result.
|
|
109
|
+
# @note if the disc is not available, the function always return 0.
|
|
110
|
+
# @param [Track] track
|
|
111
|
+
# @return [Integer] position of track on it's disc, starts at 1
|
|
112
|
+
# @method track_index(track)
|
|
18
113
|
attach_function :track_index, [ Track ], :int
|
|
114
|
+
|
|
115
|
+
# Placeholder tracks are a way to store non-track items in a playlist.
|
|
116
|
+
#
|
|
117
|
+
# This is used when sending playlists to users, for example.
|
|
118
|
+
#
|
|
119
|
+
# @see #link_create_from_track
|
|
120
|
+
# @note the track does not need to be loaded for this function to return a correct value.
|
|
121
|
+
# @note use {#link_create_from_track} to get a link to the wrapped item.
|
|
122
|
+
# @param [Track] track
|
|
123
|
+
# @return [Boolean] true if track is a placeholder
|
|
124
|
+
# @method track_is_placeholder(track)
|
|
19
125
|
attach_function :track_is_placeholder, [ Track ], :bool
|
|
126
|
+
|
|
127
|
+
# @note If the track has been autolinked, the returned track will not be the same track.
|
|
128
|
+
# @param [Session] session
|
|
129
|
+
# @param [Track] track
|
|
130
|
+
# @return [Track] the actual track that will be played if track is scheduled for playback
|
|
131
|
+
# @method track_get_playable(session, track)
|
|
20
132
|
attach_function :track_get_playable, [ Session, Track ], Track
|
|
133
|
+
|
|
134
|
+
# @param [Track] track
|
|
135
|
+
# @return [Symbol] offline status of track, one of :no, :waiting, :downloading, :done, :error, :done_expired, :limit_exceeded, :done_resync
|
|
136
|
+
# @method track_offline_get_status(track)
|
|
21
137
|
attach_function :track_offline_get_status, [ Track ], :track_offline_status
|
|
138
|
+
|
|
139
|
+
# Create a local Track reference, whatever that means.
|
|
140
|
+
#
|
|
141
|
+
# @example
|
|
142
|
+
# track = Spotify.localtrack_create("Indianer", "The Best Of Indianer", "Armonia", 343_000)
|
|
143
|
+
# # track spotify URI is: spotify:local:Indianer:Armonia:The+Best+Of+Indianer:34
|
|
144
|
+
#
|
|
145
|
+
# @param [String] artist
|
|
146
|
+
# @param [String] title
|
|
147
|
+
# @param [String] album
|
|
148
|
+
# @param [Integer] duration in milliseconds, or -1 if not available
|
|
149
|
+
# @return [Track]
|
|
150
|
+
# @method localtrack_create(artist, title, album, duration)
|
|
22
151
|
attach_function :localtrack_create, [ UTF8String, UTF8String, UTF8String, :int ], Track
|
|
23
|
-
|
|
24
|
-
attach_function :
|
|
152
|
+
|
|
153
|
+
attach_function :track_add_ref, [ Track ], APIError
|
|
154
|
+
attach_function :track_release, [ Track ], APIError
|
|
25
155
|
end
|
|
26
156
|
end
|
data/lib/spotify/api/user.rb
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
module Spotify
|
|
2
2
|
class API
|
|
3
3
|
# @!group User
|
|
4
|
+
|
|
5
|
+
# @param [User] user
|
|
6
|
+
# @return [String] canonical username of user, the one used by Spotify in just about all places
|
|
7
|
+
# @method user_canonical_name(user)
|
|
4
8
|
attach_function :user_canonical_name, [ User ], UTF8String
|
|
9
|
+
|
|
10
|
+
# @note #user_is_loaded can return true even if libspotify does not know the display name yet.
|
|
11
|
+
# @note if libspotify does not have a display name, the function will return the canonical name of the user.
|
|
12
|
+
# @param [User] user
|
|
13
|
+
# @return [String] display name of user
|
|
14
|
+
# @method user_display_name(user)
|
|
5
15
|
attach_function :user_display_name, [ User ], UTF8String
|
|
16
|
+
|
|
17
|
+
# @note the function may return true, even if display name is not yet known, make sure to {#session_process_events}!
|
|
18
|
+
# @param [Track] track
|
|
19
|
+
# @return [Boolean] true if user has finished loading
|
|
20
|
+
# @method user_is_loaded(user)
|
|
6
21
|
attach_function :user_is_loaded, [ User ], :bool
|
|
7
|
-
|
|
8
|
-
attach_function :
|
|
22
|
+
|
|
23
|
+
attach_function :user_add_ref, [ User ], APIError
|
|
24
|
+
attach_function :user_release, [ User ], APIError
|
|
9
25
|
end
|
|
10
26
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Spotify
|
|
2
|
+
# Some methods used in the implementation that makes the C calls
|
|
3
|
+
# less insane to make.
|
|
4
|
+
#
|
|
5
|
+
# @private
|
|
6
|
+
module APIHelpers
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Allocate some memory and yield it to the given block.
|
|
10
|
+
#
|
|
11
|
+
# @param type
|
|
12
|
+
# @param [Hash] options
|
|
13
|
+
# @option options [Integer] :size (1)
|
|
14
|
+
# @option options [Boolean] :clear (false)
|
|
15
|
+
def with_buffer(type, options = {})
|
|
16
|
+
size = options.fetch(:size, 1)
|
|
17
|
+
clear = options.fetch(:clear, false)
|
|
18
|
+
|
|
19
|
+
if size > 0
|
|
20
|
+
FFI::MemoryPointer.new(type, size, clear) do |buffer|
|
|
21
|
+
return yield buffer, buffer.size
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Allocate some memory, specifically for a string, and yield it to the given block.
|
|
27
|
+
#
|
|
28
|
+
# @param [Integer] length
|
|
29
|
+
# @param [Hash] options
|
|
30
|
+
# @option options [Boolean] :clear (false)
|
|
31
|
+
def with_string_buffer(length, *args)
|
|
32
|
+
if length > 0
|
|
33
|
+
with_buffer(:char, size: length + 1) do |buffer, size|
|
|
34
|
+
error = yield buffer, size
|
|
35
|
+
|
|
36
|
+
if error.is_a?(Symbol) and error != :ok
|
|
37
|
+
""
|
|
38
|
+
else
|
|
39
|
+
buffer.get_string(0, length).force_encoding(Encoding::UTF_8)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
else
|
|
43
|
+
""
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
require 'spotify/data_converters/utf8_string'
|
|
2
|
+
require 'spotify/data_converters/utf8_string_pointer'
|
|
3
|
+
require 'spotify/data_converters/image_id'
|
|
4
|
+
require 'spotify/data_converters/byte_string'
|
|
5
|
+
require 'spotify/data_converters/best_effort_string'
|
|
6
|
+
require 'spotify/data_converters/country_code'
|
|
7
|
+
require 'spotify/data_converters/type_safety'
|
|
@@ -14,7 +14,7 @@ module Spotify
|
|
|
14
14
|
# @param ctx
|
|
15
15
|
# @return [String] value, up until the first NULL byte
|
|
16
16
|
def to_native(value, ctx)
|
|
17
|
-
value && value.dup.force_encoding(
|
|
17
|
+
value && value.dup.force_encoding(Encoding::BINARY)[/[^\x00]*/n]
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Spotify
|
|
2
|
+
# A type for converting a country code to an int, and vice versa.
|
|
3
|
+
module CountryCode
|
|
4
|
+
extend FFI::DataConverter
|
|
5
|
+
native_type FFI::Type::INT
|
|
6
|
+
|
|
7
|
+
# String#pack format used by libspotify for storing country code.
|
|
8
|
+
PACK_FORMAT = "s>"
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
# Given a two-char country code, encodes it to an integer.
|
|
12
|
+
#
|
|
13
|
+
# @param [String, nil] country_code
|
|
14
|
+
# @param ctx
|
|
15
|
+
# @return [Integer] country code as int
|
|
16
|
+
def to_native(country_code, ctx)
|
|
17
|
+
country_code.unpack(PACK_FORMAT)[0]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Given a two-char country code as int, decodes it to a string.
|
|
21
|
+
#
|
|
22
|
+
# @param [Integer] country_code
|
|
23
|
+
# @param ctx
|
|
24
|
+
# @return [String] country code as string
|
|
25
|
+
def from_native(country_code, ctx)
|
|
26
|
+
[country_code].pack(PACK_FORMAT)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -38,7 +38,7 @@ module Spotify
|
|
|
38
38
|
# @param ctx
|
|
39
39
|
# @return [String, nil] the image ID as a string, or nil
|
|
40
40
|
def from_native(value, ctx)
|
|
41
|
-
value.
|
|
41
|
+
value.get_bytes(0, size) unless value.null?
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
# @see NulString.reference_required?
|
|
File without changes
|
|
@@ -18,7 +18,7 @@ module Spotify
|
|
|
18
18
|
# @param ctx
|
|
19
19
|
# @return [String] value, but in UTF-8 if it wasn’t already
|
|
20
20
|
def to_native(value, ctx)
|
|
21
|
-
value && value.encode(
|
|
21
|
+
value && value.encode(Encoding::UTF_8)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# Given an original string, assume it is in UTF-8.
|
|
@@ -28,7 +28,7 @@ module Spotify
|
|
|
28
28
|
# @param ctx
|
|
29
29
|
# @return [String] value, but with UTF-8 encoding
|
|
30
30
|
def from_native(value, ctx)
|
|
31
|
-
value && value.force_encoding(
|
|
31
|
+
value && value.force_encoding(Encoding::UTF_8)
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
end
|
|
@@ -16,7 +16,7 @@ module Spotify
|
|
|
16
16
|
# @param ctx
|
|
17
17
|
# @return [FFI::Pointer]
|
|
18
18
|
def to_native(value, ctx)
|
|
19
|
-
value && FFI::MemoryPointer.from_string(value.to_str.encode(
|
|
19
|
+
value && FFI::MemoryPointer.from_string(value.to_str.encode(Encoding::UTF_8))
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# Given a pointer, read out it’s string.
|
|
@@ -25,7 +25,7 @@ module Spotify
|
|
|
25
25
|
# @param ctx
|
|
26
26
|
# @return [String, nil]
|
|
27
27
|
def from_native(value, ctx)
|
|
28
|
-
value.read_string.force_encoding(
|
|
28
|
+
value.read_string.force_encoding(Encoding::UTF_8) unless value.null?
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# Used by FFI::StructLayoutField to know if this field
|
data/lib/spotify/error.rb
CHANGED
|
@@ -1,65 +1,198 @@
|
|
|
1
1
|
module Spotify
|
|
2
2
|
# A generic error class for Spotify errors.
|
|
3
3
|
class Error < StandardError
|
|
4
|
-
|
|
5
|
-
# Explain a Spotify error with a descriptive message.
|
|
6
|
-
#
|
|
7
|
-
# @note this method calls the API directly, since the
|
|
8
|
-
# underlying API call is considered thread-safe.
|
|
9
|
-
#
|
|
10
|
-
# @param [Symbol, Integer] error
|
|
11
|
-
# @return [String] a decriptive string of the error
|
|
12
|
-
def explain(error)
|
|
13
|
-
error, symbol = disambiguate(error)
|
|
4
|
+
end
|
|
14
5
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
# @abstract Generic error class, extended by all libspotify errors.
|
|
7
|
+
class APIError < Spotify::Error
|
|
8
|
+
extend FFI::DataConverter
|
|
9
|
+
native_type :int
|
|
19
10
|
|
|
20
|
-
|
|
21
|
-
end
|
|
11
|
+
@@code_to_class = { 0 => nil }
|
|
22
12
|
|
|
23
|
-
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# @example given an integer
|
|
27
|
-
# Spotify::Error.disambiguate(0) # => [0, :ok]
|
|
28
|
-
#
|
|
29
|
-
# @example given a symbol
|
|
30
|
-
# Spotify::Error.disambiguate(:ok) # => [0, :ok]
|
|
31
|
-
#
|
|
32
|
-
# @example given bogus
|
|
33
|
-
# Spotify::Error.disambiguate(:bogus) # => [-1, nil]
|
|
13
|
+
class << self
|
|
14
|
+
# Returns an error if applicable.
|
|
34
15
|
#
|
|
35
|
-
# @param [
|
|
36
|
-
# @return [
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if error.is_a? Symbol
|
|
41
|
-
error = @enum[symbol = error]
|
|
42
|
-
else
|
|
43
|
-
symbol = @enum[error]
|
|
16
|
+
# @param [Integer] error
|
|
17
|
+
# @return [Error, nil] an error, unless error symbol was OK
|
|
18
|
+
def from_native(error, context)
|
|
19
|
+
error_class = @@code_to_class.fetch(error) do
|
|
20
|
+
raise ArgumentError, "unknown error code: #{error}"
|
|
44
21
|
end
|
|
22
|
+
error_class.new if error_class
|
|
23
|
+
end
|
|
45
24
|
|
|
46
|
-
|
|
47
|
-
|
|
25
|
+
# From an error, retrieve it's native value.
|
|
26
|
+
#
|
|
27
|
+
# @param [Error] error
|
|
28
|
+
# @return [Symbol]
|
|
29
|
+
def to_native(error, context)
|
|
30
|
+
if error
|
|
31
|
+
error.to_i
|
|
48
32
|
else
|
|
49
|
-
|
|
33
|
+
0
|
|
50
34
|
end
|
|
51
35
|
end
|
|
52
|
-
end
|
|
53
36
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
37
|
+
# @return [Integer] error code
|
|
38
|
+
attr_reader :to_i
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def error_code(number)
|
|
43
|
+
@to_i = number
|
|
44
|
+
@@code_to_class[number] = self
|
|
60
45
|
end
|
|
46
|
+
end
|
|
61
47
|
|
|
62
|
-
|
|
48
|
+
# @param [String] message only to be supplied if overridden
|
|
49
|
+
def initialize(message = "#{Spotify::API.error_message(self)} (#{to_i})")
|
|
50
|
+
super
|
|
63
51
|
end
|
|
52
|
+
|
|
53
|
+
# @return (see .to_i)
|
|
54
|
+
def to_i
|
|
55
|
+
self.class.to_i
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class BadAPIVersionError < APIError
|
|
60
|
+
error_code 1
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class APIInitializationFailedError < APIError
|
|
64
|
+
error_code 2
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class TrackNotPlayableError < APIError
|
|
68
|
+
error_code 3
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
class BadApplicationKeyError < APIError
|
|
72
|
+
error_code 5
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class BadUsernameOrPasswordError < APIError
|
|
76
|
+
error_code 6
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class UserBannedError < APIError
|
|
80
|
+
error_code 7
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class UnableToContactServerError < APIError
|
|
84
|
+
error_code 8
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class ClientTooOldError < APIError
|
|
88
|
+
error_code 9
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class OtherPermanentError < APIError
|
|
92
|
+
error_code 10
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class BadUserAgentError < APIError
|
|
96
|
+
error_code 11
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class MissingCallbackError < APIError
|
|
100
|
+
error_code 12
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class InvalidIndataError < APIError
|
|
104
|
+
error_code 13
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class IndexOutOfRangeError < APIError
|
|
108
|
+
error_code 14
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class UserNeedsPremiumError < APIError
|
|
112
|
+
error_code 15
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
class OtherTransientError < APIError
|
|
116
|
+
error_code 16
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class IsLoadingError < APIError
|
|
120
|
+
error_code 17
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
class NoStreamAvailableError < APIError
|
|
124
|
+
error_code 18
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
class PermissionDeniedError < APIError
|
|
128
|
+
error_code 19
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class InboxIsFullError < APIError
|
|
132
|
+
error_code 20
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
class NoCacheError < APIError
|
|
136
|
+
error_code 21
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
class NoSuchUserError < APIError
|
|
140
|
+
error_code 22
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
class NoCredentialsError < APIError
|
|
144
|
+
error_code 23
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
class NetworkDisabledError < APIError
|
|
148
|
+
error_code 24
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
class InvalidDeviceIdError < APIError
|
|
152
|
+
error_code 25
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
class CantOpenTraceFileError < APIError
|
|
156
|
+
error_code 26
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
class ApplicationBannedError < APIError
|
|
160
|
+
error_code 27
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
class OfflineTooManyTracksError < APIError
|
|
164
|
+
error_code 31
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
class OfflineDiskCacheError < APIError
|
|
168
|
+
error_code 32
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
class OfflineExpiredError < APIError
|
|
172
|
+
error_code 33
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
class OfflineNotAllowedError < APIError
|
|
176
|
+
error_code 34
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
class OfflineLicenseLostError < APIError
|
|
180
|
+
error_code 35
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
class OfflineLicenseError < APIError
|
|
184
|
+
error_code 36
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class LastfmAuthError < APIError
|
|
188
|
+
error_code 39
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
class InvalidArgumentError < APIError
|
|
192
|
+
error_code 40
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
class SystemFailureError < APIError
|
|
196
|
+
error_code 41
|
|
64
197
|
end
|
|
65
198
|
end
|