track_parser 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +52 -0
- data/Rakefile +2 -0
- data/lib/track_parser.rb +5 -0
- data/lib/track_parser/filter_pipeline.rb +21 -0
- data/lib/track_parser/filtered_parts.rb +25 -0
- data/lib/track_parser/filters/featuring_filter.rb +30 -0
- data/lib/track_parser/filters/filter_nodes_mapping.rb +12 -0
- data/lib/track_parser/filters/remix_filter.rb +41 -0
- data/lib/track_parser/nodes/artist_node.rb +14 -0
- data/lib/track_parser/nodes/artists_node.rb +31 -0
- data/lib/track_parser/nodes/base_node.rb +16 -0
- data/lib/track_parser/nodes/empty_node.rb +7 -0
- data/lib/track_parser/nodes/featuring_node.rb +22 -0
- data/lib/track_parser/nodes/name_node.rb +14 -0
- data/lib/track_parser/nodes/remix_node.rb +40 -0
- data/lib/track_parser/nodes/track_node.rb +49 -0
- data/lib/track_parser/nodes/trackname_node.rb +20 -0
- data/lib/track_parser/parser.rb +19 -0
- data/lib/track_parser/processor.rb +53 -0
- data/lib/track_parser/track.rb +49 -0
- data/lib/track_parser/version.rb +3 -0
- data/spec/parser_spec.rb +96 -0
- data/spec/spec_helper.rb +11 -0
- data/track_parser.gemspec +26 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 41f48fe39a78f9c5bc0852de5826ba1473fd487e
|
4
|
+
data.tar.gz: 303e183dc8057a76b3585ea9a048bcb04f6e9c09
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f2d9a92b145ee0172a4ab131e0542fe5847b7c47217c86a9a5f393bcdc6ebb576e1b3c6727796ddf338dcc7606fc69f807c20d4c17ae0622515cec96c5626b7c
|
7
|
+
data.tar.gz: cb4bdda3a49365c7360f62ecf4d976e42f4c71d0f78de9db2b67dbada973b63866aef0342789cbcb9d6d78e77bbc87799ade5ca4e6b5616569708f0e66b4b6df
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Alexandre Quintino
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# TrackParser
|
2
|
+
|
3
|
+
Tries to parse the name of a track (e.g. ArtistA - ABC (ArtistB remix)) and extract metadata from it (e.g. artists = ArtistA, track-name = ABC, remixer = ArtistB)
|
4
|
+
It makes a best effort to extract the metadata by passing the track through a set of rules that most tracks follow. If the track does not follow those rules, the results might not be accurate.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'track_parser'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install track_parser
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Simple
|
25
|
+
```ruby
|
26
|
+
require "track_parser"
|
27
|
+
TrackParser::Parser.do("Derek Marin - We've Been Expecting You - Hreno's Deep Pockets Dub")
|
28
|
+
=> {:artists=>["Derek Marin"], :track_name=>"We've Been Expecting You", :remixer=>"Hreno", :remix_name=>"Deep Pockets Dub"}
|
29
|
+
```
|
30
|
+
|
31
|
+
### Structured
|
32
|
+
You might know already the artists. In that case you can use:
|
33
|
+
```ruby
|
34
|
+
require "track_parser"
|
35
|
+
TrackParser::Parser.do(artists: ["Derek Marin], name: "We've Been Expecting You - Hreno's Deep Pockets Dub")
|
36
|
+
=> {:artists=>["Derek Marin"], :track_name=>"We've Been Expecting You", :remixer=>"Hreno", :remix_name=>"Deep Pockets Dub"}
|
37
|
+
```
|
38
|
+
|
39
|
+
## Non-exhaustive list of formats supported (for more check the [tests](https://github.com/alexquintino/track_parser/blob/master/spec/parser_spec.rb))
|
40
|
+
|
41
|
+
* `<artist> - <track name>`
|
42
|
+
* `<artist> - <track name> [-] (<remixer> Remix|Vocal|Dub|Mix)`
|
43
|
+
* `<artist> - <track name> (<remixer>'s <remix name>)`
|
44
|
+
* `<artist> & <artist> - <track name>`
|
45
|
+
|
46
|
+
## Contributing
|
47
|
+
|
48
|
+
1. Fork it ( https://github.com/alexquintino/track_parser/fork )
|
49
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
50
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
51
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
52
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/lib/track_parser.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative "filters/remix_filter"
|
2
|
+
require_relative "filters/featuring_filter"
|
3
|
+
require_relative "filtered_parts"
|
4
|
+
|
5
|
+
module TrackParser
|
6
|
+
class FilterPipeline
|
7
|
+
|
8
|
+
FILTERS = [RemixFilter, FeaturingFilter] #order matters currently
|
9
|
+
|
10
|
+
def self.filter(text)
|
11
|
+
size_last_pass = 0
|
12
|
+
filtered = nil
|
13
|
+
loop do #make several passes until nothing new is found
|
14
|
+
filtered = FILTERS.reduce(FilteredParts.new(text)) { |data, filter_class| filter_class.filter(data) }
|
15
|
+
break if filtered.parts.size == size_last_pass
|
16
|
+
size_last_pass = filtered.parts.size
|
17
|
+
end
|
18
|
+
return filtered.parts, filtered.text
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module TrackParser
|
2
|
+
class FilteredParts
|
3
|
+
|
4
|
+
def initialize(text)
|
5
|
+
@remaining = text
|
6
|
+
@parts = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def text
|
10
|
+
@remaining
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_part(type, text)
|
14
|
+
@parts << {type: type, text: text}
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_text(text)
|
18
|
+
@remaining = @remaining.gsub(text, "")
|
19
|
+
end
|
20
|
+
|
21
|
+
def parts
|
22
|
+
@parts
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module TrackParser
|
2
|
+
class FeaturingFilter
|
3
|
+
|
4
|
+
def self.filter(data)
|
5
|
+
self.new(data).filter
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(data)
|
9
|
+
@data = data
|
10
|
+
end
|
11
|
+
|
12
|
+
def filter
|
13
|
+
if has_featuring?
|
14
|
+
featuring_text = regexp.match(@data.text)[:featuring]
|
15
|
+
@data.add_part(:featuring, featuring_text)
|
16
|
+
@data.remove_text(featuring_text) #remove featuring artists from trackname
|
17
|
+
end
|
18
|
+
return @data
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_featuring?
|
22
|
+
!!regexp.match(@data.text)
|
23
|
+
end
|
24
|
+
|
25
|
+
def regexp
|
26
|
+
/(?<featuring>\(?(#{FeaturingNode::VERSIONS_REGEXP})\s.*\)?)/i #find featuring keywords
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative '../nodes/remix_node'
|
2
|
+
require_relative '../nodes/featuring_node'
|
3
|
+
|
4
|
+
module TrackParser
|
5
|
+
class FilterNodesMapping
|
6
|
+
FILTERS_NODES_MAPPING = {remix: RemixNode, featuring: FeaturingNode}
|
7
|
+
|
8
|
+
def self.to_nodes(parts)
|
9
|
+
parts.map { |part| FILTERS_NODES_MAPPING[part[:type]].new(part[:text])}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative "../nodes/remix_node"
|
2
|
+
|
3
|
+
module TrackParser
|
4
|
+
class RemixFilter
|
5
|
+
|
6
|
+
def self.filter(data)
|
7
|
+
self.new(data).filter
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(data)
|
11
|
+
@data = data
|
12
|
+
end
|
13
|
+
|
14
|
+
def filter
|
15
|
+
if has_remix?
|
16
|
+
extracted = content.select {|match| !regexp.match(match).nil?}.first #select first that matches a remix
|
17
|
+
@data.add_part(:remix, extracted)
|
18
|
+
@data.remove_text "(#{extracted})" #remove remix string from
|
19
|
+
end
|
20
|
+
return @data
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_remix?
|
24
|
+
!!regexp #find remix keywords
|
25
|
+
end
|
26
|
+
|
27
|
+
def content
|
28
|
+
text = @data.text
|
29
|
+
if text =~ /(\(|\))+/
|
30
|
+
text.scan(/\((?<content>.*?)\)/).flatten
|
31
|
+
else
|
32
|
+
[text]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def regexp
|
37
|
+
/(?<remix>.*?\b(#{RemixNode::VERSIONS_REGEXP})+)\b/i
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'artist_node'
|
2
|
+
require_relative 'base_node'
|
3
|
+
|
4
|
+
module TrackParser
|
5
|
+
class ArtistsNode < BaseNode
|
6
|
+
|
7
|
+
def initialize(artists)
|
8
|
+
super(:artists, artists)
|
9
|
+
end
|
10
|
+
|
11
|
+
def children
|
12
|
+
if @raw.is_a? String
|
13
|
+
if multiple?
|
14
|
+
@raw.split(regex).map { |capture| ArtistNode.new(capture) }
|
15
|
+
else
|
16
|
+
[ArtistNode.new(@raw)]
|
17
|
+
end
|
18
|
+
else
|
19
|
+
@raw.map { |artist| ArtistNode.new(artist) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def multiple?
|
24
|
+
!!regex.match(@raw)
|
25
|
+
end
|
26
|
+
|
27
|
+
def regex
|
28
|
+
/\s(?:and|&)\s/i
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'ast'
|
2
|
+
require_relative 'empty_node'
|
3
|
+
|
4
|
+
module TrackParser
|
5
|
+
class BaseNode < AST::Node
|
6
|
+
|
7
|
+
def self.new(text)
|
8
|
+
return EmptyNode.new if text.nil? || (text.is_a?(String) && text.strip.empty?)
|
9
|
+
super(text)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(type, text)
|
13
|
+
super(type, [], {raw: text})
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'artist_node'
|
2
|
+
require_relative 'base_node'
|
3
|
+
|
4
|
+
module TrackParser
|
5
|
+
class FeaturingNode < BaseNode
|
6
|
+
|
7
|
+
VERSIONS_REGEXP = ["feat.", "featuring", "ft."].join("|")
|
8
|
+
|
9
|
+
def initialize(artists)
|
10
|
+
super(:featuring, artists)
|
11
|
+
end
|
12
|
+
|
13
|
+
def regexp
|
14
|
+
/\(?(#{FeaturingNode::VERSIONS_REGEXP})\s(?<artist>[^\)]*)\)?/i
|
15
|
+
end
|
16
|
+
|
17
|
+
def children
|
18
|
+
artist = regexp.match(@raw)[:artist]
|
19
|
+
[ArtistsNode.new(artist)]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative 'base_node'
|
2
|
+
require_relative 'artists_node'
|
3
|
+
require_relative 'name_node'
|
4
|
+
|
5
|
+
module TrackParser
|
6
|
+
class RemixNode < BaseNode
|
7
|
+
|
8
|
+
VERSIONS_REGEXP = ["remix", "mix", "vocal", "dub", "rework", "edit", "version", "extended", "club", "original"].join("|")
|
9
|
+
|
10
|
+
def initialize(remix)
|
11
|
+
raw = remix.gsub(/(\(|\))/, "") # remove parantheses
|
12
|
+
super(:remix, raw)
|
13
|
+
end
|
14
|
+
|
15
|
+
def children
|
16
|
+
if has_remix_name?
|
17
|
+
nodes = @raw.split(/(?:'|´)s/)
|
18
|
+
[ArtistsNode.new(nodes[0]), NameNode.new(nodes[1])]
|
19
|
+
else
|
20
|
+
[ArtistsNode.new(remixer), NameNode.new(remix_version)]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_remix_name?
|
25
|
+
@raw.include?("'s") || @raw.include?("´s")
|
26
|
+
end
|
27
|
+
|
28
|
+
def remixer
|
29
|
+
without_version.strip
|
30
|
+
end
|
31
|
+
|
32
|
+
def remix_version(remix = @raw)
|
33
|
+
remix.scan(/(#{VERSIONS_REGEXP})\b/i).flatten.join(" ")
|
34
|
+
end
|
35
|
+
|
36
|
+
def without_version(part = @raw)
|
37
|
+
part.gsub(remix_version, "")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'base_node'
|
2
|
+
require_relative 'artists_node'
|
3
|
+
require_relative 'trackname_node'
|
4
|
+
require_relative 'remix_node'
|
5
|
+
|
6
|
+
module TrackParser
|
7
|
+
class TrackNode < BaseNode
|
8
|
+
|
9
|
+
def initialize(track)
|
10
|
+
raw = replace_parentheses(track)
|
11
|
+
super(:track, raw)
|
12
|
+
end
|
13
|
+
|
14
|
+
def children
|
15
|
+
if track_sections.size == 1
|
16
|
+
raise UnparseableTrack.new("Don't know how to parse a track without \"-\". Track was:#{@raw}")
|
17
|
+
else
|
18
|
+
nodes = track_sections[2..-1].map do |track_section|
|
19
|
+
parts = FilterPipeline.filter(track_section).first
|
20
|
+
FilterNodesMapping.to_nodes(parts)
|
21
|
+
end
|
22
|
+
[ArtistsNode.new(track_sections[0]), TracknameNode.new(track_sections[1])] + nodes.flatten
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def replace_parentheses(text)
|
27
|
+
if text.is_a? String
|
28
|
+
text.gsub("[","(").gsub("]",")")
|
29
|
+
else
|
30
|
+
text[:name] = replace_parentheses(text[:name])
|
31
|
+
text
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def track_sections
|
36
|
+
if @raw.is_a? String
|
37
|
+
split_sections(@raw)
|
38
|
+
else
|
39
|
+
[@raw[:artists]] + split_sections(@raw[:name])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def split_sections(text)
|
44
|
+
text.split(" - ").map(&:strip)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class UnparseableTrack < StandardError; end
|
49
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'base_node'
|
2
|
+
require_relative 'name_node'
|
3
|
+
require_relative 'remix_node'
|
4
|
+
require_relative 'featuring_node'
|
5
|
+
require_relative '../filter_pipeline'
|
6
|
+
require_relative '../filters/filter_nodes_mapping'
|
7
|
+
|
8
|
+
module TrackParser
|
9
|
+
class TracknameNode < BaseNode
|
10
|
+
|
11
|
+
def initialize(trackname)
|
12
|
+
super(:trackname, trackname)
|
13
|
+
end
|
14
|
+
|
15
|
+
def children
|
16
|
+
parts, remaining_text = FilterPipeline.filter(@raw)
|
17
|
+
[NameNode.new(remaining_text)] + FilterNodesMapping.to_nodes(parts)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "nodes/track_node"
|
2
|
+
require_relative "processor"
|
3
|
+
|
4
|
+
module TrackParser
|
5
|
+
class Parser
|
6
|
+
|
7
|
+
def self.do(track)
|
8
|
+
self.new(track).parse
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(track)
|
12
|
+
@track = TrackNode.new(track)
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
Processor.new.process(@track)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "ast"
|
2
|
+
require_relative "track"
|
3
|
+
|
4
|
+
module TrackParser
|
5
|
+
class Processor < AST::Processor
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@track = Track.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def on_track(node)
|
12
|
+
hash = process_all(node).reduce({}) { |mem, h| mem.merge(h) }
|
13
|
+
@track.add_artists(hash[:artists])
|
14
|
+
@track
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_trackname(node)
|
18
|
+
hash = process_all(node).reduce({}) { |mem, h| mem.merge(h) }
|
19
|
+
@track.add_name(hash[:name])
|
20
|
+
{}
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_artists(node)
|
24
|
+
artists = process_all(node)
|
25
|
+
{ artists: artists }
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_artist(node)
|
29
|
+
node.name
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_name(node)
|
33
|
+
{ name: node.name }
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_remix(node)
|
37
|
+
hash = process_all(node).reduce({}) { |mem, h| mem.merge(h) }
|
38
|
+
@track.add_remixer(hash[:artists])
|
39
|
+
@track.add_remix_name(hash[:name])
|
40
|
+
{}
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_featuring(node)
|
44
|
+
artists = process_all(node).first
|
45
|
+
@track.add_featuring(artists[:artists])
|
46
|
+
{}
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_empty(node)
|
50
|
+
{}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module TrackParser
|
2
|
+
class Track
|
3
|
+
|
4
|
+
attr_accessor :artists, :name, :remixer, :remix_name, :featuring
|
5
|
+
|
6
|
+
def initialize(hash = {})
|
7
|
+
@artists = hash[:artists]
|
8
|
+
@name = hash[:name]
|
9
|
+
@remixer = hash[:remixer]
|
10
|
+
@remix_name = hash[:remix_name]
|
11
|
+
@featuring = hash[:featuring]
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_artists(artists)
|
15
|
+
@artists = artists
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_remixer(remixer)
|
19
|
+
return if remixer.nil?
|
20
|
+
if @remixer.nil?
|
21
|
+
@remixer = remixer
|
22
|
+
else
|
23
|
+
@remixer.concat(remixer)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_remix_name(remix_name)
|
28
|
+
return if remix_name.nil?
|
29
|
+
if @remix_name.nil?
|
30
|
+
@remix_name = remix_name
|
31
|
+
else
|
32
|
+
@remix_name += " " + remix_name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_name(name)
|
37
|
+
@name = name
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_featuring(featured_artists)
|
41
|
+
@featuring = featured_artists
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
"artists='#{@artists}' name='#{@name}' remixer='#{@remixer}' remix_name='#{@remix_name}' featuring='#{featuring}'"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
describe TrackParser::Parser do
|
2
|
+
|
3
|
+
tracks = {
|
4
|
+
"SIS - Orgsa" =>
|
5
|
+
{ artists: ["SIS"], name: "Orgsa", remixer: nil, remix_name: nil},
|
6
|
+
"Roy Davis Jr - About Love (Pezzner Remix)" =>
|
7
|
+
{ artists: ["Roy Davis Jr"], name: "About Love", remixer: ["Pezzner"], remix_name: "Remix" },
|
8
|
+
"Julien Perez & Octavio Camino - Parada Maya" =>
|
9
|
+
{ artists: ["Julien Perez", "Octavio Camino"], name: "Parada Maya", remixer: nil, remix_name: nil},
|
10
|
+
"Kollektiv Turmstrasse - Ordinary (Lake People's Circle Motive Remix)" =>
|
11
|
+
{ artists: ["Kollektiv Turmstrasse"], name: "Ordinary", remixer: ["Lake People"], remix_name: "Circle Motive Remix" },
|
12
|
+
"Derek Marin - We've Been Expecting You - Hreno´s Deep Pockets Dub" =>
|
13
|
+
{ artists: ["Derek Marin"], name: "We've Been Expecting You", remixer: ["Hreno"], remix_name: "Deep Pockets Dub" },
|
14
|
+
"Umek & Uto Karem - Crossing the Lines - Original Mix" =>
|
15
|
+
{ artists: ["Umek", "Uto Karem"], name: "Crossing the Lines", remixer: nil, remix_name: "Original Mix" },
|
16
|
+
"Noir & Fraser Owen - &U - Mendo and Danny Serrano Remix" =>
|
17
|
+
{ artists: ["Noir", "Fraser Owen"], name: "&U", remixer: ["Mendo", "Danny Serrano"], remix_name: "Remix"},
|
18
|
+
"Plastikman - Spastik - Dubfire Rework" =>
|
19
|
+
{ artists: ["Plastikman"], name: "Spastik", remixer: ["Dubfire"], remix_name: "Rework" },
|
20
|
+
"Alex Under - El Encuentro - Richie Hawtin Edit" =>
|
21
|
+
{ artists: ['Alex Under'], name: 'El Encuentro', remixer: ["Richie Hawtin"], remix_name: "Edit" },
|
22
|
+
"Gorge - Erotic Soul feat. The Writers Poet - Original Mix" =>
|
23
|
+
{ artists: ["Gorge"], name: "Erotic Soul", featuring: ["The Writers Poet"], remixer: nil, remix_name: "Original Mix"},
|
24
|
+
"John Talabot & Pional - Destiny (Feat. Pional)" =>
|
25
|
+
{ artists: ["John Talabot", "Pional"], name: "Destiny", featuring: ["Pional"], remixer: nil, remix_name: nil},
|
26
|
+
"Ornette - Crazy - Nôze Remix - Extended Club Version" =>
|
27
|
+
{ artists: ["Ornette"], name: "Crazy", remixer: ["Nôze"], remix_name: "Remix Extended Club Version" },
|
28
|
+
"2 Guys in Venice & Spiller & Digitalism - Encore - Spiller & 2 Guys In Venice Remix" =>
|
29
|
+
{ artists: ["2 Guys in Venice", "Spiller", "Digitalism"], name: "Encore", remixer: ["Spiller", "2 Guys In Venice"], remix_name: "Remix" },
|
30
|
+
"Subb-an - Take You Back (ft. Beckford)" =>
|
31
|
+
{ artists: ["Subb-an"], name: "Take You Back", featuring: ["Beckford"] },
|
32
|
+
"Aki Bergen & Pezzner & Terry Grant - Tarareando - Vocal Mix" =>
|
33
|
+
{ artists: ["Aki Bergen", "Pezzner", "Terry Grant"], name: "Tarareando", remixer: nil, remix_name: "Vocal Mix" },
|
34
|
+
"Jay Haze & Laila Tov - I Wait For You - Original" =>
|
35
|
+
{ artists: ["Jay Haze", "Laila Tov"], name: "I Wait For You", remixer: nil, remix_name: "Original"},
|
36
|
+
"DJ T. - City Life - feat. Cari Golden [Maceo Plex Remix]" =>
|
37
|
+
{ artists: ["DJ T."], name: "City Life", remixer: ["Maceo Plex"], remix_name: "Remix", featuring: ["Cari Golden"]},
|
38
|
+
"Fritz Kalkbrenner - Right in the Dark - Henrik Schwarz Remix - Chopstick & Johnjon Edit" =>
|
39
|
+
{ artists: ["Fritz Kalkbrenner"], name: "Right in the Dark", remixer: ["Henrik Schwarz", "Chopstick", "Johnjon"], remix_name: "Remix Edit"}
|
40
|
+
}
|
41
|
+
|
42
|
+
tracks_structured = [
|
43
|
+
{track: { artists: ["Sailor & I"], name: "Tough Love - Aril Brikha Remix" }, result:
|
44
|
+
{ artists: ["Sailor & I"], name: "Tough Love", remixer: ["Aril Brikha"], remix_name: "Remix"}}
|
45
|
+
]
|
46
|
+
|
47
|
+
let(:parsed_track) { TrackParser::Parser.do(track) }
|
48
|
+
|
49
|
+
tracks.keys.each do |track|
|
50
|
+
context "#{track}" do
|
51
|
+
let(:track) { track }
|
52
|
+
|
53
|
+
it "returns the artist correctly" do
|
54
|
+
expect(parsed_track.artists).to eq(tracks[track][:artists])
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns the track name correctly" do
|
58
|
+
expect(parsed_track.name).to eq(tracks[track][:name])
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns the remixer correctly" do
|
62
|
+
expect(parsed_track.remixer).to eq(tracks[track][:remixer])
|
63
|
+
end
|
64
|
+
|
65
|
+
it "returns the remix name correctly" do
|
66
|
+
expect(parsed_track.remix_name).to eq(tracks[track][:remix_name])
|
67
|
+
end
|
68
|
+
|
69
|
+
it "returns the featured artists correctly" do
|
70
|
+
expect(parsed_track.featuring).to eq(tracks[track][:featuring])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "invalid track" do
|
76
|
+
let(:track) { "Invalid Track" }
|
77
|
+
|
78
|
+
it "throws an exception when the track doesn't have \"-\"" do
|
79
|
+
expect{TrackParser::Parser.do(track)}.to raise_error(TrackParser::UnparseableTrack)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "Hash as input" do
|
84
|
+
let(:track) { tracks_structured.first[:track] }
|
85
|
+
let(:result) { tracks_structured.first[:result]}
|
86
|
+
|
87
|
+
it "returns all the correct info" do
|
88
|
+
expect(parsed_track.artists).to eq(result[:artists])
|
89
|
+
expect(parsed_track.name).to eq(result[:name])
|
90
|
+
expect(parsed_track.remixer).to eq(result[:remixer])
|
91
|
+
expect(parsed_track.remix_name).to eq(result[:remix_name])
|
92
|
+
expect(parsed_track.featuring).to eq(result[:featuring])
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'track_parser/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "track_parser"
|
8
|
+
spec.version = TrackParser::VERSION
|
9
|
+
spec.authors = ["Alexandre Quintino"]
|
10
|
+
spec.email = ["amsquintino@gmail.com"]
|
11
|
+
spec.summary = %q{Tries to parse the name of a track (eg. ArtistA - ABC (ArtistB remix)) and extract metadata from it (eg. artists = ArtistA, track-name = ABC, remixer = ArtistB)}
|
12
|
+
spec.description = %q{Tries to parse the name of a track (eg. ArtistA - ABC (ArtistB remix)) and extract metadata from it (eg. artists = ArtistA, track-name = ABC, remixer = ArtistB)}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "pry"
|
25
|
+
spec.add_runtime_dependency "ast"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: track_parser
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexandre Quintino
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-16 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.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: ast
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Tries to parse the name of a track (eg. ArtistA - ABC (ArtistB remix))
|
84
|
+
and extract metadata from it (eg. artists = ArtistA, track-name = ABC, remixer =
|
85
|
+
ArtistB)
|
86
|
+
email:
|
87
|
+
- amsquintino@gmail.com
|
88
|
+
executables: []
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- ".gitignore"
|
93
|
+
- ".rspec"
|
94
|
+
- Gemfile
|
95
|
+
- LICENSE.txt
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- lib/track_parser.rb
|
99
|
+
- lib/track_parser/filter_pipeline.rb
|
100
|
+
- lib/track_parser/filtered_parts.rb
|
101
|
+
- lib/track_parser/filters/featuring_filter.rb
|
102
|
+
- lib/track_parser/filters/filter_nodes_mapping.rb
|
103
|
+
- lib/track_parser/filters/remix_filter.rb
|
104
|
+
- lib/track_parser/nodes/artist_node.rb
|
105
|
+
- lib/track_parser/nodes/artists_node.rb
|
106
|
+
- lib/track_parser/nodes/base_node.rb
|
107
|
+
- lib/track_parser/nodes/empty_node.rb
|
108
|
+
- lib/track_parser/nodes/featuring_node.rb
|
109
|
+
- lib/track_parser/nodes/name_node.rb
|
110
|
+
- lib/track_parser/nodes/remix_node.rb
|
111
|
+
- lib/track_parser/nodes/track_node.rb
|
112
|
+
- lib/track_parser/nodes/trackname_node.rb
|
113
|
+
- lib/track_parser/parser.rb
|
114
|
+
- lib/track_parser/processor.rb
|
115
|
+
- lib/track_parser/track.rb
|
116
|
+
- lib/track_parser/version.rb
|
117
|
+
- spec/parser_spec.rb
|
118
|
+
- spec/spec_helper.rb
|
119
|
+
- track_parser.gemspec
|
120
|
+
homepage: ''
|
121
|
+
licenses:
|
122
|
+
- MIT
|
123
|
+
metadata: {}
|
124
|
+
post_install_message:
|
125
|
+
rdoc_options: []
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
requirements: []
|
139
|
+
rubyforge_project:
|
140
|
+
rubygems_version: 2.4.5
|
141
|
+
signing_key:
|
142
|
+
specification_version: 4
|
143
|
+
summary: Tries to parse the name of a track (eg. ArtistA - ABC (ArtistB remix)) and
|
144
|
+
extract metadata from it (eg. artists = ArtistA, track-name = ABC, remixer = ArtistB)
|
145
|
+
test_files:
|
146
|
+
- spec/parser_spec.rb
|
147
|
+
- spec/spec_helper.rb
|