spotify-ruby-kev 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ ##
5
+ # Spotify::Accounts deals with authorization using the Spotify Accounts API.
6
+ #
7
+ class Accounts
8
+ ##
9
+ # An entire list of Spotify's OAuth scopes. Stored
10
+ # in the form of a symbolized array.
11
+ # Example: `[:scope1, :scope2]`
12
+ #
13
+ # @see https://developer.spotify.com/documentation/general/guides/scopes/
14
+ #
15
+ # Last updated: 23 June 2018
16
+ #
17
+ SCOPES = %i[
18
+ playlist-read-private
19
+ playlist-read-collaborative
20
+ playlist-modify-public
21
+ playlist-modify-private
22
+ ugc-image-upload
23
+ user-follow-modify
24
+ user-follow-read
25
+ user-library-read
26
+ user-library-modify
27
+ user-read-private
28
+ user-read-birthdate
29
+ user-read-email
30
+ user-top-read
31
+ user-read-playback-state
32
+ user-modify-playback-state
33
+ user-read-currently-playing
34
+ user-read-recently-played
35
+ streaming
36
+ app-remote-control
37
+ ].freeze
38
+
39
+ ##
40
+ # Initialize the Spotify Accounts object.
41
+ #
42
+ # @example
43
+ # @accounts = Spotify::Accounts.new({
44
+ # client_id: "[client id goes here]",
45
+ # client_secret: "[client secret goes here]",
46
+ # redirect_uri: "http://localhost"
47
+ # })
48
+ #
49
+ # @accounts = Spotify::Accounts.new
50
+ # @accounts.client_id = "[client id goes here]"
51
+ # @accounts.client_secret = "[client secret goes here]"
52
+ # @accounts.redirect_uri = "http://localhost"
53
+ #
54
+ # # with SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, and SPOTIFY_REDIRECT_URI in ENV:
55
+ # @accounts = Spotify::Accounts.new
56
+ #
57
+ # @param [Hash] config The configuration containing your Client ID, Client Secret, and your Redirect URL.
58
+ #
59
+ # @see https://developer.spotify.com/dashboard/
60
+ #
61
+ def initialize(config={})
62
+ @client_id = config.delete(:client_id) { ENV["SPOTIFY_CLIENT_ID"] }
63
+ @client_secret = config.delete(:client_secret) { ENV["SPOTIFY_CLIENT_SECRET"] }
64
+ @redirect_uri = config.delete(:redirect_uri) { ENV["SPOTIFY_REDIRECT_URI"] }
65
+ end
66
+
67
+ attr_accessor :client_id, :client_secret, :redirect_uri
68
+
69
+ ##
70
+ # Get a HTTP URL to send user for authorizing with Spotify.
71
+ #
72
+ # @example
73
+ # @accounts = Spotify::Accounts.new({
74
+ # client_id: "[client id goes here]",
75
+ # client_secret: "[client secret goes here]",
76
+ # redirect_uri: "http://localhost"
77
+ # })
78
+ #
79
+ # @auth.authorize_url
80
+ # @auth.authorize_url({ scope: "user-read-private user-top-read" })
81
+ #
82
+ # @param [Hash] override_params Optional hash containing any overriding values for parameters.
83
+ # Parameters used are client_id, redirect_uri, response_type and scope.
84
+ # @return [String] A fully qualified Spotify authorization URL to send the user to.
85
+ #
86
+ # @see https://developer.spotify.com/documentation/general/guides/authorization-guide/
87
+ #
88
+ def authorize_url(override_params={})
89
+ validate_credentials!
90
+ params = {
91
+ client_id: @client_id,
92
+ redirect_uri: @redirect_uri,
93
+ response_type: "code",
94
+ scope: SCOPES.join(" ")
95
+ }.merge(override_params)
96
+ "https://accounts.spotify.com/authorize?%s" % params.to_query
97
+ end
98
+
99
+ ##
100
+ # Start a session from your authentication code.
101
+ #
102
+ # @example
103
+ # @accounts = Spotify::Accounts.new({
104
+ # client_id: "[client id goes here]",
105
+ # client_secret: "[client secret goes here]",
106
+ # redirect_uri: "http://localhost"
107
+ # })
108
+ #
109
+ # @accounts.exchange_for_session("code")
110
+ #
111
+ # @param [String] code The code provided back to your application upon authorization.
112
+ # @return [Spotify::Accounts::Session] session The session object.
113
+ #
114
+ # @see https://developer.spotify.com/documentation/general/guides/authorization-guide/
115
+ #
116
+ def exchange_for_session(code)
117
+ validate_credentials!
118
+ Spotify::Accounts::Session.from_authorization_code(code)
119
+ end
120
+
121
+ def inspect # :nodoc:
122
+ "#<%s:0x00%x>" % [self.class.name, (object_id << 1)]
123
+ end
124
+
125
+ private
126
+
127
+ def validate_credentials! # :nodoc:
128
+ raise "Missing client id" if @client_id.nil?
129
+ raise "Missing client secret" if @client_secret.nil?
130
+ raise "Missing redirect uri" if @redirect_uri.nil?
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class Accounts
5
+ ##
6
+ # A class representing an access token, with the ability to refresh.
7
+ #
8
+ class Session
9
+ class << self
10
+ ##
11
+ # Parse the response we collect from the authorization code.
12
+ #
13
+ # @example
14
+ # @session = Spotify::Accounts.from_authorization_code(@accounts, "authorization code here")
15
+ #
16
+ # @param [Spotify::Accounts] accounts A valid instance of Spotify::Accounts.
17
+ # @param [String] code The code provided in the Redirect URI from the Spotify Accounts API.
18
+ # @return [Spotify::Accounts::Session] access_token An instance of Spotify::Accounts::Session
19
+ # @see lib/spotify/accounts.rb
20
+ #
21
+ def from_authorization_code(accounts, code)
22
+ params = {
23
+ client_id: @accounts.instance_variable_get(:@client_id),
24
+ client_secret: @accounts.instance_variable_get(:@client_secret),
25
+ redirect_uri: @accounts.instance_variable_get(:@redirect_uri),
26
+ grant_type: "authorization_code",
27
+ code: code
28
+ }
29
+ request = HTTParty.post("https://accounts.spotify.com/api/token", body: params)
30
+ response = request.parsed_response.with_indifferent_access
31
+ raise response[:error_description] if response[:error]
32
+
33
+ new(accounts, response[:access_token], response[:expires_in], response[:refresh_token], response[:scope])
34
+ end
35
+
36
+ ##
37
+ # Set up an instance of Access Token with just a refresh_token.
38
+ #
39
+ # @example
40
+ # @access_token = Spotify::Accounts::Session.from_refresh_token(@accounts, "refresh token here")
41
+ # @access_token.force_refresh!
42
+ #
43
+ # @param [Spotify::Accounts] accounts A valid instance of Spotify::Accounts.
44
+ # @param [String] refresh_token A valid refresh token. You'll want to store the refresh_token in your database.
45
+ # @return [Spotify::Accounts::Session] access_token An instance of Spotify::Accounts::Session
46
+ #
47
+ def from_refresh_token(accounts, refresh_token)
48
+ new(accounts, nil, nil, refresh_token, nil)
49
+ end
50
+ end
51
+
52
+ def initialize(accounts, access_token, expires_in, refresh_token, scopes)
53
+ unless accounts.instance_of?(Spotify::Accounts)
54
+ raise "You need a valid Spotify::Accounts instance in order to use Spotify authentication."
55
+ end
56
+
57
+ @accounts = accounts
58
+ @access_token = access_token
59
+ @expires_in = expires_in
60
+ @expires_at = expires_in + Time.now.to_i unless expires_in.nil?
61
+ @refresh_token = refresh_token
62
+ @scopes = scopes
63
+ end
64
+
65
+ attr_reader :accounts, :access_token, :expires_in, :refresh_token
66
+
67
+ ##
68
+ # Converts the space-delimited scope list to a symbolized array.
69
+ #
70
+ # @example
71
+ # @access_token.scopes # => [:"user-read-private", :"user-top-read", ...]
72
+ #
73
+ # @return [Array] scopes A symbolized list of scopes.
74
+ #
75
+ def scopes
76
+ return [] if @scopes.nil?
77
+
78
+ @scopes.split(" ").map(&:to_sym)
79
+ end
80
+
81
+ ##
82
+ # Checks if a specific scope has been granted by the user.
83
+ #
84
+ # @example
85
+ # @access_token.contains_scope?("user-read-top")
86
+ # @access_token.contains_scope?(:"user-read-top")
87
+ #
88
+ # @param [String,Symbol] scope The name of the scope you'd like to check. For example, "user-read-private".
89
+ # @return [TrueClass,FalseClass] scope_included A true/false boolean if the scope is included.
90
+ #
91
+ def contains_scope?(scope)
92
+ scopes.include?(scope.downcase.to_sym)
93
+ end
94
+
95
+ ##
96
+ # When will the access token expire? Returns nil if no expires_in is defined.
97
+ #
98
+ # @example
99
+ # @session.expires_at
100
+ #
101
+ # @return [Time] time When the access token will expire.
102
+ #
103
+ def expires_at
104
+ return nil if @expires_in.nil?
105
+
106
+ Time.at(@expires_at)
107
+ end
108
+
109
+ ##
110
+ # Check if the access token has expired. Returns nil if no expires_in is defined.
111
+ #
112
+ # @example
113
+ # @session.expired?
114
+ #
115
+ # @return [TrueClass,FalseClass,NilClass] has_expired Has the access token expired?
116
+ #
117
+ def expired?
118
+ return nil if expires_at.nil?
119
+
120
+ Time.now > expires_at
121
+ end
122
+
123
+ ##
124
+ # Refresh the access token.
125
+ #
126
+ # @example
127
+ # @session.refresh!
128
+ #
129
+ # @return [TrueClass,FalseClass] success Have we been able to refresh the access token?
130
+ #
131
+ # rubocop:disable AbcSize
132
+ def refresh!
133
+ raise "You cannot refresh without a valid refresh_token." if @refresh_token.nil?
134
+
135
+ params = {
136
+ client_id: @accounts.instance_variable_get(:@client_id),
137
+ client_secret: @accounts.instance_variable_get(:@client_secret),
138
+ grant_type: "refresh_token",
139
+ refresh_token: @refresh_token
140
+ }
141
+ request = HTTParty.post("https://accounts.spotify.com/api/token", body: params)
142
+ response = request.parsed_response.with_indifferent_access
143
+
144
+ @access_token = response[:access_token]
145
+ @expires_in = response[:expires_in]
146
+ @expires_at = response[:expires_in] + Time.now.to_i
147
+ @scopes = response[:scope]
148
+
149
+ true
150
+ rescue HTTParty::Error
151
+ false
152
+ end
153
+ # rubocop:enable AbcSize
154
+
155
+ ##
156
+ # Export to JSON. Designed mostly for iOS, Android, or external use cases.
157
+ #
158
+ # @example
159
+ # @session.to_json
160
+ #
161
+ # @return [String] json The JSON output of the session instance.
162
+ #
163
+ def to_json(*_args)
164
+ {
165
+ access_token: @access_token.presence,
166
+ expires_at: @expires_at.presence,
167
+ refresh_token: @refresh_token.presence,
168
+ scopes: scopes
169
+ }.to_json
170
+ end
171
+
172
+ def inspect # :nodoc:
173
+ "#<%s:0x00%x>" % [self.class.name, (object_id << 1)]
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Scaffolding
4
+ require "spotify/sdk/base"
5
+ require "spotify/sdk/model"
6
+
7
+ # Components
8
+ require "spotify/sdk/connect"
9
+ require "spotify/sdk/me"
10
+
11
+ # Models
12
+ require "spotify/sdk/connect/device"
13
+ require "spotify/sdk/connect/playback_state"
14
+ require "spotify/sdk/me/info"
15
+ require "spotify/sdk/artist"
16
+ require "spotify/sdk/album"
17
+ require "spotify/sdk/image"
18
+ require "spotify/sdk/item"
19
+
20
+ module Spotify
21
+ ##
22
+ # Spotify::SDK contains the complete Ruby DSL to interact with the Spotify Platform.
23
+ #
24
+ class SDK
25
+ ##
26
+ # Initialize the Spotify SDK object.
27
+ #
28
+ # @example
29
+ # # Example 1: Load it in from an access token value.
30
+ # @sdk = Spotify::SDK.new("access_token_here")
31
+ #
32
+ # # Example 2: Load it in with values from your database.
33
+ # @sdk = Spotify::SDK.new({
34
+ # access_token: "access_token_here",
35
+ # expires_in: 3_000_000,
36
+ # refresh_token: "refresh_token_here"
37
+ # })
38
+ #
39
+ # # Example 4: Load it in from an OAuth2::AccessToken object.
40
+ # @sdk = Spotify::SDK.new(@auth.auth_code.get_token("auth code"))
41
+ #
42
+ # # Example 5: Load it from a query string or a fully qualified URL.
43
+ # @sdk = Spotify::SDK.new("https://localhost:8080/#token=...&expires_in=...")
44
+ # @sdk = Spotify::SDK.new("token=...&expires_in=...")
45
+ #
46
+ # @param [String,Hash,OAuth2::AccessToken] session Any supported object containing an access token.
47
+ #
48
+ def initialize(session)
49
+ raise "Invalid Spotify::Accounts::Session object" unless session.instance_of?(Spotify::Accounts::Session)
50
+
51
+ @session = session
52
+ mount_sdk_components
53
+ end
54
+
55
+ attr_reader :session
56
+
57
+ def inspect # :nodoc:
58
+ "#<%s:0x00%x>" % [self.class.name, (object_id << 1)]
59
+ end
60
+
61
+ ##
62
+ # This is where we mount new SDK components to the Spotify::SDK object.
63
+ # Simply add a key (this is your identifier) with the value being the object.
64
+ #
65
+ # Notes:
66
+ # - Make sure your SDK component is being loaded at the top of this page.
67
+ # - You can name your identifier whatever you want:
68
+ # - This will be what people will use to call your code
69
+ # - For example: it would be the `connect` in `Spotify::SDK.new(@session).connect`
70
+ # - We'll call .new on your class, providing one parameter being the instance of this SDK (aka self).
71
+ # - Make sure to a test for it in spec/lib/spotify/sdk_spec.rb (see how we did it for others)
72
+ #
73
+ SDK_COMPONENTS = {
74
+ connect: Spotify::SDK::Connect,
75
+ me: Spotify::SDK::Me
76
+ }.freeze
77
+
78
+ SDK_COMPONENTS.each_key do |component|
79
+ attr_reader(component)
80
+ end
81
+
82
+ private
83
+
84
+ ##
85
+ # This is where we map the SDK component classes to the SDK component vairables.
86
+ #
87
+ def mount_sdk_components # :nodoc:
88
+ SDK_COMPONENTS.map do |key, klass|
89
+ instance_variable_set "@#{key}".to_sym, klass.new(self)
90
+ end
91
+ end
92
+ end
93
+ end
File without changes
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class SDK
5
+ class Album < Model
6
+ ##
7
+ # Is this an album?
8
+ # Note: This is mostly to support other types of albums in the future.
9
+ #
10
+ # @example
11
+ # album = @sdk.connect.playback.item.album
12
+ # album.album?
13
+ #
14
+ # @return [TrueClass,FalseClass] is_album Returns true if type is an album.
15
+ #
16
+ def album?
17
+ type == "album"
18
+ end
19
+
20
+ ##
21
+ # Display the album's images.
22
+ #
23
+ # @example
24
+ # album = @sdk.connect.playback.item.album
25
+ # album.images[0] # => [#<Spotify::SDK::Image>, #<Spotify::SDK::Image>, ...]
26
+ #
27
+ # @return [Array] album_images Contains a list of images, wrapped in Spotify::SDK::Image
28
+ #
29
+ def images
30
+ super.map do |image|
31
+ Spotify::SDK::Image.new(image, parent)
32
+ end
33
+ end
34
+
35
+ ##
36
+ # Get the artists/creators for this album.
37
+ #
38
+ # @example
39
+ # @sdk.connect.playback.item.album.artists
40
+ #
41
+ # @return [Array] artists A list of artists, wrapped in Spotify::SDK::Artist
42
+ #
43
+ def artists
44
+ super.map do |artist|
45
+ Spotify::SDK::Artist.new(artist, parent)
46
+ end
47
+ end
48
+
49
+ ##
50
+ # Get the primary artist/creator for this album.
51
+ #
52
+ # @example
53
+ # @sdk.connect.playback.item.album.artist
54
+ #
55
+ # @return [Spotify::SDK::Artist] artist The primary artist, wrapped in Spotify::SDK::Artist
56
+ #
57
+ def artist
58
+ artists.first
59
+ end
60
+
61
+ ##
62
+ # Get the Spotify URI for this album.
63
+ # Alias to self.uri
64
+ #
65
+ # @example
66
+ # @sdk.connect.playback.item.album.spotify_uri # => "spotify:track:..."
67
+ #
68
+ # @return [String] spotify_uri The direct URI to this Spotify resource.
69
+ #
70
+ alias_attribute :spotify_uri, :uri
71
+
72
+ ##
73
+ # Get the Spotify HTTP URL for this album.
74
+ # Alias to self.external_urls[:spotify]
75
+ #
76
+ # @example
77
+ # @sdk.connect.playback.item.album.spotify_url # => "https://open.spotify.com/..."
78
+ #
79
+ # @return [String] spotify_url The direct HTTP URL to this Spotify resource.
80
+ #
81
+ alias_attribute :spotify_url, "external_urls.spotify"
82
+ end
83
+ end
84
+ end