scrobbler-ng 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -9,3 +9,6 @@ coverage
9
9
  spec.html
10
10
  catalog.xml
11
11
  .yardoc
12
+ scrobbler-ng.gemspec
13
+ tmp
14
+ .project
data/VERSION.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  ---
2
2
  :minor: 0
3
- :patch: 0
3
+ :patch: 1
4
4
  :major: 2
5
+ :build:
@@ -1,112 +1,106 @@
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
- #
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('basexml.rb', File.dirname(__FILE__))
4
+
45
5
  module Scrobbler
46
6
  # @todo Add missing functions that require authentication
47
- class Album < Base
48
- mixins :image
7
+ class Album < BaseXml
8
+ include Scrobbler::ImageObjectFuncs
49
9
 
50
10
  attr_reader :artist, :artist_mbid, :name, :mbid, :playcount, :rank, :url
51
11
  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
12
+ attr_reader :count, :chartposition, :position, :tagcount
56
13
 
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
14
+ # Alias for Album.new(:xml => xml)
15
+ #
16
+ # @deprecated
17
+ def self.new_from_libxml(xml)
18
+ Album.new(:xml => xml)
89
19
  end
90
20
 
21
+ def search
22
+ # This function require authentication, but SimpleAuth is not yet 2.0
23
+ raise NotImplementedError
24
+ end
25
+
26
+ # Create a new Scrobbler::Artist instance
27
+ #
91
28
  # If the additional parameter :include_info is set to true, additional
92
29
  # information is loaded
93
30
  #
94
31
  # @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]
32
+ #
33
+ # @param [Hash] data The options to initialize the class
34
+ def initialize(data={})
35
+ raise ArgumentError unless data.kind_of?(Hash)
36
+ super(data)
37
+ data = {:include_info => false}.merge(data)
38
+ # Load data given as method-parameter
39
+ load_info() if data.delete(:include_info)
40
+ populate_data(data)
41
+
42
+ raise ArgumentError, "Artist or mbid is required" if @artist.nil? && @mbid.nil?
43
+ raise ArgumentError, "Name is required" if @name.empty?
108
44
  end
109
45
 
46
+ # Load the data for this object out of a XML-Node
47
+ #
48
+ # @param [LibXML::XML::Node] node The XML node containing the information
49
+ # @return [nil]
50
+ def load_from_xml(node)
51
+ # Get all information from the root's children nodes
52
+ node.children.each do |child|
53
+ case child.name.to_s
54
+ when 'name'
55
+ @name = child.content
56
+ when 'title'
57
+ @name = child.content
58
+ when 'playcount'
59
+ @playcount = child.content.to_i
60
+ when 'tagcount'
61
+ @tagcount = child.content.to_i
62
+ when 'mbid'
63
+ @mbid = child.content
64
+ when 'url'
65
+ @url = child.content
66
+ when 'artist'
67
+ @artist = Artist.new(:xml => child)
68
+ when 'image'
69
+ check_image_node(child)
70
+ when 'id'
71
+ @id = child.content.to_s
72
+ when 'releasedate'
73
+ @release_date = Time.parse(child.content.strip)
74
+ when 'listeners'
75
+ @listeners = child.content.to_i
76
+ when 'toptags'
77
+ @top_tags = [] if @top_tags.nil?
78
+ child.children.each do |tag|
79
+ next unless tag.name == 'tag'
80
+ @top_tags << Tag.new_from_libxml(tag)
81
+ end
82
+ when 'wiki'
83
+ # @todo Handle wiki entries
84
+ when 'text'
85
+ # ignore, these are only blanks
86
+ when '#text'
87
+ # libxml-jruby version of blanks
88
+ else
89
+ raise NotImplementedError, "Field '#{child.name}' not known (#{child.content})"
90
+ end #^ case
91
+ end #^ do |child|
92
+
93
+ # Get all information from the root's attributes
94
+ @mbid = node['mbid'].to_s unless node['mbid'].nil?
95
+ @rank = node['rank'].to_i unless node['rank'].nil?
96
+ @position = node['position'].to_i unless node['position'].nil?
97
+
98
+ # If we have not found anything in the content of this node yet then
99
+ # this must be a simple artist node which has the name of the artist
100
+ # as its content
101
+ @name = node.content if @name.nil?
102
+ end #^ load_from_xml
103
+
110
104
  # Indicates if the info was already loaded
111
105
  @info_loaded = false
112
106
 
@@ -118,27 +112,11 @@ module Scrobbler
118
112
  # @todo Add language code for wiki translation
119
113
  def load_info
120
114
  return nil if @info_loaded
121
- xml = Base.request('album.getinfo', {'artist' => @artist, 'album' => @name})
115
+ xml = Base.request('album.getinfo', {:artist => @artist, :album => @name})
122
116
  unless xml.root['status'] == 'failed'
123
117
  xml.root.children.each do |childL1|
124
118
  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|
119
+ load_from_xml(childL1)
142
120
  end # xml.children.each do |childL1|
143
121
  @info_loaded = true
144
122
  end
@@ -1,169 +1,209 @@
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
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('basexml.rb', File.dirname(__FILE__))
4
+
59
5
  module Scrobbler
60
6
  # @todo Add missing functions that require authentication
61
7
  # @todo Integrate search functionality into this class which is already implemented in Scrobbler::Search
62
- class Artist < Base
63
- mixins :image, :streamable
8
+ class Artist < BaseXml
9
+ include Scrobbler::ImageObjectFuncs
10
+ include Scrobbler::StreamableObjectFuncs
64
11
 
65
- attr_accessor :name, :mbid, :playcount, :rank, :url, :count
66
- attr_accessor :chartposition
67
- attr_accessor :match, :tagcount, :listeners
12
+ attr_reader :name, :mbid, :playcount, :rank, :url, :count, :listeners
13
+ attr_reader :chartposition, :streamable, :match, :tagcount
68
14
 
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
15
+ # Alias for Artist.new(:xml => xml)
16
+ #
17
+ # @deprecated
18
+ def self.new_from_libxml(xml)
19
+ Artist.new(:xml => xml)
100
20
  end
101
-
102
- def initialize(name, data = {})
103
- super()
104
- raise ArgumentError, "Name is required" if name.empty?
105
- @name = name
21
+
22
+ # Create a new Scrobbler::Artist instance
23
+ #
24
+ # @param [Hash] data The options to initialize the class
25
+ def initialize(data = {})
26
+ raise ArgumentError unless data.kind_of?(Hash)
27
+ super(data)
28
+ # Load data given as method-parameter
106
29
  populate_data(data)
30
+ raise ArgumentError, "Name is required" if @name.nil? || @name.strip.empty?
107
31
  end
108
32
 
33
+ # Load the data for this object out of a XML-Node
34
+ #
35
+ # @param [LibXML::XML::Node] node The XML node containing the information
36
+ # @return [nil]
37
+ def load_from_xml(node)
38
+ # Get all information from the root's children nodes
39
+ node.children.each do |child|
40
+ case child.name
41
+ when 'playcount'
42
+ @playcount = child.content.to_i
43
+ when 'mbid'
44
+ @mbid = child.content
45
+ when 'url'
46
+ @url = child.content
47
+ when 'match'
48
+ @match = child.content.to_i
49
+ when 'tagcount'
50
+ @tagcount = child.content.to_i
51
+ when 'chartposition'
52
+ @chartposition = child.content
53
+ when 'name'
54
+ @name = child.content.to_s
55
+ when 'image'
56
+ check_image_node(child)
57
+ when 'streamable'
58
+ check_streamable_node(child)
59
+ when 'stats'
60
+ child.children.each do |childL2|
61
+ @listeners = childL2.content.to_i if childL2.name == 'listeners'
62
+ @playcount = childL2.content.to_i if childL2.name == 'playcount'
63
+ end
64
+ when 'similar'
65
+ # Ignore them for the moment, they are not stored.
66
+ when 'bio'
67
+ child.children.each do |childL2|
68
+ @bio = childL2.content if childL2.name == 'content'
69
+ end
70
+ when 'text'
71
+ # ignore, these are only blanks
72
+ when '#text'
73
+ # libxml-jruby version of blanks
74
+ else
75
+ raise NotImplementedError, "Field '#{child.name}' not known (#{child.content})"
76
+ end #^ case
77
+ end #^ do |child|
78
+
79
+ # Get all information from the root's attributes
80
+ @name = node['name'].to_s unless node['name'].nil?
81
+ @rank = node['rank'].to_i unless node['rank'].nil?
82
+ maybe_streamable_attribute(node)
83
+ @mbid = node['mbid'] unless node['mbid'].nil?
84
+
85
+ # If we have not found anything in the content of this node yet then
86
+ # this must be a simple artist node which has the name of the artist
87
+ # as its content
88
+ @name = node.content if @name.nil?
89
+ end #^ load_from_xml
90
+
109
91
  # Get the URL to the ical or rss representation of the current events that
110
92
  # a artist will play
111
93
  #
112
94
  # @todo Use the API function and parse that into a common ruby structure
95
+ # @return [String]
113
96
  def current_events(format=:ics)
114
- format = :ics if format.to_s == 'ical'
115
97
  raise ArgumentError unless ['ics', 'rss'].include?(format.to_s)
116
- "#{API_URL.chop}/2.0/artist/#{CGI::escape(@name)}/events.#{format}"
98
+ "#{API_URL.chop}/2.0/artist/#{CGI::escape(@name)}/events.#{format.to_s}"
117
99
  end
118
100
 
119
- def similar(force=false)
120
- get_response('artist.getsimilar', :similar, 'similarartists', 'artist', {'artist' => @name}, force)
101
+ # Get all the artists similar to this artist
102
+ #
103
+ # @return [Array<Scrobbler::Artist>]
104
+ def similar
105
+ call('artist.getsimilar', :similarartists, Artist, {:artist => @name})
121
106
  end
122
-
123
- def top_fans(force=false)
124
- get_response('artist.gettopfans', :top_fans, 'topfans', 'user', {'artist' => @name}, force)
107
+
108
+ # Get the top fans for an artist on Last.fm, based on listening data.
109
+ #
110
+ # @return [Array<Scrobbler::User>]
111
+ def top_fans
112
+ call('artist.gettopfans', :topfans, User, {:artist => @name})
125
113
  end
126
114
 
127
- def top_tracks(force=false)
128
- get_response('artist.gettoptracks', :top_tracks, 'toptracks', 'track', {'artist'=>@name}, force)
115
+ # Get the top tracks by an artist on Last.fm, ordered by popularity
116
+ #
117
+ # @return [Array<Scrobbler:Track>]
118
+ def top_tracks
119
+ call('artist.gettoptracks', :toptracks, Track, {:artist => @name})
129
120
  end
130
121
 
131
- def top_albums(force=false)
132
- get_response('artist.gettopalbums', :top_albums, 'topalbums', 'album', {'artist'=>@name}, force)
122
+ # Get the top albums for an artist on Last.fm, ordered by popularity.
123
+ #
124
+ # @return [Array<Scrobbler::Album>]
125
+ def top_albums
126
+ call('artist.gettopalbums', :topalbums, Album, {:artist => @name})
133
127
  end
134
128
 
135
- def top_tags(force=false)
136
- get_response('artist.gettoptags', :top_tags, 'toptags', 'tag', {'artist' => @name}, force)
129
+ # Get the top tags for an artist on Last.fm, ordered by popularity.
130
+ #
131
+ # @return [Array<Scrobbler::Tags>]
132
+ def top_tags
133
+ call('artist.gettoptags', :toptags, Tag, {:artist => @name})
137
134
  end
138
135
 
139
136
  @info_loaded = false
140
- # Get the metadata
137
+
138
+ # Get the metadata for an artist on Last.fm. Includes biography.
139
+ #
140
+ # @return [nil]
141
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
142
+ doc = request('artist.getinfo', {:artist => @name})
143
+ doc.root.children.each do |childL1|
144
+ next unless childL1.name == 'artist'
145
+ load_from_xml(childL1)
146
+ end
147
+ @info_loaded = true
159
148
  end # load_info
160
-
161
- def ==(otherArtist)
162
- if otherArtist.is_a?(Scrobbler::Artist)
163
- return (@name == otherArtist.name)
149
+
150
+ # Compare two Artists
151
+ #
152
+ # They are equal if their names are equal.
153
+ #
154
+ # @param [Scrobbler::Artist] other_artist
155
+ # @return [Boolean]
156
+ def ==(other_artist)
157
+ if other_artist.is_a?(Scrobbler::Artist)
158
+ return (@name == other_artist.name)
164
159
  end
165
160
  false
166
161
  end
162
+
163
+ def search
164
+ # This function require authentication, but SimpleAuth is not yet 2.0
165
+ raise NotImplementedError
166
+ end
167
+
168
+ def share
169
+ # This function require authentication, but SimpleAuth is not yet 2.0
170
+ raise NotImplementedError
171
+ end
172
+
173
+ def shout
174
+ # This function require authentication, but SimpleAuth is not yet 2.0
175
+ raise NotImplementedError
176
+ end
177
+
178
+ def add_tags
179
+ # This function require authentication, but SimpleAuth is not yet 2.0
180
+ raise NotImplementedError
181
+ end
182
+
183
+ def events
184
+ # This function require authentication, but SimpleAuth is not yet 2.0
185
+ raise NotImplementedError
186
+ end
187
+
188
+ def images
189
+ # This function require authentication, but SimpleAuth is not yet 2.0
190
+ raise NotImplementedError
191
+ end
192
+
193
+ def shouts
194
+ # This function require authentication, but SimpleAuth is not yet 2.0
195
+ raise NotImplementedError
196
+ end
197
+
198
+ def tags
199
+ # This function require authentication, but SimpleAuth is not yet 2.0
200
+ raise NotImplementedError
201
+ end
202
+
203
+ def remove_tag
204
+ # This function require authentication, but SimpleAuth is not yet 2.0
205
+ raise NotImplementedError
206
+ end
167
207
 
168
208
  end
169
209
  end
@@ -41,6 +41,18 @@ module Scrobbler
41
41
  options[:api_key] = @@api_key
42
42
  "http://www.last.fm/api/auth/?api_key=#{options[:api_key]}&token=#{options[:token]}"
43
43
  end
44
+
45
+ def mobile_session
46
+ # This function require authentication, but SimpleAuth is not yet 2.0
47
+ raise NotImplementedError
48
+ end
49
+
50
+ def websession
51
+ # This function require authentication, but SimpleAuth is not yet 2.0
52
+ raise NotImplementedError
53
+ end
54
+
55
+
44
56
 
45
57
  end
46
58
  end