notu 0.1.6 → 0.2.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.
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