hornairs-scrobbler 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (161) hide show
  1. data/.gitignore +13 -0
  2. data/.loadpath +5 -0
  3. data/History.txt +5 -0
  4. data/MIT-LICENSE +19 -0
  5. data/Manifest +75 -0
  6. data/README.rdoc +104 -0
  7. data/Rakefile +10 -0
  8. data/VERSION.yml +5 -0
  9. data/examples/album.rb +20 -0
  10. data/examples/artist.rb +13 -0
  11. data/examples/playlist.rb +7 -0
  12. data/examples/scrobble.rb +31 -0
  13. data/examples/tag.rb +11 -0
  14. data/examples/track.rb +12 -0
  15. data/examples/user.rb +15 -0
  16. data/lib/scrobbler.rb +33 -0
  17. data/lib/scrobbler/album.rb +181 -0
  18. data/lib/scrobbler/artist.rb +165 -0
  19. data/lib/scrobbler/auth.rb +47 -0
  20. data/lib/scrobbler/base.rb +105 -0
  21. data/lib/scrobbler/event.rb +105 -0
  22. data/lib/scrobbler/geo.rb +27 -0
  23. data/lib/scrobbler/helper/image.rb +52 -0
  24. data/lib/scrobbler/helper/streamable.rb +40 -0
  25. data/lib/scrobbler/library.rb +112 -0
  26. data/lib/scrobbler/playing.rb +49 -0
  27. data/lib/scrobbler/playlist.rb +87 -0
  28. data/lib/scrobbler/radio.rb +12 -0
  29. data/lib/scrobbler/rest.rb +47 -0
  30. data/lib/scrobbler/scrobble.rb +116 -0
  31. data/lib/scrobbler/session.rb +9 -0
  32. data/lib/scrobbler/shout.rb +21 -0
  33. data/lib/scrobbler/simpleauth.rb +60 -0
  34. data/lib/scrobbler/tag.rb +113 -0
  35. data/lib/scrobbler/track.rb +152 -0
  36. data/lib/scrobbler/user.rb +222 -0
  37. data/lib/scrobbler/venue.rb +67 -0
  38. data/setup.rb +1585 -0
  39. data/tasks/jeweler.rake +15 -0
  40. data/tasks/rdoc.rake +7 -0
  41. data/tasks/tests.rake +13 -0
  42. data/tasks/yardoc.rake +8 -0
  43. data/test/fixtures/xml/album/info.xml +43 -0
  44. data/test/fixtures/xml/artist/fans.xml +52 -0
  45. data/test/fixtures/xml/artist/info.xml +58 -0
  46. data/test/fixtures/xml/artist/similar.xml +1004 -0
  47. data/test/fixtures/xml/artist/topalbums.xml +61 -0
  48. data/test/fixtures/xml/artist/toptags.xml +19 -0
  49. data/test/fixtures/xml/artist/toptracks.xml +62 -0
  50. data/test/fixtures/xml/auth/session.xml +7 -0
  51. data/test/fixtures/xml/auth/token.xml +3 -0
  52. data/test/fixtures/xml/event/attend.xml +3 -0
  53. data/test/fixtures/xml/event/attendees.xml +53 -0
  54. data/test/fixtures/xml/event/event.xml +35 -0
  55. data/test/fixtures/xml/event/shouts.xml +35 -0
  56. data/test/fixtures/xml/geo/events-distance-p1.xml +151 -0
  57. data/test/fixtures/xml/geo/events-lat-long.xml +167 -0
  58. data/test/fixtures/xml/geo/events-p1.xml +152 -0
  59. data/test/fixtures/xml/geo/events-p2.xml +380 -0
  60. data/test/fixtures/xml/geo/events-p3.xml +427 -0
  61. data/test/fixtures/xml/geo/top_artists-p1.xml +76 -0
  62. data/test/fixtures/xml/geo/top_tracks-p1.xml +77 -0
  63. data/test/fixtures/xml/library/albums-f30.xml +454 -0
  64. data/test/fixtures/xml/library/albums-p1.xml +754 -0
  65. data/test/fixtures/xml/library/albums-p2.xml +754 -0
  66. data/test/fixtures/xml/library/albums-p3.xml +754 -0
  67. data/test/fixtures/xml/library/albums-p4.xml +739 -0
  68. data/test/fixtures/xml/library/albums-p5.xml +754 -0
  69. data/test/fixtures/xml/library/albums-p6.xml +754 -0
  70. data/test/fixtures/xml/library/albums-p7.xml +739 -0
  71. data/test/fixtures/xml/library/albums-p8.xml +724 -0
  72. data/test/fixtures/xml/library/artists-f30.xml +334 -0
  73. data/test/fixtures/xml/library/artists-p1.xml +554 -0
  74. data/test/fixtures/xml/library/artists-p2.xml +554 -0
  75. data/test/fixtures/xml/library/artists-p3.xml +554 -0
  76. data/test/fixtures/xml/library/artists-p4.xml +554 -0
  77. data/test/fixtures/xml/library/artists-p5.xml +554 -0
  78. data/test/fixtures/xml/library/artists-p6.xml +554 -0
  79. data/test/fixtures/xml/library/artists-p7.xml +444 -0
  80. data/test/fixtures/xml/library/tracks-f30.xml +472 -0
  81. data/test/fixtures/xml/library/tracks-p1.xml +789 -0
  82. data/test/fixtures/xml/library/tracks-p10.xml +771 -0
  83. data/test/fixtures/xml/library/tracks-p11.xml +792 -0
  84. data/test/fixtures/xml/library/tracks-p12.xml +777 -0
  85. data/test/fixtures/xml/library/tracks-p13.xml +762 -0
  86. data/test/fixtures/xml/library/tracks-p14.xml +759 -0
  87. data/test/fixtures/xml/library/tracks-p15.xml +762 -0
  88. data/test/fixtures/xml/library/tracks-p16.xml +756 -0
  89. data/test/fixtures/xml/library/tracks-p17.xml +780 -0
  90. data/test/fixtures/xml/library/tracks-p18.xml +756 -0
  91. data/test/fixtures/xml/library/tracks-p19.xml +774 -0
  92. data/test/fixtures/xml/library/tracks-p2.xml +765 -0
  93. data/test/fixtures/xml/library/tracks-p20.xml +777 -0
  94. data/test/fixtures/xml/library/tracks-p21.xml +777 -0
  95. data/test/fixtures/xml/library/tracks-p22.xml +771 -0
  96. data/test/fixtures/xml/library/tracks-p23.xml +765 -0
  97. data/test/fixtures/xml/library/tracks-p24.xml +783 -0
  98. data/test/fixtures/xml/library/tracks-p25.xml +777 -0
  99. data/test/fixtures/xml/library/tracks-p26.xml +777 -0
  100. data/test/fixtures/xml/library/tracks-p27.xml +756 -0
  101. data/test/fixtures/xml/library/tracks-p28.xml +771 -0
  102. data/test/fixtures/xml/library/tracks-p29.xml +753 -0
  103. data/test/fixtures/xml/library/tracks-p3.xml +771 -0
  104. data/test/fixtures/xml/library/tracks-p30.xml +780 -0
  105. data/test/fixtures/xml/library/tracks-p31.xml +753 -0
  106. data/test/fixtures/xml/library/tracks-p32.xml +771 -0
  107. data/test/fixtures/xml/library/tracks-p33.xml +762 -0
  108. data/test/fixtures/xml/library/tracks-p34.xml +538 -0
  109. data/test/fixtures/xml/library/tracks-p4.xml +792 -0
  110. data/test/fixtures/xml/library/tracks-p5.xml +780 -0
  111. data/test/fixtures/xml/library/tracks-p6.xml +789 -0
  112. data/test/fixtures/xml/library/tracks-p7.xml +789 -0
  113. data/test/fixtures/xml/library/tracks-p8.xml +780 -0
  114. data/test/fixtures/xml/library/tracks-p9.xml +774 -0
  115. data/test/fixtures/xml/tag/similar.xml +254 -0
  116. data/test/fixtures/xml/tag/topalbums.xml +805 -0
  117. data/test/fixtures/xml/tag/topartists.xml +605 -0
  118. data/test/fixtures/xml/tag/toptags.xml +1254 -0
  119. data/test/fixtures/xml/tag/toptracks.xml +852 -0
  120. data/test/fixtures/xml/track/fans.xml +34 -0
  121. data/test/fixtures/xml/track/info.xml +53 -0
  122. data/test/fixtures/xml/track/toptags.xml +504 -0
  123. data/test/fixtures/xml/user/events.xml +401 -0
  124. data/test/fixtures/xml/user/friends.xml +30 -0
  125. data/test/fixtures/xml/user/lovedtracks.xml +678 -0
  126. data/test/fixtures/xml/user/neighbours.xml +23 -0
  127. data/test/fixtures/xml/user/playlists.xml +61 -0
  128. data/test/fixtures/xml/user/profile.xml +12 -0
  129. data/test/fixtures/xml/user/recentbannedtracks.xml +24 -0
  130. data/test/fixtures/xml/user/recentlovedtracks.xml +24 -0
  131. data/test/fixtures/xml/user/recenttracks.xml +124 -0
  132. data/test/fixtures/xml/user/systemrecs.xml +18 -0
  133. data/test/fixtures/xml/user/topalbums.xml +61 -0
  134. data/test/fixtures/xml/user/topartists.xml +41 -0
  135. data/test/fixtures/xml/user/toptags.xml +44 -0
  136. data/test/fixtures/xml/user/toptracks.xml +65 -0
  137. data/test/fixtures/xml/user/weeklyalbumchart.xml +256 -0
  138. data/test/fixtures/xml/user/weeklyartistchart.xml +220 -0
  139. data/test/fixtures/xml/user/weeklytrackchart.xml +746 -0
  140. data/test/fixtures/xml/venue/events.xml +151 -0
  141. data/test/fixtures/xml/venue/venue.xml +16 -0
  142. data/test/mocks/rest.rb +212 -0
  143. data/test/spec_helper.rb +7 -0
  144. data/test/test_helper.rb +20 -0
  145. data/test/unit/album_spec.rb +52 -0
  146. data/test/unit/artist_spec.rb +130 -0
  147. data/test/unit/auth_spec.rb +36 -0
  148. data/test/unit/event_spec.rb +109 -0
  149. data/test/unit/geo_spec.rb +148 -0
  150. data/test/unit/library_spec.rb +133 -0
  151. data/test/unit/playing_test.rb +53 -0
  152. data/test/unit/playlist_spec.rb +25 -0
  153. data/test/unit/radio_spec.rb +22 -0
  154. data/test/unit/scrobble_spec.rb +55 -0
  155. data/test/unit/scrobble_test.rb +69 -0
  156. data/test/unit/simpleauth_test.rb +45 -0
  157. data/test/unit/tag_spec.rb +101 -0
  158. data/test/unit/track_spec.rb +95 -0
  159. data/test/unit/user_spec.rb +264 -0
  160. data/test/unit/venue_spec.rb +104 -0
  161. metadata +265 -0
@@ -0,0 +1,9 @@
1
+ module Scrobbler
2
+ class Session < Base
3
+ attr_reader :key, :name, :subscriber
4
+
5
+ def initialize(data={})
6
+ populate_data(data)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ module Scrobbler
2
+ class Shout < Base
3
+ attr_reader :author, :date, :body
4
+
5
+ class << self
6
+ def new_from_libxml(xml)
7
+ data={}
8
+ xml.children.each do |child|
9
+ data[:body] = child.content if child.name == 'body'
10
+ data[:author] = Scrobbler::User.new(child.content) if child.name == 'author'
11
+ data[:date] = Time.parse(child.content) if child.name == 'date'
12
+ end
13
+ Shout.new(data)
14
+ end
15
+ end
16
+
17
+ def initialize(input={})
18
+ populate_data(input)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,60 @@
1
+ require 'digest/md5'
2
+
3
+ # exception definitions
4
+ class BadAuthError < StandardError; end
5
+ class BannedError < StandardError; end
6
+ class BadTimeError < StandardError; end
7
+ module Scrobbler
8
+ AUTH_URL = 'http://post.audioscrobbler.com'
9
+ AUTH_VER = '1.2.1'
10
+
11
+ # @todo This is not 2.0!!
12
+ class SimpleAuth
13
+ # you should read last.fm/api/submissions#handshake
14
+
15
+ attr_accessor :user, :password, :client_id, :client_ver
16
+ attr_reader :status, :session_id, :now_playing_url, :submission_url
17
+
18
+ def initialize(args = {})
19
+ @user = args[:user] # last.fm username
20
+ @password = args[:password] # last.fm password
21
+ @client_id = 'rbs' # Client ID assigned by last.fm; Don't change this!
22
+ @client_ver = '0.2.13'
23
+
24
+ raise ArgumentError, 'Missing required argument' if @user.blank? || @password.blank?
25
+
26
+ @connection = REST::Connection.new(AUTH_URL)
27
+ end
28
+
29
+ def handshake!
30
+ password_hash = Digest::MD5.hexdigest(@password)
31
+ timestamp = Time.now.to_i.to_s
32
+ token = Digest::MD5.hexdigest(password_hash + timestamp)
33
+
34
+ query = { :hs => 'true',
35
+ :p => AUTH_VER,
36
+ :c => @client_id,
37
+ :v => @client_ver,
38
+ :u => @user,
39
+ :t => timestamp,
40
+ :a => token }
41
+ result = @connection.get('/', query)
42
+
43
+ @status = result.split(/\n/)[0]
44
+ case @status
45
+ when /OK/
46
+ @session_id, @now_playing_url, @submission_url = result.split(/\n/)[1,3]
47
+ when /BANNED/
48
+ raise BannedError # something is wrong with the gem, check for an update
49
+ when /BADAUTH/
50
+ raise BadAuthError # invalid user/password
51
+ when /FAILED/
52
+ raise RequestFailedError, @status
53
+ when /BADTIME/
54
+ raise BadTimeError # system time is way off
55
+ else
56
+ raise RequestFailedError
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,113 @@
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, :streamable
61
+
62
+ class << self
63
+ def new_from_xml(xml)
64
+ data = {}
65
+ xml.children.each do |child|
66
+ data[:name] = child.content if child.name == 'name'
67
+ data[:count] = child.content.to_i if child.name == 'count'
68
+ data[:url] = child.content if child.name == 'url'
69
+ if child.name == 'streamable'
70
+ if ['1', 'true'].include?(child.content)
71
+ data[:streamable] = true
72
+ else
73
+ data[:streamable] = false
74
+ end
75
+ end
76
+ end
77
+
78
+ Tag.new(data[:name], data)
79
+ end
80
+ end
81
+
82
+ def initialize(name, data={})
83
+ raise ArgumentError, "Name is required" if name.blank?
84
+ @name = name
85
+ @url = data[:url] unless data[:url].nil?
86
+ @count = data[:count] unless data[:count].nil?
87
+ @streamable = data[:streamable] unless data[:streamable].nil?
88
+ end
89
+
90
+ def top_artists(force=false)
91
+ get_response('tag.gettopartists', :top_artists, 'topartists', 'artist', {'tag'=>@name}, force)
92
+ end
93
+
94
+ def top_albums(force=false)
95
+ get_response('tag.gettopalbums', :top_albums, 'topalbums', 'album', {'tag'=>@name}, force)
96
+ end
97
+
98
+ def top_tracks(force=false)
99
+ get_response('tag.gettoptracks', :top_tracks, 'toptracks', 'track', {'tag'=>@name}, force)
100
+ end
101
+
102
+ def Tag.top_tags
103
+ self.get('tag.gettoptags', :toptags, :tag)
104
+ end
105
+
106
+ # Search for tags similar to this one. Returns tags ranked by similarity,
107
+ # based on listening data.
108
+ def similar(force=false)
109
+ params = {:tag => @name}
110
+ get_response('tag.getsimilar', :similar, 'similartags', 'tag', params, force)
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,152 @@
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
+ # Load Helper modules
39
+ include ImageObjectFuncs
40
+ extend ImageClassFuncs
41
+
42
+ attr_accessor :artist, :name, :mbid, :playcount, :rank, :url, :id, :count
43
+ attr_accessor :streamable, :album, :date, :now_playing, :tagcount
44
+ attr_accessor :duration, :listeners
45
+
46
+ class << self
47
+ def new_from_xml(xml, o = {})
48
+ data = self.data_from_xml(xml, o)
49
+ return nil if data[:name].nil?
50
+ if data[:artist].blank?
51
+ if data[:creator].blank?
52
+ data[:artist] = data[:creator];
53
+ else
54
+ raise Error, "Supplied XML to track has no artist or creator"
55
+ end
56
+ end
57
+ Track.new(data[:artist], data)
58
+ end
59
+
60
+ def data_from_xml(xml, o = {})
61
+ o = {:include_album_info => true, :include_artist_info => true}.merge(o)
62
+ data = {}
63
+ xml.children.each do |child|
64
+ data[:name] = child.content if child.name == 'name' || child.name == 'title'
65
+ data[:mbid] = child.content.to_i if child.name == 'mbid'
66
+ data[:id] = child.content.to_i if child.name == 'id'
67
+ data[:duration] = child.content.to_i if child.name == 'duration'
68
+ data[:url] = child.content if child.name == 'url' || child.name == 'identifier'
69
+ data[:date] = Time.parse(child.content) if child.name == 'date'
70
+ data[:listeners] = child.content.to_i if child.name == 'listeners'
71
+ data[:artist] = Artist.new_from_xml(child, o) if (child.name == 'artist' || child.name == 'creator') && o[:include_artist_info]
72
+ data[:album] = Album.new_from_xml(child, o) if child.name == 'album' && o[:include_album_info]
73
+ data[:playcount] = child.content.to_i if child.name == 'playcount'
74
+ data[:tagcount] = child.content.to_i if child.name == 'tagcount'
75
+ maybe_image_node(data, child)
76
+ if child.name == 'streamable'
77
+ if ['1', 'true'].include?(child.content)
78
+ data[:streamable] = true
79
+ else
80
+ data[:streamable] = false
81
+ end
82
+ end
83
+ end
84
+
85
+
86
+ data[:rank] = xml['rank'].to_i if xml['rank']
87
+ data[:now_playing] = true if xml['nowplaying'] && xml['nowplaying'] == 'true'
88
+
89
+ data[:now_playing] = false if data[:now_playing].nil?
90
+ o.merge(data)
91
+ end
92
+ end
93
+
94
+ def initialize(input, data={})
95
+ super()
96
+ #check for old style parameter arguments
97
+ if data.class == String
98
+ data = {:name => data}
99
+ end
100
+
101
+ if input.class == String && data[:mbid] && data[:mbid] == true
102
+ raise ArgumentError, "MBID is required for an MBID query" if input.blank?
103
+ @mbid = input
104
+ load_album_info() unless !data[:include_info].nil? && data[:include_info] == false
105
+ else
106
+ raise ArgumentError, "Artist is required" if input.blank?
107
+ raise ArgumentError, "Name is required" if data[:name].blank?
108
+ @artist = Artist.new(input)
109
+ @name = data[:name]
110
+ load_info() if data[:include_info]
111
+ end
112
+ end
113
+
114
+ def add_tags(tags)
115
+ # This function requires authentication, but SimpleAuth is not yet 2.0
116
+ raise NotImplementedError
117
+ end
118
+
119
+ def ban
120
+ # This function requires authentication, but SimpleAuth is not yet 2.0
121
+ raise NotImplementedError
122
+ end
123
+
124
+ @info_loaded = false
125
+ def load_info
126
+ return nil if @info_loaded
127
+ doc = Base.request('track.getinfo', :artist => @artist.name, :track => @name)
128
+ doc.root.children.each do |child|
129
+ next unless child.name == 'track'
130
+ data = self.class.data_from_xml(child)
131
+ populate_data(data)
132
+ @info_loaded = true
133
+ break
134
+ end
135
+ end
136
+
137
+ def top_fans(force=false)
138
+ get_response('track.gettopfans', :fans, 'topfans', 'user', {:artist=>@artist.name, :track=>@name}, force)
139
+ end
140
+
141
+ def top_tags(force=false)
142
+ get_response('track.gettoptags', :top_tags, 'toptags', 'tag', {:artist=>@artist.name.to_s, :track=>@name}, force)
143
+ end
144
+
145
+ def ==(otherTrack)
146
+ if otherTrack.is_a?(Scrobbler::Track)
147
+ return ((@name == otherTrack.name) && (@artist == otherTrack.artist))
148
+ end
149
+ false
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,222 @@
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
+ # Load Helper modules
50
+ include ImageObjectFuncs
51
+ extend ImageClassFuncs
52
+
53
+ attr_reader :username, :url, :weight, :match, :realname, :name
54
+
55
+ class << self
56
+ def new_from_libxml(xml)
57
+ data = {}
58
+ xml.children.each do |child|
59
+ data[:name] = child.content if child.name == 'name'
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
68
+
69
+ def find(*args)
70
+ options = {:include_profile => false}
71
+ options.merge!(args.pop) if args.last.is_a?(Hash)
72
+ users = args.flatten.inject([]) { |users, u| users << User.new(u, options); users }
73
+ users.length == 1 ? users.pop : users
74
+ end
75
+ end
76
+
77
+ def initialize(username, input={})
78
+ data = {:include_profile => false}.merge(input)
79
+ raise ArgumentError if username.blank?
80
+ @username = username
81
+ @name = @username
82
+ load_profile() if data[:include_profile]
83
+ populate_data(data)
84
+ end
85
+
86
+ # Get a list of upcoming events that this user is attending.
87
+ #
88
+ # Supports ical, ics or rss as its format
89
+ def events(force=false)
90
+ get_response('user.getevents', :events, 'events', 'event', {'user'=>@username}, force)
91
+ end
92
+
93
+ # Get a list of the user's friends on Last.fm.
94
+ def friends(force=false, page=1, limit=50)
95
+ get_response('user.getfriends', :friends, 'friends', 'user', {'user'=>@username, 'page'=>page.to_s, 'limit'=>limit.to_s}, force)
96
+ end
97
+
98
+ # Get information about a user profile.
99
+ def load_info
100
+ # This function requires authentication, but SimpleAuth is not yet 2.0
101
+ raise NotImplementedError
102
+ end
103
+
104
+ # Get the last 50 tracks loved by a user.
105
+ def loved_tracks(force=false)
106
+ get_response('user.getlovedtracks', :loved_tracks, 'lovedtracks', 'track', {'user'=>@username}, force)
107
+ end
108
+
109
+ # Get a list of a user's neighbours on Last.fm.
110
+ def neighbours(force=false)
111
+ get_response('user.getneighbours', :neighbours, 'neighbours', 'user', {'user'=>@username}, force)
112
+ end
113
+
114
+ # Get a paginated list of all events a user has attended in the past.
115
+ def past_events(format=:ics)
116
+ # This needs a Event class, which is yet not available
117
+ raise NotImplementedError
118
+ end
119
+
120
+ # Get a list of a user's playlists on Last.fm.
121
+ def playlists(force=false)
122
+ #(api_method, instance_name, parent, element, parameters, force=false)
123
+ get_response('user.getplaylists', :playlist, 'playlists', 'playlist', {'user'=>@username}, force)
124
+ end
125
+
126
+ # Get a list of the recent tracks listened to by this user. Indicates now
127
+ # playing track if the user is currently listening.
128
+ #
129
+ # Possible parameters:
130
+ # - limit: An integer used to limit the number of tracks returned.
131
+ def recent_tracks(force=false, parameters={})
132
+ parameters.merge!({'user' => @username})
133
+ get_response('user.getrecenttracks', :recent_tracks, 'recenttracks', 'track', parameters, force)
134
+ end
135
+
136
+ # Get Last.fm artist recommendations for a user
137
+ def recommended_artists
138
+ # This function require authentication, but SimpleAuth is not yet 2.0
139
+ raise NotImplementedError
140
+ end
141
+
142
+ # Get a paginated list of all events recommended to a user by Last.fm,
143
+ # based on their listening profile.
144
+ def recommended_events
145
+ # This function require authentication, but SimpleAuth is not yet 2.0
146
+ raise NotImplementedError
147
+ end
148
+
149
+ # Get shouts for this user.
150
+ def shouts
151
+ # This needs a Shout class which is yet not available
152
+ raise NotImplementedError
153
+ end
154
+
155
+ # Get the top albums listened to by a user. You can stipulate a time period.
156
+ # Sends the overall chart by default.
157
+ def top_albums(force=false, period='overall')
158
+ get_response('user.gettopalbums', :top_albums, 'topalbums', 'album', {'user'=>@username, 'period'=>period}, force)
159
+ end
160
+
161
+ # Get the top artists listened to by a user. You can stipulate a time
162
+ # period. Sends the overall chart by default.
163
+ def top_artists(force=false, period='overall')
164
+ get_response('user.gettopartists', :top_artists, 'topartists', 'artist', {'user' => @username, 'period'=>period}, force)
165
+ end
166
+
167
+ # Get the top tags used by this user.
168
+ def top_tags(force=false)
169
+ get_response('user.gettoptags', :top_tags, 'toptags', 'tag', {'user'=>@username}, force)
170
+ end
171
+
172
+ # Get the top tracks listened to by a user. You can stipulate a time period.
173
+ # Sends the overall chart by default.
174
+ def top_tracks(force=false, period='overall')
175
+ get_response('user.gettoptracks', :top_tracks, 'toptracks', 'track', {'user'=>@username, 'period'=>period}, force)
176
+ end
177
+
178
+ # Get an album chart for a user profile, for a given date range. If no date
179
+ # range is supplied, it will return the most recent album chart for this
180
+ # user.
181
+ def weekly_album_chart(from=nil, to=nil)
182
+ parameters = {'user' => @username}
183
+ parameters['from'] = from unless from.nil?
184
+ parameters['to'] = to unless to.nil?
185
+ get_response('user.getweeklyalbumchart', nil, 'weeklyalbumchart', 'album', parameters, true)
186
+ end
187
+
188
+ # Get an artist chart for a user profile, for a given date range. If no date
189
+ # range is supplied, it will return the most recent artist chart for this
190
+ # user.
191
+ def weekly_artist_chart(from=nil, to=nil)
192
+ parameters = {'user' => @username}
193
+ parameters['from'] = from unless from.nil?
194
+ parameters['to'] = to unless to.nil?
195
+ get_response('user.getweeklyartistchart', nil, 'weeklyartistchart', 'artist', parameters, true)
196
+ end
197
+
198
+ # Get a list of available charts for this user, expressed as date ranges
199
+ # which can be sent to the chart services.
200
+ def weekly_chart_list(force=false)
201
+ # @todo
202
+ raise NotImplementedError
203
+ end
204
+
205
+ # Get a track chart for a user profile, for a given date range. If no date
206
+ # range is supplied, it will return the most recent track chart for this
207
+ # user.
208
+ def weekly_track_chart(from=nil, to=nil)
209
+ parameters = {'user' => @username}
210
+ parameters['from'] = from unless from.nil?
211
+ parameters['to'] = to unless to.nil?
212
+ get_response('user.getweeklytrackchart', nil, 'weeklytrackchart', 'track', parameters, true)
213
+ end
214
+
215
+ # Shout on this user's shoutbox
216
+ def shout(message)
217
+ # This function require authentication, but SimpleAuth is not yet 2.0
218
+ raise NotImplementedError
219
+ end
220
+
221
+ end
222
+ end