music_metadata_score 0.0.2 → 0.0.3
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.
- checksums.yaml +8 -8
- data/lib/music_metadata_score/artist.rb +109 -107
- data/lib/music_metadata_score/barcode.rb +73 -71
- data/lib/music_metadata_score/composer.rb +26 -24
- data/lib/music_metadata_score/contributor.rb +40 -38
- data/lib/music_metadata_score/isrc.rb +52 -50
- data/lib/music_metadata_score/publisher.rb +26 -24
- data/lib/music_metadata_score/score.rb +234 -230
- data/lib/music_metadata_score/title_case.rb +293 -291
- data/lib/music_metadata_score/version.rb +1 -1
- data/lib/music_metadata_score.rb +6 -6
- metadata +2 -2
@@ -1,330 +1,332 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
""
|
1
|
+
module MusicMetadataScore
|
2
|
+
class TitleCase
|
3
|
+
# Split string
|
4
|
+
# Capitalize all
|
5
|
+
# Find list of words
|
6
|
+
# If they're not first or last, or have a bracket on the beginning or end make em lower case
|
7
|
+
|
8
|
+
def self.featuring_words
|
9
|
+
[
|
10
|
+
' Featuring',
|
11
|
+
'Feat ',
|
12
|
+
' Feat.',
|
13
|
+
' With '
|
14
|
+
|
15
|
+
]
|
25
16
|
end
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
17
|
+
|
18
|
+
def self.title_case(string)
|
19
|
+
if string.is_a?(String)
|
20
|
+
string = MusicMetadataScore::TitleCase.add_spacing_to_punctuation(string)
|
21
|
+
array = MusicMetadataScore::TitleCase.lower_case_array(MusicMetadataScore::TitleCase.titleize_array(MusicMetadataScore::TitleCase.split_string(string.downcase)))
|
22
|
+
new_string = MusicMetadataScore::TitleCase.capitalize_artist_the(array.join(' '))
|
23
|
+
return MusicMetadataScore::TitleCase.capitalize_anomalies(new_string)
|
24
|
+
else
|
25
|
+
""
|
26
|
+
end
|
35
27
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
28
|
+
|
29
|
+
# Add spaces after any punctuation if it doesn't already exist
|
30
|
+
def self.add_spacing_to_punctuation(string)
|
31
|
+
newstring = ""
|
32
|
+
string.split('').each_with_index do |letter, i|
|
33
|
+
newstring += ' ' if MusicMetadataScore::TitleCase.add_space_before?(string, letter, i)
|
34
|
+
newstring += letter
|
35
|
+
newstring += ' ' if MusicMetadataScore::TitleCase.add_space_after?(string, letter, i)
|
36
|
+
end
|
37
|
+
return newstring
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.add_space_after?(string, letter, i)
|
41
|
+
if letter =~ /[,.)]/ && string[i + 1] != ' '
|
44
42
|
true
|
43
|
+
elsif letter == '"'
|
44
|
+
if string[i - 1] =~ /[[:alpha:]]/ && string[i + 1] != ' '
|
45
|
+
true
|
46
|
+
end
|
47
|
+
else
|
48
|
+
false
|
45
49
|
end
|
46
|
-
else
|
47
|
-
false
|
48
50
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
|
52
|
+
def self.add_space_before?(string, letter, i)
|
53
|
+
if letter =~ /[(]/ && string[i - 1] != ' '
|
54
|
+
true
|
55
|
+
else
|
56
|
+
false
|
57
|
+
end
|
56
58
|
end
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
59
|
+
|
60
|
+
# String to Array
|
61
|
+
def self.split_string(string)
|
62
|
+
if string && string.is_a?(String)
|
63
|
+
string.split(' ')
|
64
|
+
else
|
65
|
+
[]
|
66
|
+
end
|
65
67
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
68
|
+
|
69
|
+
# Capitalize all first letters
|
70
|
+
def self.titleize_array(array)
|
71
|
+
array.each do |a|
|
72
|
+
a = MusicMetadataScore::TitleCase.titleize(a)
|
73
|
+
end
|
74
|
+
return array
|
72
75
|
end
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
76
|
+
|
77
|
+
def self.titleize(string)
|
78
|
+
if string[0] =~ /[[:alpha:]]/
|
79
|
+
string.capitalize!
|
80
|
+
elsif string.length > 1 && !string.include?('feat.')
|
81
|
+
string[1] = string[1].capitalize
|
82
|
+
end
|
83
|
+
return string
|
81
84
|
end
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
a = TitleCase.lower_case_string(a)
|
85
|
+
|
86
|
+
# Pick words for lower case
|
87
|
+
def self.lower_case_array(array)
|
88
|
+
array.each do |a|
|
89
|
+
if a != array.first && a != array.last
|
90
|
+
a = MusicMetadataScore::TitleCase.lower_case_string(a)
|
91
|
+
end
|
90
92
|
end
|
93
|
+
return array
|
91
94
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
string
|
95
|
+
|
96
|
+
def self.lower_case_string(string)
|
97
|
+
#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
|
98
|
+
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"]
|
99
|
+
if words.include?(string.downcase)
|
100
|
+
string.downcase!
|
101
|
+
else
|
102
|
+
string
|
103
|
+
end
|
102
104
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
105
|
+
|
106
|
+
# Includes Featuring artistst
|
107
|
+
def self.is_proper_title_format?(title, primary_artists, featuring_artists)
|
108
|
+
if MusicMetadataScore.is_title_case?(title) && MusicMetadataScore::TitleCase.includes_proper_featuring_artists?(title, featuring_artists) && !MusicMetadataScore::TitleCase.includes_version?(title) && !MusicMetadataScore::TitleCase.improper_abbreviations?(title)
|
109
|
+
true
|
110
|
+
else
|
111
|
+
false
|
112
|
+
end
|
111
113
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
114
|
+
|
115
|
+
def self.includes_proper_featuring_artists?(title, featuring_artists)
|
116
|
+
count = featuring_artists.count rescue 0
|
117
|
+
if count > 0
|
118
|
+
MusicMetadataScore::TitleCase.is_proper_featuring_artists?(title, featuring_artists)
|
119
|
+
else
|
120
|
+
true
|
121
|
+
end
|
120
122
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
123
|
+
|
124
|
+
def self.includes_featuring_artists?(title, featuring_artists)
|
125
|
+
value = false
|
126
|
+
featuring_artists.each do |a|
|
127
|
+
if title.include?(a)
|
128
|
+
value = true
|
129
|
+
break
|
130
|
+
end
|
129
131
|
end
|
132
|
+
value
|
130
133
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
false
|
134
|
+
|
135
|
+
def self.is_proper_featuring_artists?(title, featuring_artists)
|
136
|
+
if title.include?(MusicMetadataScore::TitleCase.featuring_artists_string(featuring_artists, "feat.")) || title.include?(MusicMetadataScore::TitleCase.featuring_artists_string(featuring_artists, "with")) || title.include?(MusicMetadataScore::TitleCase.featuring_artists_string(featuring_artists, "vs.")) || title.include?(MusicMetadataScore::TitleCase.featuring_artists_string(featuring_artists, "Meets"))
|
137
|
+
true
|
138
|
+
else
|
139
|
+
false
|
140
|
+
end
|
139
141
|
end
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
142
|
+
|
143
|
+
def self.featuring_artists_string(featuring_artists, type)
|
144
|
+
string = ""
|
145
|
+
if featuring_artists.count > 0
|
146
|
+
string = " (#{type} " if type != 'vs.'
|
147
|
+
string = MusicMetadataScore::TitleCase.add_artists_to_string(string, featuring_artists, type)
|
148
|
+
string += ")"
|
149
|
+
end
|
150
|
+
return string
|
148
151
|
end
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
string
|
152
|
+
|
153
|
+
def self.add_artists_to_string(string, featuring_artists, type)
|
154
|
+
featuring_artists.each do |a|
|
155
|
+
string += a
|
156
|
+
string += MusicMetadataScore::TitleCase.featuring_artists_join(featuring_artists, a, type)
|
157
|
+
end
|
158
|
+
return string
|
156
159
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
", "
|
160
|
+
|
161
|
+
def self.featuring_artists_join(featuring_artists, a, type)
|
162
|
+
if featuring_artists.count == 1 || a == featuring_artists.last
|
163
|
+
""
|
164
|
+
elsif featuring_artists.index(a) == featuring_artists.count - 2 && type != "vs."
|
165
|
+
" & "
|
166
|
+
elsif type == "vs."
|
167
|
+
" vs. "
|
168
|
+
elsif type == "Meets"
|
169
|
+
" Meets "
|
170
|
+
else
|
171
|
+
", "
|
172
|
+
end
|
171
173
|
end
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
174
|
+
|
175
|
+
def self.combined_artists(primary_artists, featuring_artists)
|
176
|
+
featuring_artists.each do |a|
|
177
|
+
primary_artists << a
|
178
|
+
end
|
179
|
+
primary_artists
|
177
180
|
end
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
false
|
181
|
+
|
182
|
+
# Includes a Version
|
183
|
+
def self.includes_version?(title)
|
184
|
+
if title.downcase.include?('version') || title.downcase.include?('edition') || title.downcase.include?('issue') || title.downcase.include?('print') || title.downcase.include?('anniversary') || title.downcase.include?('remix')
|
185
|
+
true
|
186
|
+
else
|
187
|
+
false
|
188
|
+
end
|
187
189
|
end
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
190
|
+
|
191
|
+
# Check for abbreviations
|
192
|
+
def self.improper_abbreviations?(title)
|
193
|
+
# TODO find pt. and vol. not formatted properly
|
194
|
+
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')
|
195
|
+
true
|
196
|
+
else
|
197
|
+
false
|
198
|
+
end
|
197
199
|
end
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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
|
200
|
+
|
201
|
+
def self.replace_improper_abbreviations(title)
|
202
|
+
title.gsub!(" part", " Pt.")
|
203
|
+
title.gsub!(" volume", " Vol.")
|
204
|
+
title.gsub!(" number", " No.")
|
205
|
+
title.gsub!(" no ", " No. ")
|
206
|
+
title.gsub!(" num ", " No. ")
|
207
|
+
title.gsub!(" d.j", " DJ")
|
208
|
+
title.gsub!(" d.j.", " DJ")
|
209
|
+
title.gsub!(" aka", " a.k.a")
|
210
|
+
title.gsub!(" AKA", " a.k.a")
|
211
|
+
title.gsub!("featuring", "feat.")
|
212
|
+
title.gsub!("(feat ", "(feat. ")
|
213
|
+
title.gsub!(" feat ", " feat. ")
|
214
|
+
title.gsub!(" vs ", " Vs. ")
|
215
|
+
title.gsub!(" VS ", " Vs. ")
|
216
|
+
|
229
217
|
return title
|
230
218
|
end
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
219
|
+
|
220
|
+
# Capitalize The's in MusicMetadataScore::Artist Names
|
221
|
+
def self.capitalize_artist_the(title)
|
222
|
+
if title[0] != '(' && title.include?('(')
|
223
|
+
split_title = title.split('(')
|
224
|
+
inside_bracket = split_title[1].split(')')
|
225
|
+
new_inside_bracket = inside_bracket[0].gsub('the', 'The')
|
226
|
+
string = split_title[0] + "(" + new_inside_bracket + ")"
|
227
|
+
string += inside_bracket[1] if inside_bracket[1]
|
228
|
+
return string
|
229
|
+
else
|
230
|
+
return title
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# Capitalize any anomalies, like DJ
|
235
|
+
def self.capitalize_anomalies(title)
|
236
|
+
title.gsub('Dj ', 'DJ ')
|
237
|
+
end
|
238
|
+
|
239
|
+
# Get Value Inside Brackets
|
240
|
+
def self.get_inside_brackets(string)
|
241
|
+
if string.include?('(') && string.include?(')')
|
242
|
+
split_string = string.split('(')
|
243
|
+
array = []
|
244
|
+
split_string.each do |s|
|
245
|
+
stripped = s.strip
|
246
|
+
if stripped[stripped.length - 1] == ')'
|
247
|
+
array << stripped[0..stripped.length - 2]
|
248
|
+
end
|
247
249
|
end
|
250
|
+
array
|
248
251
|
end
|
249
|
-
array
|
250
252
|
end
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
253
|
+
|
254
|
+
# Build proper title
|
255
|
+
def self.build_proper_title(title, version, primary_artists, featuring_artists)
|
256
|
+
# Title Case
|
257
|
+
title = MusicMetadataScore::TitleCase.title_case(title)
|
258
|
+
# find any versions
|
259
|
+
split_out_version = MusicMetadataScore::TitleCase.strip_version_from_title(title, version)
|
260
|
+
# reformat featuring artists
|
261
|
+
title = MusicMetadataScore::TitleCase.strip_artists_from_title(split_out_version[:title], primary_artists, featuring_artists)
|
262
|
+
title += MusicMetadataScore::TitleCase.featuring_artists_string(featuring_artists, "feat.")
|
263
|
+
# replace abbreviations
|
264
|
+
return {:title => title, :version => split_out_version[:version]}
|
265
|
+
end
|
266
|
+
|
267
|
+
def self.strip_version_from_title(title, version)
|
268
|
+
inside_brackets = MusicMetadataScore::TitleCase.get_inside_brackets(title)
|
269
|
+
#version = nil
|
270
|
+
if inside_brackets
|
271
|
+
inside_brackets.each do |b|
|
272
|
+
if MusicMetadataScore::TitleCase.includes_version?(b)
|
273
|
+
title.gsub!(" (#{b})", "")
|
274
|
+
version = b.strip
|
275
|
+
end
|
274
276
|
end
|
275
277
|
end
|
278
|
+
return {
|
279
|
+
:title => title.strip,
|
280
|
+
:version => version
|
281
|
+
}
|
276
282
|
end
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
if inside_brackets
|
292
|
-
inside_brackets.each do |b|
|
293
|
-
if TitleCase.includes_featuring_artists?(b, featuring_artists)
|
294
|
-
title.gsub!(" (#{b})", "")
|
283
|
+
|
284
|
+
def self.strip_artists_from_title(title, primary_artists, featuring_artists)
|
285
|
+
title = MusicMetadataScore::TitleCase.strip_artists_in_brackets_from_title(title, primary_artists, featuring_artists)
|
286
|
+
title = MusicMetadataScore::TitleCase.strip_artists_from_outside_brackets_from_title(title, primary_artists, featuring_artists)
|
287
|
+
title
|
288
|
+
end
|
289
|
+
|
290
|
+
def self.strip_artists_in_brackets_from_title(title, primary_artists, featuring_artists)
|
291
|
+
inside_brackets = MusicMetadataScore::TitleCase.get_inside_brackets(title)
|
292
|
+
if inside_brackets
|
293
|
+
inside_brackets.each do |b|
|
294
|
+
if MusicMetadataScore::TitleCase.includes_featuring_artists?(b, featuring_artists)
|
295
|
+
title.gsub!(" (#{b})", "")
|
296
|
+
end
|
295
297
|
end
|
296
298
|
end
|
299
|
+
title
|
297
300
|
end
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
title.gsub!(" #{a}", "")
|
301
|
+
|
302
|
+
def self.strip_artists_from_outside_brackets_from_title(title, primary_artists, featuring_artists)
|
303
|
+
title = MusicMetadataScore::TitleCase.remove_artist_names_from_title(title, MusicMetadataScore::TitleCase.combined_artists(primary_artists, featuring_artists))
|
304
|
+
MusicMetadataScore::TitleCase.strip_featuring_words_from_title(title)
|
305
|
+
end
|
306
|
+
|
307
|
+
def self.remove_artist_names_from_title(title, artists)
|
308
|
+
artists.each do |a|
|
309
|
+
if title.include?(" #{a} &")
|
310
|
+
title.gsub!(" #{a} &", "")
|
311
|
+
elsif title.include?(" #{a} and")
|
312
|
+
title.gsub!(" #{a} and", "")
|
313
|
+
elsif title.include?(" #{a} And")
|
314
|
+
title.gsub!(" #{a} And", "")
|
315
|
+
elsif title.include?(" #{a},")
|
316
|
+
title.gsub!(" #{a},", "")
|
317
|
+
else
|
318
|
+
title.gsub!(" #{a}", "")
|
319
|
+
end
|
318
320
|
end
|
321
|
+
title
|
319
322
|
end
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
title
|
323
|
+
|
324
|
+
def self.strip_featuring_words_from_title(title)
|
325
|
+
MusicMetadataScore::TitleCase.featuring_words.each do |w|
|
326
|
+
title.gsub!(" #{w.strip}", "")
|
327
|
+
end
|
328
|
+
title
|
326
329
|
end
|
327
|
-
|
330
|
+
|
328
331
|
end
|
329
|
-
|
330
332
|
end
|