notu 2.0.6 → 4.0.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/VERSION +1 -1
- data/lib/notu/api.rb +25 -0
- data/lib/notu/http_download.rb +4 -4
- data/lib/notu/json_document.rb +11 -0
- data/lib/notu/library.rb +8 -37
- data/lib/notu/loved_tracks.rb +20 -12
- data/lib/notu/most_played_tracks.rb +23 -30
- data/lib/notu/recent_tracks.rb +34 -0
- data/lib/notu.rb +3 -5
- data/notu.gemspec +7 -5
- metadata +70 -31
- data/lib/notu/html_document.rb +0 -19
- data/lib/notu/listing.rb +0 -32
- data/lib/notu/played_tracks.rb +0 -26
- data/lib/notu/unknown_username_error.rb +0 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: efbddd2bb568278da60c542fd1046d39a61d200bf1ca1a20614e7135ac07e66f
|
|
4
|
+
data.tar.gz: 4c14831c8b1526a402cf47870e64d17fb5e3ffa4b04d0f2ecaefb30d493de4e3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 60c8869371be7e6b80a4b8f8b26ebfa2406f63cb576d7788cf6aeacb6d963c3f4d534455a5ae33bca7ea0a33236a0bb6ab529980fc3697ba302062e764f96d0c
|
|
7
|
+
data.tar.gz: 8343035fb3d9a5ce7e9bba05676f13b0265ace6f514e2e492aa5280eeae0ddc25990eb553adf39f698f66767f4da2d5a5d55523e6b04d2fef428cb47129ece3b
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.0
|
data/lib/notu/api.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Notu
|
|
2
|
+
|
|
3
|
+
class Api
|
|
4
|
+
|
|
5
|
+
DEFAULT_KEY = '91f5d6a201de58e0c0a0d858573dddf0'.freeze
|
|
6
|
+
FORMAT = 'json'.freeze
|
|
7
|
+
HOST = 'ws.audioscrobbler.com'.freeze
|
|
8
|
+
VERSION = '2.0'.freeze
|
|
9
|
+
|
|
10
|
+
attr_reader :key
|
|
11
|
+
|
|
12
|
+
def initialize(key: DEFAULT_KEY)
|
|
13
|
+
@key = key.try(:squish).presence || raise(Error.new('API key must be specified'))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def url(params = {})
|
|
17
|
+
params = (params || {}).symbolize_keys
|
|
18
|
+
params.merge!(api_key: key, format: FORMAT)
|
|
19
|
+
query_string = params.map { |name, value| "#{CGI.escape(name.to_s)}=#{CGI.escape(value.to_s)}" }.join('&')
|
|
20
|
+
"https://#{HOST}/#{VERSION}?#{query_string}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
data/lib/notu/http_download.rb
CHANGED
|
@@ -26,13 +26,13 @@ module Notu
|
|
|
26
26
|
return response.body
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
|
-
rescue
|
|
30
|
-
raise NetworkError.new(
|
|
29
|
+
rescue Timeout::Error, Zlib::BufError => e
|
|
30
|
+
raise NetworkError.new(e) if options[:max_retries] < 1
|
|
31
31
|
options[:max_retries] -= 1
|
|
32
32
|
sleep(options[:retry_sleep])
|
|
33
33
|
get(url, options)
|
|
34
|
-
rescue =>
|
|
35
|
-
raise NetworkError.new(
|
|
34
|
+
rescue => e
|
|
35
|
+
raise NetworkError.new(e)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
end
|
data/lib/notu/library.rb
CHANGED
|
@@ -2,14 +2,11 @@ module Notu
|
|
|
2
2
|
|
|
3
3
|
class Library
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
attr_reader :api, :username
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@semaphore = Mutex.new
|
|
11
|
-
options = options.symbolize_keys
|
|
12
|
-
self.username = options[:username]
|
|
7
|
+
def initialize(username:, api: Api.new)
|
|
8
|
+
@api = api.presence || raise(Error.new('API must be specified'))
|
|
9
|
+
@username = username.try(:squish).presence || raise(Error.new('Username must be specified'))
|
|
13
10
|
end
|
|
14
11
|
|
|
15
12
|
def loved_tracks
|
|
@@ -20,38 +17,12 @@ module Notu
|
|
|
20
17
|
MostPlayedTracks.new(self, options)
|
|
21
18
|
end
|
|
22
19
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def url(options = {})
|
|
28
|
-
options = options.symbolize_keys
|
|
29
|
-
path = options[:path].presence
|
|
30
|
-
query = options[:query].presence
|
|
31
|
-
query = options[:query].map { |name, value| "#{CGI.escape(name.to_s)}=#{CGI.escape(value.to_s)}" }.join('&') if options[:query].is_a?(Hash)
|
|
32
|
-
"https://#{HOST}/user/#{username}".tap do |url|
|
|
33
|
-
if path.present?
|
|
34
|
-
url << '/' unless path.starts_with?('/')
|
|
35
|
-
url << path
|
|
36
|
-
end
|
|
37
|
-
if query.present?
|
|
38
|
-
url << '?' << query
|
|
39
|
-
end
|
|
40
|
-
end
|
|
20
|
+
def recent_tracks
|
|
21
|
+
RecentTracks.new(self)
|
|
41
22
|
end
|
|
42
23
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def username=(value)
|
|
46
|
-
@semaphore.synchronize do
|
|
47
|
-
@username = value.to_s.strip.downcase
|
|
48
|
-
raise UnknownUsernameError.new(value) if username !~ /^[a-z0-9_]+$/
|
|
49
|
-
begin
|
|
50
|
-
HtmlDocument.get(url)
|
|
51
|
-
rescue
|
|
52
|
-
raise UnknownUsernameError.new(value)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
24
|
+
def url(params = {})
|
|
25
|
+
api.url((params || {}).symbolize_keys.merge(user: username))
|
|
55
26
|
end
|
|
56
27
|
|
|
57
28
|
end
|
data/lib/notu/loved_tracks.rb
CHANGED
|
@@ -2,25 +2,33 @@ module Notu
|
|
|
2
2
|
|
|
3
3
|
class LovedTracks
|
|
4
4
|
|
|
5
|
-
include
|
|
5
|
+
include Enumerable
|
|
6
6
|
|
|
7
|
-
|
|
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
|
|
13
|
+
|
|
14
|
+
def each
|
|
8
15
|
return unless block_given?
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
pages_count = nil
|
|
17
|
+
page = 1
|
|
18
|
+
loop do
|
|
19
|
+
json = JsonDocument.get(library.url(limit: 50, method: 'user.getLovedTracks', page:))
|
|
20
|
+
pages_count = json['lovedtracks']['@attr']['totalPages'].to_i
|
|
21
|
+
json['lovedtracks']['track'].each do |track_json|
|
|
22
|
+
artist = track_json['artist']['name'] || next
|
|
23
|
+
title = track_json['name'] || next
|
|
24
|
+
yield(Track.new(artist:, title:))
|
|
15
25
|
end
|
|
26
|
+
page += 1
|
|
27
|
+
break if page > pages_count
|
|
16
28
|
end
|
|
17
29
|
nil
|
|
18
30
|
end
|
|
19
31
|
|
|
20
|
-
def path
|
|
21
|
-
'loved'
|
|
22
|
-
end
|
|
23
|
-
|
|
24
32
|
end
|
|
25
33
|
|
|
26
34
|
end
|
|
@@ -2,51 +2,44 @@ module Notu
|
|
|
2
2
|
|
|
3
3
|
class MostPlayedTracks
|
|
4
4
|
|
|
5
|
-
include
|
|
5
|
+
include Enumerable
|
|
6
6
|
|
|
7
|
-
PERIODS =
|
|
8
|
-
'7 days' => 'LAST_7_DAYS',
|
|
9
|
-
'30 days' => 'LAST_30_DAYS',
|
|
10
|
-
'90 days' => 'LAST_90_DAYS',
|
|
11
|
-
'365 days' => 'LAST_365_DAYS',
|
|
12
|
-
'Overall' => '',
|
|
13
|
-
}
|
|
7
|
+
PERIODS = %w(overall 7day 1month 3month 6month 12month).freeze
|
|
14
8
|
|
|
15
|
-
attr_reader :period
|
|
9
|
+
attr_reader :library, :period
|
|
16
10
|
|
|
17
11
|
def initialize(library, options = {})
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
raise ArgumentError.new("#{self.class}#library must be a library, #{library.inspect} given") unless library.is_a?(Library)
|
|
13
|
+
@library = library
|
|
14
|
+
options = options.symbolize_keys.reverse_merge(period: PERIODS.first)
|
|
15
|
+
self.period = options[:period]
|
|
21
16
|
end
|
|
22
17
|
|
|
23
|
-
def each
|
|
18
|
+
def each
|
|
24
19
|
return unless block_given?
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
pages_count = nil
|
|
21
|
+
page = 1
|
|
22
|
+
loop do
|
|
23
|
+
json = JsonDocument.get(library.url(limit: 50, method: 'user.getTopTracks', page:))
|
|
24
|
+
pages_count = json['toptracks']['@attr']['totalPages'].to_i
|
|
25
|
+
json['toptracks']['track'].each do |track_json|
|
|
26
|
+
artist = track_json['artist']['name'] || next
|
|
27
|
+
title = track_json['name'] || next
|
|
28
|
+
plays_count = track_json['playcount'] || next
|
|
29
|
+
yield(Track.new(artist:, plays_count:, title:))
|
|
32
30
|
end
|
|
31
|
+
page += 1
|
|
32
|
+
break if page > pages_count
|
|
33
33
|
end
|
|
34
34
|
nil
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def params
|
|
38
|
-
{ 'date_preset' => PERIODS[period] }
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def path
|
|
42
|
-
'library/tracks'
|
|
43
|
-
end
|
|
44
|
-
|
|
45
37
|
private
|
|
46
38
|
|
|
47
39
|
def period=(value)
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
string_value = value.to_s
|
|
41
|
+
raise ArgumentError.new("Notu::MostPlayedTracks#period is invalid: #{value.inspect}") unless PERIODS.include?(string_value)
|
|
42
|
+
@period = string_value
|
|
50
43
|
end
|
|
51
44
|
|
|
52
45
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Notu
|
|
2
|
+
|
|
3
|
+
class RecentTracks
|
|
4
|
+
|
|
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
|
|
13
|
+
|
|
14
|
+
def each
|
|
15
|
+
return unless block_given?
|
|
16
|
+
pages_count = nil
|
|
17
|
+
page = 1
|
|
18
|
+
loop do
|
|
19
|
+
json = JsonDocument.get(library.url(limit: 50, method: 'user.getRecentTracks', page:))
|
|
20
|
+
pages_count = json['recenttracks']['@attr']['totalPages'].to_i
|
|
21
|
+
json['recenttracks']['track'].each do |track_json|
|
|
22
|
+
artist = track_json['artist']['#text'] || next
|
|
23
|
+
title = track_json['name'] || next
|
|
24
|
+
yield(Track.new(artist:, title:))
|
|
25
|
+
end
|
|
26
|
+
page += 1
|
|
27
|
+
break if page > pages_count
|
|
28
|
+
end
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
data/lib/notu.rb
CHANGED
|
@@ -3,7 +3,6 @@ require 'active_support/core_ext'
|
|
|
3
3
|
require 'byebug' if ENV['DEBUGGER']
|
|
4
4
|
require 'cgi'
|
|
5
5
|
require 'net/https'
|
|
6
|
-
require 'nokogiri'
|
|
7
6
|
|
|
8
7
|
lib_path = "#{__dir__}/notu"
|
|
9
8
|
|
|
@@ -18,12 +17,11 @@ require "#{lib_path}/error"
|
|
|
18
17
|
require "#{lib_path}/network_error"
|
|
19
18
|
require "#{lib_path}/parse_error"
|
|
20
19
|
|
|
21
|
-
require "#{lib_path}/
|
|
20
|
+
require "#{lib_path}/api"
|
|
22
21
|
require "#{lib_path}/http_download"
|
|
22
|
+
require "#{lib_path}/json_document"
|
|
23
23
|
require "#{lib_path}/library"
|
|
24
|
-
require "#{lib_path}/listing"
|
|
25
24
|
require "#{lib_path}/loved_tracks"
|
|
26
25
|
require "#{lib_path}/most_played_tracks"
|
|
27
|
-
require "#{lib_path}/
|
|
26
|
+
require "#{lib_path}/recent_tracks"
|
|
28
27
|
require "#{lib_path}/track"
|
|
29
|
-
require "#{lib_path}/unknown_username_error"
|
data/notu.gemspec
CHANGED
|
@@ -9,18 +9,20 @@ Gem::Specification.new do |s|
|
|
|
9
9
|
s.description = 'API to get Last.fm tracks (most played, loved, etc.)'
|
|
10
10
|
s.license = 'MIT'
|
|
11
11
|
|
|
12
|
-
s.files =
|
|
13
|
-
s.executables =
|
|
12
|
+
s.files = %x(git ls-files | grep -vE '^(spec/|test/|\\.|Gemfile|Rakefile)').split("\n")
|
|
13
|
+
s.executables = %x(git ls-files -- bin/*).split("\n").map { |f| File.basename(f) }
|
|
14
14
|
s.require_paths = ['lib']
|
|
15
15
|
|
|
16
|
-
s.required_ruby_version = '>=
|
|
16
|
+
s.required_ruby_version = '>= 3.1.0'
|
|
17
17
|
|
|
18
|
-
s.add_dependency 'activesupport', '>=
|
|
19
|
-
s.add_dependency 'nokogiri', '>= 1.6.0', '< 1.12.0'
|
|
18
|
+
s.add_dependency 'activesupport', '>= 7.0.0', '< 8.0.0'
|
|
20
19
|
|
|
21
20
|
s.add_development_dependency 'byebug', '>= 3.2.0', '< 12.0.0'
|
|
22
21
|
s.add_development_dependency 'rake', '>= 10.3.0', '< 14.0.0'
|
|
23
22
|
s.add_development_dependency 'rspec', '>= 3.1.0', '< 4.0.0'
|
|
23
|
+
s.add_development_dependency 'rubocop', '>= 1.25.0', '< 2.0.0'
|
|
24
|
+
s.add_development_dependency 'rubocop-rake', '>= 0.6.0', '< 1.0.0'
|
|
25
|
+
s.add_development_dependency 'rubocop-rspec', '>= 2.8.0', '< 3.0.0'
|
|
24
26
|
s.add_development_dependency 'vcr', '>= 4.0.0', '< 7.0.0'
|
|
25
27
|
s.add_development_dependency 'webmock', '>= 3.0.0', '< 4.0.0'
|
|
26
28
|
end
|
metadata
CHANGED
|
@@ -1,55 +1,35 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: notu
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 4.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexis Toulotte
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-01-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: 4.1.0
|
|
20
|
-
- - "<"
|
|
21
|
-
- !ruby/object:Gem::Version
|
|
22
|
-
version: 7.0.0
|
|
23
|
-
type: :runtime
|
|
24
|
-
prerelease: false
|
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
-
requirements:
|
|
27
|
-
- - ">="
|
|
28
|
-
- !ruby/object:Gem::Version
|
|
29
|
-
version: 4.1.0
|
|
30
|
-
- - "<"
|
|
31
18
|
- !ruby/object:Gem::Version
|
|
32
19
|
version: 7.0.0
|
|
33
|
-
- !ruby/object:Gem::Dependency
|
|
34
|
-
name: nokogiri
|
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - ">="
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: 1.6.0
|
|
40
20
|
- - "<"
|
|
41
21
|
- !ruby/object:Gem::Version
|
|
42
|
-
version:
|
|
22
|
+
version: 8.0.0
|
|
43
23
|
type: :runtime
|
|
44
24
|
prerelease: false
|
|
45
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
46
26
|
requirements:
|
|
47
27
|
- - ">="
|
|
48
28
|
- !ruby/object:Gem::Version
|
|
49
|
-
version:
|
|
29
|
+
version: 7.0.0
|
|
50
30
|
- - "<"
|
|
51
31
|
- !ruby/object:Gem::Version
|
|
52
|
-
version:
|
|
32
|
+
version: 8.0.0
|
|
53
33
|
- !ruby/object:Gem::Dependency
|
|
54
34
|
name: byebug
|
|
55
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -110,6 +90,66 @@ dependencies:
|
|
|
110
90
|
- - "<"
|
|
111
91
|
- !ruby/object:Gem::Version
|
|
112
92
|
version: 4.0.0
|
|
93
|
+
- !ruby/object:Gem::Dependency
|
|
94
|
+
name: rubocop
|
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
|
96
|
+
requirements:
|
|
97
|
+
- - ">="
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
version: 1.25.0
|
|
100
|
+
- - "<"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 2.0.0
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 1.25.0
|
|
110
|
+
- - "<"
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: 2.0.0
|
|
113
|
+
- !ruby/object:Gem::Dependency
|
|
114
|
+
name: rubocop-rake
|
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
|
116
|
+
requirements:
|
|
117
|
+
- - ">="
|
|
118
|
+
- !ruby/object:Gem::Version
|
|
119
|
+
version: 0.6.0
|
|
120
|
+
- - "<"
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: 1.0.0
|
|
123
|
+
type: :development
|
|
124
|
+
prerelease: false
|
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - ">="
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: 0.6.0
|
|
130
|
+
- - "<"
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: 1.0.0
|
|
133
|
+
- !ruby/object:Gem::Dependency
|
|
134
|
+
name: rubocop-rspec
|
|
135
|
+
requirement: !ruby/object:Gem::Requirement
|
|
136
|
+
requirements:
|
|
137
|
+
- - ">="
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: 2.8.0
|
|
140
|
+
- - "<"
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: 3.0.0
|
|
143
|
+
type: :development
|
|
144
|
+
prerelease: false
|
|
145
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
146
|
+
requirements:
|
|
147
|
+
- - ">="
|
|
148
|
+
- !ruby/object:Gem::Version
|
|
149
|
+
version: 2.8.0
|
|
150
|
+
- - "<"
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: 3.0.0
|
|
113
153
|
- !ruby/object:Gem::Dependency
|
|
114
154
|
name: vcr
|
|
115
155
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -160,18 +200,17 @@ files:
|
|
|
160
200
|
- README.mdown
|
|
161
201
|
- VERSION
|
|
162
202
|
- lib/notu.rb
|
|
203
|
+
- lib/notu/api.rb
|
|
163
204
|
- lib/notu/error.rb
|
|
164
|
-
- lib/notu/html_document.rb
|
|
165
205
|
- lib/notu/http_download.rb
|
|
206
|
+
- lib/notu/json_document.rb
|
|
166
207
|
- lib/notu/library.rb
|
|
167
|
-
- lib/notu/listing.rb
|
|
168
208
|
- lib/notu/loved_tracks.rb
|
|
169
209
|
- lib/notu/most_played_tracks.rb
|
|
170
210
|
- lib/notu/network_error.rb
|
|
171
211
|
- lib/notu/parse_error.rb
|
|
172
|
-
- lib/notu/
|
|
212
|
+
- lib/notu/recent_tracks.rb
|
|
173
213
|
- lib/notu/track.rb
|
|
174
|
-
- lib/notu/unknown_username_error.rb
|
|
175
214
|
- notu.gemspec
|
|
176
215
|
homepage: https://github.com/alexistoulotte/notu
|
|
177
216
|
licenses:
|
|
@@ -185,14 +224,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
185
224
|
requirements:
|
|
186
225
|
- - ">="
|
|
187
226
|
- !ruby/object:Gem::Version
|
|
188
|
-
version:
|
|
227
|
+
version: 3.1.0
|
|
189
228
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
190
229
|
requirements:
|
|
191
230
|
- - ">="
|
|
192
231
|
- !ruby/object:Gem::Version
|
|
193
232
|
version: '0'
|
|
194
233
|
requirements: []
|
|
195
|
-
rubygems_version: 3.
|
|
234
|
+
rubygems_version: 3.3.3
|
|
196
235
|
signing_key:
|
|
197
236
|
specification_version: 4
|
|
198
237
|
summary: API for Last.fm
|
data/lib/notu/html_document.rb
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module Notu
|
|
2
|
-
|
|
3
|
-
module HtmlDocument
|
|
4
|
-
|
|
5
|
-
def self.get(url, options = {})
|
|
6
|
-
parse(HttpDownload.get(url, options))
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def self.parse(data)
|
|
10
|
-
data = data.gsub(/ /i, ' ').gsub(/\s+/, ' ')
|
|
11
|
-
document = Nokogiri::HTML.parse(data, nil, 'UTF-8')
|
|
12
|
-
raise ParseError.new('Invalid HTML document') if (document/'head').empty?
|
|
13
|
-
document
|
|
14
|
-
end
|
|
15
|
-
private_class_method :parse
|
|
16
|
-
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
end
|
data/lib/notu/listing.rb
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
module Notu
|
|
2
|
-
|
|
3
|
-
module Listing
|
|
4
|
-
|
|
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
|
|
13
|
-
|
|
14
|
-
def page_urls
|
|
15
|
-
(1..pages_count).map do |index|
|
|
16
|
-
library.url(path: path, query: params.merge('page' => index))
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def pages_count
|
|
21
|
-
document = HtmlDocument.get(library.url(path: path, query: params))
|
|
22
|
-
[1, (document/'ul.pagination-list li.pagination-page').text.split(/\s+/).map(&:to_i)].flatten.compact.max
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def params
|
|
26
|
-
# to be overriden
|
|
27
|
-
{}
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
end
|
data/lib/notu/played_tracks.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
module Notu
|
|
2
|
-
|
|
3
|
-
class PlayedTracks
|
|
4
|
-
|
|
5
|
-
include Listing
|
|
6
|
-
|
|
7
|
-
def each(&block)
|
|
8
|
-
return unless block_given?
|
|
9
|
-
page_urls.each do |url|
|
|
10
|
-
document = HtmlDocument.get(url)
|
|
11
|
-
(document/'table.chartlist tbody tr').each do |element|
|
|
12
|
-
artist = (element/'td.chartlist-artist a').first.try(:text) || next
|
|
13
|
-
title = (element/'td.chartlist-name a').first.try(:text) || next
|
|
14
|
-
yield(Track.new(artist: artist, title: title))
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
nil
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def path
|
|
21
|
-
'library'
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
end
|