napster 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +39 -5
- data/.rubocop.yml +4 -0
- data/README.md +336 -8
- data/Rakefile +3 -3
- data/bin/console +3 -3
- data/lib/napster.rb +27 -4
- data/lib/napster/client.rb +276 -0
- data/lib/napster/me.rb +58 -0
- data/lib/napster/models/album.rb +97 -0
- data/lib/napster/models/artist.rb +93 -0
- data/lib/napster/models/chart.rb +34 -0
- data/lib/napster/models/content.rb +28 -0
- data/lib/napster/models/favorite.rb +130 -0
- data/lib/napster/models/favorite_status.rb +34 -0
- data/lib/napster/models/follower.rb +39 -0
- data/lib/napster/models/following.rb +62 -0
- data/lib/napster/models/library.rb +136 -0
- data/lib/napster/models/library_date_time.rb +33 -0
- data/lib/napster/models/member.rb +124 -0
- data/lib/napster/models/playlist.rb +279 -0
- data/lib/napster/models/profile.rb +75 -0
- data/lib/napster/models/tag.rb +74 -0
- data/lib/napster/models/track.rb +72 -0
- data/lib/napster/models/uploaded_image.rb +36 -0
- data/lib/napster/moniker.rb +29 -0
- data/lib/napster/request.rb +24 -0
- data/lib/napster/response_error.rb +14 -0
- data/lib/napster/version.rb +1 -1
- data/lib/string_helper.rb +10 -0
- data/napster.gemspec +30 -17
- metadata +132 -11
@@ -0,0 +1,33 @@
|
|
1
|
+
using StringHelper
|
2
|
+
|
3
|
+
module Napster
|
4
|
+
module Models
|
5
|
+
# LibraryDateTime model
|
6
|
+
# Only used for /me/library/updated
|
7
|
+
class LibraryDateTime
|
8
|
+
ATTRIBUTES = [:date,
|
9
|
+
:time_zone,
|
10
|
+
:time_in_millis].freeze
|
11
|
+
|
12
|
+
ATTRIBUTES.each do |attribute|
|
13
|
+
attr_accessor attribute
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :client
|
17
|
+
|
18
|
+
def initialize(arg)
|
19
|
+
@client = arg[:client] if arg[:client]
|
20
|
+
return unless arg[:data]
|
21
|
+
ATTRIBUTES.each do |attribute|
|
22
|
+
send("#{attribute}=", arg[:data][attribute.to_s.camel_case_lower])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.collection(arg)
|
27
|
+
arg[:data].map do |library_date_time|
|
28
|
+
LibraryDateTime.new(data: library_date_time, client: @client)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
using StringHelper
|
2
|
+
|
3
|
+
module Napster
|
4
|
+
module Models
|
5
|
+
# Member model
|
6
|
+
class Member
|
7
|
+
ATTRIBUTES = [:type,
|
8
|
+
:id,
|
9
|
+
:real_name,
|
10
|
+
:screen_name,
|
11
|
+
:bio,
|
12
|
+
:location,
|
13
|
+
:genre,
|
14
|
+
:visibility,
|
15
|
+
:href,
|
16
|
+
:favorite_albums_count,
|
17
|
+
:favorite_artists_count,
|
18
|
+
:favorite_tracks_count,
|
19
|
+
:playlists_total_count,
|
20
|
+
:playlists_published_count,
|
21
|
+
:stations_count,
|
22
|
+
:radio_count,
|
23
|
+
:follower_count,
|
24
|
+
:following_count,
|
25
|
+
:avatar,
|
26
|
+
:avatar_id,
|
27
|
+
:default_avatar,
|
28
|
+
:avatar_version].freeze
|
29
|
+
|
30
|
+
ATTRIBUTES.each do |attribute|
|
31
|
+
attr_accessor attribute
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_accessor :client
|
35
|
+
|
36
|
+
def initialize(arg)
|
37
|
+
@client = arg[:client] if arg[:client]
|
38
|
+
return unless arg[:data]
|
39
|
+
|
40
|
+
ATTRIBUTES.each do |attribute|
|
41
|
+
send("#{attribute}=", arg[:data][attribute.to_s.camel_case_lower])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.collection(arg)
|
46
|
+
arg[:data].map do |member|
|
47
|
+
Member.new(data: member, client: @client)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Top level methods
|
52
|
+
|
53
|
+
def find(arg)
|
54
|
+
response = @client.get("/members/#{arg}")
|
55
|
+
Member.new(data: response['members'].first, client: @client)
|
56
|
+
end
|
57
|
+
|
58
|
+
def screenname_available?(screenname)
|
59
|
+
response = @client.get("/screenname/#{screenname}")
|
60
|
+
response['screenName'].nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
# TODO: .avatar
|
64
|
+
|
65
|
+
def playlists(params)
|
66
|
+
request_playlists(@id, params)
|
67
|
+
end
|
68
|
+
|
69
|
+
def playlists_for(guid, params)
|
70
|
+
request_playlists(guid, params)
|
71
|
+
end
|
72
|
+
|
73
|
+
def favorites(params)
|
74
|
+
request_favorites(@id, params)
|
75
|
+
end
|
76
|
+
|
77
|
+
def favorites_for(guid, params)
|
78
|
+
request_favorites(guid, params)
|
79
|
+
end
|
80
|
+
|
81
|
+
def favorite_playlists(params)
|
82
|
+
request_favorite_playlists(@id, params)
|
83
|
+
end
|
84
|
+
|
85
|
+
def favorite_playlists_for(guid, params)
|
86
|
+
request_favorite_playlists(guid, params)
|
87
|
+
end
|
88
|
+
|
89
|
+
def chart(params)
|
90
|
+
request_chart(@id, params)
|
91
|
+
end
|
92
|
+
|
93
|
+
def chart_for(guid, params)
|
94
|
+
request_chart(guid, params)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def request_playlists(guid, params)
|
100
|
+
options = { params: params }
|
101
|
+
response = @client.get("/members/#{guid}/library/playlists", options)
|
102
|
+
Playlist.collection(data: response['playlists'], client: @client)
|
103
|
+
end
|
104
|
+
|
105
|
+
def request_favorites(guid, params)
|
106
|
+
options = { params: params }
|
107
|
+
response = @client.get("/members/#{guid}/favorites", options)
|
108
|
+
Favorite.collection(data: response['favorites'], client: @client)
|
109
|
+
end
|
110
|
+
|
111
|
+
def request_favorite_playlists(guid, params)
|
112
|
+
options = { params: params }
|
113
|
+
response = @client.get("/members/#{guid}/favorites/playlists", options)
|
114
|
+
Playlist.collection(data: response['playlists'], client: @client)
|
115
|
+
end
|
116
|
+
|
117
|
+
def request_chart(guid, params)
|
118
|
+
options = { params: params }
|
119
|
+
response = @client.get("/members/#{guid}/charts", options)
|
120
|
+
Chart.collection(data: response['charts'], client: @client)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,279 @@
|
|
1
|
+
using StringHelper
|
2
|
+
|
3
|
+
module Napster
|
4
|
+
module Models
|
5
|
+
# Playlist model
|
6
|
+
class Playlist
|
7
|
+
ATTRIBUTES = [:type,
|
8
|
+
:id,
|
9
|
+
:name,
|
10
|
+
:modified,
|
11
|
+
:href,
|
12
|
+
:privacy,
|
13
|
+
:images,
|
14
|
+
:description,
|
15
|
+
:favorite_count,
|
16
|
+
:free_play_compliant].freeze
|
17
|
+
|
18
|
+
ATTRIBUTES.each do |attribute|
|
19
|
+
attr_accessor attribute
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_accessor :client
|
23
|
+
|
24
|
+
def initialize(arg)
|
25
|
+
@client = arg[:client] if arg[:client]
|
26
|
+
return unless arg[:data]
|
27
|
+
|
28
|
+
ATTRIBUTES.each do |attribute|
|
29
|
+
send("#{attribute}=", arg[:data][attribute.to_s.camel_case_lower])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.collection(arg)
|
34
|
+
arg[:data].map do |playlist|
|
35
|
+
Playlist.new(data: playlist, client: @client)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Top level methods
|
40
|
+
|
41
|
+
def playlists_of_the_day(params)
|
42
|
+
response = @client.get('/playlists', params: params)
|
43
|
+
Playlist.collection(data: response['playlists'], client: @client)
|
44
|
+
end
|
45
|
+
|
46
|
+
def featured(params)
|
47
|
+
response = @client.get('/playlists/featured', params: params)
|
48
|
+
Playlist.collection(data: response['playlists'], client: @client)
|
49
|
+
end
|
50
|
+
|
51
|
+
def find(id)
|
52
|
+
e = 'Invalid playlist id'
|
53
|
+
raise ArgumentError, e unless Napster::Moniker.check(id, :playlist)
|
54
|
+
|
55
|
+
return authenticated_find(id) if @client.access_token
|
56
|
+
|
57
|
+
response = @client.get("/playlists/#{id}")
|
58
|
+
Playlist.new(data: response['playlists'].first, client: @client)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Instance methods
|
62
|
+
|
63
|
+
def tracks(params)
|
64
|
+
return authenticated_tracks(params) if @client.access_token
|
65
|
+
|
66
|
+
hash = { params: params }
|
67
|
+
response = @client.get("/playlists/#{@id}/tracks", hash)
|
68
|
+
Track.collection(data: response['tracks'], client: @client)
|
69
|
+
end
|
70
|
+
|
71
|
+
def tags
|
72
|
+
return authenticated_tags if @client.access_token
|
73
|
+
|
74
|
+
response = @client.get("/playlists/#{@id}/tags")
|
75
|
+
Tag.collection(data: response['tags'], client: @client)
|
76
|
+
end
|
77
|
+
|
78
|
+
# /me
|
79
|
+
|
80
|
+
def all(params)
|
81
|
+
get_options = {
|
82
|
+
params: params,
|
83
|
+
headers: {
|
84
|
+
Authorization: 'Bearer ' + @client.access_token,
|
85
|
+
'Content-Type' => 'application/json',
|
86
|
+
'Accept-Version' => '2.0.0'
|
87
|
+
}
|
88
|
+
}
|
89
|
+
response = @client.get('/me/library/playlists', get_options)
|
90
|
+
Playlist.collection(data: response['playlists'])
|
91
|
+
end
|
92
|
+
|
93
|
+
def authenticated_find(playlist_id)
|
94
|
+
path = "/me/library/playlists/#{playlist_id}"
|
95
|
+
get_options = {
|
96
|
+
headers: {
|
97
|
+
Authorization: 'Bearer ' + @client.access_token,
|
98
|
+
'Content-Type' => 'application/json',
|
99
|
+
'Accept-Version' => '2.0.0'
|
100
|
+
}
|
101
|
+
}
|
102
|
+
response = @client.get(path, get_options)
|
103
|
+
return nil if response['playlists'].empty?
|
104
|
+
|
105
|
+
Playlist.new(data: response['playlists'].first, client: @client)
|
106
|
+
end
|
107
|
+
|
108
|
+
def authenticated_tracks(params)
|
109
|
+
path = "/me/library/playlists/#{@id}/tracks"
|
110
|
+
options = {
|
111
|
+
params: params,
|
112
|
+
headers: {
|
113
|
+
Authorization: 'Bearer ' + @client.access_token,
|
114
|
+
'Content-Type' => 'application/json',
|
115
|
+
'Accept-Version' => '2.0.0'
|
116
|
+
}
|
117
|
+
}
|
118
|
+
response = @client.get(path, options)
|
119
|
+
return [] if response['tracks'].empty?
|
120
|
+
|
121
|
+
Track.collection(data: response['tracks'], client: @client)
|
122
|
+
end
|
123
|
+
|
124
|
+
def create(playlist_hash)
|
125
|
+
body = Oj.dump('playlists' => playlist_hash)
|
126
|
+
path = '/me/library/playlists'
|
127
|
+
options = {
|
128
|
+
headers: {
|
129
|
+
Authorization: 'Bearer ' + @client.access_token,
|
130
|
+
'Content-Type' => 'application/json',
|
131
|
+
'Accept-Version' => '2.0.0'
|
132
|
+
}
|
133
|
+
}
|
134
|
+
response = @client.post(path, body, options)
|
135
|
+
Playlist.new(data: response['playlists'].first, client: @client)
|
136
|
+
end
|
137
|
+
|
138
|
+
def update(playlist_id, playlist_hash)
|
139
|
+
body = Oj.dump('playlists' => playlist_hash)
|
140
|
+
path = "/me/library/playlists/#{playlist_id}"
|
141
|
+
options = {
|
142
|
+
headers: {
|
143
|
+
Authorization: 'Bearer ' + @client.access_token,
|
144
|
+
'Content-Type' => 'application/json',
|
145
|
+
'Accept-Version' => '2.0.0'
|
146
|
+
}
|
147
|
+
}
|
148
|
+
response = @client.put(path, body, options)
|
149
|
+
Playlist.new(data: response['playlists'].first, client: @client)
|
150
|
+
end
|
151
|
+
|
152
|
+
def delete(playlist_id)
|
153
|
+
path = "/me/library/playlists/#{playlist_id}"
|
154
|
+
options = {
|
155
|
+
headers: {
|
156
|
+
Authorization: 'Bearer ' + @client.access_token,
|
157
|
+
'Content-Type' => 'application/json',
|
158
|
+
'Accept-Version' => '2.0.0'
|
159
|
+
}
|
160
|
+
}
|
161
|
+
@client.delete(path, options)
|
162
|
+
end
|
163
|
+
|
164
|
+
def set_private(playlist_id, boolean)
|
165
|
+
e = 'The argument should be a boolean value.'
|
166
|
+
raise ArgumentError, e unless [true, false].include?(boolean)
|
167
|
+
|
168
|
+
privacy_value = boolean ? 'private' : 'public'
|
169
|
+
body = Oj.dump('privacy' => privacy_value)
|
170
|
+
path = "/me/library/playlists/#{playlist_id}/privacy"
|
171
|
+
options = {
|
172
|
+
headers: {
|
173
|
+
Authorization: 'Bearer ' + @client.access_token,
|
174
|
+
'Content-Type' => 'application/json',
|
175
|
+
'Accept-Version' => '2.0.0'
|
176
|
+
}
|
177
|
+
}
|
178
|
+
@client.put(path, body, options)
|
179
|
+
end
|
180
|
+
|
181
|
+
def add_tracks(playlist_id, tracks)
|
182
|
+
tracks = tracks.map { |track| { 'id' => track } }
|
183
|
+
body = Oj.dump('tracks' => tracks)
|
184
|
+
path = "/me/library/playlists/#{playlist_id}/tracks"
|
185
|
+
options = {
|
186
|
+
headers: {
|
187
|
+
Authorization: 'Bearer ' + @client.access_token,
|
188
|
+
'Content-Type' => 'application/json',
|
189
|
+
'Accept-Version' => '2.0.0'
|
190
|
+
}
|
191
|
+
}
|
192
|
+
@client.post(path, body, options)
|
193
|
+
end
|
194
|
+
|
195
|
+
def authenticated_tags
|
196
|
+
return [] if @id
|
197
|
+
path = "/me/library/playlists/#{playlist_id}/tags"
|
198
|
+
options = {
|
199
|
+
headers: {
|
200
|
+
Authorization: 'Bearer ' + @client.access_token,
|
201
|
+
'Content-Type' => 'application/json',
|
202
|
+
'Accept-Version' => '2.0.0'
|
203
|
+
}
|
204
|
+
}
|
205
|
+
response = @client.get(path, options)
|
206
|
+
return [] if response['tags']
|
207
|
+
Tag.collection(data: response['tags'], client: @client)
|
208
|
+
end
|
209
|
+
|
210
|
+
def recommended_tracks(playlist_id)
|
211
|
+
options = {
|
212
|
+
params: { playlistId: playlist_id },
|
213
|
+
headers: {
|
214
|
+
Authorization: 'Bearer ' + @client.access_token,
|
215
|
+
'Content-Type' => 'application/json',
|
216
|
+
'Accept-Version' => '2.0.0'
|
217
|
+
}
|
218
|
+
}
|
219
|
+
response = @client.get('/me/recommendations/tracks', options)
|
220
|
+
Track.collection(data: response['tracks'], client: @client)
|
221
|
+
end
|
222
|
+
|
223
|
+
def uploaded_images(options)
|
224
|
+
return uploaded_images_with_size(options) if options.class == Fixnum
|
225
|
+
e = 'Playlist ID is missing.'
|
226
|
+
playlist_id = options[:id] ? options[:id] : @id
|
227
|
+
raise ArgumentError, e unless playlist_id
|
228
|
+
|
229
|
+
path = "/me/library/playlists/#{playlist_id}/images"
|
230
|
+
options = {
|
231
|
+
headers: {
|
232
|
+
Authorization: 'Bearer ' + @client.access_token,
|
233
|
+
'Content-Type' => 'application/json',
|
234
|
+
'Accept-Version' => '2.0.0'
|
235
|
+
}
|
236
|
+
}
|
237
|
+
options[:params] = { size: options[:size] } if options[:size]
|
238
|
+
response = @client.get(path, options)
|
239
|
+
UploadedImage.collection(data: response['images'], client: @client)
|
240
|
+
end
|
241
|
+
|
242
|
+
def sourced_by(sourced, params)
|
243
|
+
sourced_error = 'sourced argument should be a string'
|
244
|
+
params_error = 'params argument should be a hash'
|
245
|
+
raise ArgumentError, sourced_error unless sourced.class == String
|
246
|
+
raise ArgumentError, params_error unless params.class == Hash
|
247
|
+
|
248
|
+
path = '/me/search/playlists'
|
249
|
+
options = {
|
250
|
+
params: params,
|
251
|
+
headers: {
|
252
|
+
Authorization: 'Bearer ' + @client.access_token,
|
253
|
+
'Content-Type' => 'application/json',
|
254
|
+
'Accept-Version' => '2.0.0'
|
255
|
+
}
|
256
|
+
}
|
257
|
+
options[:params][:source] = sourced
|
258
|
+
response = @client.get(path, options)
|
259
|
+
Playlist.collection(data: response['playlists'], client: @client)
|
260
|
+
end
|
261
|
+
|
262
|
+
private
|
263
|
+
|
264
|
+
def uploaded_images_with_size(size)
|
265
|
+
path = "/me/library/playlists/#{@id}/images"
|
266
|
+
options = {
|
267
|
+
params: { size: size },
|
268
|
+
headers: {
|
269
|
+
Authorization: 'Bearer ' + @client.access_token,
|
270
|
+
'Content-Type' => 'application/json',
|
271
|
+
'Accept-Version' => '2.0.0'
|
272
|
+
}
|
273
|
+
}
|
274
|
+
response = @client.get(path, options)
|
275
|
+
UploadedImage.collection(data: response['images'], client: @client)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
using StringHelper
|
2
|
+
|
3
|
+
module Napster
|
4
|
+
module Models
|
5
|
+
# Profile model
|
6
|
+
class Profile
|
7
|
+
ATTRIBUTES = [:id,
|
8
|
+
:real_name,
|
9
|
+
:screen_name,
|
10
|
+
:bio,
|
11
|
+
:location,
|
12
|
+
:gender,
|
13
|
+
:visibility,
|
14
|
+
:type,
|
15
|
+
:href,
|
16
|
+
:favorite_albums_count,
|
17
|
+
:favorite_artists_count,
|
18
|
+
:favorite_tracks_count,
|
19
|
+
:playlists_total_count,
|
20
|
+
:playlists_published_count,
|
21
|
+
:stations_count,
|
22
|
+
:radio_count,
|
23
|
+
:follower_count,
|
24
|
+
:following_count,
|
25
|
+
:avatar,
|
26
|
+
:avatar_id,
|
27
|
+
:default_avatar,
|
28
|
+
:avatar_version,
|
29
|
+
:links].freeze
|
30
|
+
|
31
|
+
ATTRIBUTES.each do |attribute|
|
32
|
+
attr_accessor attribute
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_accessor :client
|
36
|
+
|
37
|
+
def initialize(arg)
|
38
|
+
@client = arg[:client] if arg[:client]
|
39
|
+
return unless arg[:data]
|
40
|
+
ATTRIBUTES.each do |attribute|
|
41
|
+
send("#{attribute}=", arg[:data][attribute.to_s.camel_case_lower])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.collection(arg)
|
46
|
+
arg[:data].map do |profile|
|
47
|
+
Profile.new(data: profile, client: @client)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def get
|
52
|
+
get_options = {
|
53
|
+
headers: {
|
54
|
+
Authorization: 'Bearer ' + @client.access_token,
|
55
|
+
'Content-Type' => 'application/json',
|
56
|
+
'Accept-Version' => '2.0.0'
|
57
|
+
}
|
58
|
+
}
|
59
|
+
response = @client.get('/me', get_options)
|
60
|
+
Profile.new(data: response['me'], client: @client)
|
61
|
+
end
|
62
|
+
|
63
|
+
def update(body)
|
64
|
+
put_options = {
|
65
|
+
headers: {
|
66
|
+
Authorization: 'Bearer ' + @client.access_token,
|
67
|
+
'Content-Type' => 'application/json',
|
68
|
+
'Accept-Version' => '2.0.0'
|
69
|
+
}
|
70
|
+
}
|
71
|
+
@client.put('/me', Oj.dump(body), put_options)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|