mr_eko 0.3.2 → 0.3.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.
data/Rakefile CHANGED
@@ -155,8 +155,7 @@ end
155
155
 
156
156
  desc 'Launch an IRB console'
157
157
  task :console do
158
- libs = "-rlib/mr_eko -rirb/completion -rruby-debug"
159
- exec "irb #{libs}"
158
+ exec "bundle console"
160
159
  end
161
160
 
162
161
  namespace :db do
@@ -17,15 +17,14 @@ class MrEko::Playlist < Sequel::Model
17
17
  # TODO: Is a name (or persisting) even necessary?
18
18
  MrEko.connection.transaction do
19
19
  pl = create(:name => options.delete(:name) || "Playlist #{rand(10000)}")
20
- options = prepare_options(options)
21
20
 
21
+ filter = apply_filters prepare_options(options)
22
+ songs = filter.all
22
23
 
23
- songs = MrEko::Song.where(options).all
24
24
  if songs.size > 0
25
25
  songs.each{ |song| pl.add_song(song) }
26
26
  pl.save
27
27
  else
28
- # pl.delete # TODO: Look into not creating Playlist in the 1st place
29
28
  raise NoSongsError.new("No songs match those criteria!")
30
29
  end
31
30
  end
@@ -44,7 +43,7 @@ class MrEko::Playlist < Sequel::Model
44
43
 
45
44
  case key
46
45
 
47
- when :danceability, :energy, :loudness
46
+ when :danceability, :energy
48
47
  new_options << transform(key, value, true)
49
48
 
50
49
  when :mode
@@ -94,6 +93,13 @@ class MrEko::Playlist < Sequel::Model
94
93
  end
95
94
 
96
95
 
96
+ # Recursively add Sequel where clauses
97
+ def self.apply_filters(options, filtered=nil)
98
+ filtered = (filtered || MrEko::Song).where(options.pop)
99
+ filtered = apply_filters(options, filtered) unless options.empty?
100
+ filtered
101
+ end
102
+
97
103
  # Returns a text representation of the Playlist.
98
104
  def create_text
99
105
  songs.inject("") do |list, song|
data/lib/mr_eko/song.rb CHANGED
@@ -36,42 +36,30 @@ class MrEko::Song < Sequel::Model
36
36
  # @param [String] location of the audio file
37
37
  # @param [Hash] opts
38
38
  # @option opts [String] :md5 pre-calculated MD5 of file
39
- # @return [MrEko::Song] the created Song
39
+ # @return [MrEko::Song, NilClass] the created Song
40
40
  def self.catalog_via_enmfp(filename, opts={})
41
+ return if file_too_big? filename
42
+
41
43
  md5 = opts[:md5] || MrEko.md5(filename)
42
- fingerprint_json = enmfp_data(filename, md5)
43
-
44
- if fingerprint_json.keys.include?('error')
45
- raise EnmfpError, "Errors returned in the ENMFP fingerprint data: #{fingerprint_json.error.inspect}"
46
- else
47
- begin
48
- log "Identifying with ENMFP code"
49
-
50
- identify_options = {}.tap do |opts|
51
- opts[:code] = fingerprint_json.raw_data
52
- opts[:artist] = fingerprint_json.metadata.artist
53
- opts[:title] = fingerprint_json.metadata.title
54
- opts[:release] = fingerprint_json.metadata.release
55
- opts[:bucket] = 'audio_summary'
56
- end
57
-
58
- profile = MrEko.nest.song.identify(identify_options)
59
-
60
- raise EnmfpError, "Nothing returned" if profile.songs.empty?
61
- profile = profile.songs.first
62
-
63
- # Get the extended audio data from the profile
64
- analysis = MrEko.nest.song.profile(:id => profile.id, :bucket => 'audio_summary').songs.first.audio_summary
65
- rescue Exception => e
66
- log %Q{Issues using ENMFP data "(#{e})" #{e.backtrace.join("\n")}}
67
- analysis, profile = get_datapoints_by_upload(filename)
68
- end
44
+ log "Identifying with ENMFP code #{filename}"
45
+
46
+ begin
47
+ fingerprint_json = enmfp_data(filename, md5)
48
+
49
+ 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
+ rescue EnmfpError => e
55
+ log %Q{Issues using ENMFP data "(#{e})" #{e.backtrace.join("\n")}}
56
+ analysis, profile = get_datapoints_by_upload(filename)
69
57
  end
70
58
 
71
59
  create do |song|
72
60
  song.filename = File.expand_path(filename)
73
61
  song.md5 = md5
74
- song.code = fingerprint_json.code
62
+ song.code = fingerprint_json ? fingerprint_json.code : nil
75
63
  song.tempo = analysis.tempo
76
64
  song.duration = analysis.duration
77
65
  song.fade_in = analysis.end_of_fade_in
@@ -84,10 +72,10 @@ class MrEko::Song < Sequel::Model
84
72
  song.bitrate = profile.bitrate
85
73
  song.title = profile.title
86
74
  song.artist = profile.artist || profile.artist_name
87
- song.album = profile.release
75
+ song.album = fingerprint_json ? fingerprint_json.metadata.release : profile.release
88
76
  song.danceability = profile.audio_summary? ? profile.audio_summary.danceability : analysis.danceability
89
77
  song.energy = profile.audio_summary? ? profile.audio_summary.energy : analysis.energy
90
- end
78
+ end if analysis && profile
91
79
  end
92
80
 
93
81
  # Parses the file's ID3 tags and converts and strange encoding.
@@ -110,10 +98,17 @@ class MrEko::Song < Sequel::Model
110
98
  return unless has_required_tags? tags
111
99
 
112
100
  md5 = opts[:md5] || MrEko.md5(filename)
113
- analysis = MrEko.nest.song.search(:artist => tags.artist,
114
- :title => tags.title,
115
- :bucket => 'audio_summary',
116
- :limit => 1).songs.first
101
+ begin
102
+ analysis = MrEko.nest.song.search(:artist => tags.artist,
103
+ :title => tags.title,
104
+ :bucket => 'audio_summary',
105
+ :limit => 1).songs.first
106
+ rescue => e
107
+ log "BAD TAGS? #{tags.artist} - #{tags.title} - #{filename}"
108
+ log e.message
109
+ log e.backtrace.join("\n")
110
+ return
111
+ end
117
112
 
118
113
  create do |song|
119
114
  song.filename = File.expand_path(filename)
@@ -155,13 +150,23 @@ class MrEko::Song < Sequel::Model
155
150
  # @return [Hash] data from the ENMFP process
156
151
  def self.enmfp_data(filename, md5)
157
152
  unless File.exists?(fp_location(md5))
158
- log 'Running ENMFP'
153
+ log 'Waiting for ENMFP binary...'
159
154
  `#{File.join(MrEko::HOME_DIR, 'ext', 'enmfp', MrEko.enmfp_binary)} "#{File.expand_path(filename)}" > #{fp_location(md5)}`
160
155
  end
161
156
 
162
- raw_json = File.read fp_location(md5)
163
- hash = Hashie::Mash.new(JSON.parse(raw_json).first)
164
- hash.raw_data = raw_json
157
+ begin
158
+ raw_json = File.read fp_location(md5)
159
+ hash = Hashie::Mash.new(JSON.parse(raw_json).first)
160
+ hash.raw_data = raw_json
161
+
162
+ if hash.keys.include?('error')
163
+ raise EnmfpError, "Errors returned in the ENMFP fingerprint data: #{hash.error.inspect}"
164
+ end
165
+
166
+ rescue JSON::ParserError => e
167
+ raise EnmfpError, e.message
168
+ end
169
+
165
170
  hash
166
171
  end
167
172
 
@@ -188,6 +193,31 @@ class MrEko::Song < Sequel::Model
188
193
  self.md5 ||= MrEko.md5(filename)
189
194
  end
190
195
 
196
+ # Is the song over a sane limit for uploading and ENMFP?
197
+ #
198
+ # @param [String] filename
199
+ # @return [Boolean]
200
+ def self.file_too_big?(file)
201
+ File.size(file)/1024/1024 > 50
202
+ end
203
+
204
+ # @param [Hash]
205
+ # @return [Hashie, EnmfpError] EN song API result
206
+ def self.identify_from_enmfp_data(json)
207
+ identify_options = {}.tap do |opts|
208
+ opts[:code] = json.raw_data
209
+ opts[:artist] = json.metadata.artist
210
+ opts[:title] = json.metadata.title
211
+ opts[:release] = json.metadata.release
212
+ opts[:bucket] = 'audio_summary'
213
+ end
214
+
215
+ profile = MrEko.nest.song.identify(identify_options)
216
+ raise EnmfpError, "Nothing returned from song/identify API call" if profile.songs.empty?
217
+
218
+ profile.songs.first
219
+ end
220
+
191
221
  # Return the file path of the EN fingerprint JSON file
192
222
  #
193
223
  # @param [String] MD5 hash of the file
@@ -102,7 +102,7 @@ class MrEko::TimedPlaylist
102
102
  direction = step_count > 0 ? :asc : :desc
103
103
  sorted_tempos = [attributes[:initial][:tempo], attributes[:final][:tempo]].sort
104
104
  tempo_range = Range.new(*sorted_tempos)
105
- all_songs = MrEko::Song.where(:tempo => tempo_range).order("tempo #{direction}".lit).all
105
+ all_songs = MrEko::Song.where({:tempo => tempo_range} & ~{:duration => nil}).order("tempo #{direction}".lit).all
106
106
 
107
107
  songs_to_examine_per_step = step_count > all_songs.size ? 1 : all_songs.size / step_count
108
108
 
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.2'
22
+ VERSION = '0.3.3'
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,8 +13,8 @@ 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.2'
17
- s.date = '2011-11-30'
16
+ s.version = '0.3.3'
17
+ s.date = '2011-12-02'
18
18
  s.rubyforge_project = 'mr_eko'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -60,6 +60,7 @@ Gem::Specification.new do |s|
60
60
  s.add_development_dependency('test-unit', "~> 2.1")
61
61
  s.add_development_dependency("ruby-debug")
62
62
  s.add_development_dependency("autotest")
63
+ s.add_development_dependency("pry")
63
64
 
64
65
  ## Leave this section as-is. It will be automatically generated from the
65
66
  ## contents of your Git repository via the gemspec task. DO NOT REMOVE
@@ -31,11 +31,6 @@ class PlaylistTest < Test::Unit::TestCase
31
31
  assert_equal @playlist_count + 1, MrEko::Playlist.count
32
32
  end
33
33
 
34
- should "filter out certain options before querying for songs" do
35
- unfiltered_options = {:name => "Rock You in Your Face mix #{rand(1000)}", :time_signature => 4}
36
- MrEko::Song.expects(:where).with(Not(has_key(:name))).once.returns(sequel_dataset_stub)
37
- assert_raise(MrEko::Playlist::NoSongsError){ MrEko::Playlist.create_from_options(unfiltered_options) }
38
- end
39
34
  end
40
35
 
41
36
  context 'output' do
@@ -96,7 +91,7 @@ class PlaylistTest < Test::Unit::TestCase
96
91
  end
97
92
 
98
93
  context "percentage values" do
99
- [:loudness, :energy, :danceability].each do |attribute|
94
+ [:energy, :danceability].each do |attribute|
100
95
  should "translate #{attribute} into decimal form" do
101
96
  transformed = MrEko::Playlist.prepare_options({attribute => 32})
102
97
  assert_equal( {attribute => 0.32}, transformed.last )
data/test/song_test.rb CHANGED
@@ -65,9 +65,16 @@ class SongTest < Test::Unit::TestCase
65
65
 
66
66
  context 'catalog_via_enmfp' do
67
67
 
68
- should 'raise an error if the ENMFP fingerprint contains errors' do
69
- MrEko::Song.stubs(:enmfp_data).returns(enmfp_data_stub('error' => 'BOOM'))
70
- assert_raise(MrEko::Song::EnmfpError){ MrEko::Song.catalog_via_enmfp(TEST_MP3) }
68
+ should 'try uploading if the ENMFP fingerprint contains errors' do
69
+ MrEko::Song.stubs(:enmfp_data).raises(MrEko::Song::EnmfpError)
70
+ MrEko::Song.expects(:get_datapoints_by_upload).once.with(TEST_MP3).returns([])
71
+ MrEko::Song.catalog_via_enmfp(TEST_MP3)
72
+ end
73
+
74
+ should 'return nil when the file is too big' do
75
+ MrEko::Song.stubs(:file_too_big?).returns(true)
76
+ MrEko.nest.song.expects(:identify).never
77
+ assert_nil MrEko::Song.catalog_via_enmfp(TEST_MP3)
71
78
  end
72
79
 
73
80
  should 'try to upload when no songs are returned from the Song#identify call' do
@@ -57,7 +57,7 @@ class TimedPlaylistTest < Test::Unit::TestCase
57
57
  assert !list.step_map.empty?
58
58
  end
59
59
 
60
- should 'increase the step length to 4.minutes if value is less that that' do
60
+ should 'increase the step length to 4.minutes if value is less than that' do
61
61
  list = MrEko::TimedPlaylist.new(:length => 300) do |pl|
62
62
  pl.initial(:tempo, 60)
63
63
  pl.final(:tempo, 80)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mr_eko
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 2
10
- version: 0.3.2
9
+ - 3
10
+ version: 0.3.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ed Hickey
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-11-30 00:00:00 Z
18
+ date: 2011-12-02 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: sequel
@@ -194,6 +194,20 @@ dependencies:
194
194
  version: "0"
195
195
  type: :development
196
196
  version_requirements: *id012
197
+ - !ruby/object:Gem::Dependency
198
+ name: pry
199
+ prerelease: false
200
+ requirement: &id013 !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ hash: 3
206
+ segments:
207
+ - 0
208
+ version: "0"
209
+ type: :development
210
+ version_requirements: *id013
197
211
  description: Catalogs music file data and exposes a playlist interface
198
212
  email: bassnode@gmail.com
199
213
  executables: