spotify 12.5.3 → 12.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|