musicbrainz 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/Contributors ADDED
@@ -0,0 +1,4 @@
1
+ Huge thanks to that people for making this gem better!
2
+
3
+ Applicat (https://github.com/Applicat)
4
+ Jens Fahnenbruck (https://github.com/jigfox)
data/README.md CHANGED
@@ -4,16 +4,21 @@
4
4
  ```bash
5
5
  gem install musicbrainz
6
6
  ```
7
+ or add this line to your Gemfile
8
+ ```ruby
9
+ gem "musicbrainz"
10
+ ```
11
+
7
12
  ### Usage
8
13
  ```ruby
9
- require 'musicbrainz'
14
+ require "musicbrainz"
10
15
 
11
16
  # Search for artists
12
- @suggestions = MusicBrainz::Artist.search('Jet')
17
+ @suggestions = MusicBrainz::Artist.search("Jet")
13
18
 
14
19
  # Find artist by name or mbid
15
- @foo_fighters = MusicBrainz::Artist.find_by_name('Foo Fighters')
16
- @kasabian = MusicBrainz::Artist.find('69b39eab-6577-46a4-a9f5-817839092033')
20
+ @foo_fighters = MusicBrainz::Artist.find_by_name("Foo Fighters")
21
+ @kasabian = MusicBrainz::Artist.find("69b39eab-6577-46a4-a9f5-817839092033")
17
22
 
18
23
  # Use them like ActiveRecord models
19
24
  @empire_tracks = @kasabian.release_groups[8].releases.first.tracks
@@ -71,6 +76,16 @@ MusicBrainz::Track
71
76
  @track.length
72
77
  ```
73
78
 
79
+ ### Testing
80
+ ```bash
81
+ bundle rake spec
82
+ ```
83
+
84
+ ### Debug console
85
+ ```bash
86
+ bundle rake console
87
+ ```
88
+
74
89
  ### Contributing
75
90
 
76
91
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
data/Rakefile CHANGED
@@ -6,3 +6,8 @@ RSpec::Core::RakeTask.new("spec")
6
6
 
7
7
  task :default => :spec
8
8
  task :test => :spec
9
+
10
+ desc "Open an irb session preloaded with this library"
11
+ task :console do
12
+ sh "irb -rubygems -I lib -r musicbrainz.rb"
13
+ end
data/lib/deprecated.rb CHANGED
@@ -1,12 +1,25 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  module MusicBrainz
3
- def self.query_interval=(sec)
4
- MusicBrainz::Tools::Proxy.query_interval = sec
5
- puts "WARNING! MusicBrainz.query_interval is deprecated. Use MusicBrainz::Tools::Proxy.query_interval"
6
- end
4
+ class << self
5
+ def query_interval
6
+ $stdout.send :puts, "WARNING! MusicBrainz.query_interval is deprecated. Use MusicBrainz::Tools::Proxy.query_interval"
7
+ MusicBrainz::Tools::Proxy.query_interval
8
+ end
9
+
10
+ def query_interval=(sec)
11
+ $stdout.send :puts, "WARNING! MusicBrainz.query_interval= is deprecated. Use MusicBrainz::Tools::Proxy.query_interval"
12
+ MusicBrainz::Tools::Proxy.query_interval = sec
13
+ end
14
+
15
+ def cache_path
16
+ $stdout.send :puts, "WARNING! MusicBrainz.cache_path is deprecated. Use MusicBrainz::Tools::Cache.cache_path"
17
+ MusicBrainz::Tools::Cache.cache_path
18
+ end
7
19
 
8
- def self.cache_path=(path)
9
- MusicBrainz::Tools::Cache.cache_path = path
10
- puts "WARNING! MusicBrainz.cache_path is deprecated. Use MusicBrainz::Tools::Cache.cache_path"
20
+ def cache_path=(path)
21
+ $stdout.send :puts, "WARNING! MusicBrainz.cache_path= is deprecated. Use MusicBrainz::Tools::Cache.cache_path"
22
+ MusicBrainz::Tools::Cache.cache_path = path
23
+ end
11
24
  end
12
25
  end
data/lib/musicbrainz.rb CHANGED
@@ -1,20 +1,29 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  require "open-uri"
3
4
  require "socket"
4
5
  require "nokogiri"
5
6
  require "cgi"
6
7
 
8
+ module MusicBrainz
9
+ module Tools; end
10
+ module Parsers; end
11
+ end
12
+
7
13
  require "version"
14
+ require "deprecated"
8
15
 
9
- module MusicBrainz
10
- autoload :Base, "musicbrainz/base"
11
- autoload :Artist, "musicbrainz/artist"
12
- autoload :ReleaseGroup, "musicbrainz/release_group"
13
- autoload :Release, "musicbrainz/release"
14
- autoload :Track, "musicbrainz/track"
16
+ require "musicbrainz/base"
17
+ require "musicbrainz/artist"
18
+ require "musicbrainz/release_group"
19
+ require "musicbrainz/release"
20
+ require "musicbrainz/track"
15
21
 
16
- module Tools
17
- autoload :Cache, "tools/cache"
18
- autoload :Proxy, "tools/proxy"
19
- end
20
- end
22
+ require "tools/cache"
23
+ require "tools/proxy"
24
+
25
+ require "parsers/base"
26
+ require "parsers/artist"
27
+ require "parsers/release_group"
28
+ require "parsers/release"
29
+ require "parsers/track"
@@ -1,81 +1,77 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  module MusicBrainz
4
- class Artist < MusicBrainz::Base
5
- attr_accessor :id, :type, :name, :country, :date_begin, :date_end, :urls
6
- @release_groups
4
+ class Artist < Base
5
+
6
+ field :id, String
7
+ field :type, String
8
+ field :name, String
9
+ field :country, String
10
+ field :date_begin, Time
11
+ field :date_end, Time
12
+ field :urls, Hash
7
13
 
8
14
  def release_groups
9
- if @release_groups.nil? and not self.id.nil?
10
- @release_groups = []
11
- Nokogiri::XML(self.class.load(:release_group, :artist => self.id)).css('release-group').each do |rg|
12
- @release_groups << MusicBrainz::ReleaseGroup.parse_xml(rg)
13
- end
15
+ @release_groups ||= nil
16
+ if @release_groups.nil? and !id.nil?
17
+ @release_groups = self.class.load({
18
+ :parser => :artist_release_groups,
19
+ :create_models => MusicBrainz::ReleaseGroup
20
+ }, {
21
+ :resource => :release_group,
22
+ :artist => id
23
+ })
24
+ @release_groups.sort!{ |a, b| a.first_release_date <=> b.first_release_date }
14
25
  end
15
- @release_groups.sort{ |a, b| a.first_release_date <=> b.first_release_date }
16
- end
17
-
18
- def self.find mbid
19
- res = self.load :artist, :id => mbid, :inc => [:url_rels]
20
- return nil if res.nil?
21
- @artist = self.parse_xml(Nokogiri::XML(res))
26
+ @release_groups
22
27
  end
23
28
 
24
- def self.parse_xml xml
25
- @artist = MusicBrainz::Artist.new
26
- @artist.id = self.safe_get_attr(xml, 'artist', 'id')
27
- @artist.type = self.safe_get_attr(xml, 'artist', 'type')
28
- @artist.name = self.safe_get_value(xml, 'artist > name').gsub(/[`’]/, "'")
29
- @artist.country = self.safe_get_value(xml, 'artist > country')
30
- @artist.date_begin = self.safe_get_value(xml, 'artist > life-span > begin')
31
- @artist.date_end = self.safe_get_value(xml, 'artist > life-span > end')
32
- @artist.urls = {}
33
- xml.css('relation-list[target-type="url"] > relation').each do |rel|
34
- @artist.urls[rel.attr('type').downcase.split(' ').join('_').to_sym] = rel.css('target').text
29
+ class << self
30
+ def find(mbid)
31
+ load({
32
+ :parser => :artist_model,
33
+ :create_model => MusicBrainz::Artist
34
+ }, {
35
+ :resource => :artist,
36
+ :id => mbid,
37
+ :inc => [:url_rels]
38
+ })
35
39
  end
36
- @artist
37
- end
38
40
 
39
- def self.discography mbid
40
- artist = self.find(mbid)
41
- artist.release_groups.each {|rg| rg.releases.each {|r| r.tracks } }
42
- artist
43
- end
41
+ def search(name)
42
+ artists = load({
43
+ :parser => :artist_search
44
+ }, {
45
+ :resource => :artist,
46
+ :query => CGI.escape(name).gsub(/\!/, '\!') + '~',
47
+ :limit => 50
48
+ })
49
+ artists.each { |artist|
50
+ if artist[:name].downcase == name.downcase
51
+ artist[:weight] += 80
52
+ elsif artist[:name].downcase.gsub(/\s/, "") == name.downcase.gsub(/\s/, "")
53
+ artist[:weight] += 25
54
+ elsif artist[:aliases].include? name
55
+ artist[:weight] += 20
56
+ elsif artist[:aliases].map { |item| item.downcase }.include?(name.downcase)
57
+ artist[:weight] += 10
58
+ elsif artist[:aliases].map { |item| item.downcase.gsub(/\s/, "") }.include?(name.downcase.gsub(/\s/, ""))
59
+ artist[:weight] += 5
60
+ end
61
+ }
62
+ artists.sort{ |a, b| b[:weight] <=> a[:weight] }.take(10)
63
+ end
44
64
 
45
- def self.find_by_name name
46
- matches = self.search name
47
- matches.length.zero? ? nil : self.find(matches.first[:mbid])
48
- end
65
+ def discography(mbid)
66
+ artist = find(mbid)
67
+ artist.release_groups.each { |rg| rg.releases.each { |r| r.tracks } }
68
+ artist
69
+ end
49
70
 
50
- def self.search name
51
- artists = []
52
- xml = Nokogiri::XML(self.load(:artist, :query => CGI.escape(name).gsub(/\!/, '\!') + '~', :limit => 50))
53
- xml.css('artist-list > artist').each do |a|
54
- artist = {
55
- :name => a.first_element_child.text.gsub(/[`’]/, "'"),
56
- :sort_name => self.safe_get_value(a, 'sort-name').gsub(/[`’]/, "'"),
57
- :weight => 0,
58
- :desc => self.safe_get_value(a, 'disambiguation'),
59
- :type => self.safe_get_attr(a, nil, 'type'),
60
- :mbid => self.safe_get_attr(a, nil, 'id')
61
- }
62
- aliases = a.css('alias-list > alias').map{ |item| item.text }
63
- if artist[:name] == name
64
- artist[:weight] += 100
65
- elsif artist[:name].downcase == name.downcase
66
- artist[:weight] += 50
67
- elsif artist[:name].downcase.gsub(/\s/, '') == name.downcase.gsub(/\s/, '')
68
- artist[:weight] += 25
69
- elsif aliases.include? name
70
- artist[:weight] += 20
71
- elsif aliases.map{ |item| item.downcase }.include? name.downcase
72
- artist[:weight] += 10
73
- elsif aliases.map{ |item| item.downcase.gsub(/\s/, '') }.include? name.downcase.gsub(/\s/, '')
74
- artist[:weight] += 5
75
- end
76
- artists << artist
71
+ def find_by_name(name)
72
+ matches = search(name)
73
+ matches.length.zero? ? nil : find(matches.first[:mbid])
77
74
  end
78
- artists.sort{ |a, b| b[:weight] <=> a[:weight] }.take(10)
79
75
  end
80
76
  end
81
77
  end
@@ -1,17 +1,71 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  module MusicBrainz
3
4
  class Base
4
- def self.safe_get_attr(xml, path, name)
5
- node = path.nil? ? xml : (xml.css(path).first unless xml.css(path).empty?)
6
- node.attr(name) unless node.nil? or node.attr(name).nil?
5
+ class << self
6
+ def field(name, type)
7
+ @fields ||= {}
8
+ @fields[name] = type
9
+
10
+ define_method(name) {
11
+ instance_variable_get("@#{name}")
12
+ }
13
+ define_method("#{name}=") { |val|
14
+ instance_variable_set("@#{name}", validate_type(val, type))
15
+ }
16
+ end
17
+
18
+ def load(params, query)
19
+ parser = MusicBrainz::Parsers.get_by_name(params[:parser])
20
+ xml = MusicBrainz::Tools::Proxy.query(query)
21
+ result = parser[:const].send(parser[:method], Nokogiri::XML(xml))
22
+ if params[:create_model]
23
+ result_model = params[:create_model].new
24
+ result.each { |field, value|
25
+ result_model.send("#{field}=".to_sym, value)
26
+ }
27
+ result_model
28
+ elsif params[:create_models]
29
+ result_models = []
30
+ result.each { |item|
31
+ result_model = params[:create_models].new
32
+ item.each { |field, value|
33
+ result_model.send("#{field}=".to_sym, value)
34
+ }
35
+ result_models << result_model
36
+ }
37
+ result_models
38
+ else
39
+ result
40
+ end
41
+ end
7
42
  end
8
43
 
9
- def self.safe_get_value(xml, path)
10
- xml.css(path).first.text unless xml.css(path).empty?
44
+ def initialize
45
+ self.class.instance_variable_get("@fields").each { |name, type|
46
+ instance_variable_set("@#{name}", nil)
47
+ }
11
48
  end
12
49
 
13
- def self.load(*args)
14
- MusicBrainz::Tools::Proxy.load(*args)
50
+ def validate_type(val, type)
51
+ if type == Integer
52
+ val.to_i
53
+ elsif type == Float
54
+ val.to_f
55
+ elsif type == String
56
+ val.to_s
57
+ elsif type == Time
58
+ if val.nil? or val == ""
59
+ val = "2030-12-31"
60
+ elsif val.split("-").length == 1
61
+ val << "-12-31"
62
+ elsif val.split("-").length == 2
63
+ val << "-31"
64
+ end
65
+ Time.utc(*val.split("-"))
66
+ else
67
+ val
68
+ end
15
69
  end
16
70
  end
17
71
  end
@@ -1,42 +1,43 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  module MusicBrainz
3
- class Release < MusicBrainz::Base
4
- attr_accessor :id, :title, :status, :format, :date, :country
5
- @tracks
4
+ class Release < Base
5
+
6
+ field :id, String
7
+ field :title, String
8
+ field :status, String
9
+ field :format, String
10
+ field :date, Time
11
+ field :country, String
6
12
 
7
13
  def tracks
8
- if @tracks.nil? and not self.id.nil?
9
- @tracks = []
10
- Nokogiri::XML(self.class.load(:release, :id => self.id, :inc => [:recordings, :media], :limit => 100)).css('medium-list > medium > track-list > track').each do |r|
11
- @tracks << MusicBrainz::Track.parse_xml(r)
12
- end
14
+ @tracks ||= nil
15
+ if @tracks.nil? and !id.nil?
16
+ @tracks = self.class.load({
17
+ :parser => :release_tracks,
18
+ :create_models => MusicBrainz::Track
19
+ }, {
20
+ :resource => :release,
21
+ :id => id,
22
+ :inc => [:recordings, :media],
23
+ :limit => 100
24
+ })
25
+ @tracks.sort{ |a, b| a.position <=> b.position }
13
26
  end
14
- @tracks.sort{ |a, b| a.position <=> b.position }
15
- end
16
-
17
- def self.find mbid
18
- xml = Nokogiri::XML(self.load(:release, :id => mbid, :inc => [:media])).css('release').first
19
- self.parse_xml(xml) unless xml.nil?
27
+ @tracks
20
28
  end
21
29
 
22
- def self.parse_xml xml
23
- @release = MusicBrainz::Release.new
24
- @release.id = self.safe_get_attr(xml, nil, 'id')
25
- @release.title = self.safe_get_value(xml, 'title')
26
- @release.status = self.safe_get_value(xml, 'status')
27
- @release.format = self.safe_get_value(xml, 'medium-list > medium > format')
28
- date = xml.css('date').empty? ? '2030-12-31' : xml.css('date').text
29
- if date.length == 0
30
- date = '2030-12-31'
31
- elsif date.length == 4
32
- date += '-12-31'
33
- elsif date.length == 7
34
- date += '-31'
30
+ class << self
31
+ def find(mbid)
32
+ load({
33
+ :parser => :release_model,
34
+ :create_model => MusicBrainz::Release
35
+ }, {
36
+ :resource => :release,
37
+ :id => mbid,
38
+ :inc => [:media]
39
+ })
35
40
  end
36
- date = date.split('-')
37
- @release.date = Time.utc(date[0], date[1], date[2])
38
- @release.country = self.safe_get_value(xml, 'country')
39
- @release
40
41
  end
41
42
  end
42
43
  end
@@ -1,41 +1,41 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  module MusicBrainz
3
- class ReleaseGroup < MusicBrainz::Base
4
- attr_accessor :id, :type, :title, :disambiguation, :first_release_date
5
- @releases
4
+ class ReleaseGroup < Base
5
+
6
+ field :id, String
7
+ field :type, String
8
+ field :title, String
9
+ field :disambiguation, String
10
+ field :first_release_date, Time
6
11
 
7
12
  def releases
8
- if @releases.nil? and not self.id.nil?
9
- @releases = []
10
- Nokogiri::XML(self.class.load(:release, :release_group => self.id, :inc => [:media], :limit => 100)).css('release').each do |r|
11
- @releases << MusicBrainz::Release.parse_xml(r)
12
- end
13
+ @releases ||= nil
14
+ if @releases.nil? and !id.nil?
15
+ @releases = self.class.load({
16
+ :parser => :release_group_releases,
17
+ :create_models => MusicBrainz::Release
18
+ }, {
19
+ :resource => :release,
20
+ :release_group => self.id,
21
+ :inc => [:media],
22
+ :limit => 100
23
+ })
24
+ @releases.sort!{ |a, b| a.date <=> b.date }
13
25
  end
14
- @releases.sort{ |a, b| a.date <=> b.date }
15
- end
16
-
17
- def self.find mbid
18
- xml = Nokogiri::XML(self.load(:release_group, :id => mbid)).css('release-group').first
19
- self.parse_xml(xml) unless xml.nil?
26
+ @releases
20
27
  end
21
28
 
22
- def self.parse_xml xml
23
- @release_group = MusicBrainz::ReleaseGroup.new
24
- @release_group.id = self.safe_get_attr(xml, nil, 'id')
25
- @release_group.type = self.safe_get_attr(xml, nil, 'type')
26
- @release_group.title = self.safe_get_value(xml, 'title')
27
- @release_group.disambiguation = self.safe_get_value(xml, 'disambiguation')
28
- date = xml.css('first-release-date').empty? ? '2030-12-31' : xml.css('first-release-date').text
29
- if date.length == 0
30
- date = '2030-12-31'
31
- elsif date.length == 4
32
- date += '-12-31'
33
- elsif date.length == 7
34
- date += '-31'
29
+ class << self
30
+ def find(mbid)
31
+ load({
32
+ :parser => :release_group_model,
33
+ :create_model => MusicBrainz::ReleaseGroup
34
+ }, {
35
+ :resource => :release_group,
36
+ :id => mbid
37
+ })
35
38
  end
36
- date = date.split('-')
37
- @release_group.first_release_date = Time.utc(date[0], date[1], date[2])
38
- @release_group
39
39
  end
40
40
  end
41
41
  end
@@ -1,21 +1,23 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  module MusicBrainz
3
- class Track < MusicBrainz::Base
4
- attr_accessor :position, :recording_id, :title, :length
4
+ class Track < Base
5
5
 
6
- def self.find mbid
7
- xml = Nokogiri::XML(self.load(:recording, :id => mbid))
8
- self.parse_xml(xml) unless xml.nil?
9
- end
6
+ field :position, Integer
7
+ field :recording_id, String
8
+ field :title, String
9
+ field :length, Integer
10
10
 
11
- def self.parse_xml xml
12
- @track = MusicBrainz::Track.new
13
- @track.position = self.safe_get_value(xml, 'position').to_i
14
- @track.recording_id = self.safe_get_attr(xml, 'recording', 'id')
15
- @track.title = self.safe_get_value(xml, 'recording > title')
16
- @track.length = self.safe_get_value(xml, 'length').to_i
17
- @track.length = self.safe_get_value(xml, 'recording > length').to_i
18
- @track
11
+ class << self
12
+ def find(mbid)
13
+ load({
14
+ :parser => :track_model,
15
+ :create_model => MusicBrainz::Track
16
+ }, {
17
+ :resource => :recording,
18
+ :id => mbid
19
+ })
20
+ end
19
21
  end
20
22
  end
21
23
  end
@@ -0,0 +1,49 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module MusicBrainz
4
+ module Parsers
5
+ class Artist < Base
6
+ class << self
7
+ def model(xml)
8
+ res = {
9
+ :id => safe_get_attr(xml, "artist", "id"),
10
+ :type => safe_get_attr(xml, "artist", "type"),
11
+ :name => safe_get_value(xml, "artist > name").gsub(/[`’]/, "'"),
12
+ :country => safe_get_value(xml, "artist > country"),
13
+ :date_begin => safe_get_value(xml, "artist > life-span > begin"),
14
+ :date_end => safe_get_value(xml, "artist > life-span > end"),
15
+ :urls => {}
16
+ }
17
+ xml.css("relation-list[target-type='url'] > relation").each { |rel|
18
+ res[:urls][rel.attr("type").downcase.split(" ").join("_").to_sym] = rel.css("target").text
19
+ }
20
+ res
21
+ end
22
+
23
+ def search(xml)
24
+ artists = []
25
+ xml.css("artist-list > artist").each do |a|
26
+ artists << {
27
+ :name => a.first_element_child.text.gsub(/[`’]/, "'"),
28
+ :sort_name => safe_get_value(a, "sort-name").gsub(/[`’]/, "'"),
29
+ :weight => 0,
30
+ :desc => safe_get_value(a, "disambiguation"),
31
+ :type => safe_get_attr(a, nil, "type"),
32
+ :mbid => safe_get_attr(a, nil, "id"),
33
+ :aliases => a.css("alias-list > alias").map { |item| item.text }
34
+ }
35
+ end
36
+ artists
37
+ end
38
+
39
+ def release_groups(xml)
40
+ release_groups = []
41
+ xml.css("release-group").each do |rg|
42
+ release_groups << MusicBrainz::Parsers::ReleaseGroup.model(rg)
43
+ end
44
+ release_groups
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module MusicBrainz
4
+ module Parsers
5
+ class << self
6
+ def get_by_name(name)
7
+ case name
8
+ when :artist_model
9
+ { :const => MusicBrainz::Parsers::Artist, :method => :model }
10
+ when :artist_search
11
+ { :const => MusicBrainz::Parsers::Artist, :method => :search }
12
+ when :artist_release_groups
13
+ { :const => MusicBrainz::Parsers::Artist, :method => :release_groups }
14
+ when :release_group_model
15
+ { :const => MusicBrainz::Parsers::ReleaseGroup, :method => :model }
16
+ when :release_group_releases
17
+ { :const => MusicBrainz::Parsers::ReleaseGroup, :method => :releases }
18
+ when :release_model
19
+ { :const => MusicBrainz::Parsers::Release, :method => :model }
20
+ when :release_tracks
21
+ { :const => MusicBrainz::Parsers::Release, :method => :tracks }
22
+ when :track_model
23
+ { :const => MusicBrainz::Parsers::Track, :method => :model }
24
+ end
25
+ end
26
+ end
27
+
28
+ class Base
29
+ class << self
30
+ def safe_get_attr(xml, path, name)
31
+ node = path.nil? ? xml : (xml.css(path).first unless xml.css(path).empty?)
32
+ node.attr(name) unless node.nil? or node.attr(name).nil?
33
+ end
34
+
35
+ def safe_get_value(xml, path)
36
+ xml.css(path).first.text unless xml.css(path).empty?
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module MusicBrainz
4
+ module Parsers
5
+ class Release < Base
6
+ class << self
7
+ def model(xml)
8
+ {
9
+ :id => safe_get_attr(xml, nil, "id") || safe_get_attr(xml, "release", "id"),
10
+ :title => safe_get_value(xml, "title"),
11
+ :status => safe_get_value(xml, "status"),
12
+ :country => safe_get_value(xml, "country"),
13
+ :format => safe_get_value(xml, "medium-list > medium > format"),
14
+ :date => safe_get_value(xml, "date")
15
+ }
16
+ end
17
+
18
+ def tracks(xml)
19
+ tracks = []
20
+ xml.css("medium-list > medium > track-list > track").each do |r|
21
+ tracks << MusicBrainz::Parsers::Track.model(r)
22
+ end
23
+ tracks
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module MusicBrainz
4
+ module Parsers
5
+ class ReleaseGroup < Base
6
+ class << self
7
+ def model(xml)
8
+ {
9
+ :id => safe_get_attr(xml, nil, "id") || safe_get_attr(xml, "release-group", "id"),
10
+ :type => safe_get_attr(xml, nil, "type") || safe_get_attr(xml, "release-group", "type"),
11
+ :title => safe_get_value(xml, "title"),
12
+ :disambiguation => safe_get_value(xml, "disambiguation"),
13
+ :first_release_date => safe_get_value(xml, "first-release-date")
14
+ }
15
+ end
16
+
17
+ def releases(xml)
18
+ releases = []
19
+ xml.css("release").each do |r|
20
+ releases << MusicBrainz::Parsers::Release.model(r)
21
+ end
22
+ releases
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module MusicBrainz
4
+ module Parsers
5
+ class Track < Base
6
+ class << self
7
+ def model(xml)
8
+ {
9
+ :position => safe_get_value(xml, "position"),
10
+ :recording_id => safe_get_attr(xml, "recording", "id"),
11
+ :title => safe_get_value(xml, "recording > title"),
12
+ :length => safe_get_value(xml, "length") || safe_get_value(xml, "recording > length")
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/tools/cache.rb CHANGED
@@ -1,44 +1,47 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  module MusicBrainz
3
4
  module Tools
4
5
  class Cache
5
- @@cache_path = nil
6
+ class << self
7
+ @@cache_path = nil
6
8
 
7
- def self.cache_path=(path)
8
- @@cache_path = path
9
- end
9
+ def cache_path=(path)
10
+ @@cache_path = path
11
+ end
10
12
 
11
- def self.cache_path
12
- @@cache_path
13
- end
13
+ def cache_path
14
+ @@cache_path
15
+ end
14
16
 
15
- def self.clear_cache
16
- FileUtils.rm_r(@@cache_path) if @@cache_path && File.exist?(@@cache_path)
17
- end
17
+ def clear_cache
18
+ FileUtils.rm_r(@@cache_path) if @@cache_path && File.exist?(@@cache_path)
19
+ end
20
+
21
+ def cache_contents(url)
22
+ response = nil
23
+ url_parts = url.split('/')
24
+ file_name = url_parts.pop
25
+ directory = url_parts.pop
26
+ file_path = @@cache_path ? "#{@@cache_path}/#{directory}/#{file_name}" : nil
27
+
28
+ if file_path && File.exist?(file_path)
29
+ response = File.open(file_path).gets
30
+ else
31
+ response = yield
18
32
 
19
- def self.cache_contents(url)
20
- response = nil
21
- url_parts = url.split('/')
22
- file_name = url_parts.pop
23
- directory = url_parts.pop
24
- file_path = @@cache_path ? "#{@@cache_path}/#{directory}/#{file_name}" : nil
25
-
26
- if file_path && File.exist?(file_path)
27
- response = File.open(file_path).gets
28
- else
29
- response = yield
30
-
31
- unless response.nil? or file_path.nil?
32
- FileUtils.mkdir_p file_path.split('/')[0..-2].join('/')
33
- file = File.new(file_path, 'w')
34
- file.puts(response.gets) # .force_encoding('UTF-8')
35
- file.chmod(0755)
36
- file.close
37
- response.rewind
33
+ unless response.nil? or file_path.nil?
34
+ FileUtils.mkdir_p file_path.split('/')[0..-2].join('/')
35
+ file = File.new(file_path, 'w')
36
+ file.puts(response.gets) # .force_encoding('UTF-8')
37
+ file.chmod(0755)
38
+ file.close
39
+ response.rewind
40
+ end
38
41
  end
39
- end
40
42
 
41
- response
43
+ response
44
+ end
42
45
  end
43
46
  end
44
47
  end
data/lib/tools/proxy.rb CHANGED
@@ -1,53 +1,61 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  module MusicBrainz
3
4
  module Tools
4
5
  class Proxy
5
- @@last_query_time = 0
6
- @@query_interval = 1.5 # Min: 1.0 Safe: 1.5
7
- @@tries_limit = 5
8
-
9
- WEB_SERVICE_URL = 'http://musicbrainz.org/ws/2/'
10
- USER_AGENT = "gem musicbrainz (https://github.com/magnolia-fan/musicbrainz) @ " + Socket.gethostname
11
-
12
- def self.query_interval=(sec)
13
- @@query_interval = sec.to_f
14
- end
15
-
16
- def self.tries_limit=(num)
17
- @@tries_limit = num.to_i
18
- end
19
-
20
- def self.load(resourse, params = {})
21
- url = WEB_SERVICE_URL + resourse.to_s.gsub('_', '-') + '/' + (params[:id].to_s || '')
22
- params.delete(:id) unless params[:id].nil?
23
- url << '?' + params.map{ |k, v|
24
- k = k.to_s.gsub('_', '-')
25
- v = (v.is_a?(Array) ? v.map{ |_| _.to_s.gsub('_', '-') }.join('+') : v.to_s)
26
- k + '=' + v
27
- }.join('&') unless params.empty?
28
- MusicBrainz::Tools::Cache.cache_contents(url) {
29
- self.get_contents url
30
- }
31
- end
32
-
33
- def self.get_contents(url)
34
- response = nil
35
-
36
- @@tries_limit.times {
37
- time_passed = Time.now.to_f - @@last_query_time
38
- sleep(@@query_interval - time_passed) if time_passed < @@query_interval
39
-
40
- begin
41
- response = open(url, "User-Agent" => USER_AGENT)
42
- @@last_query_time = Time.now.to_f
43
- rescue => e
44
- response = nil if e.io.status[0].to_i == 404
45
- end
46
-
47
- break unless response.nil?
48
- }
49
-
50
- response
6
+ class << self
7
+ @@last_query_time = 0
8
+ @@query_interval = 1.5 # Min: 1.0 Safe: 1.5
9
+ @@tries_limit = 5
10
+
11
+ WEB_SERVICE_URL = 'http://musicbrainz.org/ws/2/'
12
+ USER_AGENT = "gem musicbrainz (https://github.com/magnolia-fan/musicbrainz) @ " + Socket.gethostname
13
+
14
+ def query_interval=(sec)
15
+ @@query_interval = sec.to_f
16
+ end
17
+
18
+ def query_interval
19
+ @@query_interval
20
+ end
21
+
22
+ def tries_limit=(num)
23
+ @@tries_limit = num.to_i
24
+ end
25
+
26
+ def query(params = {})
27
+ url = WEB_SERVICE_URL + params[:resource].to_s.gsub('_', '-') + '/' + (params[:id].to_s || '')
28
+ params.delete(:resource)
29
+ params.delete(:id) unless params[:id].nil?
30
+ url << '?' + params.map{ |k, v|
31
+ k = k.to_s.gsub('_', '-')
32
+ v = (v.is_a?(Array) ? v.map{ |_| _.to_s.gsub('_', '-') }.join('+') : v.to_s)
33
+ k + '=' + v
34
+ }.join('&') unless params.empty?
35
+ MusicBrainz::Tools::Cache.cache_contents(url) {
36
+ get_contents url
37
+ }
38
+ end
39
+
40
+ def get_contents(url)
41
+ response = nil
42
+
43
+ @@tries_limit.times {
44
+ time_passed = Time.now.to_f - @@last_query_time
45
+ sleep(@@query_interval - time_passed) if time_passed < @@query_interval
46
+
47
+ begin
48
+ response = open(url, "User-Agent" => USER_AGENT)
49
+ @@last_query_time = Time.now.to_f
50
+ rescue => e
51
+ response = nil if e.io.status[0].to_i == 404
52
+ end
53
+
54
+ break unless response.nil?
55
+ }
56
+
57
+ response
58
+ end
51
59
  end
52
60
  end
53
61
  end
data/lib/version.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  module MusicBrainz
3
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
4
5
  end
data/musicbrainz.gemspec CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  require File.expand_path('../lib/version', __FILE__)
3
4
 
4
5
  Gem::Specification.new do |gem|
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#"><artist type="Group" id="69b39eab-6577-46a4-a9f5-817839092033"><name>Kasabian</name><sort-name>Kasabian</sort-name><country>GB</country><life-span><begin>1999</begin></life-span><relation-list target-type="url"><relation type="allmusic"><target>http://allmusic.com/artist/p678134</target></relation><relation type="wikipedia"><target>http://en.wikipedia.org/wiki/Kasabian</target></relation><relation type="microblog"><target>http://twitter.com/kasabianhq</target></relation><relation type="BBC Music page"><target>http://www.bbc.co.uk/music/artists/69b39eab-6577-46a4-a9f5-817839092033</target></relation><relation type="discogs"><target>http://www.discogs.com/artist/Kasabian</target></relation><relation type="social network"><target>http://www.facebook.com/kasabian</target></relation><relation type="IMDb"><target>http://www.imdb.com/name/nm1868442/</target></relation><relation type="official homepage"><target>http://www.kasabian.co.uk/</target></relation><relation type="myspace"><target>http://www.myspace.com/kasabian</target></relation><relation type="youtube"><target>http://www.youtube.com/kasabianvevo</target></relation><relation type="youtube"><target>http://www.youtube.com/user/KasabianTour</target></relation></relation-list></artist></metadata>
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require "spec_helper"
4
+
5
+ describe MusicBrainz do
6
+ before(:all) {
7
+ @old_cache_path = MusicBrainz::Tools::Cache.cache_path
8
+ }
9
+
10
+ before(:each) {
11
+ $stdout.stub!(:puts)
12
+ MusicBrainz::Tools::Cache.cache_path = nil
13
+ }
14
+
15
+ after(:all) {
16
+ MusicBrainz::Tools::Cache.cache_path = @old_cache_path
17
+ }
18
+
19
+ it "allows deprecated use of cache_path" do
20
+ MusicBrainz::Tools::Cache.cache_path = "some/path"
21
+ MusicBrainz::cache_path.should == "some/path"
22
+ end
23
+
24
+ it "allows deprecated use of cache_path=" do
25
+ MusicBrainz.cache_path = "some/path"
26
+ MusicBrainz::Tools::Cache.cache_path.should == "some/path"
27
+ end
28
+
29
+ it "allows deprecated use of query_interval" do
30
+ MusicBrainz::Tools::Proxy.query_interval = 2
31
+ MusicBrainz::query_interval.should == 2
32
+ end
33
+
34
+ it "allows deprecated use of query_interval=" do
35
+ MusicBrainz.query_interval = 2
36
+ MusicBrainz::Tools::Proxy.query_interval.should == 2
37
+ end
38
+ end
@@ -1,3 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  describe MusicBrainz::Artist do
@@ -21,7 +23,7 @@ describe MusicBrainz::Artist do
21
23
  it "finds name first than alias" do
22
24
  matches = MusicBrainz::Artist.search('Chris Martin')
23
25
  matches.length.should be > 0
24
- matches.first[:name].should == "Chris Martin"
26
+ matches.first[:mbid].should == "af2ab893-3212-4226-9e73-73a1660b6952"
25
27
  end
26
28
 
27
29
  it "gets correct result by name" do
@@ -35,7 +37,7 @@ describe MusicBrainz::Artist do
35
37
  artist.type.should == "Group"
36
38
  artist.name.should == "Kasabian"
37
39
  artist.country.should == "GB"
38
- artist.date_begin.should == "1999"
40
+ artist.date_begin.year.should == 1999
39
41
  end
40
42
 
41
43
  it "gets correct artist's release groups" do
@@ -1,3 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  describe MusicBrainz::ReleaseGroup do
@@ -1,3 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  describe MusicBrainz::Release do
@@ -1,3 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  describe MusicBrainz::Track do
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  require "rubygems"
2
4
  require "bundler/setup"
3
5
  require "ap"
@@ -1,3 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  describe MusicBrainz::Tools::Cache do
@@ -11,32 +13,17 @@ describe MusicBrainz::Tools::Cache do
11
13
  end
12
14
 
13
15
  before(:each) do
14
- @test_response = ::StringIO.new('<?xml version="1.0" encoding="UTF-8"?>'+
15
- '<metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#">'+
16
- '<artist type="Group" id="69b39eab-6577-46a4-a9f5-817839092033">'+
17
- '<name>Kasabian</name><sort-name>Kasabian</sort-name><country>GB</country>'+
18
- '<life-span><begin>1999</begin></life-span><relation-list target-type="url">'+
19
- '<relation type="allmusic"><target>http://allmusic.com/artist/p678134</target>'+
20
- '</relation><relation type="wikipedia"><target>http://en.wikipedia.org/wiki/Kasabian</target>'+
21
- '</relation><relation type="microblog"><target>http://twitter.com/kasabianhq</target>'+
22
- '</relation><relation type="BBC Music page"><target>'+
23
- 'http://www.bbc.co.uk/music/artists/69b39eab-6577-46a4-a9f5-817839092033</target></relation>'+
24
- '<relation type="discogs"><target>http://www.discogs.com/artist/Kasabian</target></relation>'+
25
- '<relation type="social network"><target>http://www.facebook.com/kasabian</target></relation>'+
26
- '<relation type="IMDb"><target>http://www.imdb.com/name/nm1868442/</target></relation>'+
27
- '<relation type="official homepage"><target>http://www.kasabian.co.uk/</target></relation>'+
28
- '<relation type="myspace"><target>http://www.myspace.com/kasabian</target></relation>'+
29
- '<relation type="youtube"><target>http://www.youtube.com/kasabianvevo</target></relation>'+
30
- '<relation type="youtube"><target>http://www.youtube.com/user/KasabianTour</target></relation>'+
31
- '</relation-list></artist></metadata>')
16
+ file_path = File.join(File.dirname(__FILE__), "../fixtures/kasabian.xml")
17
+ @test_response = ::StringIO.new(File.open(file_path).gets)
32
18
  end
33
19
 
34
20
  context "with cache enabled" do
35
21
  it "calls get contents only once when requesting the resource twice" do
36
22
  MusicBrainz::Tools::Cache.cache_path = @tmp_cache_path
23
+ mbid = "69b39eab-6577-46a4-a9f5-817839092033"
24
+
37
25
  MusicBrainz::Tools::Proxy.stub(:get_contents).and_return(@test_response)
38
26
  MusicBrainz::Tools::Proxy.should_receive(:get_contents).once
39
- mbid = "69b39eab-6577-46a4-a9f5-817839092033"
40
27
 
41
28
  File.exist?("#{@tmp_cache_path}/artist/#{mbid}?inc=url-rels").should be_false
42
29
  artist = MusicBrainz::Artist.find(mbid)
@@ -53,9 +40,10 @@ describe MusicBrainz::Tools::Cache do
53
40
  context "with cache disabled" do
54
41
  it "calls get contents twice when requesting the resource twice" do
55
42
  MusicBrainz::Tools::Cache.cache_path = nil
43
+ mbid = "69b39eab-6577-46a4-a9f5-817839092033"
44
+
56
45
  MusicBrainz::Tools::Proxy.stub(:get_contents).and_return(@test_response)
57
46
  MusicBrainz::Tools::Proxy.should_receive(:get_contents).twice
58
- mbid = "69b39eab-6577-46a4-a9f5-817839092033"
59
47
 
60
48
  File.exist?("#{@tmp_cache_path}/artist/#{mbid}?inc=url-rels").should be_false
61
49
  artist = MusicBrainz::Artist.find(mbid)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: musicbrainz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-04 00:00:00.000000000Z
12
+ date: 2012-07-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
16
- requirement: &70115651814840 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70115651814840
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: rake
27
- requirement: &70115651805120 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '0'
33
38
  type: :development
34
39
  prerelease: false
35
- version_requirements: *70115651805120
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: awesome_print
38
- requirement: &70115651804200 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: '0'
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *70115651804200
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: rspec
49
- requirement: &70115651803420 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,7 +69,12 @@ dependencies:
54
69
  version: '0'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *70115651803420
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  description:
59
79
  email:
60
80
  - magnolia_fan@me.com
@@ -65,6 +85,7 @@ files:
65
85
  - .gitignore
66
86
  - .rspec
67
87
  - .travis.yml
88
+ - Contributors
68
89
  - Gemfile
69
90
  - LICENSE
70
91
  - README.md
@@ -76,10 +97,17 @@ files:
76
97
  - lib/musicbrainz/release.rb
77
98
  - lib/musicbrainz/release_group.rb
78
99
  - lib/musicbrainz/track.rb
100
+ - lib/parsers/artist.rb
101
+ - lib/parsers/base.rb
102
+ - lib/parsers/release.rb
103
+ - lib/parsers/release_group.rb
104
+ - lib/parsers/track.rb
79
105
  - lib/tools/cache.rb
80
106
  - lib/tools/proxy.rb
81
107
  - lib/version.rb
82
108
  - musicbrainz.gemspec
109
+ - spec/fixtures/kasabian.xml
110
+ - spec/misc/deprecated_spec.rb
83
111
  - spec/requests/artist_spec.rb
84
112
  - spec/requests/release_group_spec.rb
85
113
  - spec/requests/release_spec.rb
@@ -107,11 +135,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
135
  version: '0'
108
136
  requirements: []
109
137
  rubyforge_project:
110
- rubygems_version: 1.8.10
138
+ rubygems_version: 1.8.23
111
139
  signing_key:
112
140
  specification_version: 3
113
141
  summary: MusicBrainz Web Service wrapper with ActiveRecord-style models
114
142
  test_files:
143
+ - spec/fixtures/kasabian.xml
144
+ - spec/misc/deprecated_spec.rb
115
145
  - spec/requests/artist_spec.rb
116
146
  - spec/requests/release_group_spec.rb
117
147
  - spec/requests/release_spec.rb