spotify-ruby 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0b63f5681311753e57dd0f3465c03c92b2f075a0
4
- data.tar.gz: 1388c3f85c842cf7521360af71a62b7ac20e2620
3
+ metadata.gz: 118af1b2f6ab08785aa78c8028bf7927003154a1
4
+ data.tar.gz: 2d655193db96272b01a9f90aef9964c5a482e324
5
5
  SHA512:
6
- metadata.gz: db844e007c167f743d38434c9a94ab16992730a75b62dd47a28f0d161a609c96205975b6c6b03e3765769b2062cfda2068c19bc40bad8da9b25b35e5e0d7ff4d
7
- data.tar.gz: c205c712540fd6f205f902912765049112b4cf052554043562a2c431b3bbf6fed90c4cf3e99e08b4c107e1539de93d061ae90997607591a4ed4ec7e982665e75
6
+ metadata.gz: b6f4a4f4018790a020e3ebd1b926b6ceda2ce0fe8a5fc3d807cd2da87b81489ae19a68d1b3236eb59cb9bf1ecdbb74b51fe1a71900486b93719eaa490df63714
7
+ data.tar.gz: b2e0da362c7786da52a7eee27f1a1277f5f953fa499c82b7d615b97745f27e9aaf043229cbdfea02ba3d91c896e06caa4375e75b9049aff2cbb385084b27fe10
data/.travis.yml CHANGED
@@ -4,6 +4,7 @@ rvm:
4
4
  - 2.4.0
5
5
  - 2.4.1
6
6
  - 2.5.0
7
+ - 2.5.1
7
8
  before_install: >
8
9
  gem install bundler -v 1.15.1
9
10
  before_script:
data/COVERAGE.md CHANGED
@@ -87,15 +87,15 @@ This covers all the Spotify API endpoints that are covered.
87
87
 
88
88
  ### Follow Endpoints
89
89
 
90
- | Endpoint | Description | Coverage Status |
91
- | ------------------------------------------------------------------ | ---------------------------------------------- | ---------------------------------------------------------------------- |
92
- | GET /v1/me/following | Get Followed Artists | [me.rb] |
93
- | GET /v1/me/following/contains | Check if Current User Follows Artists or Users | [me.rb] |
94
- | PUT /v1/me/following | Follow Artists or Users | [🔘 Partial Support][artist.rb] (Following multiple isn't supported) |
95
- | DELETE /v1/me/following | Unfollow Artists or Users | [🔘 Partial Support][artist.rb] (Unfollowing multiple isn't supported) |
96
- | GET /v1/users/{user_id}/playlists/{playlist_id}/followers/contains | Check if Users Follow a Playlist | × Not Started |
97
- | PUT /v1/users/{user_id}/playlists/{playlist_id}/followers | Follow a Playlist | × Not Started |
98
- | DELETE /v1/users/{user_id}/playlists/{playlist_id}/followers | Unfollow a Playlist | × Not Started |
90
+ | Endpoint | Description | Coverage Status |
91
+ | ------------------------------------------------------------------ | ---------------------------------------------- | -------------------------------------------------------------------- |
92
+ | GET /v1/me/following | Get Followed Artists | [me.rb] |
93
+ | GET /v1/me/following/contains | Check if Current User Follows Artists or Users | [me.rb] |
94
+ | PUT /v1/me/following | Follow Artists or Users | [🔘 Partial Support][artist.rb] (Following multiple not supported) |
95
+ | DELETE /v1/me/following | Unfollow Artists or Users | [🔘 Partial Support][artist.rb] (Unfollowing multiple not supported) |
96
+ | GET /v1/users/{user_id}/playlists/{playlist_id}/followers/contains | Check if Users Follow a Playlist | × Not Started |
97
+ | PUT /v1/users/{user_id}/playlists/{playlist_id}/followers | Follow a Playlist | × Not Started |
98
+ | DELETE /v1/users/{user_id}/playlists/{playlist_id}/followers | Unfollow a Playlist | × Not Started |
99
99
 
100
100
  ### Playlists Endpoints
101
101
 
@@ -113,10 +113,10 @@ This covers all the Spotify API endpoints that are covered.
113
113
 
114
114
  ### History Endpoints
115
115
 
116
- | Endpoint | Description | Coverage Status |
117
- | --------------------------------- | --------------------------------------------- | --------------- |
118
- | GET /v1/me/top/{type} | Get User's Top Artists and Tracks | × Not Started |
119
- | GET /v1/me/player/recently-played | Get the Current User's Recently Played Tracks | × Not Started |
116
+ | Endpoint | Description | Coverage Status |
117
+ | --------------------------------- | --------------------------------------------- | ----------------------- |
118
+ | GET /v1/me/top/{type} | Get User's Top Artists and Tracks | × Not Started |
119
+ | GET /v1/me/player/recently-played | Get the Current User's Recently Played Tracks | [Full support ✔][me.rb] |
120
120
 
121
121
  ### Connect Endpoints
122
122
 
data/README.md CHANGED
@@ -23,13 +23,15 @@ The developer-friendly, opinionated Ruby SDK for [Spotify]. Works on Ruby 2.4+
23
23
  - [Creating a Session](#creating-a-session)
24
24
  - [Recreating a Session](#recreating-a-session)
25
25
  - [Using the SDK](#using-the-sdk)
26
- - [Spotify Connect](#spotify-connect)
26
+ - [Spotify Connect API](#spotify-connect-api)
27
+ - [Me API](#me-api)
28
+ - [Listening History API](#listening-history-api)
29
+ - [Following API](#following-api)
27
30
  - [Contributing](#contributing)
28
31
  - [Community Guidelines](#community-guidelines)
29
32
  - [Code of Conduct](#code-of-conduct)
30
33
  - [Getting Started](#getting-started)
31
34
  - [Releasing a Change](#releasing-a-change)
32
- - [Changelog](#changelog)
33
35
  - [License](#license)
34
36
 
35
37
  ## Introduction
@@ -95,11 +97,25 @@ To define your app credentials, you'll need to create an instance of `Spotify::A
95
97
 
96
98
  ```ruby
97
99
  @accounts = Spotify::Accounts.new
98
- @accounts.client_id = ENV["SPOTIFY_CLIENT_ID"]
99
- @accounts.client_secret = ENV["SPOTIFY_CLIENT_SECRET"]
100
- @accounts.redirect_uri = ENV["SPOTIFY_REDIRECT_URI"]
100
+ @accounts.client_id = "spotify client ID"
101
+ @accounts.client_secret = "spotify client secret"
102
+ @accounts.redirect_uri = "redirect URI"
101
103
  ```
102
104
 
105
+ Alternatively, these credentials can be supplied as environment variables when running your application:
106
+
107
+ ```ruby
108
+ @accounts = Spotify::Accounts.new # fetches configuration from ENV
109
+ ```
110
+
111
+ The respective environment variables you'll need to set are:
112
+
113
+ | Environment Variable | Description | Required? |
114
+ | ------------------------ | ----------------------------------------- | ------------- |
115
+ | `SPOTIFY_CLIENT_ID` | Your Spotify Client ID | **Yes** |
116
+ | `SPOTIFY_CLIENT_SECRET` | Your Spotify Client Secret | **Yes** |
117
+ | `SPOTIFY_REDIRECT_URI` | Your Spotify Redirect URI (must be exact) | **Yes** |
118
+
103
119
  ### Authorization
104
120
 
105
121
  In order to use Spotify's APIs on a user's behalf, you'll need to use the Spotify [Accounts API] to redirect them to `https://accounts.spotify.com`. They will then need to explicitly approve your application and what data you're asking for (technically referred to as authorization scopes).
@@ -168,7 +184,7 @@ To create an instance of the Spotify SDK, you'll need the `@session` from above
168
184
  @sdk = Spotify::SDK.new(@session)
169
185
  ```
170
186
 
171
- ### Spotify Connect
187
+ ### Spotify Connect API
172
188
 
173
189
  With [Spotify Connect], you can take your music experience anywhere on over 300 devices. And you can read and control most devices programmatically through the SDK:
174
190
 
@@ -213,6 +229,98 @@ With [Spotify Connect], you can take your music experience anywhere on over 300
213
229
  @sdk.connect.devices[0].repeat_mode = :context
214
230
  ```
215
231
 
232
+ #### Transfer playback\*
233
+
234
+ This will transfer state, and start playback.
235
+
236
+ ```ruby
237
+ @sdk.connect.devices[0].transfer_playback!
238
+ ```
239
+
240
+ #### Transfer state\*
241
+
242
+ This will transfer state, and pause playback.
243
+
244
+ ```ruby
245
+ @sdk.connect.devices[0].transfer_state!
246
+ ```
247
+
248
+ ### Me API
249
+
250
+ This allows you to perform specific actions on behalf of a user.
251
+
252
+ #### My information\*
253
+
254
+ ```ruby
255
+ @sdk.me.info
256
+ @sdk.me.info.free? # => false
257
+ @sdk.me.info.premium? # => true
258
+ @sdk.me.info.birthdate # => 1980-01-01
259
+ @sdk.me.info.display_name? # => true
260
+ @sdk.me.info.display_name # => "ABC Smith"
261
+ @sdk.me.info.images[0].url # => "https://profile-images.scdn.co/userprofile/default/..."
262
+ @sdk.me.info.followers # => 4913313
263
+ @sdk.me.info.spotify_uri # => "spotify:user:abcsmith"
264
+ @sdk.me.info.spotify_url # => "https://open.spotify.com/user/abcsmith"
265
+ ```
266
+
267
+ ### Listening History API
268
+
269
+ #### My recently played tracks (up to last 50)\*
270
+
271
+ ```ruby
272
+ @sdk.me.history(10) # => [#<Spotify::SDK::Item...>, ...]
273
+ @sdk.me.history(10).size # => 10
274
+ @sdk.me.history(50) # => [#<Spotify::SDK::Item...>, ...]
275
+ @sdk.me.history(50).size # => 50
276
+ ```
277
+
278
+ ### Following API
279
+
280
+ #### Follow an artist\*
281
+
282
+ ```ruby
283
+ @sdk.playback.item.artist.follow!
284
+ ```
285
+
286
+ #### Unfollow an artist\*
287
+
288
+ ```ruby
289
+ @sdk.playback.item.artist.unfollow!
290
+ ```
291
+
292
+ #### Check if following Spotify artists?\*
293
+
294
+ ```ruby
295
+ @sdk.me.following_artists?(%w(3TVXtAsR1Inumwj472S9r4 6LuN9FCkKOj5PcnpouEgny 69GGBxA162lTqCwzJG5jLp))
296
+ # => {
297
+ # "3TVXtAsR1Inumwj472S9r4" => false,
298
+ # "6LuN9FCkKOj5PcnpouEgny" => true,
299
+ # "69GGBxA162lTqCwzJG5jLp" => false
300
+ # }
301
+ ```
302
+
303
+ #### Check if following Spotify users?\*
304
+
305
+ ```ruby
306
+ @sdk.me.following_users?(%w(3TVXtAsR1Inumwj472S9r4 6LuN9FCkKOj5PcnpouEgny 69GGBxA162lTqCwzJG5jLp))
307
+ # => {
308
+ # "3TVXtAsR1Inumwj472S9r4" => false,
309
+ # "6LuN9FCkKOj5PcnpouEgny" => true,
310
+ # "69GGBxA162lTqCwzJG5jLp" => false
311
+ # }
312
+ ```
313
+
314
+ #### See all followed artists\*
315
+
316
+ ```ruby
317
+ @sdk.me.following(5) # => [#<Spotify::SDK::Artist...>, ...]
318
+ @sdk.me.following(5).size # => 5
319
+ @sdk.me.following(50) # => [#<Spotify::SDK::Artist...>, ...]
320
+ @sdk.me.following(50).size # => 50
321
+ ```
322
+
323
+
216
324
  <small><i>\* Requires specific user permissions/scopes. See [Authorization Scopes] for more information.</i></small>
217
325
 
218
326
  ## Contributing
@@ -252,16 +360,6 @@ For local development, you can run `bin/console` for an interactive prompt for e
252
360
  - Push git commits and tags
253
361
  - Push the `.gem` file to [rubygems.org].
254
362
 
255
- ### Changelog
256
-
257
- ```
258
- [2018-07-21] (0.2.1) First major release.
259
- - Support for Connect and User API endpoints.
260
- - Transitioned to YARD for documentation.
261
- - Website built using Jekyll with Contributing guide.
262
- - Removed Coveralls in favour for CodeClimate
263
- ```
264
-
265
363
  ## License
266
364
 
267
365
  The gem is available as open source under the terms of the [MIT License].
@@ -51,14 +51,17 @@ module Spotify
51
51
  # @accounts.client_secret = "[client secret goes here]"
52
52
  # @accounts.redirect_uri = "http://localhost"
53
53
  #
54
+ # # with SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, and SPOTIFY_REDIRECT_URI in ENV:
55
+ # @accounts = Spotify::Accounts.new
56
+ #
54
57
  # @param [Hash] config The configuration containing your Client ID, Client Secret, and your Redirect URL.
55
58
  #
56
59
  # @see https://developer.spotify.com/dashboard/
57
60
  #
58
61
  def initialize(config={})
59
- @client_id = config.delete(:client_id)
60
- @client_secret = config.delete(:client_secret)
61
- @redirect_uri = config.delete(:redirect_uri)
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"] }
62
65
  end
63
66
 
64
67
  attr_accessor :client_id, :client_secret, :redirect_uri
@@ -133,7 +133,7 @@ module Spotify
133
133
  #
134
134
  def item
135
135
  raise "Playback information is not available if user has a private session enabled" if device.private_session?
136
- Spotify::SDK::Item.new(super, parent)
136
+ Spotify::SDK::Item.new(to_h, parent)
137
137
  end
138
138
  end
139
139
  end
@@ -3,6 +3,18 @@
3
3
  module Spotify
4
4
  class SDK
5
5
  class Item < Model
6
+ ##
7
+ # Let's transform the item object into better for us.
8
+ # Before: { track: ..., played_at: ..., context: ... }
9
+ # After: { track_properties..., played_at: ..., context: ... }
10
+ #
11
+ # :nodoc:
12
+ def initialize(payload, parent)
13
+ track = payload.delete(:track) || payload.delete(:item)
14
+ properties = payload.except(:parent, :device, :repeat_state, :shuffle_state)
15
+ super(track.merge(properties: properties), parent)
16
+ end
17
+
6
18
  ##
7
19
  # Get the album for this item.
8
20
  #
@@ -41,6 +53,17 @@ module Spotify
41
53
  artists.first
42
54
  end
43
55
 
56
+ ##
57
+ # Get the context.
58
+ #
59
+ # @example
60
+ # @sdk.connect.playback.item.context
61
+ # @sdk.me.history[0].context
62
+ #
63
+ # @return [Hash] context Information about the user's context.
64
+ #
65
+ alias_attribute :context, "properties.context"
66
+
44
67
  ##
45
68
  # Get the duration.
46
69
  # Alias to self.duration_ms
@@ -22,9 +22,47 @@ module Spotify
22
22
  Spotify::SDK::Me::Info.new(me_info, self)
23
23
  end
24
24
 
25
+ ##
26
+ # Check what tracks a user has recently played.
27
+ #
28
+ # @example
29
+ # @sdk.me.history
30
+ # @sdk.me.history(20)
31
+ #
32
+ # @param [Integer] limit How many results to request. Defaults to 10.
33
+ # @param [Hash] override_opts Custom options for HTTParty.
34
+ # @return [Array] response List of recently played tracked, in chronological order.
35
+ #
36
+ def history(n=10, override_opts={})
37
+ request = {
38
+ method: :get,
39
+ http_path: "/v1/me/player/recently-played",
40
+ keys: %i[items],
41
+ limit: n
42
+ }
43
+
44
+ send_multiple_http_requests(request, override_opts).map do |item|
45
+ Spotify::SDK::Item.new(item, self)
46
+ end
47
+ end
48
+
25
49
  ##
26
50
  # Check if the current user is following N users.
27
51
  #
52
+ # @example
53
+ # artists = %w(3q7HBObVc0L8jNeTe5Gofh 0NbfKEOTQCcwd6o7wSDOHI 3TVXtAsR1Inumwj472S9r4)
54
+ # @sdk.me.following?(artists, :artist)
55
+ # # => {"3q7HBObVc0L8jNeTe5Gofh" => false, "0NbfKEOTQCcwd6o7wSDOHI" => false, ...}
56
+ #
57
+ # users = %w(3q7HBObVc0L8jNeTe5Gofh 0NbfKEOTQCcwd6o7wSDOHI 3TVXtAsR1Inumwj472S9r4)
58
+ # @sdk.me.following?(users, :user)
59
+ # # => {"3q7HBObVc0L8jNeTe5Gofh" => false, "0NbfKEOTQCcwd6o7wSDOHI" => false, ...}
60
+ #
61
+ # @param [Array] list List of Spotify user/artist IDs. Cannot mix user and artist IDs in single request.
62
+ # @param [Symbol] type Either :user or :artist. Checks if follows respective type of account.
63
+ # @param [Hash] override_opts Custom options for HTTParty.
64
+ # @return [Hash] hash A hash containing a key with the ID, and a value that equals is_following (boolean).
65
+ #
28
66
  def following?(list, type=:artist, override_opts={})
29
67
  raise "Must contain an array" unless list.is_a?(Array)
30
68
  raise "Must contain an array of String or Spotify::SDK::Artist" if any_of?(list, [String, Spotify::SDK::Artist])
@@ -32,6 +70,14 @@ module Spotify
32
70
  send_is_following_http_requests(list.map {|id| id.try(:id) || id }, type, override_opts)
33
71
  end
34
72
 
73
+ def following_artists?(list, override_opts={})
74
+ following?(list, :artist, override_opts)
75
+ end
76
+
77
+ def following_users?(list, override_opts={})
78
+ following?(list, :user, override_opts)
79
+ end
80
+
35
81
  ##
36
82
  # Get the current user's followed artists. Requires the `user-read-follow` scope.
37
83
  # GET /v1/me/following
@@ -39,22 +85,52 @@ module Spotify
39
85
  # @example
40
86
  # @sdk.me.following
41
87
  #
88
+ # @param [Integer] n Number of results to return.
42
89
  # @param [Hash] override_opts Custom options for HTTParty.
43
90
  # @return [Array] artists A list of followed artists, wrapped in Spotify::SDK::Artist
44
91
  #
45
- def following(override_opts={})
46
- artists = send_following_http_requests("/v1/me/following?type=artist&limit=50", override_opts)
47
- artists.map do |artist|
92
+ def following(n=50, override_opts={})
93
+ request = {
94
+ method: :get,
95
+ # TODO: Spotify API bug - `limit={n}` returns n-1 artists.
96
+ # ^ Example: `limit=5` returns 4 artists.
97
+ # TODO: Support `type=users` as well as `type=artists`.
98
+ http_path: "/v1/me/following?type=artist&limit=#{[n, 50].min}",
99
+ keys: %i[artists items],
100
+ limit: n
101
+ }
102
+
103
+ send_multiple_http_requests(request, override_opts).map do |artist|
48
104
  Spotify::SDK::Artist.new(artist, self)
49
105
  end
50
106
  end
51
107
 
52
108
  private
53
109
 
54
- def any_of?(array, klasses)
110
+ def any_of?(array, klasses) # :nodoc:
55
111
  (array.map(&:class) - klasses).any?
56
112
  end
57
113
 
114
+ def send_multiple_http_requests(opts, override_opts) # :nodoc:
115
+ response = send_http_request(opts[:method], opts[:http_path], override_opts)
116
+ responses, next_request = hash_deep_lookup(response, opts[:keys].dup)
117
+ if next_request && responses.size < opts[:limit]
118
+ responses += send_multiple_http_requests(opts.merge(http_path: next_request), override_opts)
119
+ end
120
+ responses.first(opts[:limit])
121
+ end
122
+
123
+ def hash_deep_lookup(response, keys) # :nodoc:
124
+ error_message = "Cannot find '%s' key in Spotify::SDK::Me#hash_deep_lookup"
125
+ while keys.any?
126
+ next_request ||= response[:next]
127
+ next_key = keys.shift
128
+ response = next_key ? response[next_key] : raise(error_message % next_key)
129
+ end
130
+ [response, next_request ? next_request[23..-1] : nil]
131
+ end
132
+
133
+ # TODO: Migrate this into the abstracted send_multiple_http_requests
58
134
  def send_is_following_http_requests(list, type, override_opts) # :nodoc:
59
135
  max_ids = list.first(50)
60
136
  remaining_ids = list - max_ids
@@ -71,13 +147,6 @@ module Spotify
71
147
  ids.merge(send_is_following_http_requests(remaining_ids, type, override_opts))
72
148
  end || ids
73
149
  end
74
-
75
- def send_following_http_requests(http_path, override_opts) # :nodoc:
76
- request = send_http_request(:get, http_path, override_opts)[:artists]
77
- artists = request[:items]
78
- artists << send_following_http_requests(request[:next][23..-1], override_opts) if request[:next]
79
- artists.flatten
80
- end
81
150
  end
82
151
  end
83
152
  end
@@ -12,5 +12,5 @@ module Spotify
12
12
  # MINOR version when you add functionality in a backwards-compatible manner, and
13
13
  # PATCH version when you make backwards-compatible bug fixes.
14
14
  #
15
- VERSION = "0.2.2"
15
+ VERSION = "0.2.3"
16
16
  end
data/spotify-ruby.gemspec CHANGED
@@ -35,6 +35,7 @@ Gem::Specification.new do |spec|
35
35
  spec.add_development_dependency "rake", "~> 12.1"
36
36
 
37
37
  # Testing
38
+ spec.add_development_dependency "climate_control", "~> 0.2"
38
39
  spec.add_development_dependency "factory_bot", "~> 1.0.0.alpha"
39
40
  spec.add_development_dependency "rspec", "~> 3.7"
40
41
  spec.add_development_dependency "rspec-collection_matchers", "~> 1.1", ">= 1.1.2"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spotify-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bilawal Hameed
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-25 00:00:00.000000000 Z
11
+ date: 2018-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -44,6 +44,20 @@ dependencies:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '12.1'
47
+ - !ruby/object:Gem::Dependency
48
+ name: climate_control
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.2'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.2'
47
61
  - !ruby/object:Gem::Dependency
48
62
  name: factory_bot
49
63
  requirement: !ruby/object:Gem::Requirement