scrobbler-ng 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. data/.gitignore +11 -0
  2. data/MIT-LICENSE +19 -0
  3. data/README.rdoc +104 -0
  4. data/Rakefile +11 -0
  5. data/VERSION.yml +4 -0
  6. data/examples/album.rb +19 -0
  7. data/examples/artist.rb +13 -0
  8. data/examples/scrobble.rb +31 -0
  9. data/examples/tag.rb +11 -0
  10. data/examples/track.rb +6 -0
  11. data/examples/user.rb +14 -0
  12. data/lib/scrobbler/album.rb +165 -0
  13. data/lib/scrobbler/artist.rb +169 -0
  14. data/lib/scrobbler/auth.rb +47 -0
  15. data/lib/scrobbler/base.rb +124 -0
  16. data/lib/scrobbler/event.rb +105 -0
  17. data/lib/scrobbler/geo.rb +27 -0
  18. data/lib/scrobbler/helper/image.rb +52 -0
  19. data/lib/scrobbler/helper/streamable.rb +40 -0
  20. data/lib/scrobbler/library.rb +123 -0
  21. data/lib/scrobbler/playing.rb +49 -0
  22. data/lib/scrobbler/playlist.rb +45 -0
  23. data/lib/scrobbler/radio.rb +12 -0
  24. data/lib/scrobbler/rest.rb +47 -0
  25. data/lib/scrobbler/scrobble.rb +116 -0
  26. data/lib/scrobbler/session.rb +9 -0
  27. data/lib/scrobbler/shout.rb +21 -0
  28. data/lib/scrobbler/simpleauth.rb +60 -0
  29. data/lib/scrobbler/tag.rb +113 -0
  30. data/lib/scrobbler/track.rb +138 -0
  31. data/lib/scrobbler/user.rb +222 -0
  32. data/lib/scrobbler/venue.rb +67 -0
  33. data/lib/scrobbler.rb +34 -0
  34. data/tasks/jeweler.rake +18 -0
  35. data/tasks/rdoc.rake +7 -0
  36. data/tasks/tests.rake +13 -0
  37. data/tasks/yardoc.rake +8 -0
  38. data/test/fixtures/xml/album/info.xml +43 -0
  39. data/test/fixtures/xml/artist/fans.xml +52 -0
  40. data/test/fixtures/xml/artist/info.xml +58 -0
  41. data/test/fixtures/xml/artist/similar.xml +1004 -0
  42. data/test/fixtures/xml/artist/topalbums.xml +61 -0
  43. data/test/fixtures/xml/artist/toptags.xml +19 -0
  44. data/test/fixtures/xml/artist/toptracks.xml +62 -0
  45. data/test/fixtures/xml/auth/session.xml +7 -0
  46. data/test/fixtures/xml/auth/token.xml +3 -0
  47. data/test/fixtures/xml/event/attend.xml +3 -0
  48. data/test/fixtures/xml/event/attendees.xml +53 -0
  49. data/test/fixtures/xml/event/event.xml +35 -0
  50. data/test/fixtures/xml/event/shouts.xml +35 -0
  51. data/test/fixtures/xml/geo/events-distance-p1.xml +151 -0
  52. data/test/fixtures/xml/geo/events-lat-long.xml +167 -0
  53. data/test/fixtures/xml/geo/events-p1.xml +152 -0
  54. data/test/fixtures/xml/geo/events-p2.xml +380 -0
  55. data/test/fixtures/xml/geo/events-p3.xml +427 -0
  56. data/test/fixtures/xml/geo/top_artists-p1.xml +76 -0
  57. data/test/fixtures/xml/geo/top_tracks-p1.xml +77 -0
  58. data/test/fixtures/xml/library/albums-f30.xml +454 -0
  59. data/test/fixtures/xml/library/albums-p1.xml +754 -0
  60. data/test/fixtures/xml/library/albums-p2.xml +754 -0
  61. data/test/fixtures/xml/library/albums-p3.xml +754 -0
  62. data/test/fixtures/xml/library/albums-p4.xml +739 -0
  63. data/test/fixtures/xml/library/albums-p5.xml +754 -0
  64. data/test/fixtures/xml/library/albums-p6.xml +754 -0
  65. data/test/fixtures/xml/library/albums-p7.xml +739 -0
  66. data/test/fixtures/xml/library/albums-p8.xml +724 -0
  67. data/test/fixtures/xml/library/artists-f30.xml +334 -0
  68. data/test/fixtures/xml/library/artists-p1.xml +554 -0
  69. data/test/fixtures/xml/library/artists-p2.xml +554 -0
  70. data/test/fixtures/xml/library/artists-p3.xml +554 -0
  71. data/test/fixtures/xml/library/artists-p4.xml +554 -0
  72. data/test/fixtures/xml/library/artists-p5.xml +554 -0
  73. data/test/fixtures/xml/library/artists-p6.xml +554 -0
  74. data/test/fixtures/xml/library/artists-p7.xml +444 -0
  75. data/test/fixtures/xml/library/tracks-f30.xml +472 -0
  76. data/test/fixtures/xml/library/tracks-p1.xml +789 -0
  77. data/test/fixtures/xml/library/tracks-p10.xml +771 -0
  78. data/test/fixtures/xml/library/tracks-p11.xml +792 -0
  79. data/test/fixtures/xml/library/tracks-p12.xml +777 -0
  80. data/test/fixtures/xml/library/tracks-p13.xml +762 -0
  81. data/test/fixtures/xml/library/tracks-p14.xml +759 -0
  82. data/test/fixtures/xml/library/tracks-p15.xml +762 -0
  83. data/test/fixtures/xml/library/tracks-p16.xml +756 -0
  84. data/test/fixtures/xml/library/tracks-p17.xml +780 -0
  85. data/test/fixtures/xml/library/tracks-p18.xml +756 -0
  86. data/test/fixtures/xml/library/tracks-p19.xml +774 -0
  87. data/test/fixtures/xml/library/tracks-p2.xml +765 -0
  88. data/test/fixtures/xml/library/tracks-p20.xml +777 -0
  89. data/test/fixtures/xml/library/tracks-p21.xml +777 -0
  90. data/test/fixtures/xml/library/tracks-p22.xml +771 -0
  91. data/test/fixtures/xml/library/tracks-p23.xml +765 -0
  92. data/test/fixtures/xml/library/tracks-p24.xml +783 -0
  93. data/test/fixtures/xml/library/tracks-p25.xml +777 -0
  94. data/test/fixtures/xml/library/tracks-p26.xml +777 -0
  95. data/test/fixtures/xml/library/tracks-p27.xml +756 -0
  96. data/test/fixtures/xml/library/tracks-p28.xml +771 -0
  97. data/test/fixtures/xml/library/tracks-p29.xml +753 -0
  98. data/test/fixtures/xml/library/tracks-p3.xml +771 -0
  99. data/test/fixtures/xml/library/tracks-p30.xml +780 -0
  100. data/test/fixtures/xml/library/tracks-p31.xml +753 -0
  101. data/test/fixtures/xml/library/tracks-p32.xml +771 -0
  102. data/test/fixtures/xml/library/tracks-p33.xml +762 -0
  103. data/test/fixtures/xml/library/tracks-p34.xml +538 -0
  104. data/test/fixtures/xml/library/tracks-p4.xml +792 -0
  105. data/test/fixtures/xml/library/tracks-p5.xml +780 -0
  106. data/test/fixtures/xml/library/tracks-p6.xml +789 -0
  107. data/test/fixtures/xml/library/tracks-p7.xml +789 -0
  108. data/test/fixtures/xml/library/tracks-p8.xml +780 -0
  109. data/test/fixtures/xml/library/tracks-p9.xml +774 -0
  110. data/test/fixtures/xml/tag/similar.xml +254 -0
  111. data/test/fixtures/xml/tag/topalbums.xml +805 -0
  112. data/test/fixtures/xml/tag/topartists.xml +605 -0
  113. data/test/fixtures/xml/tag/toptags.xml +1254 -0
  114. data/test/fixtures/xml/tag/toptracks.xml +852 -0
  115. data/test/fixtures/xml/track/fans.xml +34 -0
  116. data/test/fixtures/xml/track/info.xml +53 -0
  117. data/test/fixtures/xml/track/toptags.xml +504 -0
  118. data/test/fixtures/xml/user/events.xml +401 -0
  119. data/test/fixtures/xml/user/friends.xml +30 -0
  120. data/test/fixtures/xml/user/lovedtracks.xml +678 -0
  121. data/test/fixtures/xml/user/neighbours.xml +23 -0
  122. data/test/fixtures/xml/user/playlists.xml +61 -0
  123. data/test/fixtures/xml/user/profile.xml +12 -0
  124. data/test/fixtures/xml/user/recentbannedtracks.xml +24 -0
  125. data/test/fixtures/xml/user/recentlovedtracks.xml +24 -0
  126. data/test/fixtures/xml/user/recenttracks.xml +124 -0
  127. data/test/fixtures/xml/user/systemrecs.xml +18 -0
  128. data/test/fixtures/xml/user/topalbums.xml +61 -0
  129. data/test/fixtures/xml/user/topartists.xml +41 -0
  130. data/test/fixtures/xml/user/toptags.xml +44 -0
  131. data/test/fixtures/xml/user/toptracks.xml +65 -0
  132. data/test/fixtures/xml/user/weeklyalbumchart.xml +256 -0
  133. data/test/fixtures/xml/user/weeklyartistchart.xml +220 -0
  134. data/test/fixtures/xml/user/weeklytrackchart.xml +746 -0
  135. data/test/fixtures/xml/venue/events.xml +151 -0
  136. data/test/fixtures/xml/venue/venue.xml +16 -0
  137. data/test/mocks/rest.rb +212 -0
  138. data/test/spec_helper.rb +7 -0
  139. data/test/test_helper.rb +20 -0
  140. data/test/unit/album_spec.rb +51 -0
  141. data/test/unit/artist_spec.rb +130 -0
  142. data/test/unit/auth_spec.rb +36 -0
  143. data/test/unit/event_spec.rb +109 -0
  144. data/test/unit/geo_spec.rb +148 -0
  145. data/test/unit/library_spec.rb +133 -0
  146. data/test/unit/playing_test.rb +53 -0
  147. data/test/unit/playlist_spec.rb +25 -0
  148. data/test/unit/radio_spec.rb +22 -0
  149. data/test/unit/scrobble_spec.rb +55 -0
  150. data/test/unit/scrobble_test.rb +69 -0
  151. data/test/unit/simpleauth_test.rb +45 -0
  152. data/test/unit/tag_spec.rb +101 -0
  153. data/test/unit/track_spec.rb +95 -0
  154. data/test/unit/user_spec.rb +264 -0
  155. data/test/unit/venue_spec.rb +104 -0
  156. metadata +253 -0
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ rdoc
2
+ doc/*
3
+ pkg/*
4
+ nbproject
5
+ scrobbler-*.gem
6
+ *.rb~
7
+ *.rake~
8
+ coverage
9
+ spec.html
10
+ catalog.xml
11
+ .yardoc
data/MIT-LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2007-2010 John Nunemaker, Kurt Schrader, Uwe L. Korn
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,104 @@
1
+ =Scrobbler-ng
2
+
3
+ Scrobbler-ng is a wrapper for the audioscrobbler web services (http://www.audioscrobbler.net/data/webservices/).
4
+
5
+ Currently updating to use the 2.0 API. It is very much a work in progress.
6
+
7
+ Below is just a sampling of how easy this lib is to use, full documentation of
8
+ all functions is available at http://rdoc.info/projects/xhochy/scrobbler
9
+
10
+ == Initialization
11
+
12
+ Scrobbler::Base.api_key = 'foo123'
13
+
14
+ == Users
15
+
16
+ user = Scrobbler::User.new('jnunemaker')
17
+
18
+ puts "#{user.username}'s Recent Tracks"
19
+ puts "=" * (user.username.length + 16)
20
+ user.recent_tracks.each { |t| puts t.name }
21
+
22
+ puts
23
+ puts
24
+
25
+ puts "#{user.username}'s Top Tracks"
26
+ puts "=" * (user.username.length + 13)
27
+ user.top_tracks.each { |t| puts "(#{t.playcount}) #{t.name}" }
28
+
29
+ == Albums
30
+
31
+ album = Scrobbler::Album.new('Some Hearts', :artist => 'Carrie Underwood', :include_info => true)
32
+
33
+ puts "Album: #{album.name}"
34
+ puts "Artist: #{album.artist}"
35
+ puts "Playcount: #{album.playcount}"
36
+ puts "URL: #{album.url}"
37
+ puts "Release Date: #{album.release_date.strftime('%m/%d/%Y')}"
38
+
39
+ ==Artists
40
+
41
+ artist = Scrobbler::Artist.new('Carrie Underwood')
42
+
43
+ puts 'Top Tracks'
44
+ puts "=" * 10
45
+ artist.top_tracks.each { |t| puts "(#{t.playcount}) #{t.name}" }
46
+
47
+ puts
48
+
49
+ puts 'Similar Artists'
50
+ puts "=" * 15
51
+ artist.similar.each { |a| puts "(#{a.match}%) #{a.name}" }
52
+
53
+ ==Geo
54
+
55
+ geo = Scrobbler::Geo.new()
56
+
57
+ puts 'Events'
58
+ puts "=" * 10
59
+ geo.events(:location => 'Manchester').each { |e| puts "(#{e.id}) #{e.title}" }
60
+
61
+ puts
62
+
63
+ puts 'Top Artists'
64
+ puts "=" * 10
65
+ geo.top_artists(:location => 'Manchester').each { |a| puts "(#{a.name}) #{a.playcount}" }
66
+
67
+ puts
68
+
69
+ puts 'Top Tracks'
70
+ puts "=" * 10
71
+ geo.top_tracks(:location => 'Manchester').each { |t| puts "(#{a.name}) #{a.playcount}" }
72
+
73
+
74
+ ==Tags
75
+
76
+ tag = Scrobbler::Tag.new('country')
77
+
78
+ puts 'Top Albums'
79
+ tag.top_albums.each { |a| puts "(#{a.count}) #{a.name} by #{a.artist}" }
80
+
81
+ puts
82
+
83
+ puts 'Top Tracks'
84
+ tag.top_tracks.each { |t| puts "(#{t.count}) #{t.name} by #{t.artist}" }
85
+
86
+ ==Tracks
87
+
88
+ track = Scrobbler::Track.new('Carrie Underwood', 'Before He Cheats')
89
+ puts 'Fans'
90
+ puts "=" * 4
91
+ track.top_fans.each { |u| puts "(#{u.weight}) #{u.username}" }
92
+
93
+ == Simple Authentication (for Scrobbling)
94
+
95
+ work in progress...
96
+
97
+ == Scrobbling
98
+
99
+ work in progress...
100
+
101
+ == Now Playing Submission
102
+
103
+ work in progress...
104
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # Rakefile
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+ require 'rake/rdoctask'
7
+ require 'spec/rake/spectask'
8
+
9
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
10
+
11
+ task :default => 'test:unit'
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 0
3
+ :patch: 0
4
+ :major: 2
data/examples/album.rb ADDED
@@ -0,0 +1,19 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'scrobbler'))
2
+
3
+ Scrobbler::Base::api_key = "..add..";
4
+
5
+ album = Scrobbler::Album.new('Some Hearts', :artist => 'Carrie Underwood', :include_info => true)
6
+
7
+ puts "Album: #{album.name}"
8
+ puts "Artist: #{album.artist}"
9
+ puts "Playcount: #{album.playcount}"
10
+ puts "URL: #{album.url}"
11
+ puts "Release Date: #{album.release_date.strftime('%m/%d/%Y')}"
12
+
13
+ puts
14
+ puts
15
+
16
+ puts "Tracks"
17
+ longest_track_name = album.tracks.collect(&:name).sort { |x, y| y.length <=> x.length }.first.length
18
+ puts "=" * longest_track_name
19
+ album.tracks.each { |t| puts t.name }
@@ -0,0 +1,13 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'scrobbler'))
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}" }
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'scrobbler'))
2
+
3
+ auth = Scrobbler::SimpleAuth.new(:user => 'chunky', :password => 'bacon')
4
+ auth.handshake!
5
+
6
+ puts "Auth Status: #{auth.status}"
7
+ puts "Session ID: #{auth.session_id}"
8
+ puts "Now Playing URL: #{auth.now_playing_url}"
9
+ puts "Submission URL: #{auth.submission_url}"
10
+
11
+ scrobble = Scrobbler::Scrobble.new(:session_id => auth.session_id,
12
+ :submission_url => auth.submission_url,
13
+ :artist => 'Coldplay',
14
+ :track => 'Viva La Vida',
15
+ :album => 'Viva La Vida',
16
+ :time => Time.new,
17
+ :length => 244,
18
+ :track_number => 7)
19
+ scrobble.submit!
20
+ puts "Scrobbler Submission Status: #{scrobble.status}"
21
+
22
+ playing = Scrobbler::Playing.new(:session_id => auth.session_id,
23
+ :now_playing_url => auth.now_playing_url,
24
+ :artist => 'Anberlin',
25
+ :track => 'A Day Late',
26
+ :album => 'Never Take Friendship Personal',
27
+ :length => 214,
28
+ :track_number => 5)
29
+
30
+ playing.submit!
31
+ puts "Playing Submission Status: #{playing.status}"
data/examples/tag.rb ADDED
@@ -0,0 +1,11 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'scrobbler'))
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}" }
data/examples/track.rb ADDED
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'scrobbler'))
2
+
3
+ track = Scrobbler::Track.new('Carrie Underwood', 'Before He Cheats')
4
+ puts 'Fans'
5
+ puts "=" * 4
6
+ track.fans.each { |u| puts "(#{u.weight}) #{u.username}" }
data/examples/user.rb ADDED
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'scrobbler'))
2
+
3
+ user = Scrobbler::User.new('jnunemaker')
4
+
5
+ puts "#{user.username}'s Recent Tracks"
6
+ puts "=" * (user.username.length + 16)
7
+ user.recent_tracks.each { |t| puts t.name }
8
+
9
+ puts
10
+ puts
11
+
12
+ puts "#{user.username}'s Top Tracks"
13
+ puts "=" * (user.username.length + 13)
14
+ user.top_tracks.each { |t| puts "(#{t.playcount}) #{t.name}" }
@@ -0,0 +1,165 @@
1
+ # Getting information about an album such as release date and the tracks on it is very easy.
2
+ #
3
+ # album = Scrobbler::Album.new('Carrie Underwood', 'Some Hearts', :include_info => true)
4
+ #
5
+ # puts "Album: #{album.name}"
6
+ # puts "Artist: #{album.artist}"
7
+ # puts "Reach: #{album.reach}"
8
+ # puts "URL: #{album.url}"
9
+ # puts "Release Date: #{album.release_date.strftime('%m/%d/%Y')}"
10
+ #
11
+ # puts
12
+ # puts
13
+ #
14
+ # puts "Tracks"
15
+ # longest_track_name = album.tracks.collect(&:name).sort { |x, y| y.length <=> x.length }.first.length
16
+ # puts "=" * longest_track_name
17
+ # album.tracks.each { |t| puts t.name }
18
+ #
19
+ # Would output:
20
+ #
21
+ # Album: Some Hearts
22
+ # Artist: Carrie Underwood
23
+ # Reach: 18729
24
+ # URL: http://www.last.fm/music/Carrie+Underwood/Some+Hearts
25
+ # Release Date: 11/15/2005
26
+ #
27
+ #
28
+ # Tracks
29
+ # ===============================
30
+ # Wasted
31
+ # Don't Forget to Remember Me
32
+ # Some Hearts
33
+ # Jesus, Take the Wheel
34
+ # The Night Before (Life Goes On)
35
+ # Lessons Learned
36
+ # Before He Cheats
37
+ # Starts With Goodbye
38
+ # I Just Can't Live a Lie
39
+ # We're Young and Beautiful
40
+ # That's Where It Is
41
+ # Whenever You Remember
42
+ # I Ain't in Checotah Anymore
43
+ # Inside Your Heaven
44
+ #
45
+ module Scrobbler
46
+ # @todo Add missing functions that require authentication
47
+ class Album < Base
48
+ mixins :image
49
+
50
+ attr_reader :artist, :artist_mbid, :name, :mbid, :playcount, :rank, :url
51
+ attr_reader :reach, :release_date, :listeners, :playcount, :top_tags
52
+ attr_reader :image_large, :image_medium, :image_small, :tagcount
53
+
54
+ # needed on top albums for tag
55
+ attr_reader :count
56
+
57
+ # needed for weekly album charts
58
+ attr_reader :chartposition, :position
59
+
60
+ class << self
61
+ def new_from_libxml(xml)
62
+ data = {}
63
+
64
+ xml.children.each do |child|
65
+ data[:name] = child.content if ['name', 'title'].include?(child.name)
66
+ data[:playcount] = child.content.to_i if child.name == 'playcount'
67
+ data[:tagcount] = child.content.to_i if child.name == 'tagcount'
68
+ data[:mbid] = child.content if child.name == 'mbid'
69
+ data[:url] = child.content if child.name == 'url'
70
+ data[:artist] = Artist.new_from_libxml(child) if child.name == 'artist'
71
+ maybe_image_node(data, child)
72
+ end
73
+
74
+ # If we have not found anything in the content of this node yet then
75
+ # this must be a simple artist node which has the name of the artist
76
+ # as its content
77
+ data[:name] = xml.content if data == {}
78
+
79
+ # Get all information from the root's attributes
80
+ data[:mbid] = xml['mbid'] if xml['mbid']
81
+ data[:rank] = xml['rank'].to_i if xml['rank']
82
+ data[:position] = xml['position'].to_i if xml['position']
83
+
84
+ # If there is no name defined, than this was an empty album tag
85
+ return nil if data[:name].empty?
86
+
87
+ Album.new(data[:name], data)
88
+ end
89
+ end
90
+
91
+ # If the additional parameter :include_info is set to true, additional
92
+ # information is loaded
93
+ #
94
+ # @todo Albums should be able to be created via a MusicBrainz id too
95
+ def initialize(name, input={})
96
+ super()
97
+ # Support old version of initialize where we had (artist_name, album_name)
98
+ if input.class == String
99
+ data = {:artist => name, :name => input}
100
+ name = input, input = data
101
+ end
102
+ data = {:include_profile => false}.merge(input)
103
+ raise ArgumentError, "Artist or mbid is required" if data[:artist].nil? && data[:mbid].nil?
104
+ raise ArgumentError, "Name is required" if name.empty?
105
+ @name = name
106
+ populate_data(data)
107
+ load_info() if data[:include_info]
108
+ end
109
+
110
+ # Indicates if the info was already loaded
111
+ @info_loaded = false
112
+
113
+ # Load additional information about this album
114
+ #
115
+ # Calls "album.getinfo" REST method
116
+ #
117
+ # @todo Parse wiki content
118
+ # @todo Add language code for wiki translation
119
+ def load_info
120
+ return nil if @info_loaded
121
+ xml = Base.request('album.getinfo', {'artist' => @artist, 'album' => @name})
122
+ unless xml.root['status'] == 'failed'
123
+ xml.root.children.each do |childL1|
124
+ next unless childL1.name == 'album'
125
+
126
+ childL1.children.each do |childL2|
127
+ @url = childL2.content if childL2.name == 'url'
128
+ @id = childL2.content if childL2.name == 'id'
129
+ @mbid = childL2.content if childL2.name == 'mbid'
130
+ @release_date = Time.parse(childL2.content.strip) if childL2.name == 'releasedate'
131
+ check_image_node childL2
132
+ @listeners = childL2.content.to_i if childL2.name == 'listeners'
133
+ @playcount = childL2.content.to_i if childL2.name == 'playcount'
134
+ if childL2.name == 'toptags'
135
+ @top_tags = []
136
+ childL2.children.each do |childL3|
137
+ next unless childL3.name == 'tag'
138
+ @top_tags << Tag.new_from_libxml(childL3)
139
+ end # childL2.children.each do |childL3|
140
+ end # if childL2.name == 'toptags'
141
+ end # childL1.children.each do |childL2|
142
+ end # xml.children.each do |childL1|
143
+ @info_loaded = true
144
+ end
145
+ end
146
+
147
+ # Tag an album using a list of user supplied tags.
148
+ def add_tags(tags)
149
+ # This function require authentication, but SimpleAuth is not yet 2.0
150
+ raise NotImplementedError
151
+ end
152
+
153
+ # Get the tags applied by an individual user to an album on Last.fm.
154
+ def tags()
155
+ # This function require authentication, but SimpleAuth is not yet 2.0
156
+ raise NotImplementedError
157
+ end
158
+
159
+ # Remove a user's tag from an album.
160
+ def remove_tag()
161
+ # This function require authentication, but SimpleAuth is not yet 2.0
162
+ raise NotImplementedError
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,169 @@
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
+ # @todo Add missing functions that require authentication
61
+ # @todo Integrate search functionality into this class which is already implemented in Scrobbler::Search
62
+ class Artist < Base
63
+ mixins :image, :streamable
64
+
65
+ attr_accessor :name, :mbid, :playcount, :rank, :url, :count
66
+ attr_accessor :chartposition
67
+ attr_accessor :match, :tagcount, :listeners
68
+
69
+ class << self
70
+ def new_from_libxml(xml)
71
+ data = {}
72
+
73
+ # Get all information from the root's children nodes
74
+ xml.children.each do |child|
75
+ data[:playcount] = child.content.to_i if child.name == 'playcount'
76
+ data[:mbid] = child.content if child.name == 'mbid'
77
+ data[:url] = child.content if child.name == 'url'
78
+ data[:match] = child.content.to_i if child.name == 'match'
79
+ data[:tagcount] = child.content.to_i if child.name == 'tagcount'
80
+ data[:chartposition] = child.content if child.name == 'chartposition'
81
+ data[:name] = child.content if child.name == 'name'
82
+ maybe_streamable_node(data, child)
83
+ maybe_image_node(data, child)
84
+ end
85
+
86
+ # If we have not found anything in the content of this node yet then
87
+ # this must be a simple artist node which has the name of the artist
88
+ # as its content
89
+ data[:name] = xml.content if data == {}
90
+
91
+ # Get all information from the root's attributes
92
+ data[:name] = xml['name'] if xml['name']
93
+ data[:rank] = xml['rank'].to_i if xml['rank']
94
+ maybe_streamable_attribute data, xml
95
+ data[:mbid] = xml['mbid'] if xml['mbid']
96
+
97
+ # Step 3 fill the object
98
+ Artist.new(data[:name], data)
99
+ end
100
+ end
101
+
102
+ def initialize(name, data = {})
103
+ super()
104
+ raise ArgumentError, "Name is required" if name.empty?
105
+ @name = name
106
+ populate_data(data)
107
+ end
108
+
109
+ # Get the URL to the ical or rss representation of the current events that
110
+ # a artist will play
111
+ #
112
+ # @todo Use the API function and parse that into a common ruby structure
113
+ def current_events(format=:ics)
114
+ format = :ics if format.to_s == 'ical'
115
+ raise ArgumentError unless ['ics', 'rss'].include?(format.to_s)
116
+ "#{API_URL.chop}/2.0/artist/#{CGI::escape(@name)}/events.#{format}"
117
+ end
118
+
119
+ def similar(force=false)
120
+ get_response('artist.getsimilar', :similar, 'similarartists', 'artist', {'artist' => @name}, force)
121
+ end
122
+
123
+ def top_fans(force=false)
124
+ get_response('artist.gettopfans', :top_fans, 'topfans', 'user', {'artist' => @name}, force)
125
+ end
126
+
127
+ def top_tracks(force=false)
128
+ get_response('artist.gettoptracks', :top_tracks, 'toptracks', 'track', {'artist'=>@name}, force)
129
+ end
130
+
131
+ def top_albums(force=false)
132
+ get_response('artist.gettopalbums', :top_albums, 'topalbums', 'album', {'artist'=>@name}, force)
133
+ end
134
+
135
+ def top_tags(force=false)
136
+ get_response('artist.gettoptags', :top_tags, 'toptags', 'tag', {'artist' => @name}, force)
137
+ end
138
+
139
+ @info_loaded = false
140
+ # Get the metadata
141
+ def load_info
142
+ doc = Base.request('artist.getinfo', {'artist' => @name})
143
+ doc.root.children.each do |childL1|
144
+ next unless childL1.name == 'artist'
145
+ childL1.children.each do |child|
146
+ @mbid = child.content if child.name == 'mbid'
147
+ @url = child.content if child.name == 'url'
148
+ check_image_node child
149
+ check_streamable_node child
150
+ if child.name == 'stats'
151
+ child.children.each do |childL3|
152
+ @listeners = childL3.content.to_i if childL3.name == 'listeners'
153
+ @playcount = childL3.content.to_i if childL3.name == 'playcount'
154
+ end
155
+ end
156
+ end
157
+ end
158
+ @info_loaded = true
159
+ end # load_info
160
+
161
+ def ==(otherArtist)
162
+ if otherArtist.is_a?(Scrobbler::Artist)
163
+ return (@name == otherArtist.name)
164
+ end
165
+ false
166
+ end
167
+
168
+ end
169
+ end
@@ -0,0 +1,47 @@
1
+ module Scrobbler
2
+ # @todo everything
3
+ class Auth < Base
4
+
5
+ def initialize(username)
6
+ super()
7
+ @username = username
8
+ end
9
+
10
+ def session(token)
11
+ doc = Base.request('auth.getsession', :signed => true, :token => token)
12
+ asession = {}
13
+ doc.root.children.each do |child1|
14
+ next unless child1.name == 'session'
15
+ child1.children.each do |child2|
16
+ if child2.name == 'name'
17
+ asession[:name] = child2.content
18
+ elsif child2.name == 'key'
19
+ asession[:key] = child2.content
20
+ elsif child2.name == 'subscriber'
21
+ asession[:subscriber] = true if child2.content == '1'
22
+ asession[:subscriber] = false unless child2.content == '1'
23
+ end
24
+ end
25
+ end
26
+ Scrobbler::Session.new(asession)
27
+ end
28
+
29
+ def token
30
+ doc = Base.request('auth.gettoken', :signed => true)
31
+ stoken = ''
32
+ doc.root.children.each do |child|
33
+ next unless child.name == 'token'
34
+ stoken = child.content
35
+ end
36
+ stoken
37
+ end
38
+
39
+ def url(options={})
40
+ options[:token] = token if options[:token].nil?
41
+ options[:api_key] = @@api_key
42
+ "http://www.last.fm/api/auth/?api_key=#{options[:api_key]}&token=#{options[:token]}"
43
+ end
44
+
45
+ end
46
+ end
47
+