scrobbler 0.1.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 (59) hide show
  1. data/History.txt +1 -0
  2. data/Manifest.txt +58 -0
  3. data/README.txt +71 -0
  4. data/Rakefile +89 -0
  5. data/examples/album.rb +18 -0
  6. data/examples/artist.rb +14 -0
  7. data/examples/tag.rb +12 -0
  8. data/examples/track.rb +7 -0
  9. data/examples/user.rb +15 -0
  10. data/lib/scrobbler.rb +13 -0
  11. data/lib/scrobbler/album.rb +143 -0
  12. data/lib/scrobbler/artist.rb +127 -0
  13. data/lib/scrobbler/base.rb +29 -0
  14. data/lib/scrobbler/chart.rb +31 -0
  15. data/lib/scrobbler/rest.rb +47 -0
  16. data/lib/scrobbler/tag.rb +104 -0
  17. data/lib/scrobbler/track.rb +96 -0
  18. data/lib/scrobbler/user.rb +192 -0
  19. data/lib/scrobbler/version.rb +9 -0
  20. data/setup.rb +1585 -0
  21. data/test/fixtures/xml/album/info.xml +70 -0
  22. data/test/fixtures/xml/artist/fans.xml +18 -0
  23. data/test/fixtures/xml/artist/similar.xml +39 -0
  24. data/test/fixtures/xml/artist/topalbums.xml +36 -0
  25. data/test/fixtures/xml/artist/toptags.xml +18 -0
  26. data/test/fixtures/xml/artist/toptracks.xml +27 -0
  27. data/test/fixtures/xml/tag/topalbums.xml +39 -0
  28. data/test/fixtures/xml/tag/topartists.xml +39 -0
  29. data/test/fixtures/xml/tag/toptags.xml +252 -0
  30. data/test/fixtures/xml/tag/toptracks.xml +30 -0
  31. data/test/fixtures/xml/track/fans.xml +28 -0
  32. data/test/fixtures/xml/track/toptags.xml +33 -0
  33. data/test/fixtures/xml/user/friends.xml +21 -0
  34. data/test/fixtures/xml/user/neighbours.xml +18 -0
  35. data/test/fixtures/xml/user/profile.xml +12 -0
  36. data/test/fixtures/xml/user/recentbannedtracks.xml +24 -0
  37. data/test/fixtures/xml/user/recentlovedtracks.xml +24 -0
  38. data/test/fixtures/xml/user/recenttracks.xml +27 -0
  39. data/test/fixtures/xml/user/systemrecs.xml +18 -0
  40. data/test/fixtures/xml/user/topalbums.xml +42 -0
  41. data/test/fixtures/xml/user/topartists.xml +30 -0
  42. data/test/fixtures/xml/user/toptags.xml +18 -0
  43. data/test/fixtures/xml/user/toptracks.xml +27 -0
  44. data/test/fixtures/xml/user/weeklyalbumchart.xml +35 -0
  45. data/test/fixtures/xml/user/weeklyalbumchart_from_1138536002_to_1139140802.xml +35 -0
  46. data/test/fixtures/xml/user/weeklyartistchart.xml +38 -0
  47. data/test/fixtures/xml/user/weeklyartistchart_from_1138536002_to_1139140802.xml +59 -0
  48. data/test/fixtures/xml/user/weeklychartlist.xml +74 -0
  49. data/test/fixtures/xml/user/weeklytrackchart.xml +35 -0
  50. data/test/fixtures/xml/user/weeklytrackchart_from_1138536002_to_1139140802.xml +35 -0
  51. data/test/mocks/rest.rb +26 -0
  52. data/test/test_helper.rb +17 -0
  53. data/test/unit/album_test.rb +86 -0
  54. data/test/unit/artist_test.rb +82 -0
  55. data/test/unit/chart_test.rb +34 -0
  56. data/test/unit/tag_test.rb +65 -0
  57. data/test/unit/track_test.rb +42 -0
  58. data/test/unit/user_test.rb +291 -0
  59. metadata +120 -0
@@ -0,0 +1,127 @@
1
+ # Below are examples of how to find an artists top tracks and similar artists.
2
+ #
3
+ # artist = Scrobbler::Artist.new('Carrie Underwood')
4
+ #
5
+ # puts 'Top Tracks'
6
+ # puts "=" * 10
7
+ # artist.top_tracks.each { |t| puts "(#{t.reach}) #{t.name}" }
8
+ #
9
+ # puts
10
+ #
11
+ # puts 'Similar Artists'
12
+ # puts "=" * 15
13
+ # artist.similar.each { |a| puts "(#{a.match}%) #{a.name}" }
14
+ #
15
+ # Would output something similar to:
16
+ #
17
+ # Top Tracks
18
+ # ==========
19
+ # (8797) Before He Cheats
20
+ # (3574) Don't Forget to Remember Me
21
+ # (3569) Wasted
22
+ # (3246) Some Hearts
23
+ # (3142) Jesus, Take the Wheel
24
+ # (2600) Starts With Goodbye
25
+ # (2511) Jesus Take The Wheel
26
+ # (2423) Inside Your Heaven
27
+ # (2328) Lessons Learned
28
+ # (2040) I Just Can't Live a Lie
29
+ # (1899) Whenever You Remember
30
+ # (1882) We're Young and Beautiful
31
+ # (1854) That's Where It Is
32
+ # (1786) I Ain't in Checotah Anymore
33
+ # (1596) The Night Before (Life Goes On)
34
+ #
35
+ # Similar Artists
36
+ # ===============
37
+ # (100%) Rascal Flatts
38
+ # (84.985%) Keith Urban
39
+ # (84.007%) Kellie Pickler
40
+ # (82.694%) Katharine McPhee
41
+ # (81.213%) Martina McBride
42
+ # (79.397%) Faith Hill
43
+ # (77.121%) Tim McGraw
44
+ # (75.191%) Jessica Simpson
45
+ # (75.182%) Sara Evans
46
+ # (75.144%) The Wreckers
47
+ # (73.034%) Kenny Chesney
48
+ # (71.765%) Dixie Chicks
49
+ # (71.084%) Kelly Clarkson
50
+ # (69.535%) Miranda Lambert
51
+ # (66.952%) LeAnn Rimes
52
+ # (66.398%) Mandy Moore
53
+ # (65.817%) Bo Bice
54
+ # (65.279%) Diana DeGarmo
55
+ # (65.115%) Gretchen Wilson
56
+ # (62.982%) Clay Aiken
57
+ # (62.436%) Ashlee Simpson
58
+ # (62.160%) Christina Aguilera
59
+ module Scrobbler
60
+ class Artist < Base
61
+ attr_accessor :name, :mbid, :playcount, :rank, :url, :thumbnail, :image, :reach, :count, :streamable
62
+ attr_accessor :chartposition
63
+
64
+ # used for similar artists
65
+ attr_accessor :match
66
+
67
+ class << self
68
+ def new_from_xml(xml, doc=nil)
69
+ name = (xml).at(:name).inner_html if (xml).at(:name)
70
+ # occasionally name can be found in root of artist element (<artist name="">) rather than as an element (<name>)
71
+ name = xml['name'] if name.nil? && xml['name']
72
+ a = Artist.new(name)
73
+ a.mbid = (xml).at(:mbid).inner_html if (xml).at(:mbid)
74
+ a.playcount = (xml).at(:playcount).inner_html if (xml).at(:playcount)
75
+ a.rank = (xml).at(:rank).inner_html if (xml).at(:rank)
76
+ a.url = (xml).at(:url).inner_html if (xml).at(:url)
77
+ a.thumbnail = (xml).at(:thumbnail).inner_html if (xml).at(:thumbnail)
78
+ a.thumbnail = (xml).at(:image_small).inner_html if a.thumbnail.nil? && (xml).at(:image_small)
79
+ a.image = (xml).at(:image).inner_html if (xml).at(:image)
80
+ a.reach = (xml).at(:reach).inner_html if (xml).at(:reach)
81
+ a.match = (xml).at(:match).inner_html if (xml).at(:match)
82
+ a.chartposition = (xml).at(:chartposition).inner_html if (xml).at(:chartposition)
83
+
84
+ # in top artists for tag
85
+ a.count = xml['count'] if xml['count']
86
+ a.streamable = xml['streamable'] if xml['streamable']
87
+ a.streamable = (xml).at(:streamable).inner_html == '1' ? 'yes' : 'no' if a.streamable.nil? && (xml).at(:streamable)
88
+ a
89
+ end
90
+ end
91
+
92
+ def initialize(name)
93
+ raise ArgumentError, "Name is required" if name.blank?
94
+ @name = name
95
+ end
96
+
97
+ def api_path
98
+ "/#{API_VERSION}/artist/#{CGI::escape(name)}"
99
+ end
100
+
101
+ def current_events(format=:ics)
102
+ format = :ics if format.to_s == 'ical'
103
+ raise ArgumentError unless ['ics', 'rss'].include?(format.to_s)
104
+ "#{API_URL.chop}#{api_path}/events.#{format}"
105
+ end
106
+
107
+ def similar(force=false)
108
+ get_instance(:similar, :similar, :artist, force)
109
+ end
110
+
111
+ def top_fans(force=false)
112
+ get_instance(:fans, :top_fans, :user, force)
113
+ end
114
+
115
+ def top_tracks(force=false)
116
+ get_instance(:toptracks, :top_tracks, :track, force)
117
+ end
118
+
119
+ def top_albums(force=false)
120
+ get_instance(:topalbums, :top_albums, :album, force)
121
+ end
122
+
123
+ def top_tags(force=false)
124
+ get_instance(:toptags, :top_tags, :tag, force)
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,29 @@
1
+ module Scrobbler
2
+
3
+ API_URL = 'http://ws.audioscrobbler.com/'
4
+ API_VERSION = '1.0'
5
+
6
+ class Base
7
+ class << self
8
+ def connection
9
+ @connection ||= REST::Connection.new(API_URL)
10
+ end
11
+
12
+ def fetch_and_parse(resource)
13
+ Hpricot::XML(connection.get(resource))
14
+ end
15
+ end
16
+
17
+ private
18
+ # in order for subclass to use, it must have api_path method
19
+ def get_instance(api_method, instance_name, element, force=false)
20
+ scrobbler_class = "scrobbler/#{element.to_s}".camelize.constantize
21
+ if instance_variable_get("@#{instance_name}").nil? || force
22
+ doc = self.class.fetch_and_parse("#{api_path}/#{api_method}.xml")
23
+ elements = (doc/element).inject([]) { |elements, el| elements << scrobbler_class.new_from_xml(el, doc); elements }
24
+ instance_variable_set("@#{instance_name}", elements)
25
+ end
26
+ instance_variable_get("@#{instance_name}")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ module Scrobbler
2
+ class Chart < Base
3
+ class << self
4
+ def new_from_xml(xml, doc)
5
+ Chart.new(xml['from'], xml['to'])
6
+ end
7
+ end
8
+ def initialize(from, to)
9
+ raise ArgumentError, "From is required" if from.blank?
10
+ raise ArgumentError, "To is required" if to.blank?
11
+ @from = from
12
+ @to = to
13
+ end
14
+
15
+ def from=(value)
16
+ @from = value.to_i
17
+ end
18
+
19
+ def to=(value)
20
+ @to = value.to_i
21
+ end
22
+
23
+ def from
24
+ @from.to_i
25
+ end
26
+
27
+ def to
28
+ @to.to_i
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,47 @@
1
+ require 'net/https'
2
+
3
+ module Scrobbler
4
+ module REST
5
+ class Connection
6
+ def initialize(base_url, args = {})
7
+ @base_url = base_url
8
+ @username = args[:username]
9
+ @password = args[:password]
10
+ end
11
+
12
+ def get(resource, args = nil)
13
+ request(resource, "get", args)
14
+ end
15
+
16
+ def post(resource, args = nil)
17
+ request(resource, "post", args)
18
+ end
19
+
20
+ def request(resource, method = "get", args = nil)
21
+ url = URI.join(@base_url, resource)
22
+
23
+ if args
24
+ # TODO: What about keys without value?
25
+ url.query = args.map { |k,v| "%s=%s" % [URI.encode(k), URI.encode(v)] }.join("&")
26
+ end
27
+
28
+ case method
29
+ when "get"
30
+ req = Net::HTTP::Get.new(url.request_uri)
31
+ when "post"
32
+ req = Net::HTTP::Post.new(url.request_uri)
33
+ end
34
+
35
+ if @username and @password
36
+ req.basic_auth(@username, @password)
37
+ end
38
+
39
+ http = Net::HTTP.new(url.host, url.port)
40
+ http.use_ssl = (url.port == 443)
41
+
42
+ res = http.start() { |conn| conn.request(req) }
43
+ res.body
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,104 @@
1
+ # Below is code samples for how to find the top albums and tracks for a tag.
2
+ #
3
+ # tag = Scrobbler::Tag.new('country')
4
+ #
5
+ # puts 'Top Albums'
6
+ # tag.top_albums.each { |a| puts "(#{a.count}) #{a.name} by #{a.artist}" }
7
+ #
8
+ # puts
9
+ #
10
+ # puts 'Top Tracks'
11
+ # tag.top_tracks.each { |t| puts "(#{t.count}) #{t.name} by #{t.artist}" }
12
+ #
13
+ # Which would output something similar to:
14
+ #
15
+ # Top Albums
16
+ # (29) American IV: The Man Comes Around by Johnny Cash
17
+ # (14) Folks Pop In at the Waterhouse by Various Artists
18
+ # (13) Hapless by Flowers From The Man Who Shot Your Cousin
19
+ # (9) Taking The Long Way by Dixie Chicks
20
+ # (8) Unchained by Johnny Cash
21
+ # (8) American III: Solitary Man by Johnny Cash
22
+ # (8) Wide Open Spaces by Dixie Chicks
23
+ # (7) It's Now or Later by Tangled Star
24
+ # (7) Greatest Hits by Hank Williams
25
+ # (7) American Recordings by Johnny Cash
26
+ # (6) Forgotten Landscape by theNoLifeKing
27
+ # (6) At Folsom Prison by Johnny Cash
28
+ # (6) Fox Confessor Brings the Flood by Neko Case
29
+ # (6) Murder by Johnny Cash
30
+ # (5) Gloom by theNoLifeKing
31
+ # (5) Set This Circus Down by Tim McGraw
32
+ # (5) Blacklisted by Neko Case
33
+ # (5) Breathe by Faith Hill
34
+ # (5) Unearthed (disc 4: My Mother's Hymn Book) by Johnny Cash
35
+ # (4) Home by Dixie Chicks
36
+ #
37
+ # Top Tracks
38
+ # (221) Hurt by Johnny Cash
39
+ # (152) I Walk the Line by Johnny Cash
40
+ # (147) Ring of Fire by Johnny Cash
41
+ # (125) Folsom Prison Blues by Johnny Cash
42
+ # (77) The Man Comes Around by Johnny Cash
43
+ # (67) Personal Jesus by Johnny Cash
44
+ # (65) Not Ready To Make Nice by Dixie Chicks
45
+ # (63) Before He Cheats by Carrie Underwood
46
+ # (62) Give My Love to Rose by Johnny Cash
47
+ # (49) Jackson by Johnny Cash
48
+ # (49) What Hurts The Most by Rascal Flatts
49
+ # (48) Big River by Johnny Cash
50
+ # (46) Man in Black by Johnny Cash
51
+ # (46) Jolene by Dolly Parton
52
+ # (46) Friends in Low Places by Garth Brooks
53
+ # (46) One by Johnny Cash
54
+ # (44) Cocaine Blues by Johnny Cash
55
+ # (41) Get Rhythm by Johnny Cash
56
+ # (41) I Still Miss Someone by Johnny Cash
57
+ # (40) The Devil Went Down to Georgia by Charlie Daniels Band
58
+ module Scrobbler
59
+ class Tag < Base
60
+ attr_accessor :name, :count, :url
61
+
62
+ class << self
63
+ def new_from_xml(xml, doc=nil)
64
+ name = (xml).at(:name).inner_html
65
+ t = Tag.new(name)
66
+ t.count = (xml).at(:count).inner_html
67
+ t.url = (xml).at(:url).inner_html
68
+ t
69
+ end
70
+
71
+ def top_tags
72
+ doc = fetch_and_parse("/#{API_VERSION}/tag/toptags.xml")
73
+ @top_tags = (doc/:tag).inject([]) do |tags, tag|
74
+ t = Tag.new(tag['name'])
75
+ t.count = tag['count']
76
+ t.url = tag['url']
77
+ tags << t
78
+ tags
79
+ end
80
+ end
81
+ end
82
+
83
+ def initialize(name)
84
+ raise ArgumentError, "Name is required" if name.blank?
85
+ @name = name
86
+ end
87
+
88
+ def api_path
89
+ "/#{API_VERSION}/tag/#{CGI::escape(name)}"
90
+ end
91
+
92
+ def top_artists(force=false)
93
+ get_instance(:topartists, :top_artists, :artist, force)
94
+ end
95
+
96
+ def top_albums(force=false)
97
+ get_instance(:topalbums, :top_albums, :album, force)
98
+ end
99
+
100
+ def top_tracks(force=false)
101
+ get_instance(:toptracks, :top_tracks, :track, force)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,96 @@
1
+ # Below is an example of how to get the top fans for a track.
2
+ #
3
+ # track = Scrobbler::Track.new('Carrie Underwood', 'Before He Cheats')
4
+ # puts 'Fans'
5
+ # puts "=" * 4
6
+ # track.fans.each { |u| puts u.username }
7
+ #
8
+ # Which would output something like:
9
+ #
10
+ # track = Scrobbler::Track.new('Carrie Underwood', 'Before He Cheats')
11
+ # puts 'Fans'
12
+ # puts "=" * 4
13
+ # track.fans.each { |u| puts "(#{u.weight}) #{u.username}" }
14
+ #
15
+ # Fans
16
+ # ====
17
+ # (69163) PimpinRose
18
+ # (7225) selene204
19
+ # (7000) CelestiaLegends
20
+ # (6817) muehllr
21
+ # (5387) Mudley
22
+ # (5368) ilovejohnny1984
23
+ # (5232) MeganIAD
24
+ # (5132) Veric
25
+ # (5097) aeVnar
26
+ # (3390) kristaaan
27
+ # (3239) kelseaowns
28
+ # (2780) syndication
29
+ # (2735) mkumm
30
+ # (2706) Kimmybeebee
31
+ # (2648) skorpcroze
32
+ # (2549) mistergreg
33
+ # (2449) mlmjcace
34
+ # (2302) tiNEey
35
+ # (2169) ajsbabiegirl
36
+ module Scrobbler
37
+ class Track < Base
38
+ attr_accessor :artist, :artist_mbid, :name, :mbid, :playcount, :rank, :url, :reach
39
+ attr_accessor :streamable, :album, :album_mbid, :date, :date_uts
40
+
41
+ # only seems to be used on top tracks for tag
42
+ attr_accessor :count, :thumbnail, :image
43
+
44
+ # for weekly top tracks
45
+ attr_accessor :chartposition
46
+
47
+ class << self
48
+ def new_from_xml(xml, doc=nil)
49
+ artist = (xml).at(:artist)['name'] if (xml).at(:artist) && (xml).at(:artist)['name']
50
+ artist = (xml).at(:artist).inner_html if artist.nil? && (xml).at(:artist)
51
+ artist = doc.root['artist'] if artist.nil? && doc.root['artist']
52
+ name = (xml).at(:name).inner_html if (xml).at(:name)
53
+ name = xml['name'] if name.nil? && xml['name']
54
+ t = Track.new(artist, name)
55
+ t.artist_mbid = (xml).at(:artist)['mbid'] if (xml).at(:artist) && (xml).at(:artist)['mbid']
56
+ t.artist_mbid = (xml).at(:artist).at(:mbid).inner_html if t.artist_mbid.nil? && (xml).at(:artist) && (xml).at(:artist).at(:mbid)
57
+ t.mbid = (xml).at(:mbid).inner_html if (xml).at(:mbid)
58
+ t.playcount = (xml).at(:playcount).inner_html if (xml).at(:playcount)
59
+ t.chartposition = (xml).at(:chartposition).inner_html if (xml).at(:chartposition)
60
+ t.rank = (xml).at(:rank).inner_html if (xml).at(:rank)
61
+ t.url = (xml/:url).last.inner_html if (xml/:url).size > 1
62
+ t.url = (xml).at(:url).inner_html if t.url.nil? && (xml).at(:url)
63
+ t.streamable = (xml).at(:track)['streamable'] if (xml).at(:track) && (xml).at(:track)['streamable']
64
+ t.streamable = xml['streamable'] if t.streamable.nil? && xml['streamable']
65
+ t.count = xml['count'] if xml['count']
66
+ t.album = (xml).at(:album).inner_html if (xml).at(:album)
67
+ t.album_mbid = (xml).at(:album)['mbid'] if (xml).at(:album) && (xml).at(:album)['mbid']
68
+ t.date = Time.parse((xml).at(:date).inner_html) if (xml).at(:date)
69
+ t.date_uts = (xml).at(:date)['uts'] if (xml).at(:date) && (xml).at(:date)['uts']
70
+ t.thumbnail = (xml).at(:thumbnail).inner_html if (xml).at(:thumbnail)
71
+ t.image = (xml).at(:image).inner_html if (xml).at(:image)
72
+ t.reach = (xml).at(:reach).inner_html if (xml).at(:reach)
73
+ t
74
+ end
75
+ end
76
+
77
+ def initialize(artist, name)
78
+ raise ArgumentError, "Artist is required" if artist.blank?
79
+ raise ArgumentError, "Name is required" if name.blank?
80
+ @artist = artist
81
+ @name = name
82
+ end
83
+
84
+ def api_path
85
+ "/#{API_VERSION}/track/#{CGI::escape(artist)}/#{CGI::escape(name)}"
86
+ end
87
+
88
+ def fans(force=false)
89
+ get_instance(:fans, :fans, :user, force)
90
+ end
91
+
92
+ def tags(force=false)
93
+ get_instance(:toptags, :tags, :tag, force)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,192 @@
1
+ # Probably the most common use of this lib would be to get your most recent tracks or your top tracks. Below are some code samples.
2
+ # user = Scrobbler::User.new('jnunemaker')
3
+ #
4
+ # puts "#{user.username}'s Recent Tracks"
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
47
+ module Scrobbler
48
+ class User < Base
49
+ # attributes needed to initialize
50
+ attr_reader :username
51
+
52
+ # profile attributes
53
+ attr_accessor :id, :cluster, :url, :realname, :mbox_sha1sum, :registered
54
+ attr_accessor :registered_unixtime, :age, :gender, :country, :playcount, :avatar
55
+
56
+ # neighbor attributes
57
+ attr_accessor :match
58
+
59
+ # track fans attributes
60
+ attr_accessor :weight
61
+
62
+ class << self
63
+ def new_from_xml(xml, doc=nil)
64
+ u = User.new((xml)['username'])
65
+ u.url = (xml).at(:url).inner_html if (xml).at(:url)
66
+ u.avatar = (xml).at(:image).inner_html if (xml).at(:image)
67
+ u.weight = (xml).at(:weight).inner_html if (xml).at(:weight)
68
+ u.match = (xml).at(:match).inner_html if (xml).at(:match)
69
+ u
70
+ end
71
+
72
+ def find(*args)
73
+ options = {:include_profile => false}
74
+ options.merge!(args.pop) if args.last.is_a?(Hash)
75
+ users = args.flatten.inject([]) { |users, u| users << User.new(u, options); users }
76
+ users.length == 1 ? users.pop : users
77
+ end
78
+ end
79
+
80
+ def initialize(username, o={})
81
+ options = {:include_profile => false}.merge(o)
82
+ raise ArgumentError if username.blank?
83
+ @username = username
84
+ load_profile() if options[:include_profile]
85
+ end
86
+
87
+ def api_path
88
+ "/#{API_VERSION}/user/#{CGI::escape(username)}"
89
+ end
90
+
91
+ def current_events(format=:ics)
92
+ format = :ics if format.to_s == 'ical'
93
+ raise ArgumentError unless ['ics', 'rss'].include?(format.to_s)
94
+ "#{API_URL.chop}#{api_path}/events.#{format}"
95
+ end
96
+
97
+ def friends_events(format=:ics)
98
+ format = :ics if format.to_s == 'ical'
99
+ raise ArgumentError unless ['ics', 'rss'].include?(format.to_s)
100
+ "#{API_URL.chop}#{api_path}/friendevents.#{format}"
101
+ end
102
+
103
+ def recommended_events(format=:ics)
104
+ format = :ics if format.to_s == 'ical'
105
+ raise ArgumentError unless ['ics', 'rss'].include?(format.to_s)
106
+ "#{API_URL.chop}#{api_path}/eventsysrecs.#{format}"
107
+ end
108
+
109
+ def load_profile
110
+ doc = self.class.fetch_and_parse("#{api_path}/profile.xml")
111
+ @id = (doc).at(:profile)['id']
112
+ @cluster = (doc).at(:profile)['cluster']
113
+ @url = (doc).at(:url).inner_html
114
+ @realname = (doc).at(:realname).inner_html
115
+ @mbox_sha1sum = (doc).at(:mbox_sha1sum).inner_html
116
+ @registered = (doc).at(:registered).inner_html
117
+ @registered_unixtime = (doc).at(:registered)['unixtime']
118
+ @age = (doc).at(:age).inner_html
119
+ @gender = (doc).at(:gender).inner_html
120
+ @country = (doc).at(:country).inner_html
121
+ @playcount = (doc).at(:playcount).inner_html
122
+ @avatar = (doc).at(:avatar).inner_html
123
+ end
124
+
125
+ def top_artists(force=false)
126
+ get_instance(:topartists, :top_artists, :artist, force)
127
+ end
128
+
129
+ def top_albums(force=false)
130
+ get_instance(:topalbums, :top_albums, :album, force)
131
+ end
132
+
133
+ def top_tracks(force=false)
134
+ get_instance(:toptracks, :top_tracks, :track, force)
135
+ end
136
+
137
+ def top_tags(force=false)
138
+ get_instance(:toptags, :top_tags, :tag, force)
139
+ end
140
+
141
+ def friends(force=false)
142
+ get_instance(:friends, :friends, :user, force)
143
+ end
144
+
145
+ def neighbours(force=false)
146
+ get_instance(:neighbours, :neighbours, :user, force)
147
+ end
148
+
149
+ def recent_tracks(force=false)
150
+ get_instance(:recenttracks, :recent_tracks, :track, force)
151
+ end
152
+
153
+ def recent_banned_tracks(force=false)
154
+ get_instance(:recentbannedtracks, :recent_banned_tracks, :track, force)
155
+ end
156
+
157
+ def recent_loved_tracks(force=false)
158
+ get_instance(:recentlovedtracks, :recent_loved_tracks, :track, force)
159
+ end
160
+
161
+ def recommendations(force=false)
162
+ get_instance(:systemrecs, :recommendations, :artist, force)
163
+ end
164
+
165
+ def charts(force=false)
166
+ get_instance(:weeklychartlist, :charts, :chart, force)
167
+ end
168
+
169
+ def weekly_artist_chart(from=nil, to=nil)
170
+ qs = create_query_string_from_timestamps(from, to)
171
+ doc = self.class.fetch_and_parse("#{api_path}/weeklyartistchart.xml#{qs}")
172
+ (doc/:artist).inject([]) { |elements, el| elements << Artist.new_from_xml(el); elements }
173
+ end
174
+
175
+ def weekly_album_chart(from=nil, to=nil)
176
+ qs = create_query_string_from_timestamps(from, to)
177
+ doc = self.class.fetch_and_parse("#{api_path}/weeklyalbumchart.xml#{qs}")
178
+ (doc/:album).inject([]) { |elements, el| elements << Album.new_from_xml(el); elements }
179
+ end
180
+
181
+ def weekly_track_chart(from=nil, to=nil)
182
+ qs = create_query_string_from_timestamps(from, to)
183
+ doc = self.class.fetch_and_parse("#{api_path}/weeklytrackchart.xml#{qs}")
184
+ (doc/:track).inject([]) { |elements, el| elements << Track.new_from_xml(el); elements }
185
+ end
186
+
187
+ private
188
+ def create_query_string_from_timestamps(from, to)
189
+ (from && to) ? "?from=#{from.to_i}&to=#{to.to_i}" : ''
190
+ end
191
+ end
192
+ end