class RSpotify::User

@attr [String] birthdate The user’s date-of-birth. This field is only available when the current user has granted access to the user-read-birthdate scope. @attr [String] country The country of the user, as set in the user’s account profile. An {en.wikipedia.org/wiki/ISO_3166-1_alpha-2 ISO 3166-1 alpha-2 country code}. This field is only available when the current user has granted access to the user-read-private scope. @attr [Hash] credentials The credentials generated for the user with OAuth. Includes access token, token type, token expiration time and refresh token. This field is only available when the current user has granted access to any scope. @attr [String] display_name The name displayed on the user’s profile. This field is only available when the current user has granted access to the user-read-private scope. @attr [String] email The user’s email address. This field is only available when the current user has granted access to the user-read-email scope. @attr [Hash] followers Information about the followers of the user @attr [Array] images The user’s profile image. This field is only available when the current user has granted access to the user-read-private scope. @attr [String] product The user’s Spotify subscription level: “premium”, “free”, etc. This field is only available when the current user has granted access to the user-read-private scope. @attr [Hash] tracks_added_at A hash containing the date and time each track was saved by the user. Note: the hash is filled and updated only when {#saved_tracks} is used.

Public Class Methods

find(id) click to toggle source

Returns User object with id provided

@param id [String] @return [User]

@example

user = RSpotify::User.find('wizzler')
user.class #=> RSpotify::User
user.id    #=> "wizzler"
Calls superclass method RSpotify::Base::find
# File lib/rspotify/user.rb, line 23
def self.find(id)
  super(id, 'user')
end
new(options = {}) click to toggle source
Calls superclass method RSpotify::Base::new
# File lib/rspotify/user.rb, line 94
def initialize(options = {})
  credentials = options['credentials']
  extra       = options['extra'].to_h
  options     = options['info'] if options['info']
  options.merge!(extra['raw_info'].to_h)

  @birthdate    ||= options['birthdate']
  @country      ||= options['country']
  @display_name ||= options['display_name']
  @email        ||= options['email']
  @followers    ||= options['followers']
  @images       ||= options['images']
  @product      ||= options['product']

  super(options)

  if credentials
    @@users_credentials ||= {}
    @@users_credentials[@id] = credentials
    @credentials = @@users_credentials[@id]
  end
end

Private Class Methods

extract_custom_headers(params) click to toggle source
# File lib/rspotify/user.rb, line 56
def self.extract_custom_headers(params)
  headers_param = params.find{|x| x.is_a?(Hash) && x[:headers]}
  params.delete(headers_param) if headers_param
  headers_param ? headers_param[:headers] : {}
end
oauth_header(user_id) click to toggle source
# File lib/rspotify/user.rb, line 63
def self.oauth_header(user_id)
  token = @@users_credentials.dig(user_id, 'token')
  # Fallback for playlist.add_tracks! if no credentials for the playlist owner exist
  token ||= @@users_credentials.values.dig(0, 'token')
  {
    'Authorization' => "Bearer #{token}",
    'Content-Type'  => 'application/json'
  }
end
oauth_send(user_id, verb, path, *params) click to toggle source
# File lib/rspotify/user.rb, line 74
def self.oauth_send(user_id, verb, path, *params)
  custom_headers = extract_custom_headers(params)
  headers = oauth_header(user_id).merge(custom_headers)
  params << headers
  RSpotify.send(:send_request, verb, path, *params)

rescue RestClient::Exception => e
  raise e if e.response !~ /access token expired/
  refresh_token(user_id)
  params[-1] = oauth_header(user_id).merge(custom_headers)
  RSpotify.send(:send_request, verb, path, *params)
end
refresh_token(user_id) click to toggle source
# File lib/rspotify/user.rb, line 33
def self.refresh_token(user_id)
  request_body = {
    grant_type: 'refresh_token',
    refresh_token: @@users_credentials[user_id]['refresh_token']
  }
  response = RestClient.post(TOKEN_URI, request_body, RSpotify.send(:auth_header))
  response = JSON.parse(response)
  @@users_credentials[user_id]['token'] = response['access_token']
  access_refresh_proc = @@users_credentials[user_id]['access_refresh_callback']
  # If the access token expires and a new one is granted via the refresh
  # token, then this proc will be called with two parameters:
  # new_access_token and token_lifetime (in seconds)
  # The purpose is to allow the calling environment to invoke some action,
  # such as persisting the new access token somewhere, when the new token
  # is generated.
  if (access_refresh_proc.respond_to? :call)
    access_refresh_proc.call(response['access_token'], response['expires_in'])
  end
rescue RestClient::BadRequest => e
  raise e if e.response !~ /Refresh token revoked/
end

Public Instance Methods

create_playlist!(name, description: nil, public: true, collaborative: false) click to toggle source

Creates a playlist in user’s Spotify account. This method is only available when the current user has granted access to the playlist-modify-public and playlist-modify-private scopes.

@note To create a collaborative playlist the public option must be set to false.

@param name [String] The name for the new playlist @param public [Boolean] Whether the playlist is public or private. Default: true @return [Playlist]

@example

user.create_playlist!('my-first-playlist')
user.playlists.last.name   #=> "my-first-playlist"
user.playlists.last.public #=> true

playlist = user.create_playlist!('my-second-playlist', public: false)
playlist.name   #=> "my-second-playlist"
playlist.public #=> false
# File lib/rspotify/user.rb, line 134
def create_playlist!(name, description: nil, public: true, collaborative: false)
  url = "users/#{@id}/playlists"
  request_data = {
    name: name,
    public: public,
    description: description,
    collaborative: collaborative
  }.to_json
  response = User.oauth_post(@id, url, request_data)
  return response if RSpotify.raw_response
  Playlist.new response
end
devices() click to toggle source

Returns the user’s available devices

@return [Array<Device>]

@example

devices = user.devices
devices.first.id #=> "5fbb3ba6aa454b5534c4ba43a8c7e8e45a63ad0e"
# File lib/rspotify/user.rb, line 523
def devices
  url = "me/player/devices"
  response = RSpotify.resolve_auth_request(@id, url)

  return response if RSpotify.raw_response
  response['devices'].map { |i| Device.new i }
end
follow(followed, public: true) click to toggle source

Add the current user as a follower of one or more artists, other Spotify users or a playlist. Following artists or users require the user-follow-modify scope. Following a playlist publicly requires the playlist-modify-public scope; following it privately requires the playlist-modify-private scope.

@note Scopes you provide for playlists determine only whether the current user can themselves follow the playlist publicly or privately (i.e. show others what they are following), not whether the playlist itself is public or private.

@param followed [Artist, Array<Artist>, User, Array<User>, Playlist] The artists, users or playlist to follow @param public [Boolean] If true the playlist will be included in user’s public playlists, if false it will remain private. @return [Artist, Array<Artist>, User, Array<User>, Playlist]

@example

artists = RSpotify::Artist.search('John')
user.follow(artists)

playlist = RSpotify::Playlist.search('Movie').first
user.follow(playlist, public: false)
# File lib/rspotify/user.rb, line 203
def follow(followed, public: true)
  if followed.is_a? Array
    ids = followed.map(&:id).join(',')
    type = followed.first.type
  else
    ids = followed.id
    type = followed.type
  end

  if type == 'playlist'
    request_body = { public: public }
    url = "users/#{followed.owner.id}/playlists/#{followed.id}/followers"
  else
    request_body = {}
    url = "me/following?type=#{type}&ids=#{ids}"
  end

  User.oauth_put(@id, url, request_body.to_json)
  followed
end
following(type: nil, limit: 20, after: nil) click to toggle source

Get the current user’s followed artists or users. Requires the user-follow-read scope.

@note The current Spotify API implementation only supports getting followed artists

@param type [String] The ID type: currently only “artist” is supported @param limit [Integer] Maximum number of items to return. Maximum: 50. Minimum: 1. Default: 20. @param after [String] Optional. The last artist ID retrieved from the previous request. @return [Array<Artist>]

@example

followed_artists = user.following(type: 'artist')
followed_artists.first.class #=> RSpotify::Artist

followed_artists = user.following(type: 'artist', limit: 50)
# File lib/rspotify/user.rb, line 238
def following(type: nil, limit: 20, after: nil)
  type_class = RSpotify.const_get(type.capitalize)
  url = "me/following?type=#{type}&limit=#{limit}"
  url << "&after=#{after}" if after

  response = User.oauth_get(@id, url)
  return response if RSpotify.raw_response
  response["#{type}s"]['items'].compact.map { |i| type_class.new i }
end
follows?(followed) click to toggle source

Check if the current user is following one or more artists or other Spotify users. This method is only available when the current user has granted access to the user-follow-read scope.

@param followed [Artist, Array<Artist>, User, Array<User>] The users or artists to check @return [Array<Boolean>]

@example

artists = RSpotify::Artist.search('John')
user.follows?(artists) #=> [true, false, true...]
# File lib/rspotify/user.rb, line 257
def follows?(followed)
  if followed.is_a? Array
    ids = followed.map(&:id).join(',')
    type = followed.first.type
  else
    ids = followed.id
    type = followed.type
  end

  url = "me/following/contains?type=#{type}&ids=#{ids}"
  User.oauth_get(@id, url)
end
player() click to toggle source

Get the current user’s player

@example

player = user.player
# File lib/rspotify/user.rb, line 151
def player
  url = "me/player"
  response = User.oauth_get(@id, url)
  return response if RSpotify.raw_response
  response ? Player.new(self, response) : Player.new(self)
end
playlists(limit: 20, offset: 0) click to toggle source

Returns all playlists from user

@param limit [Integer] Maximum number of playlists to return. Maximum: 50. Minimum: 1. Default: 20. @param offset [Integer] The index of the first playlist to return. Use with limit to get the next set of playlists. Default: 0. @return [Array<Playlist>]

@example

playlists = user.playlists
playlists.class       #=> Array
playlists.first.class #=> RSpotify::Playlist
playlists.first.name  #=> "Movie Soundtrack Masterpieces"
# File lib/rspotify/user.rb, line 281
def playlists(limit: 20, offset: 0)
  url = "users/#{@id}/playlists?limit=#{limit}&offset=#{offset}"
  response = RSpotify.resolve_auth_request(@id, url)
  return response if RSpotify.raw_response
  response['items'].map { |i| Playlist.new i }
end
recently_played(limit: 20, after: nil, before: nil) click to toggle source

Get the current user’s recently played tracks. Requires the user-read-recently-played scope.

@param limit [Integer] Optional. The number of entities to return. Default: 20. Minimum: 1. Maximum: 50. @param after [String] Optional. A Unix timestamp in milliseconds. Returns all items after (but not including) this cursor position. If after is specified, before must not be specified. @param before [String] Optional. A Unix timestamp in milliseconds. Returns all items before (but not including) this cursor position. If before is specified, after must not be specified. @return [Array<Track>]

@example

recently_played = user.recently_played
recently_played.size       #=> 20
recently_played.first.name #=> "Ice to Never"
user.recently_played(limit: 50)
user.recently_played(after: '1572561234', before: '1572562369')
# File lib/rspotify/user.rb, line 171
def recently_played(limit: 20, after: nil, before: nil)
  url = "me/player/recently-played?limit=#{limit}"
  url << "&after=#{after}" if after
  url << "&before=#{before}" if before

  response = RSpotify.resolve_auth_request(@id, url)
  return response if RSpotify.raw_response

  json = RSpotify.raw_response ? JSON.parse(response) : response
  json['items'].map do |t|
    data = t['track']
    data['played_at'] = t['played_at']
    data['context_type'] = t['context']['type'] if t['context']
    Track.new data
  end
end
remove_albums!(albums) click to toggle source

Remove albums from the user’s “Your Music” library.

@param albums [Array<Album>] The albums to remove. Maximum: 50. @return [Array<Album>] The albums removed.

@example

albums = user.saved_albums

user.saved_albums.size #=> 20
user.remove_albums!(albums)
user.saved_albums.size #=> 0
# File lib/rspotify/user.rb, line 376
def remove_albums!(albums)
  albums_ids = albums.map(&:id)
  url = "me/albums?ids=#{albums_ids.join ','}"
  User.oauth_delete(@id, url)
  albums
end
remove_tracks!(tracks) click to toggle source

Remove tracks from the user’s “Your Music” library.

@param tracks [Array<Track>] The tracks to remove. Maximum: 50. @return [Array<Track>] The tracks removed.

@example

tracks = user.saved_tracks

user.saved_tracks.size #=> 20
user.remove_tracks!(tracks)
user.saved_tracks.size #=> 0
# File lib/rspotify/user.rb, line 299
def remove_tracks!(tracks)
  tracks_ids = tracks.map(&:id)
  url = "me/tracks?ids=#{tracks_ids.join ','}"
  User.oauth_delete(@id, url)
  tracks
end
save_albums!(albums) click to toggle source

Save albums to the user’s “Your Music” library.

@param albums [Array<Album>] The albums to save. Maximum: 50. @return [Array<Album>] The albums saved.

@example

albums = RSpotify::Album.search('launeddas')

user.saved_albums.size #=> 0
user.save_albums!(albums)
user.saved_albums.size #=> 10
# File lib/rspotify/user.rb, line 394
def save_albums!(albums)
  albums_ids = albums.map(&:id)
  url = "me/albums"
  request_body = { ids: albums_ids }
  User.oauth_put(@id, url, request_body.to_json)
  albums
end
save_tracks!(tracks) click to toggle source

Save tracks to the user’s “Your Music” library.

@param tracks [Array<Track>] The tracks to save. Maximum: 100. @return [Array<Track>] The tracks saved.

@example

tracks = RSpotify::Track.search('Know')

user.saved_tracks.size #=> 0
user.save_tracks!(tracks)
user.saved_tracks.size #=> 20
# File lib/rspotify/user.rb, line 317
def save_tracks!(tracks)
  tracks_ids = tracks.map(&:id)
  url = "me/tracks"
  request_body = { ids: tracks_ids }
  User.oauth_put(@id, url, request_body.to_json)
  tracks
end
saved_albums(limit: 20, offset: 0, market: nil) click to toggle source

Returns the albums saved in the Spotify user’s “Your Music” library. ** Includes albums whose tracks you saved

@param limit [Integer] Maximum number of albums to return. Maximum: 50. Minimum: 1. Default: 20. @param offset [Integer] The index of the first album to return. Use with limit to get the next set of albums. Default: 0. @param market [String] Optional. An {en.wikipedia.org/wiki/ISO_3166-1_alpha-2 ISO 3166-1 alpha-2 country code}. @return [Array<Album>]

@example

albums = user.saved_albums
albums.size       #=> 20
albums.first.name #=> "Launeddas"
# File lib/rspotify/user.rb, line 413
def saved_albums(limit: 20, offset: 0, market: nil)
  url = "me/albums?limit=#{limit}&offset=#{offset}"
  url << "&market=#{market}" if market
  response = User.oauth_get(@id, url)
  json = RSpotify.raw_response ? JSON.parse(response) : response

  albums = json['items'].select { |i| i['album'] }

  return response if RSpotify.raw_response
  albums.map { |a| Album.new a['album'] }
end
saved_albums?(albums) click to toggle source

Check if albums are already saved in the Spotify user’s “Your Music” library. ** Only returns true if the album was saved via me/albums, not if you saved each track individually.

@param albums [Array<Album>] The albums to check. Maximum: 50. @return [Array<Boolean>] Array of booleans, in the same order in which the albums were specified.

@example

albums = RSpotify::Album.search('launeddas')
user.saved_albums?(albums) #=> [true, false, true...]
# File lib/rspotify/user.rb, line 433
def saved_albums?(albums)
  albums_ids = albums.map(&:id)
  url = "me/albums/contains?ids=#{albums_ids.join ','}"
  User.oauth_get(@id, url)
end
saved_tracks(limit: 20, offset: 0, market: nil) click to toggle source

Returns the tracks saved in the Spotify user’s “Your Music” library

@param limit [Integer] Maximum number of tracks to return. Maximum: 50. Minimum: 1. Default: 20. @param offset [Integer] The index of the first track to return. Use with limit to get the next set of tracks. Default: 0. @param market [String] Optional. An {en.wikipedia.org/wiki/ISO_3166-1_alpha-2 ISO 3166-1 alpha-2 country code}. @return [Array<Track>]

@example

tracks = user.saved_tracks
tracks.size       #=> 20
tracks.first.name #=> "Do I Wanna Know?"
# File lib/rspotify/user.rb, line 336
def saved_tracks(limit: 20, offset: 0, market: nil)
  url = "me/tracks?limit=#{limit}&offset=#{offset}"
  url << "&market=#{market}" if market
  response = User.oauth_get(@id, url)
  json = RSpotify.raw_response ? JSON.parse(response) : response

  tracks = json['items'].select { |i| i['track'] }
  @tracks_added_at = hash_for(tracks, 'added_at') do |added_at|
    Time.parse added_at
  end

  return response if RSpotify.raw_response
  tracks.map { |t| Track.new t['track'] }
end
saved_tracks?(tracks) click to toggle source

Check if tracks are already saved in the Spotify user’s “Your Music” library

@param tracks [Array<Track>] The tracks to check. Maximum: 50. @return [Array<Boolean>] Array of booleans, in the same order in which the tracks were specified.

@example

tracks = RSpotify::Track.search('Know')
user.saved_tracks?(tracks) #=> [true, false, true...]
# File lib/rspotify/user.rb, line 359
def saved_tracks?(tracks)
  tracks_ids = tracks.map(&:id)
  url = "me/tracks/contains?ids=#{tracks_ids.join ','}"
  User.oauth_get(@id, url)
end
to_hash() click to toggle source

Returns a hash containing all user attributes

# File lib/rspotify/user.rb, line 440
def to_hash
  pairs = instance_variables.map do |var|
    [var.to_s.delete('@'), instance_variable_get(var)]
  end
  Hash[pairs]
end
top_artists(limit: 20, offset: 0, time_range: 'medium_term') click to toggle source

Get the current user’s top artists based on calculated affinity. Requires the user-top-read scope.

@param limit [Integer] Optional. The number of entities to return. Default: 20. Minimum: 1. Maximum: 50. @param offset [Integer] Optional. The index of the first entity to return. Default: 0 (i.e., the first track). Use with limit to get the next set of entities. @param time_range [String] Optional. Over what time frame the affinities are computed. Valid values: long_term (calculated from several years of data and including all new data as it becomes available), medium_term (approximately last 6 months), short_term (approximately last 4 weeks). Default: medium_term. @return [Array<Artist>]

@example

top_artists = user.top_artists
top_artists.size       #=> 20
top_artists.first.name #=> "Nine Inch Nails"
# File lib/rspotify/user.rb, line 458
def top_artists(limit: 20, offset: 0, time_range: 'medium_term')
  url = "me/top/artists?limit=#{limit}&offset=#{offset}&time_range=#{time_range}"
  response = User.oauth_get(@id, url)
  return response if RSpotify.raw_response
  response['items'].map { |i| Artist.new i }
end
top_tracks(limit: 20, offset: 0, time_range: 'medium_term') click to toggle source

Get the current user’s top tracks based on calculated affinity. Requires the user-top-read scope.

@param limit [Integer] Optional. The number of entities to return. Default: 20. Minimum: 1. Maximum: 50. @param offset [Integer] Optional. The index of the first entity to return. Default: 0 (i.e., the first track). Use with limit to get the next set of entities. @param time_range [String] Optional. Over what time frame the affinities are computed. Valid values: long_term (calculated from several years of data and including all new data as it becomes available), medium_term (approximately last 6 months), short_term (approximately last 4 weeks). Default: medium_term. @return [Array<Track>]

@example

top_tracks = user.top_tracks
top_tracks.size       #=> 20
top_tracks.first.name #=> "Ice to Never"
# File lib/rspotify/user.rb, line 476
def top_tracks(limit: 20, offset: 0, time_range: 'medium_term')
  url = "me/top/tracks?limit=#{limit}&offset=#{offset}&time_range=#{time_range}"
  response = User.oauth_get(@id, url)
  return response if RSpotify.raw_response
  response['items'].map { |i| Track.new i }
end
unfollow(unfollowed) click to toggle source

Remove the current user as a follower of one or more artists, other Spotify users or a playlist. Unfollowing artists or users require the user-follow-modify scope. Unfollowing a publicly followed playlist requires the playlist-modify-public scope; unfollowing a privately followed playlist requires the playlist-modify-private scope.

@note Note that the scopes you provide for playlists relate only to whether the current user is following the playlist publicly or privately (i.e. showing others what they are following), not whether the playlist itself is public or private.

@param unfollowed [Artist, Array<Artist>, User, Array<User>, Playlist] The artists, users or playlist to unfollow @return [Artist, Array<Artist>, User, Array<User>, Playlist]

@example

artists = RSpotify::Artist.search('John')
user.unfollow(artists)

playlist = RSpotify::Playlist.search('Movie').first
user.unfollow(playlist)
# File lib/rspotify/user.rb, line 497
def unfollow(unfollowed)
  if unfollowed.is_a? Array
    ids = unfollowed.map(&:id).join(',')
    type = unfollowed.first.type
  else
    ids = unfollowed.id
    type = unfollowed.type
  end

  url = if type == 'playlist'
    "users/#{unfollowed.owner.id}/playlists/#{unfollowed.id}/followers"
  else
    "me/following?type=#{type}&ids=#{ids}"
  end

  User.oauth_delete(@id, url)
  unfollowed
end