scrobbler-ng 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/VERSION.yml +2 -1
- data/lib/scrobbler/album.rb +91 -113
- data/lib/scrobbler/artist.rb +174 -134
- data/lib/scrobbler/auth.rb +12 -0
- data/lib/scrobbler/base.rb +114 -64
- data/lib/scrobbler/basexml.rb +17 -0
- data/lib/scrobbler/event.rb +109 -74
- data/lib/scrobbler/geo.rb +3 -11
- data/lib/scrobbler/helper/image.rb +37 -12
- data/lib/scrobbler/helper/streamable.rb +11 -3
- data/lib/scrobbler/library.rb +49 -75
- data/lib/scrobbler/playlist.rb +74 -32
- data/lib/scrobbler/radio.rb +11 -0
- data/lib/scrobbler/shout.rb +40 -14
- data/lib/scrobbler/tag.rb +19 -2
- data/lib/scrobbler/track.rb +62 -67
- data/lib/scrobbler/user.rb +93 -111
- data/lib/scrobbler.rb +1 -2
- data/tasks/jeweler.rake +10 -6
- data/tasks/metric_fu.rake +27 -0
- data/tasks/reek.rake +13 -0
- data/tasks/roodi.rake +13 -0
- data/tasks/tests.rake +8 -0
- data/test/mocks/library.rb +35 -0
- data/test/mocks/rest.rb +91 -189
- data/test/test_helper.rb +2 -16
- data/test/unit/album_spec.rb +1 -1
- data/test/unit/artist_spec.rb +4 -2
- data/test/unit/event_spec.rb +2 -2
- data/test/unit/library_spec.rb +70 -70
- data/test/unit/playlist_spec.rb +1 -1
- data/test/unit/scrobble_spec.rb +1 -1
- data/test/unit/tag_spec.rb +3 -1
- data/test/unit/track_spec.rb +1 -1
- data/test/unit/user_spec.rb +1 -1
- metadata +38 -6
data/lib/scrobbler/user.rb
CHANGED
@@ -1,98 +1,79 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
# puts "=" * (user.username.length + 16)
|
6
|
-
# user.recent_tracks.each { |t| puts t.name }
|
7
|
-
#
|
8
|
-
# puts
|
9
|
-
# puts
|
10
|
-
#
|
11
|
-
# puts "#{user.username}'s Top Tracks"
|
12
|
-
# puts "=" * (user.username.length + 13)
|
13
|
-
# user.top_tracks.each { |t| puts "(#{t.playcount}) #{t.name}" }
|
14
|
-
#
|
15
|
-
# Which would output something like:
|
16
|
-
#
|
17
|
-
# jnunemaker's Recent Tracks
|
18
|
-
# ==========================
|
19
|
-
# Everything You Want
|
20
|
-
# You're a God
|
21
|
-
# Bitter Sweet Symphony [Original Version]
|
22
|
-
# Lord I Guess I'll Never Know
|
23
|
-
# Country Song
|
24
|
-
# Bitter Sweet Symphony (Radio Edit)
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# jnunemaker's Top Tracks
|
28
|
-
# =======================
|
29
|
-
# (62) Probably Wouldn't Be This Way
|
30
|
-
# (55) Not Ready To Make Nice
|
31
|
-
# (45) Easy Silence
|
32
|
-
# (43) Song 2
|
33
|
-
# (40) Everybody Knows
|
34
|
-
# (39) Before He Cheats
|
35
|
-
# (39) Something's Gotta Give
|
36
|
-
# (38) Hips Don't Lie (featuring Wyclef Jean)
|
37
|
-
# (37) Unwritten
|
38
|
-
# (37) Move Along
|
39
|
-
# (37) Dance, Dance
|
40
|
-
# (36) We Belong Together
|
41
|
-
# (36) Jesus, Take the Wheel
|
42
|
-
# (36) Black Horse and the Cherry Tree (radio version)
|
43
|
-
# (35) Photograph
|
44
|
-
# (35) You're Beautiful
|
45
|
-
# (35) Walk Away
|
46
|
-
# (34) Stickwitu
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path('basexml.rb', File.dirname(__FILE__))
|
4
|
+
|
47
5
|
module Scrobbler
|
48
|
-
class User <
|
6
|
+
class User < BaseXml
|
49
7
|
# Load Helper modules
|
50
8
|
include ImageObjectFuncs
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
data[:url] = child.content if child.name == 'url'
|
61
|
-
data[:weight] = child.content.to_i if child.name == 'weight'
|
62
|
-
data[:match] = child.content if child.name == 'match'
|
63
|
-
data[:realname] = child.content if child.name == 'realname'
|
64
|
-
maybe_image_node(data, child)
|
65
|
-
end
|
66
|
-
User.new(data[:name], data)
|
67
|
-
end
|
9
|
+
|
10
|
+
attr_reader :url, :weight, :match, :realname, :name
|
11
|
+
|
12
|
+
# Alias for User.new(:xml => xml)
|
13
|
+
#
|
14
|
+
# @deprecated
|
15
|
+
def self.new_from_libxml(xml)
|
16
|
+
User.new(:xml => xml)
|
17
|
+
end
|
68
18
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
19
|
+
def find(*args)
|
20
|
+
options = {:include_profile => false}
|
21
|
+
options.merge!(args.pop) if args.last.is_a?(Hash)
|
22
|
+
users = args.flatten.inject([]) { |users, u| users << User.new(u, options); users }
|
23
|
+
users.length == 1 ? users.pop : users
|
75
24
|
end
|
76
25
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
26
|
+
# Load the data for this object out of a XML-Node
|
27
|
+
#
|
28
|
+
# @param [LibXML::XML::Node] node The XML node containing the information
|
29
|
+
# @return [nil]
|
30
|
+
def load_from_xml(node)
|
31
|
+
# Get all information from the root's children nodes
|
32
|
+
node.children.each do |child|
|
33
|
+
case child.name.to_s
|
34
|
+
when 'name'
|
35
|
+
@name = child.content
|
36
|
+
when 'url'
|
37
|
+
@url = child.content
|
38
|
+
when 'weight'
|
39
|
+
@weight = child.content.to_i
|
40
|
+
when 'match'
|
41
|
+
@match = child.content
|
42
|
+
when 'realname'
|
43
|
+
@realname = child.content
|
44
|
+
when 'image'
|
45
|
+
check_image_node(child)
|
46
|
+
when 'text'
|
47
|
+
# ignore, these are only blanks
|
48
|
+
when '#text'
|
49
|
+
# libxml-jruby version of blanks
|
50
|
+
else
|
51
|
+
raise NotImplementedError, "Field '#{child.name}' not known (#{child.content})"
|
52
|
+
end #^ case
|
53
|
+
end #^ do |child|
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(data={})
|
57
|
+
raise ArgumentError unless data.kind_of?(Hash)
|
58
|
+
super(data)
|
59
|
+
data = {:include_info => false}.merge(data)
|
60
|
+
# Load data given as method-parameter
|
61
|
+
load_info() if data.delete(:include_info)
|
62
|
+
populate_data(data)
|
63
|
+
|
64
|
+
raise ArgumentError, "Name is required" if @name.empty?
|
84
65
|
end
|
85
66
|
|
86
67
|
# Get a list of upcoming events that this user is attending.
|
87
68
|
#
|
88
69
|
# Supports ical, ics or rss as its format
|
89
|
-
def events
|
90
|
-
|
70
|
+
def events
|
71
|
+
call('user.getevents', :events, :event, {:user => @name})
|
91
72
|
end
|
92
73
|
|
93
74
|
# Get a list of the user's friends on Last.fm.
|
94
|
-
def friends(
|
95
|
-
|
75
|
+
def friends(page=1, limit=50)
|
76
|
+
call('user.getfriends', :friends, :user, {:user => @name, :page => page, :limit => limit})
|
96
77
|
end
|
97
78
|
|
98
79
|
# Get information about a user profile.
|
@@ -102,13 +83,13 @@ module Scrobbler
|
|
102
83
|
end
|
103
84
|
|
104
85
|
# Get the last 50 tracks loved by a user.
|
105
|
-
def loved_tracks
|
106
|
-
|
86
|
+
def loved_tracks
|
87
|
+
call('user.getlovedtracks', :lovedtracks, :track, {:user => @name})
|
107
88
|
end
|
108
89
|
|
109
90
|
# Get a list of a user's neighbours on Last.fm.
|
110
|
-
def neighbours
|
111
|
-
|
91
|
+
def neighbours
|
92
|
+
call('user.getneighbours', :neighbours, :user, {:user => @name})
|
112
93
|
end
|
113
94
|
|
114
95
|
# Get a paginated list of all events a user has attended in the past.
|
@@ -118,9 +99,8 @@ module Scrobbler
|
|
118
99
|
end
|
119
100
|
|
120
101
|
# Get a list of a user's playlists on Last.fm.
|
121
|
-
def playlists
|
122
|
-
|
123
|
-
get_response('user.getplaylists', :playlist, 'playlists', 'playlist', {'user'=>@username}, force)
|
102
|
+
def playlists
|
103
|
+
call('user.getplaylists', :playlists, :playlist, {:user => @name})
|
124
104
|
end
|
125
105
|
|
126
106
|
# Get a list of the recent tracks listened to by this user. Indicates now
|
@@ -128,9 +108,9 @@ module Scrobbler
|
|
128
108
|
#
|
129
109
|
# Possible parameters:
|
130
110
|
# - limit: An integer used to limit the number of tracks returned.
|
131
|
-
def recent_tracks(
|
132
|
-
parameters.merge!({
|
133
|
-
|
111
|
+
def recent_tracks(parameters={})
|
112
|
+
parameters.merge!({:user => @name})
|
113
|
+
call('user.getrecenttracks', :recenttracks, :track, parameters)
|
134
114
|
end
|
135
115
|
|
136
116
|
# Get Last.fm artist recommendations for a user
|
@@ -154,45 +134,49 @@ module Scrobbler
|
|
154
134
|
|
155
135
|
# Get the top albums listened to by a user. You can stipulate a time period.
|
156
136
|
# Sends the overall chart by default.
|
157
|
-
def top_albums(
|
158
|
-
|
137
|
+
def top_albums(period=:overall)
|
138
|
+
call('user.gettopalbums', :topalbums, :album, {:user => @name, :period => period})
|
159
139
|
end
|
160
140
|
|
161
141
|
# Get the top artists listened to by a user. You can stipulate a time
|
162
142
|
# period. Sends the overall chart by default.
|
163
|
-
def top_artists(
|
164
|
-
|
143
|
+
def top_artists(period=:overall)
|
144
|
+
call('user.gettopartists', :topartists, :artist, {:user => @name, :period => period})
|
165
145
|
end
|
166
146
|
|
167
147
|
# Get the top tags used by this user.
|
168
|
-
def top_tags
|
169
|
-
|
148
|
+
def top_tags
|
149
|
+
call('user.gettoptags', :toptags, :tag, {:user => @name})
|
170
150
|
end
|
171
151
|
|
172
152
|
# Get the top tracks listened to by a user. You can stipulate a time period.
|
173
153
|
# Sends the overall chart by default.
|
174
|
-
def top_tracks(
|
175
|
-
|
154
|
+
def top_tracks(period=:overall)
|
155
|
+
call('user.gettoptracks', :toptracks, :track, {:user => @name, :period => period})
|
156
|
+
end
|
157
|
+
|
158
|
+
# Setup the parameters for a *chart API call
|
159
|
+
def setup_chart_params(from=nil, to=nil)
|
160
|
+
parameters = {:user => @name}
|
161
|
+
parameters[:from] = from unless from.nil?
|
162
|
+
parameters[:to] = to unless to.nil?
|
163
|
+
parameters
|
176
164
|
end
|
177
165
|
|
178
166
|
# Get an album chart for a user profile, for a given date range. If no date
|
179
167
|
# range is supplied, it will return the most recent album chart for this
|
180
168
|
# user.
|
181
169
|
def weekly_album_chart(from=nil, to=nil)
|
182
|
-
parameters =
|
183
|
-
|
184
|
-
parameters['to'] = to unless to.nil?
|
185
|
-
get_response('user.getweeklyalbumchart', nil, 'weeklyalbumchart', 'album', parameters, true)
|
170
|
+
parameters = setup_chart_params(from, to)
|
171
|
+
call('user.getweeklyalbumchart', :weeklyalbumchart, Album, parameters)
|
186
172
|
end
|
187
173
|
|
188
174
|
# Get an artist chart for a user profile, for a given date range. If no date
|
189
175
|
# range is supplied, it will return the most recent artist chart for this
|
190
176
|
# user.
|
191
177
|
def weekly_artist_chart(from=nil, to=nil)
|
192
|
-
parameters =
|
193
|
-
|
194
|
-
parameters['to'] = to unless to.nil?
|
195
|
-
get_response('user.getweeklyartistchart', nil, 'weeklyartistchart', 'artist', parameters, true)
|
178
|
+
parameters = setup_chart_params(from, to)
|
179
|
+
call('user.getweeklyartistchart', :weeklyartistchart, Artist, parameters)
|
196
180
|
end
|
197
181
|
|
198
182
|
# Get a list of available charts for this user, expressed as date ranges
|
@@ -206,10 +190,8 @@ module Scrobbler
|
|
206
190
|
# range is supplied, it will return the most recent track chart for this
|
207
191
|
# user.
|
208
192
|
def weekly_track_chart(from=nil, to=nil)
|
209
|
-
parameters =
|
210
|
-
|
211
|
-
parameters['to'] = to unless to.nil?
|
212
|
-
get_response('user.getweeklytrackchart', nil, 'weeklytrackchart', 'track', parameters, true)
|
193
|
+
parameters = setup_chart_params(from, to)
|
194
|
+
call('user.getweeklytrackchart', :weeklytrackchart, Track, parameters)
|
213
195
|
end
|
214
196
|
|
215
197
|
# Shout on this user's shoutbox
|
data/lib/scrobbler.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
|
2
2
|
%w{uri rubygems libxml time}.each { |x| require x }
|
3
|
-
require 'digest/md5'
|
4
3
|
|
5
4
|
$: << File.expand_path(File.dirname(__FILE__))
|
6
5
|
|
7
6
|
# Load base class
|
8
|
-
require 'scrobbler/base'
|
7
|
+
require File.expand_path('scrobbler/base.rb', File.dirname(__FILE__))
|
9
8
|
|
10
9
|
# Load helper modules
|
11
10
|
require 'scrobbler/helper/image'
|
data/tasks/jeweler.rake
CHANGED
@@ -7,12 +7,16 @@ begin
|
|
7
7
|
s.homepage = "http://github.com/xhochy/scrobbler"
|
8
8
|
s.description = "A ruby library for accessing the last.fm v2 webservices"
|
9
9
|
s.authors = ['John Nunemaker', 'Jonathan Rudenberg', 'Uwe L. Korn']
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
s.add_development_dependency "rspec", ">= 1.2.9"
|
11
|
+
s.add_development_dependency "yard", ">= 0"
|
12
|
+
s.add_development_dependency "fakeweb", ">= 0"
|
13
|
+
#if RUBY_PLATFORM =~ /\bjava\b/
|
14
|
+
# s.add_dependency 'libxml-jruby'
|
15
|
+
#else
|
16
|
+
# s.add_dependency 'libxml-ruby'
|
17
|
+
#end
|
15
18
|
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
16
20
|
rescue LoadError
|
17
|
-
puts "Jeweler not available. Install it with:
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
22
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
begin
|
2
|
+
require 'metric_fu'
|
3
|
+
MetricFu::Configuration.run do |config|
|
4
|
+
# Deactivated: saikuro, stats, rcov
|
5
|
+
config.metrics = [:churn, :flog, :flay, :reek, :roodi]
|
6
|
+
config.graphs = [:flog, :flay, :reek, :roodi, :rcov]
|
7
|
+
config.flay = { :dirs_to_flay => ['lib'],
|
8
|
+
:minimum_score => 100 }
|
9
|
+
config.flog = { :dirs_to_flog => ['lib'] }
|
10
|
+
config.reek = { :dirs_to_reek => ['lib'] }
|
11
|
+
config.roodi = { :dirs_to_roodi => ['lib'] }
|
12
|
+
config.churn = { :start_date => "1 year ago", :minimum_churn_count => 10}
|
13
|
+
config.rcov = { :environment => 'test',
|
14
|
+
:test_files => ['test/**/*_test.rb',
|
15
|
+
'spec/**/*_spec.rb'],
|
16
|
+
:rcov_opts => ["--sort coverage",
|
17
|
+
"--no-html",
|
18
|
+
"--text-coverage",
|
19
|
+
"--no-color",
|
20
|
+
"--profile",
|
21
|
+
"--rails",
|
22
|
+
"--exclude /gems/,/Library/,spec"]}
|
23
|
+
config.graph_engine = :bluff
|
24
|
+
end
|
25
|
+
rescue LoadError
|
26
|
+
puts "metric_fu not available. Install it with: gem install metric_fu"
|
27
|
+
end
|
data/tasks/reek.rake
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
begin
|
2
|
+
require 'reek/rake/task'
|
3
|
+
Reek::Rake::Task.new do |t|
|
4
|
+
t.fail_on_error = true
|
5
|
+
t.verbose = false
|
6
|
+
t.source_files = 'lib/**/*.rb'
|
7
|
+
end
|
8
|
+
rescue LoadError
|
9
|
+
task :reek do
|
10
|
+
abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
data/tasks/roodi.rake
ADDED
data/tasks/tests.rake
CHANGED
@@ -9,5 +9,13 @@ Spec::Rake::SpecTask.new('test:unit') do |t|
|
|
9
9
|
end
|
10
10
|
t.spec_opts << '--format' << 'html:spec.html'
|
11
11
|
t.spec_opts << '--format' << 'progress'
|
12
|
+
t.spec_opts << '-b'
|
12
13
|
t.spec_files = FileList['test/unit/*_spec.rb']
|
13
14
|
end
|
15
|
+
|
16
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
17
|
+
spec.libs << 'lib' << 'spec'
|
18
|
+
spec.pattern = 'test/unit/*_spec.rb'
|
19
|
+
spec.rcov = true
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
## ## library.getartists
|
4
|
+
{
|
5
|
+
'api_key=foo123&user=xhochy' => 'artists-p1.xml',
|
6
|
+
'limit=30&api_key=foo123&user=xhochy' => 'artists-f30.xml'
|
7
|
+
}.each do |url, file|
|
8
|
+
register_fw('method=library.getartists&' + url, 'library', file)
|
9
|
+
end
|
10
|
+
(2..7).each do |n|
|
11
|
+
register_fw('method=library.getartists&page=' + n.to_s +
|
12
|
+
'&api_key=foo123&user=xhochy', 'library', "artists-p#{n}.xml")
|
13
|
+
end
|
14
|
+
|
15
|
+
## ## library.gettracks
|
16
|
+
{
|
17
|
+
'api_key=foo123&user=xhochy' => 'tracks-p1.xml',
|
18
|
+
'limit=30&api_key=foo123&user=xhochy' => 'tracks-f30.xml'
|
19
|
+
}.each do |url, file|
|
20
|
+
register_fw('method=library.gettracks&' + url, 'library', file)
|
21
|
+
end
|
22
|
+
(2..34).each do |n|
|
23
|
+
register_fw("user=xhochy&page=#{n}&api_key=foo123&method=library.gettracks",
|
24
|
+
'library', "tracks-p#{n}.xml")
|
25
|
+
end
|
26
|
+
|
27
|
+
## ## library.getalbums
|
28
|
+
FakeWeb.register_uri(:get, WEB_BASE + 'limit=30&user=xhochy&api_key=foo123&method=library.getalbums', :body => File.join([FIXTURES_BASE, 'library', 'albums-f30.xml']))
|
29
|
+
FakeWeb.register_uri(:get, WEB_BASE + 'user=xhochy&api_key=foo123&method=library.getalbums', :body => File.join([FIXTURES_BASE, 'library', 'albums-p1.xml']))
|
30
|
+
(2..8).each do |n|
|
31
|
+
FakeWeb.register_uri(:get, WEB_BASE + 'method=library.getalbums&page=' +
|
32
|
+
n.to_s + '&api_key=foo123&user=xhochy',
|
33
|
+
:body => File.join([FIXTURES_BASE, 'library', 'albums-p' +
|
34
|
+
n.to_s + '.xml']))
|
35
|
+
end
|