mr_eko 0.6.0 → 0.6.1
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/README.md +2 -2
- data/lib/mr_eko/song.rb +6 -29
- data/lib/mr_eko/tag_parser.rb +22 -0
- data/lib/mr_eko.rb +3 -2
- data/mr_eko.gemspec +5 -2
- data/test/data/unicode.mp3 +0 -0
- data/test/song_test.rb +3 -27
- data/test/tag_parser_test.rb +21 -0
- data/test/test.rb +5 -0
- metadata +6 -3
data/README.md
CHANGED
@@ -11,10 +11,10 @@ Example:
|
|
11
11
|
mreko scan jakob-semaphore.mp3 pelican-drought.mp3
|
12
12
|
|
13
13
|
# Output a PLS playlist of fast, minor-key music
|
14
|
-
mreko playlist --tempo '>120' --mode minor
|
14
|
+
mreko playlist --tempo '>120' --mode minor > rainy_day_suicidal_playlist.pls
|
15
15
|
|
16
16
|
# Output a playlist of shorter, up-tempo, danceable, major-keyed songs
|
17
|
-
mreko playlist --preset gym
|
17
|
+
mreko playlist --preset gym > sweaty_playlist.pls
|
18
18
|
|
19
19
|
# Output a 30 minute playlist which builds from tempo 120 to 185 BPM. See notes on TimedPlaylists below.
|
20
20
|
mreko playlist --timed 1800 --tempo 120 --final 185 > morning_commute.pls
|
data/lib/mr_eko/song.rb
CHANGED
@@ -72,14 +72,14 @@ class MrEko::Song < Sequel::Model
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
# Parses the file's ID3 tags
|
75
|
+
# Parses the file's ID3 tags
|
76
76
|
#
|
77
77
|
# @param [String] The file path
|
78
|
-
# @return [
|
78
|
+
# @return [MrEko::TagParser::Result]
|
79
79
|
def self.parse_id3_tags(filename)
|
80
80
|
log "Parsing ID3 tags"
|
81
81
|
|
82
|
-
|
82
|
+
MrEko::TagParser.parse(filename)
|
83
83
|
end
|
84
84
|
|
85
85
|
|
@@ -89,7 +89,8 @@ class MrEko::Song < Sequel::Model
|
|
89
89
|
# @return [MrEko::Song]
|
90
90
|
def self.catalog_via_tags(filename, opts={})
|
91
91
|
tags = parse_id3_tags(filename)
|
92
|
-
|
92
|
+
|
93
|
+
return unless tags.reject{ |k, v| v.blank? }.size >= REQUIRED_ID3_TAGS.size
|
93
94
|
|
94
95
|
md5 = opts[:md5] || MrEko.md5(filename)
|
95
96
|
begin
|
@@ -98,7 +99,7 @@ class MrEko::Song < Sequel::Model
|
|
98
99
|
:bucket => 'audio_summary',
|
99
100
|
:limit => 1).songs.first
|
100
101
|
rescue => e
|
101
|
-
log "BAD TAGS? #{tags.
|
102
|
+
log "BAD TAGS? #{tags.inspect} #{filename}"
|
102
103
|
log e.message
|
103
104
|
log e.backtrace.join("\n")
|
104
105
|
return
|
@@ -126,13 +127,6 @@ class MrEko::Song < Sequel::Model
|
|
126
127
|
end if analysis
|
127
128
|
end
|
128
129
|
|
129
|
-
def self.has_required_tags?(tags)
|
130
|
-
found = REQUIRED_ID3_TAGS.inject([]) do |present, meth|
|
131
|
-
present << tags.send(meth)
|
132
|
-
end
|
133
|
-
|
134
|
-
found.compact.size == REQUIRED_ID3_TAGS.size ? true : false
|
135
|
-
end
|
136
130
|
|
137
131
|
# Using the Echonest Musical Fingerprint lib in the hopes
|
138
132
|
# of sidestepping the mp3 upload process.
|
@@ -218,21 +212,4 @@ class MrEko::Song < Sequel::Model
|
|
218
212
|
File.expand_path File.join(MrEko::FINGERPRINTS_DIR, "#{md5}.json")
|
219
213
|
end
|
220
214
|
|
221
|
-
# @param [Array<ID3Lib::Tag>]
|
222
|
-
# @return [Array<ID3Lib::Tag>]
|
223
|
-
def self.clean_tags(tags)
|
224
|
-
|
225
|
-
(REQUIRED_ID3_TAGS + [:album]).each do |rt|
|
226
|
-
tag = tags.send(rt).to_s
|
227
|
-
if tag.encoding != Encoding::UTF_8
|
228
|
-
decoded = tag.encode
|
229
|
-
else
|
230
|
-
decoded = tag
|
231
|
-
end
|
232
|
-
|
233
|
-
tags.send("#{rt}=", decoded)
|
234
|
-
end
|
235
|
-
|
236
|
-
tags
|
237
|
-
end
|
238
215
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class MrEko::TagParser
|
2
|
+
|
3
|
+
def self.parse(filename)
|
4
|
+
TagLib::FileRef.open(filename, false) do |file|
|
5
|
+
tag = file.tag
|
6
|
+
result = Result.new
|
7
|
+
result.artist = tag.artist
|
8
|
+
result.title = tag.title
|
9
|
+
result.album = tag.album
|
10
|
+
|
11
|
+
result
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class Result < Hash
|
17
|
+
include Hashie::Extensions::MethodAccess
|
18
|
+
include Hashie::Extensions::IndifferentAccess
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
data/lib/mr_eko.rb
CHANGED
@@ -9,7 +9,7 @@ require 'sequel/extensions/migration'
|
|
9
9
|
require "logger"
|
10
10
|
require "hashie"
|
11
11
|
require "digest/md5"
|
12
|
-
require '
|
12
|
+
require 'taglib'
|
13
13
|
require "echonest"
|
14
14
|
begin
|
15
15
|
require 'debugger'
|
@@ -22,7 +22,7 @@ EKO_ENV = ENV['EKO_ENV'] || 'development'
|
|
22
22
|
Sequel.default_timezone = :utc
|
23
23
|
|
24
24
|
module MrEko
|
25
|
-
VERSION = '0.6.
|
25
|
+
VERSION = '0.6.1'
|
26
26
|
USER_DIR = File.join(ENV['HOME'], ".mreko")
|
27
27
|
FINGERPRINTS_DIR = File.join(USER_DIR, 'fingerprints')
|
28
28
|
LOG_DIR = File.join(USER_DIR, 'logs')
|
@@ -144,3 +144,4 @@ require "mr_eko/playlist"
|
|
144
144
|
require "mr_eko/timed_playlist"
|
145
145
|
require "mr_eko/song"
|
146
146
|
require "mr_eko/playlist_entry"
|
147
|
+
require "mr_eko/tag_parser"
|
data/mr_eko.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
## If your rubyforge_project name is different, then edit it and comment out
|
14
14
|
## the sub! line in the Rakefile
|
15
15
|
s.name = 'mr_eko'
|
16
|
-
s.version = '0.6.
|
16
|
+
s.version = '0.6.1'
|
17
17
|
s.date = '2013-08-13'
|
18
18
|
s.rubyforge_project = 'mr_eko'
|
19
19
|
|
@@ -51,7 +51,7 @@ Gem::Specification.new do |s|
|
|
51
51
|
s.add_dependency('httpclient', "~> 2.1")
|
52
52
|
s.add_dependency('bassnode-ruby-echonest')
|
53
53
|
s.add_dependency('json', "= 1.4.6")
|
54
|
-
s.add_dependency('
|
54
|
+
s.add_dependency('taglib-ruby')
|
55
55
|
|
56
56
|
## List your development dependencies here. Development dependencies are
|
57
57
|
## those that are only needed during development
|
@@ -101,13 +101,16 @@ Gem::Specification.new do |s|
|
|
101
101
|
lib/mr_eko/playlist_entry.rb
|
102
102
|
lib/mr_eko/presets.rb
|
103
103
|
lib/mr_eko/song.rb
|
104
|
+
lib/mr_eko/tag_parser.rb
|
104
105
|
lib/mr_eko/timed_playlist.rb
|
105
106
|
mr_eko.gemspec
|
106
107
|
test/data/tagless.mp3
|
107
108
|
test/data/they_want_a_test.mp3
|
109
|
+
test/data/unicode.mp3
|
108
110
|
test/mr_eko_test.rb
|
109
111
|
test/playlist_test.rb
|
110
112
|
test/song_test.rb
|
113
|
+
test/tag_parser_test.rb
|
111
114
|
test/test.rb
|
112
115
|
test/timed_playlist_test.rb
|
113
116
|
]
|
Binary file
|
data/test/song_test.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
require 'ostruct'
|
2
2
|
class SongTest < Test::Unit::TestCase
|
3
3
|
|
4
|
-
TEST_MP3 = File.join(File.dirname(__FILE__), 'data', 'they_want_a_test.mp3')
|
5
|
-
TAGLESS_MP3 = File.join(File.dirname(__FILE__), 'data', 'tagless.mp3')
|
6
|
-
|
7
4
|
def enmfp_data_stub(overrides={})
|
8
5
|
opts = {
|
9
6
|
'code' => '98ouhajsnd081oi2he0da8sdoihjasdi2y9e8aASD3e8yaushdjQWD',
|
@@ -100,12 +97,12 @@ class SongTest < Test::Unit::TestCase
|
|
100
97
|
|
101
98
|
context 'catalog_via_tags' do
|
102
99
|
|
103
|
-
context 'for a mp3 with no
|
100
|
+
context 'for a mp3 with no ID3 tag information' do
|
104
101
|
|
105
102
|
setup do
|
106
103
|
@mp3 = MrEko::Song.parse_id3_tags(TAGLESS_MP3)
|
107
|
-
|
108
|
-
|
104
|
+
assert_nil @mp3.artist
|
105
|
+
assert_nil @mp3.title
|
109
106
|
end
|
110
107
|
|
111
108
|
should 'return nil' do
|
@@ -132,25 +129,4 @@ class SongTest < Test::Unit::TestCase
|
|
132
129
|
end
|
133
130
|
end
|
134
131
|
|
135
|
-
context 'cleaning encoded ID3 tags' do
|
136
|
-
|
137
|
-
should "decode iTunes' encoded tags" do
|
138
|
-
dm = 'Dead Meadow'.encode(Encoding::UCS_2BE, Encoding::UTF_8)
|
139
|
-
tag_stub = OpenStruct.new(:artist => dm, :title => 'Good Moaning')
|
140
|
-
ID3Lib::Tag.expects(:new).once.returns(tag_stub)
|
141
|
-
parsed_tags = MrEko::Song.parse_id3_tags(TEST_MP3)
|
142
|
-
|
143
|
-
assert_equal "Dead Meadow", parsed_tags.artist
|
144
|
-
end
|
145
|
-
|
146
|
-
should "not alter the tags when they aren't encoded" do
|
147
|
-
tag_stub = OpenStruct.new(:artist => 'Dead Meadow', :title => 'Good Moaning')
|
148
|
-
ID3Lib::Tag.expects(:new).once.returns(tag_stub)
|
149
|
-
|
150
|
-
parsed = MrEko::Song.parse_id3_tags(TEST_MP3)
|
151
|
-
|
152
|
-
assert_equal parsed.artist, 'Dead Meadow'
|
153
|
-
assert_equal parsed.title, 'Good Moaning'
|
154
|
-
end
|
155
|
-
end
|
156
132
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class TagParserTest < Test::Unit::TestCase
|
2
|
+
|
3
|
+
context 'parse' do
|
4
|
+
|
5
|
+
should 'return an abject with the tags' do
|
6
|
+
parser = MrEko::TagParser.parse(TEST_MP3)
|
7
|
+
|
8
|
+
assert_equal 'Swamp Rooters', parser.artist
|
9
|
+
assert_equal 'Swamp Cat Rag', parser.title
|
10
|
+
assert_equal 'Misc', parser.album
|
11
|
+
end
|
12
|
+
|
13
|
+
should 'handles files without tags' do
|
14
|
+
parser = MrEko::TagParser.parse(TAGLESS_MP3)
|
15
|
+
|
16
|
+
assert_nil parser.artist
|
17
|
+
assert_nil parser.title
|
18
|
+
assert_nil parser.album
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/test/test.rb
CHANGED
@@ -13,6 +13,11 @@ end
|
|
13
13
|
|
14
14
|
class Test::Unit::TestCase
|
15
15
|
|
16
|
+
TEST_MP3 = File.join(File.dirname(__FILE__), 'data', 'they_want_a_test.mp3')
|
17
|
+
TAGLESS_MP3 = File.join(File.dirname(__FILE__), 'data', 'tagless.mp3')
|
18
|
+
UNICODE_MP3 = File.join(File.dirname(__FILE__), 'data', 'unicode.mp3')
|
19
|
+
|
20
|
+
|
16
21
|
# Could be fleshed out some more.
|
17
22
|
def sequel_dataset_stub
|
18
23
|
data = mock()
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mr_eko
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -108,7 +108,7 @@ dependencies:
|
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: 1.4.6
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
|
-
name:
|
111
|
+
name: taglib-ruby
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
113
113
|
none: false
|
114
114
|
requirements:
|
@@ -276,13 +276,16 @@ files:
|
|
276
276
|
- lib/mr_eko/playlist_entry.rb
|
277
277
|
- lib/mr_eko/presets.rb
|
278
278
|
- lib/mr_eko/song.rb
|
279
|
+
- lib/mr_eko/tag_parser.rb
|
279
280
|
- lib/mr_eko/timed_playlist.rb
|
280
281
|
- mr_eko.gemspec
|
281
282
|
- test/data/tagless.mp3
|
282
283
|
- test/data/they_want_a_test.mp3
|
284
|
+
- test/data/unicode.mp3
|
283
285
|
- test/mr_eko_test.rb
|
284
286
|
- test/playlist_test.rb
|
285
287
|
- test/song_test.rb
|
288
|
+
- test/tag_parser_test.rb
|
286
289
|
- test/test.rb
|
287
290
|
- test/timed_playlist_test.rb
|
288
291
|
homepage: http://github.com/bassnode/mreko
|
@@ -300,7 +303,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
300
303
|
version: '0'
|
301
304
|
segments:
|
302
305
|
- 0
|
303
|
-
hash:
|
306
|
+
hash: -723694834172722563
|
304
307
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
305
308
|
none: false
|
306
309
|
requirements:
|