spotify-ruby-api 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE +21 -0
  7. data/README.md +119 -0
  8. data/Rakefile +7 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +7 -0
  11. data/lib/spotify/api/album.rb +164 -0
  12. data/lib/spotify/api/artist.rb +248 -0
  13. data/lib/spotify/api/base.rb +150 -0
  14. data/lib/spotify/api/track.rb +127 -0
  15. data/lib/spotify/api/user.rb +58 -0
  16. data/lib/spotify/api/version.rb +5 -0
  17. data/lib/spotify/api.rb +12 -0
  18. data/lib/spotify/models/album.rb +42 -0
  19. data/lib/spotify/models/artist.rb +34 -0
  20. data/lib/spotify/models/category.rb +23 -0
  21. data/lib/spotify/models/copyright.rb +27 -0
  22. data/lib/spotify/models/cursor.rb +19 -0
  23. data/lib/spotify/models/cursorbased_paging.rb +24 -0
  24. data/lib/spotify/models/error.rb +122 -0
  25. data/lib/spotify/models/external_id.rb +41 -0
  26. data/lib/spotify/models/external_url.rb +39 -0
  27. data/lib/spotify/models/follower.rb +27 -0
  28. data/lib/spotify/models/full/album.rb +46 -0
  29. data/lib/spotify/models/full/artist.rb +37 -0
  30. data/lib/spotify/models/full/track.rb +34 -0
  31. data/lib/spotify/models/full/user.rb +31 -0
  32. data/lib/spotify/models/full.rb +11 -0
  33. data/lib/spotify/models/image.rb +28 -0
  34. data/lib/spotify/models/paging.rb +38 -0
  35. data/lib/spotify/models/playlist_track.rb +24 -0
  36. data/lib/spotify/models/saved_album.rb +22 -0
  37. data/lib/spotify/models/saved_track.rb +22 -0
  38. data/lib/spotify/models/simplified/album.rb +23 -0
  39. data/lib/spotify/models/simplified/artist.rb +23 -0
  40. data/lib/spotify/models/simplified/track.rb +23 -0
  41. data/lib/spotify/models/simplified/user.rb +23 -0
  42. data/lib/spotify/models/simplified.rb +11 -0
  43. data/lib/spotify/models/track.rb +50 -0
  44. data/lib/spotify/models/track_link.rb +33 -0
  45. data/lib/spotify/models/user.rb +41 -0
  46. data/lib/spotify/models.rb +27 -0
  47. data/lib/spotify.rb +6 -0
  48. data/spotify-api.gemspec +34 -0
  49. metadata +160 -0
@@ -0,0 +1,150 @@
1
+ require 'json'
2
+
3
+ module Spotify
4
+ module API
5
+
6
+ class Base
7
+
8
+ attr_reader :response, :retries, :timeout, :url, :params, :request
9
+
10
+ #
11
+ # The API base URL.
12
+ #
13
+ BASE_URL = "https://api.spotify.com/v1/"
14
+
15
+ #
16
+ # The API search endpoint.
17
+ #
18
+ SEARCH_URL = "#{BASE_URL}search"
19
+
20
+ #
21
+ # The max retries limit.
22
+ #
23
+ MAX_RETRIES = 5
24
+
25
+ #
26
+ # Restrict value for market variable on search methods.
27
+ #
28
+ FROM_TOKEN = :from_token
29
+
30
+ #
31
+ # Initializes the optional arguments.
32
+ #
33
+ # @param [Hash] the optional arguments.
34
+ # @option [Fixnum] :timeout the max time a request can take.
35
+ # @option [Fixnum] :retries the number of retries if necessary.
36
+ #
37
+ def initialize(args = {})
38
+ @args = args.except(:timeout, :retries)
39
+ @timeout = args[:timeout].to_i
40
+ @retries = args[:retries].to_i
41
+ end
42
+
43
+ #
44
+ # Performs a request on the given url with its arguments.
45
+ #
46
+ # @param [String] url the request API url.
47
+ # @param [Hash] params the request arguments.
48
+ #
49
+ def get(url, params = {})
50
+ @url = url
51
+ @params = params
52
+ @request = prepare_request
53
+
54
+ make_request
55
+ end
56
+
57
+ #
58
+ # Performs the request respecting the given timeout.
59
+ #
60
+ # @param [Proc] block a block of code to perform the request.
61
+ #
62
+ def run_with_timeout(&block)
63
+ Timeout.timeout(@timeout) do
64
+ yield
65
+ end
66
+ end
67
+
68
+ #
69
+ # Prepares the Proc instructions to perform the request.
70
+ #
71
+ def prepare_request
72
+ lambda do
73
+ sleep(3)
74
+ uri = URI(@url)
75
+ uri.query = URI.encode_www_form(@params)
76
+ @response = Net::HTTP.get(uri)
77
+ end
78
+ end
79
+
80
+ #
81
+ # Handles the request behavior.
82
+ #
83
+ # @param [Fixnum] attempts the current attempt number.
84
+ #
85
+ def make_request(attempts = 0)
86
+ @attempts = attempts
87
+
88
+ if attempts > MAX_RETRIES || attempts > @retries
89
+ set_response(Spotify::API::Errors::MAX_RETRIES, true)
90
+
91
+ else
92
+ begin
93
+ if @timeout > 0
94
+ run_with_timeout { request.call }
95
+ else
96
+ request.call
97
+ end
98
+
99
+ rescue Timeout::Error
100
+ set_response(Spotify::API::Errors::TIMEOUT)
101
+ rescue
102
+ set_response(Spotify::API::Errors::UNEXPECTED)
103
+ end
104
+ end
105
+
106
+ end
107
+
108
+ #
109
+ # Sets the response in case of something goes wrong during the
110
+ # extraction process.
111
+ #
112
+ # @param [String] error the raised error during the extraction.
113
+ # @param [Boolean] force if should return error independent of retries.
114
+ #
115
+ def set_response(error, force = false)
116
+ if force == false && @retries > 0
117
+ make_request(@attempts + 1)
118
+ else
119
+ @response = { error: error }.to_json
120
+ end
121
+ end
122
+
123
+ #
124
+ # Parses the response to JSON to get more flexible.
125
+ #
126
+ # @return [Hash] the parsed response.
127
+ #
128
+ def body
129
+ @response = JSON.parse(response)
130
+ end
131
+
132
+ def define_response(&block)
133
+ response = body
134
+
135
+ # The if statement covers a case for Users requests.
136
+ if response.class != Spotify::Models::Error
137
+ if response['error']
138
+ response = Spotify::Models::Error.default(response['error'])
139
+ else
140
+ response = yield
141
+ end
142
+ end
143
+
144
+ response
145
+ end
146
+
147
+ end
148
+
149
+ end
150
+ end
@@ -0,0 +1,127 @@
1
+ module Spotify
2
+ module API
3
+
4
+ class Track < Spotify::API::Base
5
+
6
+ #
7
+ # API endpoint for tracks.
8
+ #
9
+ TRACKS_URL = "#{BASE_URL}tracks"
10
+
11
+ #
12
+ # Gets the tracks related to the given parameters.
13
+ #
14
+ # @param [Hash] args the search arguments.
15
+ # @option [Fixnum] :timeout the max time a request can take.
16
+ # @option [Fixnum] :retries the number of retries if necessary.
17
+ #
18
+ # @return [Spotify::Models::Paging] the extracted tracks.
19
+ #
20
+ def self.search(args = {})
21
+ args[:type] = :track
22
+
23
+ service_params = args.slice(:timeout, :retries)
24
+ args = args.slice(:q, :market, :type, :limit, :offset)
25
+
26
+ self.new(service_params).search(args)
27
+ end
28
+
29
+ #
30
+ # Gets a track.
31
+ #
32
+ # @param [Hash] args the search arguments.
33
+ # @option [Fixnum] :timeout the max time a request can take.
34
+ # @option [Fixnum] :retries the number of retries if necessary.
35
+ #
36
+ # @return [Full::Track] the extracted track.
37
+ #
38
+ def self.search_by_id(args = {})
39
+ service_params = args.slice(:timeout, :retries)
40
+ args = args.slice(:id, :market)
41
+
42
+ self.new(service_params).search_by_id(args)
43
+ end
44
+
45
+ #
46
+ # Gets several tracks.
47
+ #
48
+ # @param [Hash] args the search arguments.
49
+ # @option [Fixnum] :timeout the max time a request can take.
50
+ # @option [Fixnum] :retries the number of retries if necessary.
51
+ #
52
+ # @return [Array<Full::Track>] an array containing
53
+ # the extracted tracks.
54
+ #
55
+ def self.search_by_ids(args = {})
56
+ args[:ids] = Array(args[:ids]).join(',')
57
+
58
+ service_params = args.slice(:timeout, :retries)
59
+ args = args.slice(:ids, :market)
60
+
61
+ self.new(service_params).search_by_ids(args)
62
+ end
63
+
64
+ #
65
+ # Gets the tracks related to the given parameters.
66
+ #
67
+ # @param [Hash] args the search arguments.
68
+ # @option [Fixnum] :timeout the max time a request can take.
69
+ # @option [Fixnum] :retries the number of retries if necessary.
70
+ #
71
+ # @return [Spotify::Models::Paging] the extracted tracks.
72
+ #
73
+ def search(args = {})
74
+ if args[:market].to_s.to_sym == FROM_TOKEN
75
+ # TODO: Authorization.
76
+ return Spotify::API::Errors::NOT_AVAILABLE
77
+ end
78
+
79
+ get(SEARCH_URL, args)
80
+
81
+ define_response do
82
+ klass = Spotify::Models::Full::Track
83
+
84
+ Spotify::Models::Paging.new(response["tracks"], klass)
85
+ end
86
+ end
87
+
88
+ #
89
+ # Gets a track.
90
+ #
91
+ # @param [Hash] args the search arguments.
92
+ # @option [String] :id the track id.
93
+ # @option [String] :market the market.
94
+ #
95
+ # @return [Full::Track] the extracted track.
96
+ #
97
+ def search_by_id(args = {})
98
+ get(TRACKS_URL + '/' + args[:id].to_s, args.slice(:market))
99
+
100
+ define_response do
101
+ Spotify::Models::Full::Track.new(response)
102
+ end
103
+ end
104
+
105
+ #
106
+ # Gets several tracks.
107
+ #
108
+ # @param [Hash] args the search arguments.
109
+ # @option [String] :ids the tracks ids between ",".
110
+ # @option [String] :market the market.
111
+ #
112
+ # @return [Array<Full::Track>] an array containing
113
+ # the extracted tracks.
114
+ #
115
+ def search_by_ids(args = {})
116
+ get(TRACKS_URL, args)
117
+
118
+ define_response do
119
+ response["tracks"].map do |track|
120
+ Spotify::Models::Full::Track.new(track)
121
+ end
122
+ end
123
+ end
124
+
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,58 @@
1
+ module Spotify
2
+ module API
3
+
4
+ class User < Spotify::API::Base
5
+
6
+ #
7
+ # API endpoint for users.
8
+ #
9
+ USERS_URL = "#{BASE_URL}users"
10
+
11
+ #
12
+ # Gets an user.
13
+ #
14
+ # @param [Hash] args the search arguments.
15
+ # @option [Fixnum] :timeout the max time a request can take.
16
+ # @option [Fixnum] :retries the number of retries if necessary.
17
+ #
18
+ # @return [Public::User] the extracted user.
19
+ #
20
+ def self.search_by_id(args = {})
21
+ service_params = args.slice(:timeout, :retries)
22
+ args = args.slice(:id)
23
+
24
+ self.new(service_params).search_by_id(args)
25
+ end
26
+
27
+ #
28
+ # Gets a track.
29
+ #
30
+ # @param [Hash] args the search arguments.
31
+ # @option [String] :id the track id.
32
+ # @option [String] :market the market.
33
+ #
34
+ # @return [Public::User] the extracted user.
35
+ #
36
+ def search_by_id(args = {})
37
+ get(USERS_URL + '/' + args[:id].to_s)
38
+
39
+ define_response do
40
+ Spotify::Models::Simplified::User.new(response)
41
+ end
42
+ end
43
+
44
+ #
45
+ # Parses the response to JSON to get more flexible.
46
+ #
47
+ # @return [Hash] the parsed response.
48
+ #
49
+ def body
50
+ @response = JSON.parse(response)
51
+ rescue
52
+ Spotify::Models::Error.parser_error
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ module Spotify
2
+ module API
3
+ VERSION = "0.7.0"
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ require "spotify/api/base"
2
+ require "spotify/api/version"
3
+
4
+ require "spotify/api/album"
5
+ require "spotify/api/artist"
6
+ require "spotify/api/track"
7
+ require "spotify/api/user"
8
+
9
+ module Spotify
10
+ module API
11
+ end
12
+ end
@@ -0,0 +1,42 @@
1
+ module Spotify
2
+
3
+ module Models
4
+
5
+ class Album
6
+
7
+ attr_reader :album_type, :available_markets, :external_urls,
8
+ :genres, :href, :id, :images, :name, :type, :uri
9
+
10
+ #
11
+ # Sets the arguments to its variables.
12
+ #
13
+ # @param [Hash] args the arguments that will be placed on each variable.
14
+ #
15
+ # @return [Album] an album object.
16
+ #
17
+ def initialize(args = {})
18
+ args = Hash(args).with_indifferent_access
19
+
20
+ # Arrays
21
+ images = Array(args[:images]).map { |i| Spotify::Models::Image.new(i) }
22
+
23
+ # Objects
24
+ external_urls = Spotify::Models::ExternalURL.new(args[:external_urls])
25
+
26
+ @album_type = args[:album_type]
27
+ @available_markets = args[:available_markets]
28
+ @external_urls = external_urls
29
+ @genres = args[:genres]
30
+ @href = args[:href]
31
+ @id = args[:id]
32
+ @images = images
33
+ @name = args[:name]
34
+ @type = args[:type]
35
+ @uri = args[:uri]
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,34 @@
1
+ module Spotify
2
+
3
+ module Models
4
+
5
+ class Artist
6
+
7
+ attr_reader :external_urls, :href, :id, :name, :type, :uri
8
+
9
+ #
10
+ # Sets the arguments to its variables.
11
+ #
12
+ # @param [Hash] args the arguments that will be placed on each variable.
13
+ #
14
+ # @return [Album] an artist object.
15
+ #
16
+ def initialize(args = {})
17
+ args = Hash(args).with_indifferent_access
18
+
19
+ # Objects
20
+ external_urls = Spotify::Models::ExternalURL.new(args[:external_urls])
21
+
22
+ @external_urls = external_urls
23
+ @href = args[:href]
24
+ @id = args[:id]
25
+ @name = args[:name]
26
+ @type = args[:type]
27
+ @uri = args[:uri]
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,23 @@
1
+ module Spotify
2
+
3
+ module Models
4
+
5
+ class Category
6
+
7
+ attr_reader :href, :icons, :id, :name
8
+
9
+ def initialize(args = {})
10
+ args = args.with_indifferent_access
11
+ icons = args[:icons].map { |icon| Spotify::Models::Image.new(icon) }
12
+
13
+ @href = args[:href]
14
+ @icons = icons
15
+ @id = args[:id]
16
+ @name = args[:name]
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,27 @@
1
+ module Spotify
2
+
3
+ module Models
4
+
5
+ class Copyright
6
+
7
+ attr_reader :text, :type
8
+
9
+ #
10
+ # Sets the arguments to its variables.
11
+ #
12
+ # @param [Hash] args the arguments that will be placed on each variable.
13
+ #
14
+ # @return [Image] a copyright object.
15
+ #
16
+ def initialize(args = {})
17
+ args = Hash(args).with_indifferent_access
18
+
19
+ @text = args[:text]
20
+ @type = args[:type]
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,19 @@
1
+ module Spotify
2
+
3
+ module Models
4
+
5
+ class Cursor
6
+
7
+ attr_reader :after
8
+
9
+ def initialize(args = {})
10
+ args = args.with_indifferent_access
11
+
12
+ @after = args[:after]
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,24 @@
1
+ module Spotify
2
+
3
+ module Models
4
+
5
+ class CursorbasedPaging
6
+
7
+ attr_reader :href, :items, :limit, :next, :cursors, :total
8
+
9
+ def initialize(args = {}, klass)
10
+ args = args.with_indifferent_access
11
+
12
+ @href = args[:href]
13
+ @items = args[:items].map { |item| klass.new(item) }
14
+ @limit = args[:limit]
15
+ @next = args[:next]
16
+ @total = args[:total]
17
+ @cursors = Cursor.new(args[:cursors])
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,122 @@
1
+ module Spotify
2
+
3
+ module Models
4
+
5
+ class Error
6
+
7
+ attr_reader :status, :message
8
+
9
+ #
10
+ # Identifies that the error is returned from some request.
11
+ #
12
+ DEFAULT = :default
13
+
14
+ #
15
+ # Defines the text and status for each message known.
16
+ #
17
+ ERRORS = {
18
+ timeout: {
19
+ status: 700,
20
+ message: "Timed out."
21
+ },
22
+ unexpected: {
23
+ status: 710,
24
+ message: "Unexpected error."
25
+ },
26
+ max_retries: {
27
+ status: 720,
28
+ message: "Max retries exceeded."
29
+ },
30
+ not_available: {
31
+ status: 730,
32
+ message: "This feature is not available yet."
33
+ },
34
+ parser_error: {
35
+ status: 740,
36
+ message: "Something went wrong. Please verify the parameters"
37
+ }
38
+ }
39
+
40
+ #
41
+ # Creates the message.
42
+ #
43
+ # @param [Hash] type the type of the message to be shown.
44
+ #
45
+ # @return [Parsers::Error] a message to be shown to the user.
46
+ #
47
+ def initialize(type, args = {})
48
+ if type == DEFAULT
49
+ error = {
50
+ status: args[:status],
51
+ message: args[:message]
52
+ }
53
+ else
54
+ error = ERRORS[type]
55
+ end
56
+
57
+ @status = error[:status]
58
+ @message = error[:message]
59
+ end
60
+
61
+ #
62
+ # Creates an error representing a timeout error.
63
+ #
64
+ # @return [Parsers::Error] a message to be shown to the user.
65
+ #
66
+ def self.timeout
67
+ self.new(:timeout)
68
+ end
69
+
70
+ #
71
+ # Creates an error representing an unexpected error.
72
+ #
73
+ # @return [Parsers::Error] a message to be shown to the user.
74
+ #
75
+ def self.unexpected_error
76
+ self.new(:unexpected_error)
77
+ end
78
+
79
+ #
80
+ # Creates an error representing that the retries limit was reached.
81
+ #
82
+ # @return [Parsers::Error] a message to be shown to the user.
83
+ #
84
+ def self.max_retries
85
+ self.new(:max_retries)
86
+ end
87
+
88
+ #
89
+ # Creates an error representing that the requested feature is not
90
+ # available yet.
91
+ #
92
+ # @return [Parsers::Error] a message to be shown to the user.
93
+ #
94
+ def self.not_available
95
+ self.new(:not_available)
96
+ end
97
+
98
+ #
99
+ # Creates an error representing a parser error.
100
+ #
101
+ # @return [Parsers::Error] a message to be shown to the user.
102
+ #
103
+ def self.parser_error
104
+ self.new(:parser_error)
105
+ end
106
+
107
+ #
108
+ # Creates an error representing the site response error.
109
+ #
110
+ # @return [Parsers::Error] a message to be shown to the user.
111
+ #
112
+ def self.default(args = {})
113
+ args = Hash(args).with_indifferent_access
114
+
115
+ self.new(:default, args)
116
+ end
117
+
118
+ end
119
+
120
+ end
121
+
122
+ end
@@ -0,0 +1,41 @@
1
+ module Spotify
2
+
3
+ module Models
4
+
5
+ class ExternalID
6
+
7
+ #
8
+ # Sets the arguments to its variables.
9
+ #
10
+ # @param [Hash] args the arguments that will be placed on each variable.
11
+ #
12
+ # @return [ExternalID] an external id object.
13
+ #
14
+ def initialize(args = {})
15
+ args = Hash(args).with_indifferent_access
16
+
17
+ hsh = {
18
+ isrc: args[:isrc],
19
+ ean: args[:ean],
20
+ upc: args[:upc]
21
+ }
22
+
23
+ hsh = hsh.reject { |_, v| v.blank? }
24
+
25
+ if hsh.present?
26
+ # Generates the keys dynamically
27
+ hsh.each do |k, v|
28
+ key = k
29
+ value = v
30
+
31
+ eval("@#{key} = value")
32
+ class_eval { attr_reader key.to_sym }
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end