notu 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -4
  3. data/README.mdown +12 -8
  4. data/VERSION +1 -1
  5. data/lib/notu.rb +1 -0
  6. data/lib/notu/listing.rb +30 -0
  7. data/lib/notu/loved_tracks.rb +7 -20
  8. data/lib/notu/most_played_tracks.rb +24 -15
  9. data/spec/cassettes/Notu_HtmlDocument/_get/follows_redirects.yml +23 -32
  10. data/spec/cassettes/Notu_HtmlDocument/_get/raise_a_NetworkError_on_404.yml +9 -16
  11. data/spec/cassettes/Notu_HtmlDocument/_get/raise_a_ParseError_if_not_a_valid_document.yml +22 -20
  12. data/spec/cassettes/Notu_HtmlDocument/_get/returns_document_parsed.yml +21 -30
  13. data/spec/cassettes/Notu_HttpDownload/_get/accepts_HTTPS_URL.yml +21 -30
  14. data/spec/cassettes/Notu_HttpDownload/_get/follow_redirects.yml +23 -32
  15. data/spec/cassettes/Notu_HttpDownload/_get/raise_a_NetworkError_if_too_many_redirects.yml +3 -3
  16. data/spec/cassettes/Notu_HttpDownload/_get/raise_a_NetworkError_on_404.yml +9 -16
  17. data/spec/cassettes/Notu_HttpDownload/_get/retrives_document_from_given_URL.yml +21 -30
  18. data/spec/cassettes/Notu_LovedTracks/_each/returns_nil.yml +3906 -2531
  19. data/spec/cassettes/Notu_LovedTracks/_each/returns_some_tracks.yml +7805 -5039
  20. data/spec/cassettes/Notu_LovedTracks/_page_urls/is_correct.yml +7800 -5013
  21. data/spec/cassettes/Notu_LovedTracks/_pages_count/is_correct.yml +3900 -2507
  22. data/spec/cassettes/Notu_MostPlayedTracks/_each/returns_nil.yml +2597 -6489
  23. data/spec/cassettes/Notu_MostPlayedTracks/_each/returns_some_tracks.yml +9435 -13402
  24. data/spec/cassettes/Notu_MostPlayedTracks/_page_urls/is_correct.yml +9613 -0
  25. data/spec/cassettes/Notu_MostPlayedTracks/_pages_count/is_correct.yml +5085 -0
  26. data/spec/notu/library_spec.rb +2 -2
  27. data/spec/notu/loved_tracks_spec.rb +20 -4
  28. data/spec/notu/most_played_tracks_spec.rb +44 -7
  29. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cc3c92dbc057aac0be99593d86f551a78ef79554
4
- data.tar.gz: 5e0f907bcca8347d1908c46057c8ae50239bc653
3
+ metadata.gz: 83c849e6aa154adb3210c2665d5ef2ffc64ca013
4
+ data.tar.gz: 69173c99d6eec9cdb3fd52a671c7c64d80553e86
5
5
  SHA512:
6
- metadata.gz: 4266ff783b146b3a77bcfc727daab611cad9863c5d3e7be53d79e8edb8874b44e6e2fb468aa454b0ef2195efcb3e3f7d40360d83ce5c2012910c61315fd18477
7
- data.tar.gz: 507390a80d95d3bf8d2d8d7c94ff69efc7933096fab9441a89ce284d43ff399dcd42d6d003d158128b491b10f2e4c69506b1b6652452eecfbeb14de6eab19baf
6
+ metadata.gz: 6700b41e5b70ec40928596227c2bded73092ac80620826d0029c09a041b531142366e7a3ced25751b9bf20d894f6835c3c219b84742e8567f20ee817ab4f57f3
7
+ data.tar.gz: 6b82941fd297bb5b1255338cd5d642bbc7c91dfe2a6dd9ad827d18d542d75dd812a9f1203096f64e950bde924685c3f320da2d33532212c3a4083566955f856d
data/.gitignore CHANGED
@@ -1,5 +1,5 @@
1
1
  .DS_Store
2
- .bundle
3
- .ruby-version
4
- Gemfile.lock
5
- pkg/*
2
+ /.bundle/
3
+ /.ruby-version
4
+ /Gemfile.lock
5
+ /pkg/
@@ -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
- gem 'notu'
9
+ ```ruby
10
+ gem 'notu'
11
+ ```
10
12
 
11
13
  Then, just run `bundle install`.
12
14
 
13
15
  ## Example
14
16
 
15
- library = Notu::Library.new(username: 'johndoe')
17
+ ```ruby
18
+ library = Notu::Library.new(username: 'johndoe')
16
19
 
17
- library.loved_tracks.each do |track|
18
- puts track.artist
19
- end
20
+ library.loved_tracks.each do |track|
21
+ puts track.artist
22
+ end
20
23
 
21
- library.most_played_tracks(period: 'last_month').each do |track|
22
- puts "#{track.artist}: #{track.plays_count}"
23
- end
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.6
1
+ 0.2.0
@@ -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"
@@ -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
@@ -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/'#lovedTracks td.subjectCell').each do |element|
19
- yield(Track.new(artist: (element/'a').first.text, title: (element/'a').last.text))
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
- private
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
- 'last_week' => 'week',
9
- 'last_month' => '1month',
10
- 'last_3_months' => '3month',
11
- 'last_6_months' => '6month',
12
- 'last_year' => 'year',
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 :library, :period
16
+ attr_reader :period
17
17
 
18
18
  def initialize(library, options = {})
19
- raise ArgumentError.new("#{self.class}#library must be a library, #{library.inspect} given") unless library.is_a?(Library)
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
- document = HtmlDocument.get(library.url(path: 'charts', query: { 'rangetype' => PERIODS[period], 'subtype' => 'tracks' }))
28
- (document/'table.chart tbody tr').each do |element|
29
- artist = (element/'td.subjectCell a').first.text
30
- plays_count = (element/'td.chartbarCell a span').text.strip
31
- title = (element/'td.subjectCell a').last.text
32
- yield(Track.new(artist: artist, plays_count: plays_count, title: title))
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
- - Thu, 28 Aug 2014 22:42:20 GMT
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: Thu, 28 Aug 2014 22:42:21 GMT
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
- - Thu, 28 Aug 2014 22:42:21 GMT
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
- - '"f24a7f595f7d88d135193e6f203bfbd5"'
76
+ - '"17883192c98ca0216d45df6579fc74e5"'
77
77
  Cache-Control:
78
78
  - public
79
79
  X-Request-Id:
80
- - 005d51c9-30e8-45bc-886a-476b46509307
80
+ - 7755283b-c2d8-4431-94ad-881df833cf2b
81
81
  X-Runtime:
82
- - '0.005857'
82
+ - '0.006922'
83
83
  X-Powered-By:
84
- - Phusion Passenger 4.0.49
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
- - '929'
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-2faf062622c53593fc90ee0ba5667a48.js"></script>
114
- <link href="/assets/application-a374b3c70bad2d98bffceac38d81f6d5.css" media="screen" rel="stylesheet" />
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="https://github.com/alexistoulotte"><span>GitHub</span></a>
126
+ <a href="/github"><span>GitHub</span></a>
127
127
  </li>
128
128
  <li class="rubygems">
129
- <a href="https://rubygems.org/profiles/alexistoulotte"><span>RubyGems</span></a>
129
+ <a href="/rubygems"><span>RubyGems</span></a>
130
130
  </li>
131
131
  <li class="cv">
132
- <a href="https://dl.dropbox.com/u/5601946/CV.pdf"><span>CV</span></a>
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="https://twitter.com/alexistoulotte"><span>Twitter</span></a>
144
+ <a href="/twitter"><span>Twitter</span></a>
145
145
  </li>
146
146
  <li class="facebook">
147
- <a href="https://www.facebook.com/alexis.toulotte"><span>Facebook</span></a>
147
+ <a href="/facebook"><span>Facebook</span></a>
148
148
  </li>
149
- <li class="linked-in">
150
- <a href="https://www.linkedin.com/in/alexistoulotte"><span>LinkedIn</span></a>
149
+ <li class="linkedin">
150
+ <a href="/linkedin"><span>LinkedIn</span></a>
151
151
  </li>
152
152
  <li class="google-plus">
153
- <a href="https://plus.google.com/111956742901852825014/posts"><span>Google+</span></a>
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="https://www.mixcloud.com/alexistoulotte"><span>Mixcloud</span></a>
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="delicious">
168
- <a href="https://delicious.com/alexistoulotte"><span>Delicious</span></a>
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: Thu, 28 Aug 2014 22:42:21 GMT
178
- recorded_with: VCR 2.9.2
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
- - Thu, 28 Aug 2014 22:42:24 GMT
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
- - b680fd44-8c4c-4057-b22c-137c56a3d2fe
26
+ - cd80c4e1-b689-4add-a080-552bc5fb6ca2
27
27
  X-Runtime:
28
- - '0.003575'
28
+ - '0.003299'
29
29
  X-Powered-By:
30
- - Phusion Passenger 4.0.49
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
- - '495'
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 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
42
+ <!DOCTYPE html>
43
43
 
44
- <html xmlns="http://www.w3.org/1999/xhtml">
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: Thu, 28 Aug 2014 22:42:24 GMT
80
- recorded_with: VCR 2.9.2
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
- - Thu, 28 Aug 2014 22:42:23 GMT
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
- - '"f24a7f595f7d88d135193e6f203bfbd5"'
32
+ - '"fcc9c11bad7747937dfc96cd94e35bca"'
33
33
  Cache-Control:
34
34
  - public
35
35
  X-Request-Id:
36
- - e9e38d91-fea7-449b-9ae1-3c5390ef8bdc
36
+ - 8b9c16a1-d4b1-4634-8b62-2781713921f7
37
37
  X-Runtime:
38
- - '0.003488'
38
+ - '0.003552'
39
39
  X-Powered-By:
40
- - Phusion Passenger 4.0.49
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: Thu, 28 Aug 2014 22:42:23 GMT
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
- - Thu, 28 Aug 2014 22:47:23 GMT
88
- Last-Modified:
89
- - Thu, 24 Jan 2013 00:31:59 GMT
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
- - '271336745'
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: Thu, 28 Aug 2014 22:42:23 GMT
369
- recorded_with: VCR 2.9.2
370
+ recorded_at: Wed, 19 Aug 2015 01:03:29 GMT
371
+ recorded_with: VCR 2.9.3