mostscrobbled 0.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 +7 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/README.rdoc +33 -0
- data/Rakefile +2 -0
- data/lib/mostscrobbled.rb +14 -0
- data/lib/mostscrobbled/artist.rb +25 -0
- data/lib/mostscrobbled/last_fm.rb +62 -0
- data/lib/mostscrobbled/version.rb +3 -0
- data/mostscrobbled.gemspec +25 -0
- data/spec/artist_spec.rb +21 -0
- data/spec/fixtures/artists_failed.xml +3 -0
- data/spec/fixtures/artists_ok.xml +30 -0
- data/spec/last_fm_spec.rb +92 -0
- data/spec/most_scrobbled_spec.rb +13 -0
- data/spec/spec_helper.rb +2 -0
- metadata +109 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
= Mostscrobbled
|
2
|
+
|
3
|
+
Extracts the artists from a {last.fm}[http://last.fm] user's music library and turns them into nice ruby objects.
|
4
|
+
|
5
|
+
{Official Documentation}[http://rubydoc.info/github/paulsturgess/mostscrobbled/master/frames]
|
6
|
+
|
7
|
+
== Usage
|
8
|
+
|
9
|
+
Before you start you'll need:
|
10
|
+
* {A last.fm account}[http://last.fm]
|
11
|
+
* {A last.fm api key}[http://last.fm/api]
|
12
|
+
|
13
|
+
Example:
|
14
|
+
|
15
|
+
require 'rubygems'
|
16
|
+
require 'mostscrobbled'
|
17
|
+
|
18
|
+
# The main method call returns an array of artists ordered by the number of scrobbles
|
19
|
+
artists = Mostscrobbled.find(:username => "foobar", :api_key => "123abc")
|
20
|
+
|
21
|
+
my_favourite_artist = artists.first
|
22
|
+
|
23
|
+
# Artists have a number of methods to easily access the attributes returned by the last.fm api
|
24
|
+
# For example...
|
25
|
+
my_favourite_artist.name # => "Bibio"
|
26
|
+
my_favourite_artist.playcount # => "391"
|
27
|
+
my_favourite_artist.url # => "http://www.last.fm/music/Bibio"
|
28
|
+
my_favourite_artist.mbid # => "9f9953f0-68bb-4ce3-aace-2f44c87f0aa3"
|
29
|
+
my_favourite_artist.image_small # => "http://userserve-ak.last.fm/serve/34/39790231.jpg"
|
30
|
+
|
31
|
+
== Installation
|
32
|
+
|
33
|
+
gem install mostscrobbled
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Mostscrobbled
|
2
|
+
|
3
|
+
def self.find(opts = {})
|
4
|
+
begin
|
5
|
+
Mostscrobbled::LastFm::Connection.new(opts).artists
|
6
|
+
rescue Mostscrobbled::LastFm::Error => e
|
7
|
+
puts e
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'mostscrobbled/last_fm'
|
14
|
+
require 'mostscrobbled/artist'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Mostscrobbled
|
2
|
+
class Artist
|
3
|
+
|
4
|
+
attr_accessor :name, :playcount, :tagcount, :mbid, :url, :streamable, :image_small, :image_medium, :image_large, :image_extralarge, :image_mega
|
5
|
+
|
6
|
+
def initialize(attrs = {}, *args)
|
7
|
+
super(*args)
|
8
|
+
attrs.each do |k,v|
|
9
|
+
self.send "#{k}=", v
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.build_artist(node)
|
14
|
+
(artist = Artist.new).tap do
|
15
|
+
[:name, :playcount, :tagcount, :mbid, :url, :streamable].each do |attribute|
|
16
|
+
artist.send("#{attribute}=", node.xpath(".//#{attribute}").first.content)
|
17
|
+
end
|
18
|
+
node.xpath(".//image").each do |image_node| # set :image_small, :image_medium, :image_large, :image_extralarge, :image_mega
|
19
|
+
artist.send("image_#{image_node.attributes["size"].value}=", image_node.children.first.content) if image_node.children.first
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
module Mostscrobbled
|
6
|
+
module LastFm
|
7
|
+
class Error < StandardError; end
|
8
|
+
class ResponseError < Error; end
|
9
|
+
class ConfigError < Error; end
|
10
|
+
class Connection
|
11
|
+
|
12
|
+
REQUIRED_ATTRIBUTES = :username, :api_key
|
13
|
+
attr_accessor *(REQUIRED_ATTRIBUTES)
|
14
|
+
|
15
|
+
def initialize(opts = {})
|
16
|
+
raise ConfigError, missing_options(opts).map{ |argument| ":#{argument} argument missing" }.join(" and ") unless missing_options(opts).empty?
|
17
|
+
opts.keys.each{ |key| send("#{key}=", opts[key]) }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns an array of MostscrobbledArtist objects
|
21
|
+
def artists
|
22
|
+
(artists = []).tap do
|
23
|
+
total_artist_pages.times do |page|
|
24
|
+
artists_xml(page).xpath('//artist').collect do |node|
|
25
|
+
artists << Mostscrobbled::Artist.build_artist(node)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def artists_uri(page_number = nil)
|
32
|
+
URI.parse("http://ws.audioscrobbler.com/2.0/?method=library.getartists&api_key=#{api_key}&user=#{username}#{"&page=#{page_number + 1}" if page_number}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def artists_xml(page_number = nil)
|
36
|
+
uri = artists_uri(page_number)
|
37
|
+
http = Net::HTTP.new(uri.host)
|
38
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
39
|
+
response = http.request(request)
|
40
|
+
case response.code
|
41
|
+
when "200"
|
42
|
+
@artists_xml ||= {}
|
43
|
+
@artists_xml[page_number || 0] ||= Nokogiri::XML(response.body)
|
44
|
+
when "403"
|
45
|
+
raise ResponseError, Nokogiri::XML(response.body).xpath(".//error").first.content.inspect
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def total_artist_pages
|
50
|
+
artists_xml.xpath('//artists').first["totalPages"].to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def missing_options(opts)
|
56
|
+
REQUIRED_ATTRIBUTES.select{ |argument| opts[argument].nil? }
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "mostscrobbled/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "mostscrobbled"
|
7
|
+
s.version = Mostscrobbled::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Paul Sturgess"]
|
10
|
+
s.email = ["paulsturgess@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = "Extracts your most scrobbled artists from last.fm and turns them into nice ruby objects"
|
13
|
+
s.description = "Uses the last.fm api to analyse your scrobbles library and extracts the artists out in order of number of scrobbles"
|
14
|
+
|
15
|
+
s.rubyforge_project = "mostscrobbled"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_runtime_dependency "nokogiri", "~> 1.4"
|
23
|
+
s.add_development_dependency "rspec", "~> 2.0.0.beta.22"
|
24
|
+
s.add_development_dependency "fakeweb", "~> 1.3.0"
|
25
|
+
end
|
data/spec/artist_spec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Mostscrobbled::Artist do
|
4
|
+
|
5
|
+
context "when inititalizing an artist" do
|
6
|
+
let(:artist) {Mostscrobbled::Artist.new(:name => "Bibio", :mbid => "123", :playcount => 200)}
|
7
|
+
|
8
|
+
it "should set the name" do
|
9
|
+
artist.name.should == "Bibio"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should set the mbid" do
|
13
|
+
artist.mbid.should == "123"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should set the scrobbles_count" do
|
17
|
+
artist.playcount.should == 200
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<lfm status="ok">
|
3
|
+
<artists user="timberford" page="1" perPage="50" totalPages="1" total="2">
|
4
|
+
<artist>
|
5
|
+
<name>Bibio</name>
|
6
|
+
<playcount>391</playcount>
|
7
|
+
<tagcount>0</tagcount>
|
8
|
+
<mbid>9f9953f0-68bb-4ce3-aace-2f44c87f0aa3</mbid>
|
9
|
+
<url>http://www.last.fm/music/Bibio</url>
|
10
|
+
<streamable>1</streamable>
|
11
|
+
<image size="small">http://userserve-ak.last.fm/serve/34/27532381.jpg</image>
|
12
|
+
<image size="medium">http://userserve-ak.last.fm/serve/64/27532381.jpg</image>
|
13
|
+
<image size="large">http://userserve-ak.last.fm/serve/126/27532381.jpg</image>
|
14
|
+
<image size="extralarge">http://userserve-ak.last.fm/serve/252/27532381.jpg</image>
|
15
|
+
<image size="mega">http://userserve-ak.last.fm/serve/500/27532381/Bibio+flap+flap.jpg</image>
|
16
|
+
</artist>
|
17
|
+
<artist>
|
18
|
+
<name>Bonobo</name>
|
19
|
+
<playcount>387</playcount>
|
20
|
+
<tagcount>0</tagcount>
|
21
|
+
<mbid>9a709693-b4f8-4da9-8cc1-038c911a61be</mbid>
|
22
|
+
<url>http://www.last.fm/music/Bonobo</url>
|
23
|
+
<streamable>1</streamable>
|
24
|
+
<image size="small">http://userserve-ak.last.fm/serve/34/53939783.png</image>
|
25
|
+
<image size="medium">http://userserve-ak.last.fm/serve/64/53939783.png</image>
|
26
|
+
<image size="large">http://userserve-ak.last.fm/serve/126/53939783.png</image>
|
27
|
+
<image size="extralarge">http://userserve-ak.last.fm/serve/252/53939783.png</image>
|
28
|
+
<image size="mega"></image>
|
29
|
+
</artist>
|
30
|
+
</artists></lfm>
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Mostscrobbled::LastFm::Connection do
|
4
|
+
|
5
|
+
before { FakeWeb.allow_net_connect = false }
|
6
|
+
after { FakeWeb.allow_net_connect = true }
|
7
|
+
|
8
|
+
let(:connection) {Mostscrobbled::LastFm::Connection.new(:username => "johnsmith", :api_key => "023456789")}
|
9
|
+
|
10
|
+
|
11
|
+
context "when intializing a connection with valid argument" do
|
12
|
+
it "should set the username" do
|
13
|
+
connection.username.should == "johnsmith"
|
14
|
+
end
|
15
|
+
it "should set the api_key" do
|
16
|
+
connection.api_key.should == "023456789"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when initializing a connection without the username argument" do
|
21
|
+
it "should raise an exception" do
|
22
|
+
lambda {Mostscrobbled::LastFm::Connection.new(:api_key => "123")}.should raise_exception(Mostscrobbled::LastFm::ConfigError)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when initializing a connection without the api_key argument" do
|
27
|
+
it "should raise an exception" do
|
28
|
+
lambda {Mostscrobbled::LastFm::Connection.new(:username => "John")}.should raise_exception(Mostscrobbled::LastFm::ConfigError)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "artists_uri" do
|
33
|
+
context "when not providing a page number" do
|
34
|
+
it "should set the artists uri without a page number" do
|
35
|
+
connection.artists_uri.should == URI.parse("http://ws.audioscrobbler.com/2.0/?method=library.getartists&api_key=023456789&user=johnsmith")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
context "when providing a page number" do
|
39
|
+
it "should set the artists uri with a page number" do
|
40
|
+
connection.artists_uri(2).should == URI.parse("http://ws.audioscrobbler.com/2.0/?method=library.getartists&api_key=023456789&user=johnsmith&page=3")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Note that you cannot compare two Nokogiri documents that have been parsed, even if you are parsing the same document
|
46
|
+
# In any case we're not testing that Nokogiri works, we're testing that Nokogiri is called with the correct options
|
47
|
+
describe "artists_xml" do
|
48
|
+
context "when the result is ok" do
|
49
|
+
before do
|
50
|
+
FakeWeb.register_uri(:get, "http://example.com", :body => File.read("spec/fixtures/artists_ok.xml"))
|
51
|
+
connection.stub(:artists_uri){URI.parse("http://example.com")}
|
52
|
+
end
|
53
|
+
it "should call the artists url with Nokogiri parse to get the xml" do
|
54
|
+
Nokogiri::XML::Document.should_receive(:parse).with(Net::HTTP.get(URI.parse("http://example.com")), nil, nil, 1)
|
55
|
+
connection.artists_xml
|
56
|
+
end
|
57
|
+
it "should not raise the error message as an exception" do
|
58
|
+
lambda {connection.artists_xml}.should_not raise_exception(Mostscrobbled::LastFm::ResponseError)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
context "when the result is failed" do
|
62
|
+
before do
|
63
|
+
FakeWeb.register_uri(:get, "http://example.com", :body => File.read("spec/fixtures/artists_failed.xml"), :status => ["403", "Forbidden"])
|
64
|
+
connection.stub(:artists_uri){URI.parse("http://example.com")}
|
65
|
+
end
|
66
|
+
it "should raise the a ResponseError exception" do
|
67
|
+
lambda {connection.artists_xml}.should raise_exception(Mostscrobbled::LastFm::ResponseError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "total_artist_pages" do
|
73
|
+
before { connection.stub(:artists_xml).and_return(Nokogiri::XML(File.read("spec/fixtures/artists_ok.xml"))) }
|
74
|
+
it "should return the correct number of pages" do
|
75
|
+
connection.total_artist_pages.should == 1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "artists" do
|
80
|
+
before do
|
81
|
+
connection.stub(:total_artist_pages){1}
|
82
|
+
connection.stub(:artists_xml).and_return(Nokogiri::XML(File.read("spec/fixtures/artists_ok.xml")))
|
83
|
+
end
|
84
|
+
it "should include the artists returned by artists_xml" do
|
85
|
+
connection.artists.map(&:name).should include("Bibio")
|
86
|
+
connection.artists.map(&:name).should include("Bonobo")
|
87
|
+
connection.artists.first.should be_kind_of(Mostscrobbled::Artist)
|
88
|
+
connection.artists.length.should == 2
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Mostscrobbled do
|
4
|
+
|
5
|
+
context "when intializing Mostscrobbled" do
|
6
|
+
it "should create a lastfm connection and call artists on it" do
|
7
|
+
connection = mock(Mostscrobbled::LastFm::Connection)
|
8
|
+
Mostscrobbled::LastFm::Connection.should_receive(:new).with(:username => "john", :api_key => "123") {connection}
|
9
|
+
connection.should_receive(:artists)
|
10
|
+
Mostscrobbled.find(:username => "john", :api_key => "123")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mostscrobbled
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paul Sturgess
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-07-13 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: nokogiri
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "1.4"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ~>
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 2.0.0.beta.22
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: fakeweb
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.3.0
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
description: Uses the last.fm api to analyse your scrobbles library and extracts the artists out in order of number of scrobbles
|
50
|
+
email:
|
51
|
+
- paulsturgess@gmail.com
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files: []
|
57
|
+
|
58
|
+
files:
|
59
|
+
- .gitignore
|
60
|
+
- .rspec
|
61
|
+
- Gemfile
|
62
|
+
- README.rdoc
|
63
|
+
- Rakefile
|
64
|
+
- lib/mostscrobbled.rb
|
65
|
+
- lib/mostscrobbled/artist.rb
|
66
|
+
- lib/mostscrobbled/last_fm.rb
|
67
|
+
- lib/mostscrobbled/version.rb
|
68
|
+
- mostscrobbled.gemspec
|
69
|
+
- spec/artist_spec.rb
|
70
|
+
- spec/fixtures/artists_failed.xml
|
71
|
+
- spec/fixtures/artists_ok.xml
|
72
|
+
- spec/last_fm_spec.rb
|
73
|
+
- spec/most_scrobbled_spec.rb
|
74
|
+
- spec/spec_helper.rb
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: ""
|
77
|
+
licenses: []
|
78
|
+
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: "0"
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: "0"
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project: mostscrobbled
|
99
|
+
rubygems_version: 1.6.2
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: Extracts your most scrobbled artists from last.fm and turns them into nice ruby objects
|
103
|
+
test_files:
|
104
|
+
- spec/artist_spec.rb
|
105
|
+
- spec/fixtures/artists_failed.xml
|
106
|
+
- spec/fixtures/artists_ok.xml
|
107
|
+
- spec/last_fm_spec.rb
|
108
|
+
- spec/most_scrobbled_spec.rb
|
109
|
+
- spec/spec_helper.rb
|