notu 0.1.6 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -4
- data/README.mdown +12 -8
- data/VERSION +1 -1
- data/lib/notu.rb +1 -0
- data/lib/notu/listing.rb +30 -0
- data/lib/notu/loved_tracks.rb +7 -20
- data/lib/notu/most_played_tracks.rb +24 -15
- data/spec/cassettes/Notu_HtmlDocument/_get/follows_redirects.yml +23 -32
- data/spec/cassettes/Notu_HtmlDocument/_get/raise_a_NetworkError_on_404.yml +9 -16
- data/spec/cassettes/Notu_HtmlDocument/_get/raise_a_ParseError_if_not_a_valid_document.yml +22 -20
- data/spec/cassettes/Notu_HtmlDocument/_get/returns_document_parsed.yml +21 -30
- data/spec/cassettes/Notu_HttpDownload/_get/accepts_HTTPS_URL.yml +21 -30
- data/spec/cassettes/Notu_HttpDownload/_get/follow_redirects.yml +23 -32
- data/spec/cassettes/Notu_HttpDownload/_get/raise_a_NetworkError_if_too_many_redirects.yml +3 -3
- data/spec/cassettes/Notu_HttpDownload/_get/raise_a_NetworkError_on_404.yml +9 -16
- data/spec/cassettes/Notu_HttpDownload/_get/retrives_document_from_given_URL.yml +21 -30
- data/spec/cassettes/Notu_LovedTracks/_each/returns_nil.yml +3906 -2531
- data/spec/cassettes/Notu_LovedTracks/_each/returns_some_tracks.yml +7805 -5039
- data/spec/cassettes/Notu_LovedTracks/_page_urls/is_correct.yml +7800 -5013
- data/spec/cassettes/Notu_LovedTracks/_pages_count/is_correct.yml +3900 -2507
- data/spec/cassettes/Notu_MostPlayedTracks/_each/returns_nil.yml +2597 -6489
- data/spec/cassettes/Notu_MostPlayedTracks/_each/returns_some_tracks.yml +9435 -13402
- data/spec/cassettes/Notu_MostPlayedTracks/_page_urls/is_correct.yml +9613 -0
- data/spec/cassettes/Notu_MostPlayedTracks/_pages_count/is_correct.yml +5085 -0
- data/spec/notu/library_spec.rb +2 -2
- data/spec/notu/loved_tracks_spec.rb +20 -4
- data/spec/notu/most_played_tracks_spec.rb +44 -7
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83c849e6aa154adb3210c2665d5ef2ffc64ca013
|
4
|
+
data.tar.gz: 69173c99d6eec9cdb3fd52a671c7c64d80553e86
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6700b41e5b70ec40928596227c2bded73092ac80620826d0029c09a041b531142366e7a3ced25751b9bf20d894f6835c3c219b84742e8567f20ee817ab4f57f3
|
7
|
+
data.tar.gz: 6b82941fd297bb5b1255338cd5d642bbc7c91dfe2a6dd9ad827d18d542d75dd812a9f1203096f64e950bde924685c3f320da2d33532212c3a4083566955f856d
|
data/.gitignore
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
.DS_Store
|
2
|
-
|
3
|
-
|
4
|
-
Gemfile.lock
|
5
|
-
pkg
|
2
|
+
/.bundle/
|
3
|
+
/.ruby-version
|
4
|
+
/Gemfile.lock
|
5
|
+
/pkg/
|
data/README.mdown
CHANGED
@@ -6,21 +6,25 @@ API to get Last.fm most played and loved tracks.
|
|
6
6
|
|
7
7
|
Just add this into your `Gemfile`:
|
8
8
|
|
9
|
-
|
9
|
+
```ruby
|
10
|
+
gem 'notu'
|
11
|
+
```
|
10
12
|
|
11
13
|
Then, just run `bundle install`.
|
12
14
|
|
13
15
|
## Example
|
14
16
|
|
15
|
-
|
17
|
+
```ruby
|
18
|
+
library = Notu::Library.new(username: 'johndoe')
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
library.loved_tracks.each do |track|
|
21
|
+
puts track.artist
|
22
|
+
end
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
library.most_played_tracks(period: 'last_month').each do |track|
|
25
|
+
puts "#{track.artist}: #{track.plays_count}"
|
26
|
+
end
|
27
|
+
```
|
24
28
|
|
25
29
|
## Executing test suite
|
26
30
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/notu.rb
CHANGED
@@ -14,6 +14,7 @@ require "#{lib_path}/parse_error"
|
|
14
14
|
require "#{lib_path}/html_document"
|
15
15
|
require "#{lib_path}/http_download"
|
16
16
|
require "#{lib_path}/library"
|
17
|
+
require "#{lib_path}/listing"
|
17
18
|
require "#{lib_path}/loved_tracks"
|
18
19
|
require "#{lib_path}/most_played_tracks"
|
19
20
|
require "#{lib_path}/track"
|
data/lib/notu/listing.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Notu
|
2
|
+
|
3
|
+
module Listing
|
4
|
+
|
5
|
+
attr_reader :library
|
6
|
+
|
7
|
+
def initialize(library)
|
8
|
+
raise ArgumentError.new("#{self.class}#library must be a library, #{library.inspect} given") unless library.is_a?(Library)
|
9
|
+
@library = library
|
10
|
+
end
|
11
|
+
|
12
|
+
def page_urls
|
13
|
+
(1..pages_count).map do |index|
|
14
|
+
library.url(path: path, query: params.merge('page' => index))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def pages_count
|
19
|
+
document = HtmlDocument.get(library.url(path: path, query: params))
|
20
|
+
[1, (document/'ul.pagination li.pages').text.split(/\s+/).map { |text| text.to_i }].flatten.compact.max
|
21
|
+
end
|
22
|
+
|
23
|
+
def params
|
24
|
+
# to be overriden
|
25
|
+
{}
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/lib/notu/loved_tracks.rb
CHANGED
@@ -3,36 +3,23 @@ module Notu
|
|
3
3
|
class LovedTracks
|
4
4
|
|
5
5
|
include Enumerable
|
6
|
-
|
7
|
-
attr_reader :library
|
8
|
-
|
9
|
-
def initialize(library)
|
10
|
-
raise ArgumentError.new("#{self.class}#library must be a library, #{library.inspect} given") unless library.is_a?(Library)
|
11
|
-
@library = library
|
12
|
-
end
|
6
|
+
include Listing
|
13
7
|
|
14
8
|
def each(&block)
|
15
9
|
return unless block_given?
|
16
10
|
page_urls.each do |url|
|
17
11
|
document = HtmlDocument.get(url)
|
18
|
-
(document/'#
|
19
|
-
|
12
|
+
(document/'#user-loved-tracks-section tbody tr').each do |element|
|
13
|
+
artist = (element/'td.chartlist-name .link-block-target').first.text
|
14
|
+
title = (element/'td.chartlist-name .chartlist-artists').first.text
|
15
|
+
yield(Track.new(artist: artist, title: title))
|
20
16
|
end
|
21
17
|
end
|
22
18
|
nil
|
23
19
|
end
|
24
20
|
|
25
|
-
|
26
|
-
|
27
|
-
def page_urls
|
28
|
-
(1..pages_count).map do |index|
|
29
|
-
library.url(path: 'library/loved', query: { 'sortBy' => 'date', 'sortOrder' => 'desc', 'page' => index })
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def pages_count
|
34
|
-
document = HtmlDocument.get(library.url(path: 'library/loved'))
|
35
|
-
[1, (document/'div.whittle-pagination a').map { |link| link.text.to_i }].flatten.max
|
21
|
+
def path
|
22
|
+
'loved'
|
36
23
|
end
|
37
24
|
|
38
25
|
end
|
@@ -3,37 +3,46 @@ module Notu
|
|
3
3
|
class MostPlayedTracks
|
4
4
|
|
5
5
|
include Enumerable
|
6
|
+
include Listing
|
6
7
|
|
7
8
|
PERIODS = {
|
8
|
-
'
|
9
|
-
'
|
10
|
-
'
|
11
|
-
'
|
12
|
-
'
|
13
|
-
'overall' => 'overall',
|
9
|
+
'7 days' => 'LAST_7_DAYS',
|
10
|
+
'30 days' => 'LAST_30_DAYS',
|
11
|
+
'90 days' => 'LAST_90_DAYS',
|
12
|
+
'365 days' => 'LAST_365_DAYS',
|
13
|
+
'Overall' => '',
|
14
14
|
}
|
15
15
|
|
16
|
-
attr_reader :
|
16
|
+
attr_reader :period
|
17
17
|
|
18
18
|
def initialize(library, options = {})
|
19
|
-
|
20
|
-
@library = library
|
19
|
+
super(library)
|
21
20
|
options = options.stringify_keys.reverse_merge('period' => PERIODS.keys.first)
|
22
21
|
self.period = options['period']
|
23
22
|
end
|
24
23
|
|
25
24
|
def each(&block)
|
26
25
|
return unless block_given?
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
page_urls.each do |url|
|
27
|
+
document = HtmlDocument.get(url)
|
28
|
+
(document/'table.chartlist tbody tr').each do |element|
|
29
|
+
artist = (element/'td.chartlist-name .link-block-target').first.text
|
30
|
+
title = (element/'td.chartlist-name .chartlist-artists').first.text
|
31
|
+
plays_count = (element/'td.chartlist-countbar .countbar-bar-value').text.gsub(/[^\d]/, '')
|
32
|
+
yield(Track.new(artist: artist, plays_count: plays_count, title: title))
|
33
|
+
end
|
33
34
|
end
|
34
35
|
nil
|
35
36
|
end
|
36
37
|
|
38
|
+
def params
|
39
|
+
{ 'date_preset' => PERIODS[period] }
|
40
|
+
end
|
41
|
+
|
42
|
+
def path
|
43
|
+
'library/tracks'
|
44
|
+
end
|
45
|
+
|
37
46
|
private
|
38
47
|
|
39
48
|
def period=(value)
|
@@ -19,7 +19,7 @@ http_interactions:
|
|
19
19
|
message: Moved Permanently
|
20
20
|
headers:
|
21
21
|
Date:
|
22
|
-
-
|
22
|
+
- Wed, 19 Aug 2015 01:03:26 GMT
|
23
23
|
Server:
|
24
24
|
- Apache/2.2.16 (Debian)
|
25
25
|
Location:
|
@@ -43,7 +43,7 @@ http_interactions:
|
|
43
43
|
<address>Apache/2.2.16 (Debian) Server at www.alweb.org Port 80</address>
|
44
44
|
</body></html>
|
45
45
|
http_version:
|
46
|
-
recorded_at:
|
46
|
+
recorded_at: Wed, 19 Aug 2015 01:03:26 GMT
|
47
47
|
- request:
|
48
48
|
method: get
|
49
49
|
uri: http://alweb.org/
|
@@ -63,7 +63,7 @@ http_interactions:
|
|
63
63
|
message: OK
|
64
64
|
headers:
|
65
65
|
Date:
|
66
|
-
-
|
66
|
+
- Wed, 19 Aug 2015 01:03:27 GMT
|
67
67
|
Server:
|
68
68
|
- Apache/2.2.16 (Debian)
|
69
69
|
X-Frame-Options:
|
@@ -73,21 +73,21 @@ http_interactions:
|
|
73
73
|
X-Content-Type-Options:
|
74
74
|
- nosniff
|
75
75
|
Etag:
|
76
|
-
- '"
|
76
|
+
- '"17883192c98ca0216d45df6579fc74e5"'
|
77
77
|
Cache-Control:
|
78
78
|
- public
|
79
79
|
X-Request-Id:
|
80
|
-
-
|
80
|
+
- 7755283b-c2d8-4431-94ad-881df833cf2b
|
81
81
|
X-Runtime:
|
82
|
-
- '0.
|
82
|
+
- '0.006922'
|
83
83
|
X-Powered-By:
|
84
|
-
- Phusion Passenger 4.0.
|
84
|
+
- Phusion Passenger 4.0.53
|
85
85
|
Status:
|
86
86
|
- 200 OK
|
87
87
|
Vary:
|
88
88
|
- Accept-Encoding
|
89
89
|
Content-Length:
|
90
|
-
- '
|
90
|
+
- '787'
|
91
91
|
Content-Type:
|
92
92
|
- text/html; charset=utf-8
|
93
93
|
body:
|
@@ -110,8 +110,8 @@ http_interactions:
|
|
110
110
|
<meta name="DC.creator" content="Alexis Toulotte" />
|
111
111
|
<meta name="DC.description" content="Page personnelle d'Alexis Toulotte." />
|
112
112
|
<meta name="google-site-verification" content="1dodRvC0Zp-iAZilnTeT5PaY4bPKHwSzs9994-3g304" />
|
113
|
-
<script src="/assets/application-
|
114
|
-
<link
|
113
|
+
<script src="/assets/application-eae74a30b96601b52819de1d8f7920d7.js"></script>
|
114
|
+
<link rel="stylesheet" media="screen" href="/assets/application-ccd3cc87d5f63a3e28a2f20ec2ea3a97.css" />
|
115
115
|
<link href="/avatar?size=128" rel="icon" type="image/png" />
|
116
116
|
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Dancing Script" />
|
117
117
|
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Raleway" />
|
@@ -123,13 +123,13 @@ http_interactions:
|
|
123
123
|
|
124
124
|
<ul class="services">
|
125
125
|
<li class="github">
|
126
|
-
<a href="
|
126
|
+
<a href="/github"><span>GitHub</span></a>
|
127
127
|
</li>
|
128
128
|
<li class="rubygems">
|
129
|
-
<a href="
|
129
|
+
<a href="/rubygems"><span>RubyGems</span></a>
|
130
130
|
</li>
|
131
131
|
<li class="cv">
|
132
|
-
<a href="
|
132
|
+
<a href="/cv"><span>CV</span></a>
|
133
133
|
</li>
|
134
134
|
<li class="email">
|
135
135
|
<a href="#"><span>Email</span></a>
|
@@ -141,31 +141,22 @@ http_interactions:
|
|
141
141
|
<a href="skype:alexistoulotte"><span>Skype</span></a>
|
142
142
|
</li>
|
143
143
|
<li class="twitter">
|
144
|
-
<a href="
|
144
|
+
<a href="/twitter"><span>Twitter</span></a>
|
145
145
|
</li>
|
146
146
|
<li class="facebook">
|
147
|
-
<a href="
|
147
|
+
<a href="/facebook"><span>Facebook</span></a>
|
148
148
|
</li>
|
149
|
-
<li class="
|
150
|
-
<a href="
|
149
|
+
<li class="linkedin">
|
150
|
+
<a href="/linkedin"><span>LinkedIn</span></a>
|
151
151
|
</li>
|
152
152
|
<li class="google-plus">
|
153
|
-
<a href="
|
154
|
-
</li>
|
155
|
-
<li class="youtube">
|
156
|
-
<a href="https://www.youtube.com/alexistoulotte"><span>YouTube</span></a>
|
153
|
+
<a href="/google_plus"><span>Google+</span></a>
|
157
154
|
</li>
|
158
155
|
<li class="mixcloud">
|
159
|
-
<a href="
|
160
|
-
</li>
|
161
|
-
<li class="last-fm">
|
162
|
-
<a href="http://www.lastfm.fr/user/alexistoulotte"><span>Last.fm</span></a>
|
163
|
-
</li>
|
164
|
-
<li class="sound-cloud">
|
165
|
-
<a href="https://soundcloud.com/alexistoulotte"><span>SoundCloud</span></a>
|
156
|
+
<a href="/mixcloud"><span>Mixcloud</span></a>
|
166
157
|
</li>
|
167
|
-
<li class="
|
168
|
-
<a href="
|
158
|
+
<li class="lastfm">
|
159
|
+
<a href="/lastfm"><span>Last.fm</span></a>
|
169
160
|
</li>
|
170
161
|
</ul>
|
171
162
|
|
@@ -174,5 +165,5 @@ http_interactions:
|
|
174
165
|
|
175
166
|
</html>
|
176
167
|
http_version:
|
177
|
-
recorded_at:
|
178
|
-
recorded_with: VCR 2.9.
|
168
|
+
recorded_at: Wed, 19 Aug 2015 01:03:27 GMT
|
169
|
+
recorded_with: VCR 2.9.3
|
@@ -19,29 +19,29 @@ http_interactions:
|
|
19
19
|
message: Not Found
|
20
20
|
headers:
|
21
21
|
Date:
|
22
|
-
-
|
22
|
+
- Wed, 19 Aug 2015 01:03:25 GMT
|
23
23
|
Server:
|
24
24
|
- Apache/2.2.16 (Debian)
|
25
25
|
X-Request-Id:
|
26
|
-
-
|
26
|
+
- cd80c4e1-b689-4add-a080-552bc5fb6ca2
|
27
27
|
X-Runtime:
|
28
|
-
- '0.
|
28
|
+
- '0.003299'
|
29
29
|
X-Powered-By:
|
30
|
-
- Phusion Passenger 4.0.
|
30
|
+
- Phusion Passenger 4.0.53
|
31
31
|
Status:
|
32
32
|
- 404 Not Found
|
33
33
|
Vary:
|
34
34
|
- Accept-Encoding
|
35
35
|
Content-Length:
|
36
|
-
- '
|
36
|
+
- '309'
|
37
37
|
Content-Type:
|
38
38
|
- text/html; charset=utf-8
|
39
39
|
body:
|
40
40
|
encoding: UTF-8
|
41
41
|
string: |
|
42
|
-
<!DOCTYPE html
|
42
|
+
<!DOCTYPE html>
|
43
43
|
|
44
|
-
<html
|
44
|
+
<html>
|
45
45
|
|
46
46
|
<head>
|
47
47
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
@@ -63,18 +63,11 @@ http_interactions:
|
|
63
63
|
<p>
|
64
64
|
You may have mistyped the address or the page may have moved.
|
65
65
|
</p>
|
66
|
-
|
67
|
-
<p class="footer">
|
68
|
-
Powered by
|
69
|
-
<a href="http://httpd.apache.org" title="The Apache HTTP Server Project">Apache</a>
|
70
|
-
and
|
71
|
-
<a href="http://debian.org" title="The Universal Operating System">Debian</a>.
|
72
|
-
</p>
|
73
66
|
</div>
|
74
67
|
|
75
68
|
</body>
|
76
69
|
|
77
70
|
</html>
|
78
71
|
http_version:
|
79
|
-
recorded_at:
|
80
|
-
recorded_with: VCR 2.9.
|
72
|
+
recorded_at: Wed, 19 Aug 2015 01:03:25 GMT
|
73
|
+
recorded_with: VCR 2.9.3
|
@@ -19,7 +19,7 @@ http_interactions:
|
|
19
19
|
message: Found
|
20
20
|
headers:
|
21
21
|
Date:
|
22
|
-
-
|
22
|
+
- Wed, 19 Aug 2015 01:03:28 GMT
|
23
23
|
Server:
|
24
24
|
- Apache/2.2.16 (Debian)
|
25
25
|
X-Frame-Options:
|
@@ -29,15 +29,15 @@ http_interactions:
|
|
29
29
|
X-Content-Type-Options:
|
30
30
|
- nosniff
|
31
31
|
Etag:
|
32
|
-
- '"
|
32
|
+
- '"fcc9c11bad7747937dfc96cd94e35bca"'
|
33
33
|
Cache-Control:
|
34
34
|
- public
|
35
35
|
X-Request-Id:
|
36
|
-
-
|
36
|
+
- 8b9c16a1-d4b1-4634-8b62-2781713921f7
|
37
37
|
X-Runtime:
|
38
|
-
- '0.
|
38
|
+
- '0.003552'
|
39
39
|
X-Powered-By:
|
40
|
-
- Phusion Passenger 4.0.
|
40
|
+
- Phusion Passenger 4.0.53
|
41
41
|
Location:
|
42
42
|
- http://www.gravatar.com/avatar/c090d5749bd9877588ac8563e0817042
|
43
43
|
Status:
|
@@ -52,7 +52,7 @@ http_interactions:
|
|
52
52
|
encoding: UTF-8
|
53
53
|
string: <html><body>You are being <a href="http://www.gravatar.com/avatar/c090d5749bd9877588ac8563e0817042">redirected</a>.</body></html>
|
54
54
|
http_version:
|
55
|
-
recorded_at:
|
55
|
+
recorded_at: Wed, 19 Aug 2015 01:03:28 GMT
|
56
56
|
- request:
|
57
57
|
method: get
|
58
58
|
uri: http://www.gravatar.com/avatar/c090d5749bd9877588ac8563e0817042
|
@@ -71,32 +71,34 @@ http_interactions:
|
|
71
71
|
code: 200
|
72
72
|
message: OK
|
73
73
|
headers:
|
74
|
+
Date:
|
75
|
+
- Wed, 19 Aug 2015 00:59:59 GMT
|
76
|
+
Last-Modified:
|
77
|
+
- Thu, 24 Jan 2013 00:31:59 GMT
|
78
|
+
Connection:
|
79
|
+
- Keep-Alive
|
80
|
+
Cache-Control:
|
81
|
+
- max-age=300
|
82
|
+
Age:
|
83
|
+
- '210'
|
74
84
|
Accept-Ranges:
|
75
85
|
- bytes
|
76
86
|
Access-Control-Allow-Origin:
|
77
87
|
- "*"
|
78
|
-
Cache-Control:
|
79
|
-
- max-age=300
|
80
88
|
Content-Disposition:
|
81
89
|
- inline; filename="c090d5749bd9877588ac8563e0817042.png"
|
82
90
|
Content-Type:
|
83
91
|
- image/png
|
84
|
-
Date:
|
85
|
-
- Thu, 28 Aug 2014 22:42:23 GMT
|
86
92
|
Expires:
|
87
|
-
-
|
88
|
-
|
89
|
-
-
|
93
|
+
- Wed, 19 Aug 2015 01:04:59 GMT
|
94
|
+
Link:
|
95
|
+
- <http://www.gravatar.com/avatar/c090d5749bd9877588ac8563e0817042>; rel="canonical"
|
90
96
|
Server:
|
91
97
|
- ECS (syd/EBA1)
|
92
98
|
Source-Age:
|
93
99
|
- '0'
|
94
|
-
Via:
|
95
|
-
- 1.1 varnish
|
96
|
-
X-Cache:
|
97
|
-
- HIT
|
98
100
|
X-Varnish:
|
99
|
-
- '
|
101
|
+
- '429679271'
|
100
102
|
Content-Length:
|
101
103
|
- '11789'
|
102
104
|
body:
|
@@ -365,5 +367,5 @@ http_interactions:
|
|
365
367
|
0nq4U7qFDsaylSsypzHmjP5Aa8QrNs33AowZ7kMMtcj/Kwwzi4jsmgCKY16w
|
366
368
|
HkbsMQmjmC6gSeNGLyXZEkoxlIaZ8f8Dl9VUi97Go/gAAAAASUVORK5CYII=
|
367
369
|
http_version:
|
368
|
-
recorded_at:
|
369
|
-
recorded_with: VCR 2.9.
|
370
|
+
recorded_at: Wed, 19 Aug 2015 01:03:29 GMT
|
371
|
+
recorded_with: VCR 2.9.3
|