musicbrainz 0.6.0 → 0.7.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/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