scrobbler-ng 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+