hornairs-scrobbler 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +13 -0
- data/.loadpath +5 -0
- data/History.txt +5 -0
- data/MIT-LICENSE +19 -0
- data/Manifest +75 -0
- data/README.rdoc +104 -0
- data/Rakefile +10 -0
- data/VERSION.yml +5 -0
- data/examples/album.rb +20 -0
- data/examples/artist.rb +13 -0
- data/examples/playlist.rb +7 -0
- data/examples/scrobble.rb +31 -0
- data/examples/tag.rb +11 -0
- data/examples/track.rb +12 -0
- data/examples/user.rb +15 -0
- data/lib/scrobbler.rb +33 -0
- data/lib/scrobbler/album.rb +181 -0
- data/lib/scrobbler/artist.rb +165 -0
- data/lib/scrobbler/auth.rb +47 -0
- data/lib/scrobbler/base.rb +105 -0
- data/lib/scrobbler/event.rb +105 -0
- data/lib/scrobbler/geo.rb +27 -0
- data/lib/scrobbler/helper/image.rb +52 -0
- data/lib/scrobbler/helper/streamable.rb +40 -0
- data/lib/scrobbler/library.rb +112 -0
- data/lib/scrobbler/playing.rb +49 -0
- data/lib/scrobbler/playlist.rb +87 -0
- data/lib/scrobbler/radio.rb +12 -0
- data/lib/scrobbler/rest.rb +47 -0
- data/lib/scrobbler/scrobble.rb +116 -0
- data/lib/scrobbler/session.rb +9 -0
- data/lib/scrobbler/shout.rb +21 -0
- data/lib/scrobbler/simpleauth.rb +60 -0
- data/lib/scrobbler/tag.rb +113 -0
- data/lib/scrobbler/track.rb +152 -0
- data/lib/scrobbler/user.rb +222 -0
- data/lib/scrobbler/venue.rb +67 -0
- data/setup.rb +1585 -0
- data/tasks/jeweler.rake +15 -0
- data/tasks/rdoc.rake +7 -0
- data/tasks/tests.rake +13 -0
- data/tasks/yardoc.rake +8 -0
- data/test/fixtures/xml/album/info.xml +43 -0
- data/test/fixtures/xml/artist/fans.xml +52 -0
- data/test/fixtures/xml/artist/info.xml +58 -0
- data/test/fixtures/xml/artist/similar.xml +1004 -0
- data/test/fixtures/xml/artist/topalbums.xml +61 -0
- data/test/fixtures/xml/artist/toptags.xml +19 -0
- data/test/fixtures/xml/artist/toptracks.xml +62 -0
- data/test/fixtures/xml/auth/session.xml +7 -0
- data/test/fixtures/xml/auth/token.xml +3 -0
- data/test/fixtures/xml/event/attend.xml +3 -0
- data/test/fixtures/xml/event/attendees.xml +53 -0
- data/test/fixtures/xml/event/event.xml +35 -0
- data/test/fixtures/xml/event/shouts.xml +35 -0
- data/test/fixtures/xml/geo/events-distance-p1.xml +151 -0
- data/test/fixtures/xml/geo/events-lat-long.xml +167 -0
- data/test/fixtures/xml/geo/events-p1.xml +152 -0
- data/test/fixtures/xml/geo/events-p2.xml +380 -0
- data/test/fixtures/xml/geo/events-p3.xml +427 -0
- data/test/fixtures/xml/geo/top_artists-p1.xml +76 -0
- data/test/fixtures/xml/geo/top_tracks-p1.xml +77 -0
- data/test/fixtures/xml/library/albums-f30.xml +454 -0
- data/test/fixtures/xml/library/albums-p1.xml +754 -0
- data/test/fixtures/xml/library/albums-p2.xml +754 -0
- data/test/fixtures/xml/library/albums-p3.xml +754 -0
- data/test/fixtures/xml/library/albums-p4.xml +739 -0
- data/test/fixtures/xml/library/albums-p5.xml +754 -0
- data/test/fixtures/xml/library/albums-p6.xml +754 -0
- data/test/fixtures/xml/library/albums-p7.xml +739 -0
- data/test/fixtures/xml/library/albums-p8.xml +724 -0
- data/test/fixtures/xml/library/artists-f30.xml +334 -0
- data/test/fixtures/xml/library/artists-p1.xml +554 -0
- data/test/fixtures/xml/library/artists-p2.xml +554 -0
- data/test/fixtures/xml/library/artists-p3.xml +554 -0
- data/test/fixtures/xml/library/artists-p4.xml +554 -0
- data/test/fixtures/xml/library/artists-p5.xml +554 -0
- data/test/fixtures/xml/library/artists-p6.xml +554 -0
- data/test/fixtures/xml/library/artists-p7.xml +444 -0
- data/test/fixtures/xml/library/tracks-f30.xml +472 -0
- data/test/fixtures/xml/library/tracks-p1.xml +789 -0
- data/test/fixtures/xml/library/tracks-p10.xml +771 -0
- data/test/fixtures/xml/library/tracks-p11.xml +792 -0
- data/test/fixtures/xml/library/tracks-p12.xml +777 -0
- data/test/fixtures/xml/library/tracks-p13.xml +762 -0
- data/test/fixtures/xml/library/tracks-p14.xml +759 -0
- data/test/fixtures/xml/library/tracks-p15.xml +762 -0
- data/test/fixtures/xml/library/tracks-p16.xml +756 -0
- data/test/fixtures/xml/library/tracks-p17.xml +780 -0
- data/test/fixtures/xml/library/tracks-p18.xml +756 -0
- data/test/fixtures/xml/library/tracks-p19.xml +774 -0
- data/test/fixtures/xml/library/tracks-p2.xml +765 -0
- data/test/fixtures/xml/library/tracks-p20.xml +777 -0
- data/test/fixtures/xml/library/tracks-p21.xml +777 -0
- data/test/fixtures/xml/library/tracks-p22.xml +771 -0
- data/test/fixtures/xml/library/tracks-p23.xml +765 -0
- data/test/fixtures/xml/library/tracks-p24.xml +783 -0
- data/test/fixtures/xml/library/tracks-p25.xml +777 -0
- data/test/fixtures/xml/library/tracks-p26.xml +777 -0
- data/test/fixtures/xml/library/tracks-p27.xml +756 -0
- data/test/fixtures/xml/library/tracks-p28.xml +771 -0
- data/test/fixtures/xml/library/tracks-p29.xml +753 -0
- data/test/fixtures/xml/library/tracks-p3.xml +771 -0
- data/test/fixtures/xml/library/tracks-p30.xml +780 -0
- data/test/fixtures/xml/library/tracks-p31.xml +753 -0
- data/test/fixtures/xml/library/tracks-p32.xml +771 -0
- data/test/fixtures/xml/library/tracks-p33.xml +762 -0
- data/test/fixtures/xml/library/tracks-p34.xml +538 -0
- data/test/fixtures/xml/library/tracks-p4.xml +792 -0
- data/test/fixtures/xml/library/tracks-p5.xml +780 -0
- data/test/fixtures/xml/library/tracks-p6.xml +789 -0
- data/test/fixtures/xml/library/tracks-p7.xml +789 -0
- data/test/fixtures/xml/library/tracks-p8.xml +780 -0
- data/test/fixtures/xml/library/tracks-p9.xml +774 -0
- data/test/fixtures/xml/tag/similar.xml +254 -0
- data/test/fixtures/xml/tag/topalbums.xml +805 -0
- data/test/fixtures/xml/tag/topartists.xml +605 -0
- data/test/fixtures/xml/tag/toptags.xml +1254 -0
- data/test/fixtures/xml/tag/toptracks.xml +852 -0
- data/test/fixtures/xml/track/fans.xml +34 -0
- data/test/fixtures/xml/track/info.xml +53 -0
- data/test/fixtures/xml/track/toptags.xml +504 -0
- data/test/fixtures/xml/user/events.xml +401 -0
- data/test/fixtures/xml/user/friends.xml +30 -0
- data/test/fixtures/xml/user/lovedtracks.xml +678 -0
- data/test/fixtures/xml/user/neighbours.xml +23 -0
- data/test/fixtures/xml/user/playlists.xml +61 -0
- data/test/fixtures/xml/user/profile.xml +12 -0
- data/test/fixtures/xml/user/recentbannedtracks.xml +24 -0
- data/test/fixtures/xml/user/recentlovedtracks.xml +24 -0
- data/test/fixtures/xml/user/recenttracks.xml +124 -0
- data/test/fixtures/xml/user/systemrecs.xml +18 -0
- data/test/fixtures/xml/user/topalbums.xml +61 -0
- data/test/fixtures/xml/user/topartists.xml +41 -0
- data/test/fixtures/xml/user/toptags.xml +44 -0
- data/test/fixtures/xml/user/toptracks.xml +65 -0
- data/test/fixtures/xml/user/weeklyalbumchart.xml +256 -0
- data/test/fixtures/xml/user/weeklyartistchart.xml +220 -0
- data/test/fixtures/xml/user/weeklytrackchart.xml +746 -0
- data/test/fixtures/xml/venue/events.xml +151 -0
- data/test/fixtures/xml/venue/venue.xml +16 -0
- data/test/mocks/rest.rb +212 -0
- data/test/spec_helper.rb +7 -0
- data/test/test_helper.rb +20 -0
- data/test/unit/album_spec.rb +52 -0
- data/test/unit/artist_spec.rb +130 -0
- data/test/unit/auth_spec.rb +36 -0
- data/test/unit/event_spec.rb +109 -0
- data/test/unit/geo_spec.rb +148 -0
- data/test/unit/library_spec.rb +133 -0
- data/test/unit/playing_test.rb +53 -0
- data/test/unit/playlist_spec.rb +25 -0
- data/test/unit/radio_spec.rb +22 -0
- data/test/unit/scrobble_spec.rb +55 -0
- data/test/unit/scrobble_test.rb +69 -0
- data/test/unit/simpleauth_test.rb +45 -0
- data/test/unit/tag_spec.rb +101 -0
- data/test/unit/track_spec.rb +95 -0
- data/test/unit/user_spec.rb +264 -0
- data/test/unit/venue_spec.rb +104 -0
- metadata +265 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module Scrobbler
|
2
|
+
class Shout < Base
|
3
|
+
attr_reader :author, :date, :body
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def new_from_libxml(xml)
|
7
|
+
data={}
|
8
|
+
xml.children.each do |child|
|
9
|
+
data[:body] = child.content if child.name == 'body'
|
10
|
+
data[:author] = Scrobbler::User.new(child.content) if child.name == 'author'
|
11
|
+
data[:date] = Time.parse(child.content) if child.name == 'date'
|
12
|
+
end
|
13
|
+
Shout.new(data)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(input={})
|
18
|
+
populate_data(input)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
# exception definitions
|
4
|
+
class BadAuthError < StandardError; end
|
5
|
+
class BannedError < StandardError; end
|
6
|
+
class BadTimeError < StandardError; end
|
7
|
+
module Scrobbler
|
8
|
+
AUTH_URL = 'http://post.audioscrobbler.com'
|
9
|
+
AUTH_VER = '1.2.1'
|
10
|
+
|
11
|
+
# @todo This is not 2.0!!
|
12
|
+
class SimpleAuth
|
13
|
+
# you should read last.fm/api/submissions#handshake
|
14
|
+
|
15
|
+
attr_accessor :user, :password, :client_id, :client_ver
|
16
|
+
attr_reader :status, :session_id, :now_playing_url, :submission_url
|
17
|
+
|
18
|
+
def initialize(args = {})
|
19
|
+
@user = args[:user] # last.fm username
|
20
|
+
@password = args[:password] # last.fm password
|
21
|
+
@client_id = 'rbs' # Client ID assigned by last.fm; Don't change this!
|
22
|
+
@client_ver = '0.2.13'
|
23
|
+
|
24
|
+
raise ArgumentError, 'Missing required argument' if @user.blank? || @password.blank?
|
25
|
+
|
26
|
+
@connection = REST::Connection.new(AUTH_URL)
|
27
|
+
end
|
28
|
+
|
29
|
+
def handshake!
|
30
|
+
password_hash = Digest::MD5.hexdigest(@password)
|
31
|
+
timestamp = Time.now.to_i.to_s
|
32
|
+
token = Digest::MD5.hexdigest(password_hash + timestamp)
|
33
|
+
|
34
|
+
query = { :hs => 'true',
|
35
|
+
:p => AUTH_VER,
|
36
|
+
:c => @client_id,
|
37
|
+
:v => @client_ver,
|
38
|
+
:u => @user,
|
39
|
+
:t => timestamp,
|
40
|
+
:a => token }
|
41
|
+
result = @connection.get('/', query)
|
42
|
+
|
43
|
+
@status = result.split(/\n/)[0]
|
44
|
+
case @status
|
45
|
+
when /OK/
|
46
|
+
@session_id, @now_playing_url, @submission_url = result.split(/\n/)[1,3]
|
47
|
+
when /BANNED/
|
48
|
+
raise BannedError # something is wrong with the gem, check for an update
|
49
|
+
when /BADAUTH/
|
50
|
+
raise BadAuthError # invalid user/password
|
51
|
+
when /FAILED/
|
52
|
+
raise RequestFailedError, @status
|
53
|
+
when /BADTIME/
|
54
|
+
raise BadTimeError # system time is way off
|
55
|
+
else
|
56
|
+
raise RequestFailedError
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# Below is code samples for how to find the top albums and tracks for a tag.
|
2
|
+
#
|
3
|
+
# tag = Scrobbler::Tag.new('country')
|
4
|
+
#
|
5
|
+
# puts 'Top Albums'
|
6
|
+
# tag.top_albums.each { |a| puts "(#{a.count}) #{a.name} by #{a.artist}" }
|
7
|
+
#
|
8
|
+
# puts
|
9
|
+
#
|
10
|
+
# puts 'Top Tracks'
|
11
|
+
# tag.top_tracks.each { |t| puts "(#{t.count}) #{t.name} by #{t.artist}" }
|
12
|
+
#
|
13
|
+
# Which would output something similar to:
|
14
|
+
#
|
15
|
+
# Top Albums
|
16
|
+
# (29) American IV: The Man Comes Around by Johnny Cash
|
17
|
+
# (14) Folks Pop In at the Waterhouse by Various Artists
|
18
|
+
# (13) Hapless by Flowers From The Man Who Shot Your Cousin
|
19
|
+
# (9) Taking The Long Way by Dixie Chicks
|
20
|
+
# (8) Unchained by Johnny Cash
|
21
|
+
# (8) American III: Solitary Man by Johnny Cash
|
22
|
+
# (8) Wide Open Spaces by Dixie Chicks
|
23
|
+
# (7) It's Now or Later by Tangled Star
|
24
|
+
# (7) Greatest Hits by Hank Williams
|
25
|
+
# (7) American Recordings by Johnny Cash
|
26
|
+
# (6) Forgotten Landscape by theNoLifeKing
|
27
|
+
# (6) At Folsom Prison by Johnny Cash
|
28
|
+
# (6) Fox Confessor Brings the Flood by Neko Case
|
29
|
+
# (6) Murder by Johnny Cash
|
30
|
+
# (5) Gloom by theNoLifeKing
|
31
|
+
# (5) Set This Circus Down by Tim McGraw
|
32
|
+
# (5) Blacklisted by Neko Case
|
33
|
+
# (5) Breathe by Faith Hill
|
34
|
+
# (5) Unearthed (disc 4: My Mother's Hymn Book) by Johnny Cash
|
35
|
+
# (4) Home by Dixie Chicks
|
36
|
+
#
|
37
|
+
# Top Tracks
|
38
|
+
# (221) Hurt by Johnny Cash
|
39
|
+
# (152) I Walk the Line by Johnny Cash
|
40
|
+
# (147) Ring of Fire by Johnny Cash
|
41
|
+
# (125) Folsom Prison Blues by Johnny Cash
|
42
|
+
# (77) The Man Comes Around by Johnny Cash
|
43
|
+
# (67) Personal Jesus by Johnny Cash
|
44
|
+
# (65) Not Ready To Make Nice by Dixie Chicks
|
45
|
+
# (63) Before He Cheats by Carrie Underwood
|
46
|
+
# (62) Give My Love to Rose by Johnny Cash
|
47
|
+
# (49) Jackson by Johnny Cash
|
48
|
+
# (49) What Hurts The Most by Rascal Flatts
|
49
|
+
# (48) Big River by Johnny Cash
|
50
|
+
# (46) Man in Black by Johnny Cash
|
51
|
+
# (46) Jolene by Dolly Parton
|
52
|
+
# (46) Friends in Low Places by Garth Brooks
|
53
|
+
# (46) One by Johnny Cash
|
54
|
+
# (44) Cocaine Blues by Johnny Cash
|
55
|
+
# (41) Get Rhythm by Johnny Cash
|
56
|
+
# (41) I Still Miss Someone by Johnny Cash
|
57
|
+
# (40) The Devil Went Down to Georgia by Charlie Daniels Band
|
58
|
+
module Scrobbler
|
59
|
+
class Tag < Base
|
60
|
+
attr_accessor :name, :count, :url, :streamable
|
61
|
+
|
62
|
+
class << self
|
63
|
+
def new_from_xml(xml)
|
64
|
+
data = {}
|
65
|
+
xml.children.each do |child|
|
66
|
+
data[:name] = child.content if child.name == 'name'
|
67
|
+
data[:count] = child.content.to_i if child.name == 'count'
|
68
|
+
data[:url] = child.content if child.name == 'url'
|
69
|
+
if child.name == 'streamable'
|
70
|
+
if ['1', 'true'].include?(child.content)
|
71
|
+
data[:streamable] = true
|
72
|
+
else
|
73
|
+
data[:streamable] = false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
Tag.new(data[:name], data)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize(name, data={})
|
83
|
+
raise ArgumentError, "Name is required" if name.blank?
|
84
|
+
@name = name
|
85
|
+
@url = data[:url] unless data[:url].nil?
|
86
|
+
@count = data[:count] unless data[:count].nil?
|
87
|
+
@streamable = data[:streamable] unless data[:streamable].nil?
|
88
|
+
end
|
89
|
+
|
90
|
+
def top_artists(force=false)
|
91
|
+
get_response('tag.gettopartists', :top_artists, 'topartists', 'artist', {'tag'=>@name}, force)
|
92
|
+
end
|
93
|
+
|
94
|
+
def top_albums(force=false)
|
95
|
+
get_response('tag.gettopalbums', :top_albums, 'topalbums', 'album', {'tag'=>@name}, force)
|
96
|
+
end
|
97
|
+
|
98
|
+
def top_tracks(force=false)
|
99
|
+
get_response('tag.gettoptracks', :top_tracks, 'toptracks', 'track', {'tag'=>@name}, force)
|
100
|
+
end
|
101
|
+
|
102
|
+
def Tag.top_tags
|
103
|
+
self.get('tag.gettoptags', :toptags, :tag)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Search for tags similar to this one. Returns tags ranked by similarity,
|
107
|
+
# based on listening data.
|
108
|
+
def similar(force=false)
|
109
|
+
params = {:tag => @name}
|
110
|
+
get_response('tag.getsimilar', :similar, 'similartags', 'tag', params, force)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# Below is an example of how to get the top fans for a track.
|
2
|
+
#
|
3
|
+
# track = Scrobbler::Track.new('Carrie Underwood', 'Before He Cheats')
|
4
|
+
# puts 'Fans'
|
5
|
+
# puts "=" * 4
|
6
|
+
# track.fans.each { |u| puts u.username }
|
7
|
+
#
|
8
|
+
# Which would output something like:
|
9
|
+
#
|
10
|
+
# track = Scrobbler::Track.new('Carrie Underwood', 'Before He Cheats')
|
11
|
+
# puts 'Fans'
|
12
|
+
# puts "=" * 4
|
13
|
+
# track.fans.each { |u| puts "(#{u.weight}) #{u.username}" }
|
14
|
+
#
|
15
|
+
# Fans
|
16
|
+
# ====
|
17
|
+
# (69163) PimpinRose
|
18
|
+
# (7225) selene204
|
19
|
+
# (7000) CelestiaLegends
|
20
|
+
# (6817) muehllr
|
21
|
+
# (5387) Mudley
|
22
|
+
# (5368) ilovejohnny1984
|
23
|
+
# (5232) MeganIAD
|
24
|
+
# (5132) Veric
|
25
|
+
# (5097) aeVnar
|
26
|
+
# (3390) kristaaan
|
27
|
+
# (3239) kelseaowns
|
28
|
+
# (2780) syndication
|
29
|
+
# (2735) mkumm
|
30
|
+
# (2706) Kimmybeebee
|
31
|
+
# (2648) skorpcroze
|
32
|
+
# (2549) mistergreg
|
33
|
+
# (2449) mlmjcace
|
34
|
+
# (2302) tiNEey
|
35
|
+
# (2169) ajsbabiegirl
|
36
|
+
module Scrobbler
|
37
|
+
class Track < Base
|
38
|
+
# Load Helper modules
|
39
|
+
include ImageObjectFuncs
|
40
|
+
extend ImageClassFuncs
|
41
|
+
|
42
|
+
attr_accessor :artist, :name, :mbid, :playcount, :rank, :url, :id, :count
|
43
|
+
attr_accessor :streamable, :album, :date, :now_playing, :tagcount
|
44
|
+
attr_accessor :duration, :listeners
|
45
|
+
|
46
|
+
class << self
|
47
|
+
def new_from_xml(xml, o = {})
|
48
|
+
data = self.data_from_xml(xml, o)
|
49
|
+
return nil if data[:name].nil?
|
50
|
+
if data[:artist].blank?
|
51
|
+
if data[:creator].blank?
|
52
|
+
data[:artist] = data[:creator];
|
53
|
+
else
|
54
|
+
raise Error, "Supplied XML to track has no artist or creator"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
Track.new(data[:artist], data)
|
58
|
+
end
|
59
|
+
|
60
|
+
def data_from_xml(xml, o = {})
|
61
|
+
o = {:include_album_info => true, :include_artist_info => true}.merge(o)
|
62
|
+
data = {}
|
63
|
+
xml.children.each do |child|
|
64
|
+
data[:name] = child.content if child.name == 'name' || child.name == 'title'
|
65
|
+
data[:mbid] = child.content.to_i if child.name == 'mbid'
|
66
|
+
data[:id] = child.content.to_i if child.name == 'id'
|
67
|
+
data[:duration] = child.content.to_i if child.name == 'duration'
|
68
|
+
data[:url] = child.content if child.name == 'url' || child.name == 'identifier'
|
69
|
+
data[:date] = Time.parse(child.content) if child.name == 'date'
|
70
|
+
data[:listeners] = child.content.to_i if child.name == 'listeners'
|
71
|
+
data[:artist] = Artist.new_from_xml(child, o) if (child.name == 'artist' || child.name == 'creator') && o[:include_artist_info]
|
72
|
+
data[:album] = Album.new_from_xml(child, o) if child.name == 'album' && o[:include_album_info]
|
73
|
+
data[:playcount] = child.content.to_i if child.name == 'playcount'
|
74
|
+
data[:tagcount] = child.content.to_i if child.name == 'tagcount'
|
75
|
+
maybe_image_node(data, child)
|
76
|
+
if child.name == 'streamable'
|
77
|
+
if ['1', 'true'].include?(child.content)
|
78
|
+
data[:streamable] = true
|
79
|
+
else
|
80
|
+
data[:streamable] = false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
data[:rank] = xml['rank'].to_i if xml['rank']
|
87
|
+
data[:now_playing] = true if xml['nowplaying'] && xml['nowplaying'] == 'true'
|
88
|
+
|
89
|
+
data[:now_playing] = false if data[:now_playing].nil?
|
90
|
+
o.merge(data)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def initialize(input, data={})
|
95
|
+
super()
|
96
|
+
#check for old style parameter arguments
|
97
|
+
if data.class == String
|
98
|
+
data = {:name => data}
|
99
|
+
end
|
100
|
+
|
101
|
+
if input.class == String && data[:mbid] && data[:mbid] == true
|
102
|
+
raise ArgumentError, "MBID is required for an MBID query" if input.blank?
|
103
|
+
@mbid = input
|
104
|
+
load_album_info() unless !data[:include_info].nil? && data[:include_info] == false
|
105
|
+
else
|
106
|
+
raise ArgumentError, "Artist is required" if input.blank?
|
107
|
+
raise ArgumentError, "Name is required" if data[:name].blank?
|
108
|
+
@artist = Artist.new(input)
|
109
|
+
@name = data[:name]
|
110
|
+
load_info() if data[:include_info]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def add_tags(tags)
|
115
|
+
# This function requires authentication, but SimpleAuth is not yet 2.0
|
116
|
+
raise NotImplementedError
|
117
|
+
end
|
118
|
+
|
119
|
+
def ban
|
120
|
+
# This function requires authentication, but SimpleAuth is not yet 2.0
|
121
|
+
raise NotImplementedError
|
122
|
+
end
|
123
|
+
|
124
|
+
@info_loaded = false
|
125
|
+
def load_info
|
126
|
+
return nil if @info_loaded
|
127
|
+
doc = Base.request('track.getinfo', :artist => @artist.name, :track => @name)
|
128
|
+
doc.root.children.each do |child|
|
129
|
+
next unless child.name == 'track'
|
130
|
+
data = self.class.data_from_xml(child)
|
131
|
+
populate_data(data)
|
132
|
+
@info_loaded = true
|
133
|
+
break
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def top_fans(force=false)
|
138
|
+
get_response('track.gettopfans', :fans, 'topfans', 'user', {:artist=>@artist.name, :track=>@name}, force)
|
139
|
+
end
|
140
|
+
|
141
|
+
def top_tags(force=false)
|
142
|
+
get_response('track.gettoptags', :top_tags, 'toptags', 'tag', {:artist=>@artist.name.to_s, :track=>@name}, force)
|
143
|
+
end
|
144
|
+
|
145
|
+
def ==(otherTrack)
|
146
|
+
if otherTrack.is_a?(Scrobbler::Track)
|
147
|
+
return ((@name == otherTrack.name) && (@artist == otherTrack.artist))
|
148
|
+
end
|
149
|
+
false
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
# Probably the most common use of this lib would be to get your most recent tracks or your top tracks. Below are some code samples.
|
2
|
+
# user = Scrobbler::User.new('jnunemaker')
|
3
|
+
#
|
4
|
+
# puts "#{user.username}'s Recent Tracks"
|
5
|
+
# puts "=" * (user.username.length + 16)
|
6
|
+
# user.recent_tracks.each { |t| puts t.name }
|
7
|
+
#
|
8
|
+
# puts
|
9
|
+
# puts
|
10
|
+
#
|
11
|
+
# puts "#{user.username}'s Top Tracks"
|
12
|
+
# puts "=" * (user.username.length + 13)
|
13
|
+
# user.top_tracks.each { |t| puts "(#{t.playcount}) #{t.name}" }
|
14
|
+
#
|
15
|
+
# Which would output something like:
|
16
|
+
#
|
17
|
+
# jnunemaker's Recent Tracks
|
18
|
+
# ==========================
|
19
|
+
# Everything You Want
|
20
|
+
# You're a God
|
21
|
+
# Bitter Sweet Symphony [Original Version]
|
22
|
+
# Lord I Guess I'll Never Know
|
23
|
+
# Country Song
|
24
|
+
# Bitter Sweet Symphony (Radio Edit)
|
25
|
+
#
|
26
|
+
#
|
27
|
+
# jnunemaker's Top Tracks
|
28
|
+
# =======================
|
29
|
+
# (62) Probably Wouldn't Be This Way
|
30
|
+
# (55) Not Ready To Make Nice
|
31
|
+
# (45) Easy Silence
|
32
|
+
# (43) Song 2
|
33
|
+
# (40) Everybody Knows
|
34
|
+
# (39) Before He Cheats
|
35
|
+
# (39) Something's Gotta Give
|
36
|
+
# (38) Hips Don't Lie (featuring Wyclef Jean)
|
37
|
+
# (37) Unwritten
|
38
|
+
# (37) Move Along
|
39
|
+
# (37) Dance, Dance
|
40
|
+
# (36) We Belong Together
|
41
|
+
# (36) Jesus, Take the Wheel
|
42
|
+
# (36) Black Horse and the Cherry Tree (radio version)
|
43
|
+
# (35) Photograph
|
44
|
+
# (35) You're Beautiful
|
45
|
+
# (35) Walk Away
|
46
|
+
# (34) Stickwitu
|
47
|
+
module Scrobbler
|
48
|
+
class User < Base
|
49
|
+
# Load Helper modules
|
50
|
+
include ImageObjectFuncs
|
51
|
+
extend ImageClassFuncs
|
52
|
+
|
53
|
+
attr_reader :username, :url, :weight, :match, :realname, :name
|
54
|
+
|
55
|
+
class << self
|
56
|
+
def new_from_libxml(xml)
|
57
|
+
data = {}
|
58
|
+
xml.children.each do |child|
|
59
|
+
data[:name] = child.content if child.name == 'name'
|
60
|
+
data[:url] = child.content if child.name == 'url'
|
61
|
+
data[:weight] = child.content.to_i if child.name == 'weight'
|
62
|
+
data[:match] = child.content if child.name == 'match'
|
63
|
+
data[:realname] = child.content if child.name == 'realname'
|
64
|
+
maybe_image_node(data, child)
|
65
|
+
end
|
66
|
+
User.new(data[:name], data)
|
67
|
+
end
|
68
|
+
|
69
|
+
def find(*args)
|
70
|
+
options = {:include_profile => false}
|
71
|
+
options.merge!(args.pop) if args.last.is_a?(Hash)
|
72
|
+
users = args.flatten.inject([]) { |users, u| users << User.new(u, options); users }
|
73
|
+
users.length == 1 ? users.pop : users
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def initialize(username, input={})
|
78
|
+
data = {:include_profile => false}.merge(input)
|
79
|
+
raise ArgumentError if username.blank?
|
80
|
+
@username = username
|
81
|
+
@name = @username
|
82
|
+
load_profile() if data[:include_profile]
|
83
|
+
populate_data(data)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get a list of upcoming events that this user is attending.
|
87
|
+
#
|
88
|
+
# Supports ical, ics or rss as its format
|
89
|
+
def events(force=false)
|
90
|
+
get_response('user.getevents', :events, 'events', 'event', {'user'=>@username}, force)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get a list of the user's friends on Last.fm.
|
94
|
+
def friends(force=false, page=1, limit=50)
|
95
|
+
get_response('user.getfriends', :friends, 'friends', 'user', {'user'=>@username, 'page'=>page.to_s, 'limit'=>limit.to_s}, force)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get information about a user profile.
|
99
|
+
def load_info
|
100
|
+
# This function requires authentication, but SimpleAuth is not yet 2.0
|
101
|
+
raise NotImplementedError
|
102
|
+
end
|
103
|
+
|
104
|
+
# Get the last 50 tracks loved by a user.
|
105
|
+
def loved_tracks(force=false)
|
106
|
+
get_response('user.getlovedtracks', :loved_tracks, 'lovedtracks', 'track', {'user'=>@username}, force)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Get a list of a user's neighbours on Last.fm.
|
110
|
+
def neighbours(force=false)
|
111
|
+
get_response('user.getneighbours', :neighbours, 'neighbours', 'user', {'user'=>@username}, force)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Get a paginated list of all events a user has attended in the past.
|
115
|
+
def past_events(format=:ics)
|
116
|
+
# This needs a Event class, which is yet not available
|
117
|
+
raise NotImplementedError
|
118
|
+
end
|
119
|
+
|
120
|
+
# Get a list of a user's playlists on Last.fm.
|
121
|
+
def playlists(force=false)
|
122
|
+
#(api_method, instance_name, parent, element, parameters, force=false)
|
123
|
+
get_response('user.getplaylists', :playlist, 'playlists', 'playlist', {'user'=>@username}, force)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Get a list of the recent tracks listened to by this user. Indicates now
|
127
|
+
# playing track if the user is currently listening.
|
128
|
+
#
|
129
|
+
# Possible parameters:
|
130
|
+
# - limit: An integer used to limit the number of tracks returned.
|
131
|
+
def recent_tracks(force=false, parameters={})
|
132
|
+
parameters.merge!({'user' => @username})
|
133
|
+
get_response('user.getrecenttracks', :recent_tracks, 'recenttracks', 'track', parameters, force)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Get Last.fm artist recommendations for a user
|
137
|
+
def recommended_artists
|
138
|
+
# This function require authentication, but SimpleAuth is not yet 2.0
|
139
|
+
raise NotImplementedError
|
140
|
+
end
|
141
|
+
|
142
|
+
# Get a paginated list of all events recommended to a user by Last.fm,
|
143
|
+
# based on their listening profile.
|
144
|
+
def recommended_events
|
145
|
+
# This function require authentication, but SimpleAuth is not yet 2.0
|
146
|
+
raise NotImplementedError
|
147
|
+
end
|
148
|
+
|
149
|
+
# Get shouts for this user.
|
150
|
+
def shouts
|
151
|
+
# This needs a Shout class which is yet not available
|
152
|
+
raise NotImplementedError
|
153
|
+
end
|
154
|
+
|
155
|
+
# Get the top albums listened to by a user. You can stipulate a time period.
|
156
|
+
# Sends the overall chart by default.
|
157
|
+
def top_albums(force=false, period='overall')
|
158
|
+
get_response('user.gettopalbums', :top_albums, 'topalbums', 'album', {'user'=>@username, 'period'=>period}, force)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Get the top artists listened to by a user. You can stipulate a time
|
162
|
+
# period. Sends the overall chart by default.
|
163
|
+
def top_artists(force=false, period='overall')
|
164
|
+
get_response('user.gettopartists', :top_artists, 'topartists', 'artist', {'user' => @username, 'period'=>period}, force)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Get the top tags used by this user.
|
168
|
+
def top_tags(force=false)
|
169
|
+
get_response('user.gettoptags', :top_tags, 'toptags', 'tag', {'user'=>@username}, force)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Get the top tracks listened to by a user. You can stipulate a time period.
|
173
|
+
# Sends the overall chart by default.
|
174
|
+
def top_tracks(force=false, period='overall')
|
175
|
+
get_response('user.gettoptracks', :top_tracks, 'toptracks', 'track', {'user'=>@username, 'period'=>period}, force)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Get an album chart for a user profile, for a given date range. If no date
|
179
|
+
# range is supplied, it will return the most recent album chart for this
|
180
|
+
# user.
|
181
|
+
def weekly_album_chart(from=nil, to=nil)
|
182
|
+
parameters = {'user' => @username}
|
183
|
+
parameters['from'] = from unless from.nil?
|
184
|
+
parameters['to'] = to unless to.nil?
|
185
|
+
get_response('user.getweeklyalbumchart', nil, 'weeklyalbumchart', 'album', parameters, true)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Get an artist chart for a user profile, for a given date range. If no date
|
189
|
+
# range is supplied, it will return the most recent artist chart for this
|
190
|
+
# user.
|
191
|
+
def weekly_artist_chart(from=nil, to=nil)
|
192
|
+
parameters = {'user' => @username}
|
193
|
+
parameters['from'] = from unless from.nil?
|
194
|
+
parameters['to'] = to unless to.nil?
|
195
|
+
get_response('user.getweeklyartistchart', nil, 'weeklyartistchart', 'artist', parameters, true)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Get a list of available charts for this user, expressed as date ranges
|
199
|
+
# which can be sent to the chart services.
|
200
|
+
def weekly_chart_list(force=false)
|
201
|
+
# @todo
|
202
|
+
raise NotImplementedError
|
203
|
+
end
|
204
|
+
|
205
|
+
# Get a track chart for a user profile, for a given date range. If no date
|
206
|
+
# range is supplied, it will return the most recent track chart for this
|
207
|
+
# user.
|
208
|
+
def weekly_track_chart(from=nil, to=nil)
|
209
|
+
parameters = {'user' => @username}
|
210
|
+
parameters['from'] = from unless from.nil?
|
211
|
+
parameters['to'] = to unless to.nil?
|
212
|
+
get_response('user.getweeklytrackchart', nil, 'weeklytrackchart', 'track', parameters, true)
|
213
|
+
end
|
214
|
+
|
215
|
+
# Shout on this user's shoutbox
|
216
|
+
def shout(message)
|
217
|
+
# This function require authentication, but SimpleAuth is not yet 2.0
|
218
|
+
raise NotImplementedError
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
end
|