music_metadata_score 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ODhjMzM4ODU3YTFjOWU1NmExNjA4YWQ5YWFhNzU2MDllMjgwYzA1Nw==
5
+ data.tar.gz: !binary |-
6
+ NjcyYzU4YTA5NmI3Y2RmYmQwY2IzNjk2MzMwNWFkZGVkOTVmYzZmNw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ Y2MyMTAxMzAyMDY1YWJiNjdlNTQ3YWMzZGNiYTYzYWVhZmNkMGY5ZDNmNGUz
10
+ MzNhNTY5OWMwZDgwMTQzMjIyMjEwYTJmYzQ2YTRiODdkNjZmMDM5Mzc1ZWU3
11
+ MWVhMjc3NzRkMDk4NGZlYzgyNGFjM2FjOTA1OTc5YWIwMTgyZDM=
12
+ data.tar.gz: !binary |-
13
+ ZWMzZWRmNjg3YzA3ZjFiZDM0ZDRmMjNiYjY5MjRjNzRjY2RiMDVhMzE5ZjJi
14
+ NGQyYjA1ZjMwMmQyNDE4MGJkNTUyM2YwNWY1NGFlNjc4MTFlMzgyY2NkNzI3
15
+ OTUxMTczY2Y4NWY5YmI2NGVhODYzMDRlYTA4ODZmODE0ZmUwMWU=
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in music_metadata_score.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # MusicMetadataScore
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'music_metadata_score'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install music_metadata_score
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ #t.libs << "test"
6
+ #t.test_files = FileList['test/test*.rb']
7
+ #t.verbose = true
8
+ t.libs = ["lib"]
9
+ t.warning = true
10
+ t.verbose = true
11
+ t.test_files = FileList['test/test_*.rb']
12
+ end
13
+
14
+ desc "Run tests"
15
+ task :default => :test
@@ -0,0 +1,123 @@
1
+ class Artist
2
+
3
+ def self.are_proper_artist_format?(artists, override, level)
4
+ if artists && artists != []
5
+ Artist.loop_artists(artists, override, level)
6
+ else
7
+ false
8
+ end
9
+ end
10
+
11
+ def self.loop_artists(artists, override, level)
12
+ value = true
13
+ artists.each do |a|
14
+ if !Artist.is_proper_artist_format?(a, override, level)
15
+ value = false
16
+ break
17
+ end
18
+ end
19
+ value
20
+ end
21
+
22
+ def self.is_proper_artist_format?(artist, override, level)
23
+ if Artist.is_title_case?(artist, override) && !Artist.is_compound_artist?(artist, override) && !Artist.is_bad_various_artists?(artist) && Artist.artist_fits_level?(artist, override, level)
24
+ true
25
+ else
26
+ false
27
+ end
28
+ end
29
+
30
+ def self.is_title_case?(artist, override)
31
+ if MusicMetadataScore.is_title_case?(artist) || override == true
32
+ true
33
+ else
34
+ false
35
+ end
36
+ end
37
+
38
+ def self.is_compound_artist?(artist, override)
39
+ if override != true && (artist.downcase.include?('and') || artist.include?('&') || artist.downcase.include?('vs') || artist.downcase.include?('with') || artist.downcase.include?('feat') || artist.downcase.include?('present')) || artist.include?('/')
40
+ true
41
+ else
42
+ false
43
+ end
44
+ end
45
+
46
+ def self.is_bad_various_artists?(artist)
47
+ # various, va, v/a
48
+ if artist.downcase == 'various' || artist.downcase == 'va' || artist.downcase == 'v/a' || artist.downcase == 'various artist'
49
+ true
50
+ else
51
+ false
52
+ end
53
+ end
54
+
55
+ def self.artist_fits_level?(artist, override, level)
56
+ if override == false && level == :track && (artist.downcase == 'various artists' || artist.downcase == 'various artist' || artist.downcase == 'v/a' || artist.downcase == 'various')
57
+ false
58
+ else
59
+ true
60
+ end
61
+ end
62
+
63
+ def self.format_artists(artists, override, level)
64
+ array = []
65
+ artists.each do |a|
66
+ new_artist = Artist.title_case(a)
67
+ separated_artists = Artist.separate_compound_artists(new_artist)
68
+ separated_artists.each do |sa|
69
+ array << sa
70
+ end
71
+ end
72
+ return array
73
+ end
74
+
75
+ def self.title_case(artist)
76
+ new_artist = MusicMetadataScore.title_case(artist)
77
+ Artist.capitalize_the(new_artist)
78
+ end
79
+
80
+ def self.capitalize_the(string)
81
+ split = string.split(' ')
82
+ split.each_with_index do |s, i|
83
+ if s == 'the'
84
+ split[i] = 'The'
85
+ end
86
+ end
87
+ return split.join(' ')
88
+ end
89
+
90
+ def self.separate_compound_artists(artist)
91
+ if artist.downcase.include?('and')
92
+ Artist.split_artists(artist, 'and')
93
+ elsif artist.downcase.include?('&')
94
+ Artist.split_artists(artist, '&')
95
+ elsif artist.downcase.include?('vs.')
96
+ Artist.split_artists(artist, 'vs.')
97
+ elsif artist.downcase.include?('vs')
98
+ Artist.split_artists(artist, 'vs')
99
+ elsif artist.downcase.include?('with')
100
+ Artist.split_artists(artist, 'with')
101
+ elsif artist.downcase.include?('feat.')
102
+ Artist.split_artists(artist, 'feat.')
103
+ elsif artist.downcase.include?('featuring')
104
+ Artist.split_artists(artist, 'featuring')
105
+ elsif artist.downcase.include?('feat')
106
+ Artist.split_artists(artist, 'feat')
107
+ elsif artist.downcase.include?('presents')
108
+ Artist.split_artists(artist, 'presents')
109
+ else
110
+ [artist]
111
+ end
112
+ end
113
+
114
+ def self.split_artists(string, separator)
115
+ array = string.split(separator)
116
+ new_array = []
117
+ array.each_with_index do |a, i|
118
+ new_array << a.strip
119
+ end
120
+ return new_array
121
+ end
122
+
123
+ end
@@ -0,0 +1,80 @@
1
+ class Barcode
2
+ # Check length
3
+ # Check checksum
4
+
5
+ def self.is_valid?(barcode)
6
+ if Barcode.valid_length?(barcode) && Barcode.valid_characters?(barcode) && Barcode.valid_checksum?(barcode)
7
+ true
8
+ else
9
+ false
10
+ end
11
+ end
12
+
13
+ def self.valid_length?(barcode)
14
+ if barcode.to_s.length == 12 || barcode.to_s.length == 13
15
+ true
16
+ else
17
+ false
18
+ end
19
+ end
20
+
21
+ def self.valid_characters?(barcode)
22
+ number = Integer(barcode) rescue false
23
+ if number != false
24
+ true
25
+ else
26
+ false
27
+ end
28
+ end
29
+
30
+ def self.valid_checksum?(barcode)
31
+ if barcode.to_s[barcode.to_s.length - 1].to_i == Barcode.get_checksum(barcode)
32
+ true
33
+ else
34
+ false
35
+ end
36
+ end
37
+
38
+ def self.get_checksum(barcode)
39
+ barcode_sum = Barcode.calculate_barcode(Barcode.remove_checksum(barcode))
40
+ if barcode_sum != nil
41
+ Barcode.calculate_checksum(barcode_sum)
42
+ else
43
+ nil
44
+ end
45
+ end
46
+
47
+ def self.remove_checksum(barcode)
48
+ barcode = barcode.to_s
49
+ return barcode[0..barcode.length - 2]
50
+ end
51
+
52
+ def self.calculate_barcode(barcode)
53
+ barcode = barcode.to_s
54
+ if barcode.length == 11
55
+ (barcode[0].to_i * 3) + barcode[1].to_i + (barcode[2].to_i * 3) + barcode[3].to_i + (barcode[4].to_i * 3) + barcode[5].to_i + (barcode[6].to_i * 3) + barcode[7].to_i + (barcode[8].to_i * 3) + barcode[9].to_i + (barcode[10].to_i * 3)
56
+ elsif barcode.length == 12
57
+ barcode[0].to_i + (barcode[1].to_i * 3) + barcode[2].to_i + (barcode[3].to_i * 3) + barcode[4].to_i + (barcode[5].to_i * 3) + barcode[6].to_i + (barcode[7].to_i * 3) + barcode[8].to_i + (barcode[9].to_i * 3) + barcode[10].to_i + (barcode[11].to_i * 3)
58
+ else
59
+ nil
60
+ end
61
+ end
62
+
63
+ def self.calculate_checksum(number)
64
+ over = number % 10
65
+ if over != 0
66
+ 10 - over
67
+ else
68
+ 0
69
+ end
70
+ end
71
+
72
+ def self.valid_barcode_or_nil(barcode)
73
+ if Barcode.is_valid?(barcode)
74
+ barcode
75
+ else
76
+ nil
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,30 @@
1
+ class Composer
2
+
3
+ def self.are_proper_format?(composers)
4
+ if composers && composers != []
5
+ Composer.loop_composers(composers)
6
+ else
7
+ false
8
+ end
9
+ end
10
+
11
+ def self.loop_composers(composers)
12
+ value = true
13
+ composers.each do |c|
14
+ if !Composer.is_proper_format?(c)
15
+ value = false
16
+ break
17
+ end
18
+ end
19
+ value
20
+ end
21
+
22
+ def self.is_proper_format?(composer)
23
+ if Contributor.is_human_name?(composer) && MusicMetadataScore.is_title_case?(composer) && !Artist.is_compound_artist?(composer, false)
24
+ true
25
+ else
26
+ false
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,46 @@
1
+ class Contributor
2
+
3
+ def self.are_proper_format?(contributors)
4
+ if contributors && contributors != []
5
+ Contributor.loop_contributors(contributors)
6
+ else
7
+ false
8
+ end
9
+ end
10
+
11
+ def self.loop_contributors(contributors)
12
+ value = true
13
+ contributors.each do |c|
14
+ if !Contributor.is_proper_format?(c)
15
+ value = false
16
+ break
17
+ end
18
+ end
19
+ value
20
+ end
21
+
22
+ def self.is_proper_format?(contributor)
23
+ if Contributor.is_human_name?(contributor[:name]) && Contributor.has_role?(contributor[:role]) && !Artist.is_compound_artist?(contributor[:name], false) && MusicMetadataScore.is_title_case?(contributor[:name])
24
+ true
25
+ else
26
+ false
27
+ end
28
+ end
29
+
30
+ def self.is_human_name?(contributor)
31
+ if contributor.downcase.include?('the ') || contributor.split(' ').count < 2
32
+ false
33
+ else
34
+ true
35
+ end
36
+ end
37
+
38
+ def self.has_role?(role)
39
+ if role && role != ""
40
+ true
41
+ else
42
+ false
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,55 @@
1
+ class Isrc
2
+
3
+ def self.is_valid?(isrc)
4
+ isrc = Isrc.strip_isrc(isrc)
5
+ if Isrc.valid_length?(isrc) && Isrc.valid_country_code?(isrc[0..1]) && Isrc.valid_numbers?(isrc[5..11])
6
+ true
7
+ else
8
+ false
9
+ end
10
+ end
11
+
12
+ def self.valid_length?(isrc)
13
+ length = Isrc.strip_isrc(isrc).length rescue 0
14
+ if length != 12
15
+ false
16
+ else
17
+ true
18
+ end
19
+ end
20
+
21
+ def self.strip_isrc(isrc)
22
+ if isrc
23
+ isrc.strip!
24
+ isrc.tr!('^A-Za-z0-9', '')
25
+ isrc.upcase!
26
+ end
27
+ return isrc
28
+ end
29
+
30
+ def self.valid_country_code?(code)
31
+ if code.length == 2 && code[/[a-zA-Z]+/] == code
32
+ true
33
+ else
34
+ false
35
+ end
36
+ end
37
+
38
+ def self.valid_numbers?(numbers)
39
+ number = Integer(numbers) rescue false
40
+ if numbers.to_s.length == 7 && number
41
+ true
42
+ else
43
+ false
44
+ end
45
+ end
46
+
47
+ def self.valid_isrc_or_nil(isrc)
48
+ if Isrc.is_valid?(isrc)
49
+ Isrc.strip_isrc(isrc)
50
+ else
51
+ nil
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,30 @@
1
+ class Publisher
2
+
3
+ def self.are_proper_format?(publishers)
4
+ if publishers && publishers != []
5
+ Publisher.loop_publishers(publishers)
6
+ else
7
+ false
8
+ end
9
+ end
10
+
11
+ def self.loop_publishers(publishers)
12
+ value = true
13
+ publishers.each do |c|
14
+ if !Publisher.is_proper_format?(c)
15
+ value = false
16
+ break
17
+ end
18
+ end
19
+ value
20
+ end
21
+
22
+ def self.is_proper_format?(publisher)
23
+ if publisher && publisher != ""
24
+ true
25
+ else
26
+ false
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,234 @@
1
+ class Score
2
+ # Errors
3
+
4
+ # Product level scores
5
+ def self.product_score(metadata)
6
+ score = Score.product_fields_exist(metadata) + Score.product_fields_quality(metadata)
7
+ percent = score.to_f / 87 * 100
8
+ return percent.round(1)
9
+ end
10
+
11
+ def self.product_fields_exist(metadata)
12
+ score = 0
13
+ score += 2 if !Score.object_is_blank?(metadata[:title])
14
+ score += 2 if !Score.object_is_blank?(metadata[:cat_no])
15
+ score += 2 if !Score.object_is_blank?(metadata[:barcode])
16
+ score += 2 if !Score.object_is_blank?(metadata[:label])
17
+ score += 2 if !Score.object_is_blank?(metadata[:primary_artists])
18
+ score += 2 if !Score.object_is_blank?(metadata[:release_date])
19
+ score += 1 if !Score.object_is_blank?(metadata[:original_release_date])
20
+ score += 2 if !Score.object_is_blank?(metadata[:p_year])
21
+ score += 2 if !Score.object_is_blank?(metadata[:p_line])
22
+ score += 2 if !Score.object_is_blank?(metadata[:c_year])
23
+ score += 2 if !Score.object_is_blank?(metadata[:c_line])
24
+ score += 2 if !Score.object_is_blank?(metadata[:parental_advisory])
25
+ score += 1 if !Score.object_is_blank?(metadata[:live])
26
+ score += 1 if !Score.object_is_blank?(metadata[:remastered])
27
+ score += 2 if !Score.object_is_blank?(metadata[:type])
28
+ score += 2 if !Score.object_is_blank?(metadata[:media])
29
+ score += 2 if !Score.object_is_blank?(metadata[:format])
30
+ score += 2 if !Score.object_is_blank?(metadata[:primary_genre])
31
+ score += 1 if !Score.object_is_blank?(metadata[:secondary_genre])
32
+ score += 1 if !Score.object_is_blank?(metadata[:description])
33
+ return score
34
+ end
35
+
36
+ def self.product_fields_quality(metadata)
37
+ score = 0
38
+ score += 10 if TitleCase.is_proper_title_format?(metadata[:title], metadata[:primary_artists], metadata[:featuring_artists])
39
+ score += 10 if MusicMetadataScore.is_title_case?(metadata[:version])
40
+ score += 5 if MusicMetadataScore.valid_barcode?(metadata[:barcode])
41
+ score += 2 if MusicMetadataScore.is_title_case?(metadata[:label])
42
+ score += 5 if MusicMetadataScore.is_title_case?(metadata[:p_line])
43
+ score += 5 if MusicMetadataScore.is_title_case?(metadata[:c_line])
44
+ score += 10 if Artist.are_proper_artist_format?(metadata[:primary_artists], false, :product)
45
+ score += 5 if Artist.are_proper_artist_format?(metadata[:featuring_artists], false, :product)
46
+ return score
47
+ end
48
+
49
+ def self.product_fields_corrected(metadata)
50
+ titles = TitleCase.build_proper_title(metadata[:title], metadata[:version], metadata[:primary_artists], metadata[:featuring_artists])
51
+ {
52
+ :title => titles[:title],
53
+ :version => titles[:version],
54
+ :cat_no => metadata[:cat_no].upcase,
55
+ :barcode => Barcode.valid_barcode_or_nil(metadata[:barcode]),
56
+ :label => TitleCase.title_case(metadata[:label]),
57
+ :primary_artists => Artist.format_artists(metadata[:primary_artists], false, :product),
58
+ :featuring_artists => Artist.format_artists(metadata[:featuring_artists], false, :product),
59
+ :release_date => metadata[:release_date],
60
+ :original_release_date => metadata[:original_release_date],
61
+ :p_year => metadata[:p_year],
62
+ :p_line => TitleCase.title_case(metadata[:p_line]),
63
+ :c_year => metadata[:c_year],
64
+ :c_line => TitleCase.title_case(metadata[:c_line]),
65
+ :parental_advisory => metadata[:parental_advisory],
66
+ :live => metadata[:live],
67
+ :remastered => metadata[:remastered],
68
+ :type => metadata[:type],
69
+ :media => metadata[:media],
70
+ :format => metadata[:format],
71
+ :primary_genre => TitleCase.title_case(metadata[:primary_genre]),
72
+ :secondary_genre => TitleCase.title_case(metadata[:secondary_genre]),
73
+ :description => metadata[:description]
74
+ }
75
+ end
76
+
77
+ # Track level scores
78
+ def self.track_score(metadata)
79
+ score = Score.track_fields_exist(metadata) + Score.track_fields_quality(metadata)
80
+ percent = score.to_f / 107 * 100
81
+ return percent.round(1)
82
+ end
83
+
84
+ def self.track_fields_exist(metadata)
85
+ score = 0
86
+ score += 2 if !Score.object_is_blank?(metadata[:title])
87
+ score += 2 if !Score.object_is_blank?(metadata[:isrc])
88
+ score += 2 if !Score.object_is_blank?(metadata[:primary_artists])
89
+ score += 2 if !Score.object_is_blank?(metadata[:p_year])
90
+ score += 2 if !Score.object_is_blank?(metadata[:p_line])
91
+ score += 2 if !Score.object_is_blank?(metadata[:parental_advisory])
92
+ score += 2 if !Score.object_is_blank?(metadata[:primary_genre])
93
+ score += 2 if !Score.object_is_blank?(metadata[:secondary_genre])
94
+ score += 2 if !Score.object_is_blank?(metadata[:recording_year])
95
+ score += 2 if !Score.object_is_blank?(metadata[:recording_country])
96
+ score += 2 if !Score.object_is_blank?(metadata[:recording_location])
97
+ score += 2 if !Score.object_is_blank?(metadata[:country_of_commission])
98
+ score += 2 if !Score.object_is_blank?(metadata[:rights_holder])
99
+ score += 2 if !Score.object_is_blank?(metadata[:contributors])
100
+ score += 2 if !Score.object_is_blank?(metadata[:composers])
101
+ score += 2 if !Score.object_is_blank?(metadata[:publishers])
102
+ return score
103
+ end
104
+
105
+ def self.track_fields_quality(metadata)
106
+ score = 0
107
+ score += 10 if TitleCase.is_proper_title_format?(metadata[:title], metadata[:primary_artists], metadata[:featuring_artists])
108
+ score += 10 if MusicMetadataScore.is_title_case?(metadata[:version])
109
+ score += 5 if MusicMetadataScore.valid_isrc?(metadata[:isrc])
110
+ score += 5 if MusicMetadataScore.is_title_case?(metadata[:p_line])
111
+ score += 10 if Artist.are_proper_artist_format?(metadata[:primary_artists], false, :product)
112
+ score += 5 if Artist.are_proper_artist_format?(metadata[:featuring_artists], false, :product)
113
+ score += 10 if Contributor.are_proper_format?(metadata[:contributors])
114
+ score += 10 if Composer.are_proper_format?(metadata[:composers])
115
+ score += 10 if Publisher.are_proper_format?(metadata[:publishers])
116
+ return score
117
+ end
118
+
119
+ def self.track_fields_corrected(metadata)
120
+ titles = TitleCase.build_proper_title(metadata[:title], metadata[:version], metadata[:primary_artists], metadata[:featuring_artists])
121
+ {
122
+ :title => titles[:title],
123
+ :version => titles[:version],
124
+ :isrc => Isrc.valid_isrc_or_nil(metadata[:isrc]),
125
+ :primary_artists => Artist.format_artists(metadata[:primary_artists], false, :track),
126
+ :featuring_artists => Artist.format_artists(metadata[:featuring_artists], false, :track),
127
+ :p_year => metadata[:p_year],
128
+ :p_line => TitleCase.title_case(metadata[:p_line]),
129
+ :parental_advisory => metadata[:parental_advisory],
130
+ :primary_genre => TitleCase.title_case(metadata[:primary_genre]),
131
+ :secondary_genre => TitleCase.title_case(metadata[:secondary_genre]),
132
+ :recording_year => metadata[:recording_year],
133
+ :recording_country => metadata[:recording_country],
134
+ :recording_location => metadata[:recording_location],
135
+ :country_of_commission => metadata[:country_of_commission],
136
+ :rights_holder => metadata[:rights_holder],
137
+ :contributors => metadata[:contributors],
138
+ :composers => metadata[:composers],
139
+ :publishers => metadata[:publishers]
140
+ }
141
+ end
142
+
143
+ def self.object_is_blank?(value)
144
+ if value != nil && value != "" && value != [] && value != {}
145
+ false
146
+ else
147
+ true
148
+ end
149
+ end
150
+
151
+ # Total Product Score
152
+ def self.total_product_score(metadata)
153
+ product_score = Score.product_score(metadata[:product])
154
+ tracks_score = Score.tracks_score(metadata[:tracks])
155
+ final_score = (product_score + tracks_score) / (metadata[:tracks].count + 1)
156
+ return final_score.round(1)
157
+ end
158
+
159
+ def self.tracks_score(tracks)
160
+ tracks_score = 0
161
+ if tracks
162
+ tracks.each do |t|
163
+ tracks_score += Score.track_score(t)
164
+ end
165
+ end
166
+ return tracks_score
167
+ end
168
+
169
+ # Corrections
170
+ def self.corrections(metadata)
171
+ array = []
172
+ array = Score.combine_arrays(array, Score.product_corrections(metadata[:product]))
173
+ array = Score.combine_arrays(array, Score.tracks_corrections(metadata[:tracks]))
174
+ end
175
+
176
+ def self.product_corrections(metadata)
177
+ array = []
178
+ corrected_metadata = Score.product_fields_corrected(metadata)
179
+ metadata.each do |m|
180
+ error = Score.missing_or_corrected(m[0].to_s, m[1], corrected_metadata[m[0]])
181
+ array << error if error
182
+ end
183
+ array
184
+ end
185
+
186
+ def self.tracks_corrections(tracks)
187
+ array = []
188
+ tracks.each_with_index do |t, index|
189
+ track_returned = Score.track_corrections(t)
190
+ track_returned.each do |tr|
191
+ array << "Track #{index + 1} #{tr}"
192
+ end
193
+ end
194
+ array
195
+ end
196
+
197
+ def self.track_corrections(metadata)
198
+ array = []
199
+ corrected_metadata = Score.track_fields_corrected(metadata)
200
+ metadata.each do |m|
201
+ error = Score.missing_or_corrected(m[0].to_s, m[1], corrected_metadata[m[0]])
202
+ array << error if error
203
+ end
204
+ array
205
+ end
206
+
207
+ def self.missing_or_corrected(field, original, corrected)
208
+ if original && original != "" && original != [] || original == false
209
+ if original == corrected || corrected == ''
210
+ nil
211
+ else
212
+ "#{Score.format_field_name(field)} is incorrect. It should be #{corrected}"
213
+ end
214
+ else
215
+ "#{Score.format_field_name(field)} is missing"
216
+ end
217
+ end
218
+
219
+ def self.format_field_name(field)
220
+ split = field.split('_')
221
+ split.each do |s|
222
+ s = TitleCase.titleize(s)
223
+ end
224
+ split.join(' ')
225
+ end
226
+
227
+ def self.combine_arrays(first, second)
228
+ second.each do |s|
229
+ first << s
230
+ end
231
+ first
232
+ end
233
+
234
+ end
@@ -0,0 +1,330 @@
1
+ class TitleCase
2
+ # Split string
3
+ # Capitalize all
4
+ # Find list of words
5
+ # If they're not first or last, or have a bracket on the beginning or end make em lower case
6
+
7
+ def self.featuring_words
8
+ [
9
+ ' Featuring',
10
+ 'Feat ',
11
+ ' Feat.',
12
+ ' With '
13
+
14
+ ]
15
+ end
16
+
17
+ def self.title_case(string)
18
+ if string.is_a?(String)
19
+ string = TitleCase.add_spacing_to_punctuation(string)
20
+ array = TitleCase.lower_case_array(TitleCase.titleize_array(TitleCase.split_string(string.downcase)))
21
+ new_string = TitleCase.capitalize_artist_the(array.join(' '))
22
+ return TitleCase.capitalize_anomalies(new_string)
23
+ else
24
+ ""
25
+ end
26
+ end
27
+
28
+ # Add spaces after any punctuation if it doesn't already exist
29
+ def self.add_spacing_to_punctuation(string)
30
+ newstring = ""
31
+ string.split('').each_with_index do |letter, i|
32
+ newstring += ' ' if TitleCase.add_space_before?(string, letter, i)
33
+ newstring += letter
34
+ newstring += ' ' if TitleCase.add_space_after?(string, letter, i)
35
+ end
36
+ return newstring
37
+ end
38
+
39
+ def self.add_space_after?(string, letter, i)
40
+ if letter =~ /[,.)]/ && string[i + 1] != ' '
41
+ true
42
+ elsif letter == '"'
43
+ if string[i - 1] =~ /[[:alpha:]]/ && string[i + 1] != ' '
44
+ true
45
+ end
46
+ else
47
+ false
48
+ end
49
+ end
50
+
51
+ def self.add_space_before?(string, letter, i)
52
+ if letter =~ /[(]/ && string[i - 1] != ' '
53
+ true
54
+ else
55
+ false
56
+ end
57
+ end
58
+
59
+ # String to Array
60
+ def self.split_string(string)
61
+ if string && string.is_a?(String)
62
+ string.split(' ')
63
+ else
64
+ []
65
+ end
66
+ end
67
+
68
+ # Capitalize all first letters
69
+ def self.titleize_array(array)
70
+ array.each do |a|
71
+ a = TitleCase.titleize(a)
72
+ end
73
+ return array
74
+ end
75
+
76
+ def self.titleize(string)
77
+ if string[0] =~ /[[:alpha:]]/
78
+ string.capitalize!
79
+ elsif string.length > 1 && !string.include?('feat.')
80
+ string[1] = string[1].capitalize
81
+ end
82
+ return string
83
+ end
84
+
85
+ # Pick words for lower case
86
+ def self.lower_case_array(array)
87
+ array.each do |a|
88
+ if a != array.first && a != array.last
89
+ a = TitleCase.lower_case_string(a)
90
+ end
91
+ end
92
+ return array
93
+ end
94
+
95
+ def self.lower_case_string(string)
96
+ #a, an, and, as, at, but, by, for, from, in, into, nor, of, off, on, onto, or, out, over, so, the, to, up, with and yet
97
+ words = ["a", "an", "and", "as", "at", "but", "by", "for", "from", "in", "into", "nor", "of", "off", "on", "onto", "or", "out", "over", "so", "the", "to", "up", "with", "yet"]
98
+ if words.include?(string.downcase)
99
+ string.downcase!
100
+ else
101
+ string
102
+ end
103
+ end
104
+
105
+ # Includes Featuring Artistst
106
+ def self.is_proper_title_format?(title, primary_artists, featuring_artists)
107
+ if MusicMetadataScore.is_title_case?(title) && TitleCase.includes_proper_featuring_artists?(title, featuring_artists) && !TitleCase.includes_version?(title) && !TitleCase.improper_abbreviations?(title)
108
+ true
109
+ else
110
+ false
111
+ end
112
+ end
113
+
114
+ def self.includes_proper_featuring_artists?(title, featuring_artists)
115
+ count = featuring_artists.count rescue 0
116
+ if count > 0
117
+ TitleCase.is_proper_featuring_artists?(title, featuring_artists)
118
+ else
119
+ true
120
+ end
121
+ end
122
+
123
+ def self.includes_featuring_artists?(title, featuring_artists)
124
+ value = false
125
+ featuring_artists.each do |a|
126
+ if title.include?(a)
127
+ value = true
128
+ break
129
+ end
130
+ end
131
+ value
132
+ end
133
+
134
+ def self.is_proper_featuring_artists?(title, featuring_artists)
135
+ if title.include?(TitleCase.featuring_artists_string(featuring_artists, "feat.")) || title.include?(TitleCase.featuring_artists_string(featuring_artists, "with")) || title.include?(TitleCase.featuring_artists_string(featuring_artists, "vs.")) || title.include?(TitleCase.featuring_artists_string(featuring_artists, "Meets"))
136
+ true
137
+ else
138
+ false
139
+ end
140
+ end
141
+
142
+ def self.featuring_artists_string(featuring_artists, type)
143
+ string = ""
144
+ if featuring_artists.count > 0
145
+ string = " (#{type} " if type != 'vs.'
146
+ string = TitleCase.add_artists_to_string(string, featuring_artists, type)
147
+ string += ")"
148
+ end
149
+ return string
150
+ end
151
+
152
+ def self.add_artists_to_string(string, featuring_artists, type)
153
+ featuring_artists.each do |a|
154
+ string += a
155
+ string += TitleCase.featuring_artists_join(featuring_artists, a, type)
156
+ end
157
+ return string
158
+ end
159
+
160
+ def self.featuring_artists_join(featuring_artists, a, type)
161
+ if featuring_artists.count == 1 || a == featuring_artists.last
162
+ ""
163
+ elsif featuring_artists.index(a) == featuring_artists.count - 2 && type != "vs."
164
+ " & "
165
+ elsif type == "vs."
166
+ " vs. "
167
+ elsif type == "Meets"
168
+ " Meets "
169
+ else
170
+ ", "
171
+ end
172
+ end
173
+
174
+ def self.combined_artists(primary_artists, featuring_artists)
175
+ featuring_artists.each do |a|
176
+ primary_artists << a
177
+ end
178
+ primary_artists
179
+ end
180
+
181
+ # Includes a Version
182
+ def self.includes_version?(title)
183
+ if title.downcase.include?('version') || title.downcase.include?('edition') || title.downcase.include?('issue') || title.downcase.include?('print') || title.downcase.include?('anniversary')
184
+ true
185
+ else
186
+ false
187
+ end
188
+ end
189
+
190
+ # Check for abbreviations
191
+ def self.improper_abbreviations?(title)
192
+ # TODO find pt. and vol. not formatted properly
193
+ if title.downcase.include?(' part') || title.downcase.include?(' volume') || title.downcase.include?(' number') || title.downcase.include?(' aka') || title.downcase.include?(' d.j') || title.downcase.include?(' num')
194
+ true
195
+ else
196
+ false
197
+ end
198
+ end
199
+
200
+ def self.replace_improper_abbreviations(title)
201
+ title.gsub!(" part", " Pt.")
202
+ title.gsub!(" volume", " Vol.")
203
+ title.gsub!(" number", " No.")
204
+ title.gsub!(" no ", " No. ")
205
+ title.gsub!(" num ", " No. ")
206
+ title.gsub!(" d.j", " DJ")
207
+ title.gsub!(" d.j.", " DJ")
208
+ title.gsub!(" aka", " a.k.a")
209
+ title.gsub!(" AKA", " a.k.a")
210
+ title.gsub!("featuring", "feat.")
211
+ title.gsub!("(feat ", "(feat. ")
212
+ title.gsub!(" feat ", " feat. ")
213
+ title.gsub!(" vs ", " Vs. ")
214
+ title.gsub!(" VS ", " Vs. ")
215
+
216
+ return title
217
+ end
218
+
219
+ # Capitalize The's in Artist Names
220
+ def self.capitalize_artist_the(title)
221
+ if title[0] != '(' && title.include?('(')
222
+ split_title = title.split('(')
223
+ inside_bracket = split_title[1].split(')')
224
+ new_inside_bracket = inside_bracket[0].gsub('the', 'The')
225
+ string = split_title[0] + "(" + new_inside_bracket + ")"
226
+ string += inside_bracket[1] if inside_bracket[1]
227
+ return string
228
+ else
229
+ return title
230
+ end
231
+ end
232
+
233
+ # Capitalize any anomalies, like DJ
234
+ def self.capitalize_anomalies(title)
235
+ title.gsub('Dj ', 'DJ ')
236
+ end
237
+
238
+ # Get Value Inside Brackets
239
+ def self.get_inside_brackets(string)
240
+ if string.include?('(') && string.include?(')')
241
+ split_string = string.split('(')
242
+ array = []
243
+ split_string.each do |s|
244
+ stripped = s.strip
245
+ if stripped[stripped.length - 1] == ')'
246
+ array << stripped[0..stripped.length - 2]
247
+ end
248
+ end
249
+ array
250
+ end
251
+ end
252
+
253
+ # Build proper title
254
+ def self.build_proper_title(title, version, primary_artists, featuring_artists)
255
+ # Title Case
256
+ title = TitleCase.title_case(title)
257
+ # find any versions
258
+ split_out_version = TitleCase.strip_version_from_title(title, version)
259
+ # reformat featuring artists
260
+ title = TitleCase.strip_artists_from_title(split_out_version[:title], primary_artists, featuring_artists)
261
+ title += TitleCase.featuring_artists_string(featuring_artists, "feat.")
262
+ # replace abbreviations
263
+ return {:title => title, :version => split_out_version[:version]}
264
+ end
265
+
266
+ def self.strip_version_from_title(title, version)
267
+ inside_brackets = TitleCase.get_inside_brackets(title)
268
+ #version = nil
269
+ if inside_brackets
270
+ inside_brackets.each do |b|
271
+ if TitleCase.includes_version?(b)
272
+ title.gsub!(" (#{b})", "")
273
+ version = b.strip
274
+ end
275
+ end
276
+ end
277
+ return {
278
+ :title => title.strip,
279
+ :version => version
280
+ }
281
+ end
282
+
283
+ def self.strip_artists_from_title(title, primary_artists, featuring_artists)
284
+ title = TitleCase.strip_artists_in_brackets_from_title(title, primary_artists, featuring_artists)
285
+ title = TitleCase.strip_artists_from_outside_brackets_from_title(title, primary_artists, featuring_artists)
286
+ title
287
+ end
288
+
289
+ def self.strip_artists_in_brackets_from_title(title, primary_artists, featuring_artists)
290
+ inside_brackets = TitleCase.get_inside_brackets(title)
291
+ if inside_brackets
292
+ inside_brackets.each do |b|
293
+ if TitleCase.includes_featuring_artists?(b, featuring_artists)
294
+ title.gsub!(" (#{b})", "")
295
+ end
296
+ end
297
+ end
298
+ title
299
+ end
300
+
301
+ def self.strip_artists_from_outside_brackets_from_title(title, primary_artists, featuring_artists)
302
+ title = TitleCase.remove_artist_names_from_title(title, TitleCase.combined_artists(primary_artists, featuring_artists))
303
+ TitleCase.strip_featuring_words_from_title(title)
304
+ end
305
+
306
+ def self.remove_artist_names_from_title(title, artists)
307
+ artists.each do |a|
308
+ if title.include?(" #{a} &")
309
+ title.gsub!(" #{a} &", "")
310
+ elsif title.include?(" #{a} and")
311
+ title.gsub!(" #{a} and", "")
312
+ elsif title.include?(" #{a} And")
313
+ title.gsub!(" #{a} And", "")
314
+ elsif title.include?(" #{a},")
315
+ title.gsub!(" #{a},", "")
316
+ else
317
+ title.gsub!(" #{a}", "")
318
+ end
319
+ end
320
+ title
321
+ end
322
+
323
+ def self.strip_featuring_words_from_title(title)
324
+ TitleCase.featuring_words.each do |w|
325
+ title.gsub!(" #{w.strip}", "")
326
+ end
327
+ title
328
+ end
329
+
330
+ end
@@ -0,0 +1,3 @@
1
+ module MusicMetadataScore
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,46 @@
1
+ require "music_metadata_score/version"
2
+ require "music_metadata_score/score"
3
+ require "music_metadata_score/title_case"
4
+ require "music_metadata_score/barcode"
5
+ require "music_metadata_score/isrc"
6
+ require "music_metadata_score/artist"
7
+ require "music_metadata_score/contributor"
8
+ require "music_metadata_score/composer"
9
+ require "music_metadata_score/publisher"
10
+
11
+ module MusicMetadataScore
12
+
13
+ # Title Casing
14
+ def self.title_case(string)
15
+ TitleCase.title_case(string)
16
+ end
17
+
18
+ def self.is_title_case?(string)
19
+ proper = TitleCase.title_case(string)
20
+ if proper == string
21
+ true
22
+ else
23
+ false
24
+ end
25
+ end
26
+
27
+ # Barcode
28
+ def self.valid_barcode?(barcode)
29
+ Barcode.is_valid?(barcode)
30
+ end
31
+
32
+ def self.generate_checksum(barcode)
33
+ barcode_sum = Barcode.calculate_barcode(barcode)
34
+ if barcode_sum != nil
35
+ Barcode.calculate_checksum(barcode_sum)
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ # ISRC
42
+ def self.valid_isrc?(isrc)
43
+ Isrc.is_valid?(isrc)
44
+ end
45
+
46
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: music_metadata_score
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - tomallen400
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: ''
42
+ email:
43
+ - tomallen4000@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - README.md
49
+ - Gemfile
50
+ - Rakefile
51
+ - lib/music_metadata_score.rb
52
+ - lib/music_metadata_score/artist.rb
53
+ - lib/music_metadata_score/barcode.rb
54
+ - lib/music_metadata_score/composer.rb
55
+ - lib/music_metadata_score/contributor.rb
56
+ - lib/music_metadata_score/isrc.rb
57
+ - lib/music_metadata_score/publisher.rb
58
+ - lib/music_metadata_score/score.rb
59
+ - lib/music_metadata_score/title_case.rb
60
+ - lib/music_metadata_score/version.rb
61
+ homepage: ''
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.0.3
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: ''
85
+ test_files: []