rockstar 0.3.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.
- data/.gitignore +3 -0
- data/History.txt +6 -0
- data/MIT-LICENSE +19 -0
- data/Manifest +72 -0
- data/README.rdoc +140 -0
- data/Rakefile +21 -0
- data/VERSION +1 -0
- data/examples/.gitignore +1 -0
- data/examples/album.rb +16 -0
- data/examples/artist.rb +17 -0
- data/examples/lastfm.yml_example +3 -0
- data/examples/scrobble.rb +55 -0
- data/examples/tag.rb +20 -0
- data/examples/track.rb +14 -0
- data/examples/user.rb +18 -0
- data/lib/rockstar.rb +39 -0
- data/lib/rockstar/album.rb +108 -0
- data/lib/rockstar/artist.rb +135 -0
- data/lib/rockstar/auth.rb +21 -0
- data/lib/rockstar/base.rb +37 -0
- data/lib/rockstar/chart.rb +31 -0
- data/lib/rockstar/playing.rb +49 -0
- data/lib/rockstar/rest.rb +66 -0
- data/lib/rockstar/scrobble.rb +67 -0
- data/lib/rockstar/session.rb +19 -0
- data/lib/rockstar/simpleauth.rb +62 -0
- data/lib/rockstar/tag.rb +100 -0
- data/lib/rockstar/tokenauth.rb +84 -0
- data/lib/rockstar/track.rb +103 -0
- data/lib/rockstar/user.rb +194 -0
- data/lib/rockstar/version.rb +3 -0
- data/rockstar.gemspec +138 -0
- data/test/fixtures/xml/album/getinfo_album_Some_Hearts_artist_Carrie_Underwood.xml +63 -0
- data/test/fixtures/xml/artist/getsimilar_artist_Metallica.xml +1203 -0
- data/test/fixtures/xml/artist/gettopalbums_artist_Metallica.xml +704 -0
- data/test/fixtures/xml/artist/gettopfans_artist_Metallica.xml +504 -0
- data/test/fixtures/xml/artist/gettoptags_artist_Metallica.xml +403 -0
- data/test/fixtures/xml/artist/gettoptracks_artist_Metallica.xml +800 -0
- data/test/fixtures/xml/tag/gettopalbums_tag_rock.xml +654 -0
- data/test/fixtures/xml/tag/gettopartists_tag_rock.xml +504 -0
- data/test/fixtures/xml/tag/gettoptags.xml +1253 -0
- data/test/fixtures/xml/tag/gettoptracks_tag_rock.xml +704 -0
- data/test/fixtures/xml/track/gettopfans_artist_Carrie_Underwood_track_Before_He_Cheats.xml +504 -0
- data/test/fixtures/xml/track/gettoptags_artist_Carrie_Underwood_track_Before_He_Cheats.xml +403 -0
- data/test/fixtures/xml/track/love_artist_Carrie_Underwood_sk_tag_track_Before_He_Cheats.xml +2 -0
- data/test/fixtures/xml/user/getfriends_user_jnunemaker.xml +173 -0
- data/test/fixtures/xml/user/getinfo_user_jnunemaker.xml +22 -0
- data/test/fixtures/xml/user/getinfo_user_oaknd1.xml +22 -0
- data/test/fixtures/xml/user/getinfo_user_wharle.xml +22 -0
- data/test/fixtures/xml/user/getlovedtracks_user_jnunemaker.xml +775 -0
- data/test/fixtures/xml/user/getneighbours_user_jnunemaker.xml +503 -0
- data/test/fixtures/xml/user/getrecenttracks_user_jnunemaker.xml +133 -0
- data/test/fixtures/xml/user/getrecommendedartists_sk_token_user_jnunemaker.xml +553 -0
- data/test/fixtures/xml/user/gettopalbums_user_jnunemaker.xml +704 -0
- data/test/fixtures/xml/user/gettopartists_user_jnunemaker.xml +554 -0
- data/test/fixtures/xml/user/gettoptags_user_jnunemaker.xml +63 -0
- data/test/fixtures/xml/user/gettoptracks_user_jnunemaker.xml +750 -0
- data/test/fixtures/xml/user/getweeklyalbumchart_from_1138536002_to_1139140802_user_jnunemaker.xml +143 -0
- data/test/fixtures/xml/user/getweeklyalbumchart_from__to__user_jnunemaker.xml +31 -0
- data/test/fixtures/xml/user/getweeklyartistchart_from_1138536002_to_1139140802_user_jnunemaker.xml +201 -0
- data/test/fixtures/xml/user/getweeklyartistchart_from__to__user_jnunemaker.xml +21 -0
- data/test/fixtures/xml/user/getweeklychartlist_user_jnunemaker.xml +232 -0
- data/test/fixtures/xml/user/getweeklytrackchart_from_1138536002_to_1139140802_user_jnunemaker.xml +883 -0
- data/test/fixtures/xml/user/getweeklytrackchart_from__to__user_jnunemaker.xml +423 -0
- data/test/mocks/rest.rb +61 -0
- data/test/test_helper.rb +17 -0
- data/test/unit/test_album.rb +67 -0
- data/test/unit/test_artist.rb +69 -0
- data/test/unit/test_chart.rb +35 -0
- data/test/unit/test_playing.rb +53 -0
- data/test/unit/test_scrobble.rb +69 -0
- data/test/unit/test_simpleauth.rb +45 -0
- data/test/unit/test_tag.rb +57 -0
- data/test/unit/test_tokenauth.rb +45 -0
- data/test/unit/test_track.rb +44 -0
- data/test/unit/test_user.rb +252 -0
- metadata +190 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Getting information about an album such as release date and the summary or description on it is very easy.
|
|
2
|
+
#
|
|
3
|
+
# album = Rockstar::Album.new('Carrie Underwood', 'Some Hearts', :include_info => true)
|
|
4
|
+
#
|
|
5
|
+
# puts "Album: #{album.name}"
|
|
6
|
+
# puts "Artist: #{album.artist}"
|
|
7
|
+
# puts "URL: #{album.url}"
|
|
8
|
+
# puts "Release Date: #{album.release_date.strftime('%m/%d/%Y')}"
|
|
9
|
+
#
|
|
10
|
+
# Would output:
|
|
11
|
+
#
|
|
12
|
+
# Album: Some Hearts
|
|
13
|
+
# Artist: Carrie Underwood
|
|
14
|
+
# URL: http://www.last.fm/music/Carrie+Underwood/Some+Hearts
|
|
15
|
+
# Release Date: 11/15/2005
|
|
16
|
+
#
|
|
17
|
+
module Rockstar
|
|
18
|
+
class Album < Base
|
|
19
|
+
attr_accessor :artist, :artist_mbid, :name, :mbid, :playcount, :rank, :url, :release_date
|
|
20
|
+
attr_accessor :image_large, :image_medium, :image_small, :summary, :content, :images
|
|
21
|
+
|
|
22
|
+
# needed on top albums for tag
|
|
23
|
+
attr_accessor :count, :streamable
|
|
24
|
+
|
|
25
|
+
# needed for weekly album charts
|
|
26
|
+
attr_accessor :chartposition
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
def find(artist, name, o={})
|
|
30
|
+
new(artist, name, o)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def new_from_xml(xml, doc=nil)
|
|
34
|
+
name = (xml).at(:name).inner_html if (xml).at(:name)
|
|
35
|
+
name = xml['name'] if name.nil? && xml['name']
|
|
36
|
+
artist = (xml).at(:artist).at(:name).inner_html if (xml).at(:artist) && (xml).at(:artist).at(:name)
|
|
37
|
+
artist = (xml).at(:artist).inner_html if artist.nil? && (xml).at(:artist)
|
|
38
|
+
artist = doc.root['artist'] if artist.nil? && doc.root['artist']
|
|
39
|
+
a = Album.new(artist, name)
|
|
40
|
+
a.artist_mbid = (xml).at(:artist)['mbid'] if (xml).at(:artist) && (xml).at(:artist)['mbid']
|
|
41
|
+
a.artist_mbid = (xml).at(:artist).at(:mbid).inner_html if a.artist_mbid.nil? && (xml).at(:artist) && (xml).at(:artist).at(:mbid)
|
|
42
|
+
a.mbid = (xml).at(:mbid).inner_html if (xml).at(:mbid)
|
|
43
|
+
a.playcount = (xml).at(:playcount).inner_html if (xml).at(:playcount)
|
|
44
|
+
a.rank = xml['rank'] if xml['rank']
|
|
45
|
+
a.rank = (xml).at(:rank).inner_html if (xml).at(:rank) if a.rank.nil?
|
|
46
|
+
a.url = Base.fix_url((xml).at(:url).inner_html) if (xml).at(:url)
|
|
47
|
+
|
|
48
|
+
a.chartposition = a.rank
|
|
49
|
+
|
|
50
|
+
a.images = {}
|
|
51
|
+
(xml/'image').each {|image|
|
|
52
|
+
a.images[image['size']] = image.inner_html
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
a.image_large = a.images['large']
|
|
56
|
+
a.image_medium = a.images['medium']
|
|
57
|
+
a.image_small = a.images['small']
|
|
58
|
+
|
|
59
|
+
# needed on top albums for tag
|
|
60
|
+
a.count = xml['count'] if xml['count']
|
|
61
|
+
a.streamable = xml['streamable'] if xml['streamable']
|
|
62
|
+
a
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def initialize(artist, name, o={})
|
|
67
|
+
raise ArgumentError, "Artist is required" if artist.blank?
|
|
68
|
+
raise ArgumentError, "Name is required" if name.blank?
|
|
69
|
+
@artist = artist
|
|
70
|
+
@name = name
|
|
71
|
+
options = {:include_info => false}.merge(o)
|
|
72
|
+
load_info() if options[:include_info]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def load_info
|
|
76
|
+
doc = self.class.fetch_and_parse("album.getInfo", {:artist => @artist, :album =>@name})
|
|
77
|
+
@url = Base.fix_url((doc).at(:url).inner_html)
|
|
78
|
+
@release_date = Time.parse((doc).at(:releasedate).inner_html.strip)
|
|
79
|
+
|
|
80
|
+
@images = {}
|
|
81
|
+
(doc/'image').each {|image|
|
|
82
|
+
@images[image['size']] = image.inner_html
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@image_large = @images['large']
|
|
86
|
+
@image_medium = @images['medium']
|
|
87
|
+
@image_small = @images['small']
|
|
88
|
+
|
|
89
|
+
@mbid = (doc).at(:mbid).inner_html
|
|
90
|
+
@summary = (doc).at(:summary).to_plain_text
|
|
91
|
+
@content = (doc).at(:content).to_plain_text
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def tracks
|
|
95
|
+
warn "[DEPRECATION] `tracks` is deprecated. The current api doesn't offer this function"
|
|
96
|
+
[]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def image(which=:small)
|
|
100
|
+
which = which.to_s
|
|
101
|
+
raise ArgumentError unless ['small', 'medium', 'large', 'extralarge'].include?(which)
|
|
102
|
+
if (self.images.nil?)
|
|
103
|
+
load_info
|
|
104
|
+
end
|
|
105
|
+
self.images[which]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Below are examples of how to find an artists top tracks and similar artists.
|
|
2
|
+
#
|
|
3
|
+
# artist = Rockstar::Artist.new('Carrie Underwood')
|
|
4
|
+
#
|
|
5
|
+
# puts 'Top Tracks'
|
|
6
|
+
# puts "=" * 10
|
|
7
|
+
# artist.top_tracks.each { |t| puts "#{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 Rockstar
|
|
60
|
+
class Artist < Base
|
|
61
|
+
attr_accessor :name, :mbid, :playcount, :rank, :url, :thumbnail, :images, :count, :streamable
|
|
62
|
+
attr_accessor :chartposition
|
|
63
|
+
|
|
64
|
+
# used for similar artists
|
|
65
|
+
attr_accessor :match
|
|
66
|
+
|
|
67
|
+
class << self
|
|
68
|
+
def new_from_xml(xml, doc=nil)
|
|
69
|
+
name = (xml).at(:name).inner_html if (xml).at(:name)
|
|
70
|
+
# occasionally name can be found in root of artist element (<artist name="">) rather than as an element (<name>)
|
|
71
|
+
name = xml['name'] if name.nil? && xml['name']
|
|
72
|
+
a = Artist.new(name)
|
|
73
|
+
a.mbid = (xml).at(:mbid).inner_html if (xml).at(:mbid)
|
|
74
|
+
a.playcount = (xml).at(:playcount).inner_html if (xml).at(:playcount)
|
|
75
|
+
a.chartposition = a.rank = xml['rank'] if xml['rank']
|
|
76
|
+
a.chartposition = a.rank = (xml).at(:rank).inner_html if (xml).at(:rank) if a.rank.nil?
|
|
77
|
+
a.url = Base.fix_url((xml).at(:url).inner_html) if (xml).at(:url)
|
|
78
|
+
|
|
79
|
+
a.images = {}
|
|
80
|
+
(xml/'image').each {|image|
|
|
81
|
+
a.images[image['size']] = image.inner_html
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
a.thumbnail = a.images['small']
|
|
85
|
+
|
|
86
|
+
a.match = (xml).at(:match).inner_html if (xml).at(:match)
|
|
87
|
+
|
|
88
|
+
# in top artists for tag
|
|
89
|
+
a.count = xml['count'] if xml['count']
|
|
90
|
+
a.streamable = xml['streamable'] if xml['streamable']
|
|
91
|
+
a.streamable = (xml).at(:streamable).inner_html == '1' ? 'yes' : 'no' if a.streamable.nil? && (xml).at(:streamable)
|
|
92
|
+
a
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def initialize(name)
|
|
97
|
+
raise ArgumentError, "Name is required" if name.blank?
|
|
98
|
+
@name = name
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def current_events(format=:ics)
|
|
102
|
+
warn "[DEPRECATION] `current_events` is deprecated. The current api doesn't offer this function"
|
|
103
|
+
[]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def similar(force=false)
|
|
107
|
+
get_instance("artist.getSimilar", :similar, :artist, {:artist => @name}, force)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def top_fans(force=false)
|
|
111
|
+
get_instance("artist.getTopFans", :top_fans, :user, {:artist => @name}, force)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def top_tracks(force=false)
|
|
115
|
+
get_instance("artist.getTopTracks", :top_tracks, :track, {:artist => @name}, force)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def top_albums(force=false)
|
|
119
|
+
get_instance("artist.getTopAlbums", :top_albums, :album, {:artist => @name}, force)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def top_tags(force=false)
|
|
123
|
+
get_instance("artist.getTopTags", :top_tags, :tag, {:artist => @name}, force)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def image(which=:medium)
|
|
127
|
+
which = which.to_s
|
|
128
|
+
raise ArgumentError unless ['small', 'medium', 'large', 'extralarge'].include?(which)
|
|
129
|
+
if (self.images.nil?)
|
|
130
|
+
load_info
|
|
131
|
+
end
|
|
132
|
+
self.images[which]
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Rockstar
|
|
2
|
+
|
|
3
|
+
class Auth < Base
|
|
4
|
+
# Returns the token for a session. You have to use
|
|
5
|
+
# TokenAuth first and then use this class with the token
|
|
6
|
+
# that is given by last.fm to create a session token
|
|
7
|
+
# This session token can be stored in your database, it is
|
|
8
|
+
# not expiring.
|
|
9
|
+
# See Rockstar::TokenAuth for a detailed example
|
|
10
|
+
def session(token)
|
|
11
|
+
doc = self.class.fetch_and_parse("auth.getSession", {:token => token}, true)
|
|
12
|
+
Rockstar::Session.new_from_xml(doc)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def token
|
|
16
|
+
doc = self.class.fetch_and_parse("auth.getToken", {}, true)
|
|
17
|
+
token = (doc).at(:token).inner_html if (doc).at(:token)
|
|
18
|
+
token
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Rockstar
|
|
2
|
+
|
|
3
|
+
API_URL = 'http://ws.audioscrobbler.com/'
|
|
4
|
+
API_VERSION = '2.0'
|
|
5
|
+
|
|
6
|
+
class Base
|
|
7
|
+
class << self
|
|
8
|
+
def connection
|
|
9
|
+
@connection ||= REST::Connection.new(API_URL + API_VERSION + "/")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def fetch_and_parse(resource, params = {}, sign_request = false)
|
|
13
|
+
Hpricot::XML(connection.get(resource, sign_request, params))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
# in order for subclass to use, it must have api_path method
|
|
19
|
+
def get_instance(api_method, instance_name, element, params, force=false, sign_request = false)
|
|
20
|
+
rockstar_class = "rockstar/#{element.to_s}".camelize.constantize
|
|
21
|
+
|
|
22
|
+
if instance_variable_get("@#{instance_name}").nil? || force
|
|
23
|
+
doc = self.class.fetch_and_parse(api_method, params, sign_request)
|
|
24
|
+
elements = (doc/element).inject([]) { |elements, el| elements << rockstar_class.new_from_xml(el, doc); elements }
|
|
25
|
+
instance_variable_set("@#{instance_name}", elements)
|
|
26
|
+
end
|
|
27
|
+
instance_variable_get("@#{instance_name}")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.fix_url(url)
|
|
31
|
+
if (!url.blank? && !url.match("^http://"))
|
|
32
|
+
url = "http://#{url}"
|
|
33
|
+
end
|
|
34
|
+
url
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Rockstar
|
|
2
|
+
class Chart < Base
|
|
3
|
+
class << self
|
|
4
|
+
def new_from_xml(xml, doc)
|
|
5
|
+
Chart.new(xml['from'], xml['to'])
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
def initialize(from, to)
|
|
9
|
+
raise ArgumentError, "From is required" if from.blank?
|
|
10
|
+
raise ArgumentError, "To is required" if to.blank?
|
|
11
|
+
@from = from
|
|
12
|
+
@to = to
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def from=(value)
|
|
16
|
+
@from = value.to_i
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to=(value)
|
|
20
|
+
@to = value.to_i
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def from
|
|
24
|
+
@from.to_i
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to
|
|
28
|
+
@to.to_i
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Rockstar
|
|
2
|
+
class Playing
|
|
3
|
+
# you should read last.fm/api/submissions#np first!
|
|
4
|
+
|
|
5
|
+
attr_accessor :session_id, :now_playing_url, :artist, :track,
|
|
6
|
+
:album, :length, :track_number, :mb_track_id
|
|
7
|
+
attr_reader :status
|
|
8
|
+
|
|
9
|
+
def initialize(args = {})
|
|
10
|
+
@session_id = args[:session_id] # from Rockstar::SimpleAuth
|
|
11
|
+
@now_playing_url = args[:now_playing_url] # from Rockstar::SimpleAuth (can change)
|
|
12
|
+
@artist = args[:artist] # track artist
|
|
13
|
+
@track = args[:track] # track name
|
|
14
|
+
@album = args[:album] || '' # track album (optional)
|
|
15
|
+
@length = args[:length] || '' # track length in seconds (optional)
|
|
16
|
+
@track_number = args[:track_number] || '' # track number (optional)
|
|
17
|
+
@mb_track_id = args[:mb_track_id] || '' # MusicBrainz track ID (optional)
|
|
18
|
+
|
|
19
|
+
if [@session_id, @now_playing_url, @artist, @track].any?(&:blank?)
|
|
20
|
+
raise ArgumentError, 'Missing required argument'
|
|
21
|
+
elsif !@length.to_s.empty? && @length.to_i <= 30 # see last.fm/api
|
|
22
|
+
raise ArgumentError, 'Length must be greater than 30 seconds'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@connection = REST::Connection.new(@now_playing_url)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def submit!
|
|
29
|
+
query = { :s => @session_id,
|
|
30
|
+
:a => @artist,
|
|
31
|
+
:t => @track,
|
|
32
|
+
:b => @album,
|
|
33
|
+
:l => @length,
|
|
34
|
+
:n => @track_number,
|
|
35
|
+
:m => @mb_track_id }
|
|
36
|
+
|
|
37
|
+
@status = @connection.post('', false, query)
|
|
38
|
+
|
|
39
|
+
case @status
|
|
40
|
+
when /OK/
|
|
41
|
+
|
|
42
|
+
when /BADSESSION/
|
|
43
|
+
raise BadSessionError # rerun Rockstar::SimpleAuth#handshake!
|
|
44
|
+
else
|
|
45
|
+
raise RequestFailedError
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'net/https'
|
|
2
|
+
require 'digest/md5'
|
|
3
|
+
|
|
4
|
+
module Rockstar
|
|
5
|
+
module REST
|
|
6
|
+
class Connection
|
|
7
|
+
def initialize(base_url, args = {})
|
|
8
|
+
@base_url = base_url
|
|
9
|
+
@username = args[:username]
|
|
10
|
+
@password = args[:password]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get(resource, sign_request, args = {})
|
|
14
|
+
request(resource, "get", args, sign_request)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def post(resource, sign_request, args = nil)
|
|
18
|
+
request(resource, "post", args, sign_request)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def request(resource, method = "get", args = {}, sign_request=false)
|
|
22
|
+
url = URI.parse(@base_url)
|
|
23
|
+
|
|
24
|
+
if (!resource.blank?)
|
|
25
|
+
args[:method] = resource
|
|
26
|
+
args[:api_key]= Rockstar.lastfm_api_key
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if args
|
|
30
|
+
sorted_keys = args.keys.sort_by{|k|k.to_s}
|
|
31
|
+
query = sorted_keys.collect { |k| "%s=%s" % [escape(k.to_s), escape(args[k].to_s)] }.join("&")
|
|
32
|
+
|
|
33
|
+
if !args[:sk].nil? ||sign_request # Session Key available => sign the request or sign_request = true?
|
|
34
|
+
signed = sorted_keys.collect {|k| "%s%s" % [k.to_s, args[k].to_s]}.join()
|
|
35
|
+
|
|
36
|
+
auth = Digest::MD5.hexdigest("#{signed}#{Rockstar.lastfm_api_secret}")
|
|
37
|
+
query += "&api_sig=#{auth}"
|
|
38
|
+
end
|
|
39
|
+
url.query = query
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
case method
|
|
43
|
+
when "get"
|
|
44
|
+
req = Net::HTTP::Get.new(url.request_uri)
|
|
45
|
+
when "post"
|
|
46
|
+
req = Net::HTTP::Post.new(url.request_uri)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if @username and @password
|
|
50
|
+
req.basic_auth(@username, @password)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
54
|
+
http.use_ssl = (url.port == 443)
|
|
55
|
+
|
|
56
|
+
res = http.start() { |conn| conn.request(req) }
|
|
57
|
+
res.body
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
def escape(str)
|
|
62
|
+
URI.escape(str, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|