mr_eko 0.3.3 → 0.3.4
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/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