mr_eko 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mr_eko/core.rb +4 -0
- data/lib/mr_eko/song.rb +10 -17
- data/lib/mr_eko/timed_playlist.rb +10 -5
- data/lib/mr_eko.rb +1 -1
- data/mr_eko.gemspec +1 -1
- data/test/song_test.rb +16 -28
- metadata +3 -3
data/lib/mr_eko/core.rb
CHANGED
@@ -4,6 +4,10 @@ module MrEko
|
|
4
4
|
def self.included(base)
|
5
5
|
base.send(:include, GlobalHelpers)
|
6
6
|
base.extend GlobalHelpers
|
7
|
+
|
8
|
+
# The Sequel timestamps plugin doesn't seem to work, so...
|
9
|
+
def before_create; self.created_on = Time.now.utc; end
|
10
|
+
def before_update; self.updated_on = Time.now.utc; end
|
7
11
|
end
|
8
12
|
|
9
13
|
module GlobalHelpers
|
data/lib/mr_eko/song.rb
CHANGED
@@ -45,12 +45,7 @@ class MrEko::Song < Sequel::Model
|
|
45
45
|
|
46
46
|
begin
|
47
47
|
fingerprint_json = enmfp_data(filename, md5)
|
48
|
-
|
49
48
|
profile = identify_from_enmfp_data(fingerprint_json)
|
50
|
-
|
51
|
-
# Get the extended audio data from the profile
|
52
|
-
analysis = MrEko.nest.song.profile(:id => profile.id, :bucket => 'audio_summary').songs.first.audio_summary
|
53
|
-
|
54
49
|
rescue EnmfpError => e
|
55
50
|
log %Q{Issues using ENMFP data "(#{e})" #{e.backtrace.join("\n")}}
|
56
51
|
analysis, profile = get_datapoints_by_upload(filename)
|
@@ -60,22 +55,20 @@ class MrEko::Song < Sequel::Model
|
|
60
55
|
song.filename = File.expand_path(filename)
|
61
56
|
song.md5 = md5
|
62
57
|
song.code = fingerprint_json ? fingerprint_json.code : nil
|
63
|
-
song.tempo =
|
64
|
-
song.duration =
|
65
|
-
song.
|
66
|
-
song.
|
67
|
-
song.
|
68
|
-
song.
|
69
|
-
song.loudness = analysis.loudness
|
70
|
-
song.time_signature = analysis.time_signature
|
58
|
+
song.tempo = profile.audio_summary.tempo
|
59
|
+
song.duration = profile.audio_summary.duration
|
60
|
+
song.key = profile.audio_summary.key
|
61
|
+
song.mode = profile.audio_summary.mode
|
62
|
+
song.loudness = profile.audio_summary.loudness
|
63
|
+
song.time_signature = profile.audio_summary.time_signature
|
71
64
|
song.echonest_id = profile.id
|
72
|
-
song.bitrate =
|
65
|
+
song.bitrate = fingerprint_json ? fingerprint_json.metadata.bitrate : nil
|
73
66
|
song.title = profile.title
|
74
67
|
song.artist = profile.artist || profile.artist_name
|
75
68
|
song.album = fingerprint_json ? fingerprint_json.metadata.release : profile.release
|
76
|
-
song.danceability = profile.audio_summary
|
77
|
-
song.energy = profile.audio_summary
|
78
|
-
end
|
69
|
+
song.danceability = profile.audio_summary.danceability
|
70
|
+
song.energy = profile.audio_summary.energy
|
71
|
+
end
|
79
72
|
end
|
80
73
|
|
81
74
|
# Parses the file's ID3 tags and converts and strange encoding.
|
@@ -95,7 +95,7 @@ class MrEko::TimedPlaylist
|
|
95
95
|
end
|
96
96
|
|
97
97
|
# XXX Just sketching this part out at the moment...
|
98
|
-
# needs tests
|
98
|
+
# needs tests and to work with attributes other than tempo!
|
99
99
|
def find_songs
|
100
100
|
step_count, step_length = step_map[:tempo]
|
101
101
|
return unless step_count && step_length
|
@@ -107,27 +107,32 @@ class MrEko::TimedPlaylist
|
|
107
107
|
songs_to_examine_per_step = step_count > all_songs.size ? 1 : all_songs.size / step_count
|
108
108
|
|
109
109
|
overall_seconds_used = 0
|
110
|
-
all_songs.each_slice(songs_to_examine_per_step).each do |
|
110
|
+
all_songs.each_slice(songs_to_examine_per_step).each do |step_songs|
|
111
111
|
break if overall_seconds_used >= @length
|
112
112
|
|
113
113
|
song_length_proximity = 0
|
114
|
-
length_map =
|
114
|
+
length_map = step_songs.inject({}) do |hsh, song|
|
115
115
|
song_length_proximity = (song.duration - step_length).abs
|
116
116
|
hsh[song_length_proximity] = song
|
117
117
|
hsh
|
118
118
|
end
|
119
119
|
|
120
120
|
step_seconds_used = 0
|
121
|
+
song_set = []
|
121
122
|
length_map.sort_by{ |key, song| key }.each do |length, song|
|
122
|
-
|
123
|
+
song_set << song
|
123
124
|
step_seconds_used += song.duration
|
124
125
|
overall_seconds_used += song.duration
|
125
126
|
break if step_seconds_used >= step_length
|
126
127
|
end
|
127
128
|
|
129
|
+
# Make sure the songs are added the required order as they have been
|
130
|
+
# sorted by duration and thus may be in an odd order.
|
131
|
+
song_set = direction == :asc ? song_set.sort_by(&:tempo) : song_set.sort_by(&:tempo).reverse
|
132
|
+
@songs = @songs + song_set
|
128
133
|
end
|
129
134
|
# Might need to make a cluster map here instead of just choosing enough
|
130
|
-
# songs to fulfill the step_length. This is because the
|
135
|
+
# songs to fulfill the step_length. This is because the
|
131
136
|
# Playlist#length can be fulfilled even before we reach the target/final
|
132
137
|
# target. I think a better rule would be to pluck a song having the
|
133
138
|
# initial and final values and then try to evenly spread out the remaining
|
data/lib/mr_eko.rb
CHANGED
@@ -19,7 +19,7 @@ EKO_ENV = ENV['EKO_ENV'] || 'development'
|
|
19
19
|
Sequel.default_timezone = :utc
|
20
20
|
|
21
21
|
module MrEko
|
22
|
-
VERSION = '0.3.
|
22
|
+
VERSION = '0.3.4'
|
23
23
|
USER_DIR = File.join(ENV['HOME'], ".mreko")
|
24
24
|
FINGERPRINTS_DIR = File.join(USER_DIR, 'fingerprints')
|
25
25
|
LOG_DIR = File.join(USER_DIR, 'logs')
|
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.3.
|
16
|
+
s.version = '0.3.4'
|
17
17
|
s.date = '2011-12-02'
|
18
18
|
s.rubyforge_project = 'mr_eko'
|
19
19
|
|
data/test/song_test.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'ostruct'
|
1
2
|
class SongTest < Test::Unit::TestCase
|
2
3
|
|
3
4
|
TEST_MP3 = File.join(File.dirname(__FILE__), 'data', 'they_want_a_test.mp3')
|
@@ -24,6 +25,16 @@ class SongTest < Test::Unit::TestCase
|
|
24
25
|
Hashie::Mash.new(opts)
|
25
26
|
end
|
26
27
|
|
28
|
+
def song_identify_stub
|
29
|
+
Hashie::Mash.new(:artist_id => "ARZQYSZ1187FB3AC39", :artist_name => "Sebastian",
|
30
|
+
:id => "SOEQMAC12A6701D920", :message => "OK (match type 6)", :score =>57, :tag => 0, :title => "Ross Ross Ross",
|
31
|
+
:audio_summary => Hashie::Mash.new(:analysis_url => "url", :audio_md5 => "fb592e1fa581a8ad0b0478a45130e9e0",
|
32
|
+
:danceability => 0.265574327869162, :duration =>1223,
|
33
|
+
:energy => 0.732951527606216, :key => 11, :loudness =>-10.328,
|
34
|
+
:mode => 0, :tempo => 137.538, :time_signature =>1)
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
27
38
|
def setup
|
28
39
|
MrEko::Song.delete
|
29
40
|
end
|
@@ -67,7 +78,7 @@ class SongTest < Test::Unit::TestCase
|
|
67
78
|
|
68
79
|
should 'try uploading if the ENMFP fingerprint contains errors' do
|
69
80
|
MrEko::Song.stubs(:enmfp_data).raises(MrEko::Song::EnmfpError)
|
70
|
-
MrEko::Song.expects(:get_datapoints_by_upload).
|
81
|
+
MrEko::Song.expects(:get_datapoints_by_upload).with(TEST_MP3).returns([stub_everything, stub_everything(:audio_summary => stub_everything, :id => 'yu82')])
|
71
82
|
MrEko::Song.catalog_via_enmfp(TEST_MP3)
|
72
83
|
end
|
73
84
|
|
@@ -78,36 +89,13 @@ class SongTest < Test::Unit::TestCase
|
|
78
89
|
end
|
79
90
|
|
80
91
|
should 'try to upload when no songs are returned from the Song#identify call' do
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
:artist => stub_data.metadata.artist,
|
86
|
-
:title => stub_data.metadata.title,
|
87
|
-
:release => stub_data.metadata.release,
|
88
|
-
:bucket => 'audio_summary'
|
89
|
-
}
|
90
|
-
MrEko::Song.stubs(:enmfp_data).returns(stub_data)
|
91
|
-
Echonest::ApiMethods::Song.any_instance.expects(:identify).with(id_opts).returns(empty_profile_stub)
|
92
|
-
MrEko::Song.expects(:get_datapoints_by_upload).returns([stub_everything, stub_everything(:id => 'whatever')])
|
92
|
+
MrEko::Song.stubs(:enmfp_data).returns(enmfp_data_stub)
|
93
|
+
MrEko::Song.expects(:identify_from_enmfp_data).with(enmfp_data_stub).raises(MrEko::Song::EnmfpError.new("no songs"))
|
94
|
+
MrEko::Song.expects(:get_datapoints_by_upload).returns([stub_everything, stub_everything(:audio_summary => stub_everything, :id => 'yu82')])
|
95
|
+
|
93
96
|
MrEko::Song.catalog_via_enmfp(TEST_MP3)
|
94
97
|
end
|
95
98
|
|
96
|
-
should 'try to get the profile data when a song is returned from the Song#identify call' do
|
97
|
-
stub_data = enmfp_data_stub
|
98
|
-
profile_stub = stub(:songs => [stub_everything(:id => 'FJJ299KLOP')])
|
99
|
-
profile_details_stub = stub(:songs => [stub(:audio_summary => stub_everything)])
|
100
|
-
|
101
|
-
MrEko::Song.stubs(:enmfp_data).returns(stub_data)
|
102
|
-
Echonest::ApiMethods::Song.any_instance.expects(:identify).returns(profile_stub)
|
103
|
-
Echonest::ApiMethods::Song.any_instance.expects(:profile).with(:id => 'FJJ299KLOP', :bucket => 'audio_summary').returns(profile_details_stub)
|
104
|
-
MrEko::Song.expects(:get_datapoints_by_upload).never
|
105
|
-
|
106
|
-
|
107
|
-
assert_difference 'MrEko::Song.count' do
|
108
|
-
MrEko::Song.catalog_via_enmfp(TEST_MP3)
|
109
|
-
end
|
110
|
-
end
|
111
99
|
end
|
112
100
|
|
113
101
|
context 'catalog_via_tags' do
|
metadata
CHANGED