scrobbler-ng 2.0.0 → 2.0.1

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.
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