mr_eko 0.4.1 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -18,9 +18,11 @@ Example:
18
18
 
19
19
  Requirements:
20
20
  -------------
21
- * [ffmpeg](http://www.ffmpeg.org/download.htmlr) in your path.
21
+ * [ffmpeg](http://www.ffmpeg.org/download.html) in your path.
22
22
  * an [Echonest API token](http://developer.echonest.com/) which goes here: ~/.mreko/echonest_api.key
23
23
 
24
24
  Notes:
25
25
  ------
26
- MrEko will make an audio fingerprint of a music file and attempt to use it to identify the song. This is faster but not always successful (due to Echonest limitations) so some songs must be uploaded to Echonest. This means that scanning a large and/or somewhat eclectic library **could take a good while**.
26
+ MrEko will make an audio fingerprint of a music file and attempt to use it (along with ID3 tags) to retrieve data from Echonest.
27
+ This can be fast but not always successful due to Echonest catalog limitations, so some songs must be uploaded to Echonest.
28
+ This means that scanning a large and/or somewhat eclectic library **could take a good while**.
data/lib/mr_eko/song.rb CHANGED
@@ -36,9 +36,12 @@ class MrEko::Song < Sequel::Model
36
36
  # @option opts [String] :md5 pre-calculated MD5 of file
37
37
  # @return [MrEko::Song, NilClass] the created Song
38
38
  def self.catalog_via_enmfp(filename, opts={})
39
+ md5 = opts[:md5] || MrEko.md5(filename)
40
+ existing = where(:md5 => md5).first
41
+ return existing unless existing.nil?
42
+
39
43
  return if file_too_big? filename
40
44
 
41
- md5 = opts[:md5] || MrEko.md5(filename)
42
45
  log "Identifying with ENMFP code #{filename}"
43
46
 
44
47
  begin
@@ -55,7 +58,7 @@ class MrEko::Song < Sequel::Model
55
58
  song.code = fingerprint_json ? fingerprint_json.code : nil
56
59
  song.tempo = profile.audio_summary.tempo
57
60
  song.duration = profile.audio_summary.duration
58
- song.key = profile.audio_summary.key
61
+ song.key = profile.audio_summary[:key]
59
62
  song.mode = profile.audio_summary.mode
60
63
  song.loudness = profile.audio_summary.loudness
61
64
  song.time_signature = profile.audio_summary.time_signature
@@ -16,12 +16,16 @@ class MrEko::TimedPlaylist < MrEko::Playlist
16
16
  super
17
17
  end
18
18
 
19
- # Have to add songs after save due to the Playlist needing to have a primary
20
- # key (generated at time of save). Lame.
21
- def after_save
19
+ # Run these essential steps before saving, so that any fatal exception halt
20
+ # the creation of the playlist.
21
+ def before_save
22
22
  prepare_attributes
23
23
  find_song_groups!
24
+ end
24
25
 
26
+ # Have to add songs after save due to the Playlist needing to have a primary
27
+ # key (generated at time of save). Lame.
28
+ def after_save
25
29
  songs = @song_groups.sort_by{ |group| cost_of group }.first
26
30
 
27
31
  # Sort em
@@ -43,8 +47,8 @@ class MrEko::TimedPlaylist < MrEko::Playlist
43
47
 
44
48
  case facet
45
49
  when :danceability, :energy
46
- @initial = (intial * 10).round
47
- @final = (final * 10).round
50
+ @initial = (initial / 100.0)
51
+ @final = (final / 100.0)
48
52
  when :mode
49
53
  @initial = MrEko.mode_lookup(initial)
50
54
  @final = MrEko.mode_lookup(final)
@@ -55,30 +59,34 @@ class MrEko::TimedPlaylist < MrEko::Playlist
55
59
  end
56
60
 
57
61
  def find_song_groups!(iterations=50)
58
-
59
62
  sorted = [initial, final].sort
60
63
  # Get every song in the required range
61
64
  all_songs = MrEko::Song.where({facet => Range.new(*sorted)} & ~{:duration => nil}).all
62
- raise MrEko::NoSongsError, "no songs with those '#{facet}' parameters" if all_songs.blank?
65
+
66
+ if all_songs.blank?
67
+ raise MrEko::NoSongsError, "no songs with those '#{facet}' parameters"
68
+ elsif all_songs.length == 1 # Might want to warn/raise here?
69
+ @song_groups = [ all_songs ]
70
+ return
71
+ end
63
72
 
64
73
  # Populate a number of potential playlists
65
74
  @song_groups = Array.new(iterations) do
66
75
  seconds_used = 0
67
76
  group = []
68
- until seconds_used >= @length do
69
- random_index = rand(all_songs.length - 1)
70
- song = all_songs.delete_at(random_index)
77
+ available_songs = all_songs.dup
78
+
79
+ until seconds_used >= @length or available_songs.empty? do
80
+ random_index = rand(available_songs.length - 1)
81
+ song = available_songs.delete_at(random_index)
71
82
  seconds_used += song.duration
72
83
  group << song
73
84
  end
74
85
 
75
86
  group
76
87
  end
77
-
78
88
  end
79
89
 
80
- private
81
-
82
90
  # How bad is the passed group of songs with respect to the TimedPlaylist's
83
91
  # facet and length contraints?
84
92
  #
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.4.1'
22
+ VERSION = '0.4.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.4.1'
17
- s.date = '2012-01-01'
16
+ s.version = '0.4.3'
17
+ s.date = '2012-02-12'
18
18
  s.rubyforge_project = 'mr_eko'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -10,30 +10,9 @@ class TimedPlaylistTest < Test::Unit::TestCase
10
10
 
11
11
  end
12
12
 
13
- context 'initial' do
14
-
15
- should 'add the attribute to the list of initial attributes' do
16
- MrEko::TimedPlaylist.new(:length => 600, :name => 'sad shit', :facet => :mode) do |pl|
17
- assert pl.initial = :minor
18
- assert_equal :minor, pl.initial
19
- end
20
- end
21
- end
22
-
23
- context 'final' do
24
-
25
- should 'add the attribute to the list of final attributes' do
26
- MrEko::TimedPlaylist.new(:length => 200, :name => 'Rock', :facet => :tempo) do |pl|
27
- assert pl.final = 120
28
- assert_equal 120, pl.final
29
- end
30
- end
31
- end
32
-
33
13
  context 'save' do
34
14
 
35
15
  should 'raise an exception when there are no songs for the parameters' do
36
-
37
16
  # No moar songs!
38
17
  MrEko::Song.delete
39
18
 
@@ -45,14 +24,84 @@ class TimedPlaylistTest < Test::Unit::TestCase
45
24
  assert_raises(MrEko::NoSongsError){ list.save }
46
25
  end
47
26
 
48
- context 'validation' do
49
- should "raise an exception when initial and final attribute keys don't match" do
50
- pl = MrEko::TimedPlaylist.new(:length => 1000, :facet => :tempo) do |pl|
51
- assert pl.initial = 66
27
+ context 'facet range' do
28
+
29
+ should 'add the attribute to the list of initial attributes' do
30
+ list = MrEko::TimedPlaylist.new(:length => 600, :name => 'sad shit', :facet => :mode)
31
+ list.initial = :minor
32
+
33
+ assert_equal :minor, list.initial
34
+ end
35
+
36
+ should 'add the attribute to the list of final attributes' do
37
+ list = MrEko::TimedPlaylist.new(:length => 200, :name => 'Rock', :facet => :tempo)
38
+ list.final = 120
39
+
40
+ assert_equal 120, list.final
41
+ end
42
+
43
+ should 'raise an error if only one facet boundary is set' do
44
+ list = MrEko::TimedPlaylist.new(:length => 3600, :facet => :mode)
45
+ list.initial = :minor
46
+
47
+ assert_raise(MrEko::InvalidAttributes){ list.save }
48
+ end
49
+
50
+ context 'value translation' do
51
+ [:energy, :danceability].each do |attr|
52
+ should "turn #{attr} into a percentage" do
53
+ song = create_song(attr => 0.76)
54
+ list = MrEko::TimedPlaylist.new(:length => 3600, :facet => attr)
55
+ list.initial = 60
56
+ list.final = 100
57
+ assert list.save
58
+
59
+ assert_equal 0.60, list.initial
60
+ assert_equal 1.0, list.final
61
+ end
52
62
  end
53
63
 
54
- assert_raise(MrEko::InvalidAttributes){ pl.save }
64
+ should "translate mode into a number" do
65
+ song = create_song(:mode => 0)
66
+ list = MrEko::TimedPlaylist.new(:length => 3600, :facet => :mode)
67
+ list.initial = 'minor'
68
+ list.final = 'major'
69
+ assert list.save
70
+
71
+ assert_equal 0, list.initial
72
+ assert_equal 1, list.final
73
+ end
74
+
75
+ should "translate key into a number" do
76
+ song = create_song(:key => 5)
77
+ list = MrEko::TimedPlaylist.new(:length => 3600, :facet => :key)
78
+ list.initial = 'F'
79
+ list.final = 'A'
80
+ assert list.save
81
+
82
+ assert_equal 5, list.initial
83
+ assert_equal 9, list.final
84
+ end
55
85
  end
56
86
  end
87
+
88
+ # pending, until next time...
89
+ #context 'the songs' do
90
+ # setup do
91
+ # MrEko::Song.delete
92
+ # end
93
+
94
+ # should "populate the list of songs" do
95
+ # 60.times do |i|
96
+ # create_song(:tempo => 50 + i , :duration => 1.minutes, :title => "Song #{i}")
97
+ # end
98
+
99
+ # list = MrEko::TimedPlaylist.new(:length => 30.minutes, :facet => :tempo)
100
+ # list.initial = 50
101
+ # list.final = 100
102
+ # list.save
103
+ # end
104
+ #end
57
105
  end
106
+
58
107
  end
metadata CHANGED
@@ -1,236 +1,178 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: mr_eko
3
- version: !ruby/object:Gem::Version
4
- hash: 13
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.3
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 4
9
- - 1
10
- version: 0.4.1
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Ed Hickey
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-01-01 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-02-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: sequel
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2160313480 !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - "="
27
- - !ruby/object:Gem::Version
28
- hash: 25
29
- segments:
30
- - 3
31
- - 15
32
- version: "3.15"
18
+ requirements:
19
+ - - =
20
+ - !ruby/object:Gem::Version
21
+ version: '3.15'
33
22
  type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: sqlite3-ruby
37
23
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *2160313480
25
+ - !ruby/object:Gem::Dependency
26
+ name: sqlite3-ruby
27
+ requirement: &2160312580 !ruby/object:Gem::Requirement
39
28
  none: false
40
- requirements:
29
+ requirements:
41
30
  - - ~>
42
- - !ruby/object:Gem::Version
43
- hash: 9
44
- segments:
45
- - 1
46
- - 3
47
- version: "1.3"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.3'
48
33
  type: :runtime
49
- version_requirements: *id002
50
- - !ruby/object:Gem::Dependency
51
- name: hashie
52
34
  prerelease: false
53
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *2160312580
36
+ - !ruby/object:Gem::Dependency
37
+ name: hashie
38
+ requirement: &2160327920 !ruby/object:Gem::Requirement
54
39
  none: false
55
- requirements:
56
- - - ">="
57
- - !ruby/object:Gem::Version
58
- hash: 3
59
- segments:
60
- - 0
61
- version: "0"
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
62
44
  type: :runtime
63
- version_requirements: *id003
64
- - !ruby/object:Gem::Dependency
65
- name: httpclient
66
45
  prerelease: false
67
- requirement: &id004 !ruby/object:Gem::Requirement
46
+ version_requirements: *2160327920
47
+ - !ruby/object:Gem::Dependency
48
+ name: httpclient
49
+ requirement: &2160327060 !ruby/object:Gem::Requirement
68
50
  none: false
69
- requirements:
51
+ requirements:
70
52
  - - ~>
71
- - !ruby/object:Gem::Version
72
- hash: 1
73
- segments:
74
- - 2
75
- - 1
76
- version: "2.1"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.1'
77
55
  type: :runtime
78
- version_requirements: *id004
79
- - !ruby/object:Gem::Dependency
80
- name: bassnode-ruby-echonest
81
56
  prerelease: false
82
- requirement: &id005 !ruby/object:Gem::Requirement
57
+ version_requirements: *2160327060
58
+ - !ruby/object:Gem::Dependency
59
+ name: bassnode-ruby-echonest
60
+ requirement: &2160326540 !ruby/object:Gem::Requirement
83
61
  none: false
84
- requirements:
85
- - - ">="
86
- - !ruby/object:Gem::Version
87
- hash: 3
88
- segments:
89
- - 0
90
- version: "0"
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
91
66
  type: :runtime
92
- version_requirements: *id005
93
- - !ruby/object:Gem::Dependency
94
- name: json
95
67
  prerelease: false
96
- requirement: &id006 !ruby/object:Gem::Requirement
68
+ version_requirements: *2160326540
69
+ - !ruby/object:Gem::Dependency
70
+ name: json
71
+ requirement: &2160325800 !ruby/object:Gem::Requirement
97
72
  none: false
98
- requirements:
99
- - - "="
100
- - !ruby/object:Gem::Version
101
- hash: 11
102
- segments:
103
- - 1
104
- - 4
105
- - 6
73
+ requirements:
74
+ - - =
75
+ - !ruby/object:Gem::Version
106
76
  version: 1.4.6
107
77
  type: :runtime
108
- version_requirements: *id006
109
- - !ruby/object:Gem::Dependency
110
- name: id3lib-ruby
111
78
  prerelease: false
112
- requirement: &id007 !ruby/object:Gem::Requirement
79
+ version_requirements: *2160325800
80
+ - !ruby/object:Gem::Dependency
81
+ name: id3lib-ruby
82
+ requirement: &2160325140 !ruby/object:Gem::Requirement
113
83
  none: false
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- hash: 3
118
- segments:
119
- - 0
120
- version: "0"
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
121
88
  type: :runtime
122
- version_requirements: *id007
123
- - !ruby/object:Gem::Dependency
124
- name: mocha
125
89
  prerelease: false
126
- requirement: &id008 !ruby/object:Gem::Requirement
90
+ version_requirements: *2160325140
91
+ - !ruby/object:Gem::Dependency
92
+ name: mocha
93
+ requirement: &2160324320 !ruby/object:Gem::Requirement
127
94
  none: false
128
- requirements:
129
- - - "="
130
- - !ruby/object:Gem::Version
131
- hash: 43
132
- segments:
133
- - 0
134
- - 9
135
- - 8
95
+ requirements:
96
+ - - =
97
+ - !ruby/object:Gem::Version
136
98
  version: 0.9.8
137
99
  type: :development
138
- version_requirements: *id008
139
- - !ruby/object:Gem::Dependency
140
- name: shoulda
141
100
  prerelease: false
142
- requirement: &id009 !ruby/object:Gem::Requirement
101
+ version_requirements: *2160324320
102
+ - !ruby/object:Gem::Dependency
103
+ name: shoulda
104
+ requirement: &2160323560 !ruby/object:Gem::Requirement
143
105
  none: false
144
- requirements:
106
+ requirements:
145
107
  - - ~>
146
- - !ruby/object:Gem::Version
147
- hash: 21
148
- segments:
149
- - 2
150
- - 11
151
- version: "2.11"
108
+ - !ruby/object:Gem::Version
109
+ version: '2.11'
152
110
  type: :development
153
- version_requirements: *id009
154
- - !ruby/object:Gem::Dependency
155
- name: test-unit
156
111
  prerelease: false
157
- requirement: &id010 !ruby/object:Gem::Requirement
112
+ version_requirements: *2160323560
113
+ - !ruby/object:Gem::Dependency
114
+ name: test-unit
115
+ requirement: &2160322960 !ruby/object:Gem::Requirement
158
116
  none: false
159
- requirements:
117
+ requirements:
160
118
  - - ~>
161
- - !ruby/object:Gem::Version
162
- hash: 1
163
- segments:
164
- - 2
165
- - 1
166
- version: "2.1"
119
+ - !ruby/object:Gem::Version
120
+ version: '2.1'
167
121
  type: :development
168
- version_requirements: *id010
169
- - !ruby/object:Gem::Dependency
170
- name: ruby-debug
171
122
  prerelease: false
172
- requirement: &id011 !ruby/object:Gem::Requirement
123
+ version_requirements: *2160322960
124
+ - !ruby/object:Gem::Dependency
125
+ name: ruby-debug
126
+ requirement: &2160322580 !ruby/object:Gem::Requirement
173
127
  none: false
174
- requirements:
175
- - - ">="
176
- - !ruby/object:Gem::Version
177
- hash: 3
178
- segments:
179
- - 0
180
- version: "0"
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
181
132
  type: :development
182
- version_requirements: *id011
183
- - !ruby/object:Gem::Dependency
184
- name: autotest
185
133
  prerelease: false
186
- requirement: &id012 !ruby/object:Gem::Requirement
134
+ version_requirements: *2160322580
135
+ - !ruby/object:Gem::Dependency
136
+ name: autotest
137
+ requirement: &2160322120 !ruby/object:Gem::Requirement
187
138
  none: false
188
- requirements:
189
- - - ">="
190
- - !ruby/object:Gem::Version
191
- hash: 3
192
- segments:
193
- - 0
194
- version: "0"
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
195
143
  type: :development
196
- version_requirements: *id012
197
- - !ruby/object:Gem::Dependency
198
- name: pry
199
144
  prerelease: false
200
- requirement: &id013 !ruby/object:Gem::Requirement
145
+ version_requirements: *2160322120
146
+ - !ruby/object:Gem::Dependency
147
+ name: pry
148
+ requirement: &2160321660 !ruby/object:Gem::Requirement
201
149
  none: false
202
- requirements:
203
- - - ">="
204
- - !ruby/object:Gem::Version
205
- hash: 3
206
- segments:
207
- - 0
208
- version: "0"
150
+ requirements:
151
+ - - ! '>='
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
209
154
  type: :development
210
- version_requirements: *id013
211
- - !ruby/object:Gem::Dependency
212
- name: yard
213
155
  prerelease: false
214
- requirement: &id014 !ruby/object:Gem::Requirement
156
+ version_requirements: *2160321660
157
+ - !ruby/object:Gem::Dependency
158
+ name: yard
159
+ requirement: &2160321240 !ruby/object:Gem::Requirement
215
160
  none: false
216
- requirements:
217
- - - ">="
218
- - !ruby/object:Gem::Version
219
- hash: 3
220
- segments:
221
- - 0
222
- version: "0"
161
+ requirements:
162
+ - - ! '>='
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
223
165
  type: :development
224
- version_requirements: *id014
166
+ prerelease: false
167
+ version_requirements: *2160321240
225
168
  description: Catalogs music file data and exposes a playlist interface
226
169
  email: bassnode@gmail.com
227
- executables:
170
+ executables:
228
171
  - mreko
229
172
  extensions: []
230
-
231
- extra_rdoc_files:
173
+ extra_rdoc_files:
232
174
  - README.md
233
- files:
175
+ files:
234
176
  - Gemfile
235
177
  - README.md
236
178
  - Rakefile
@@ -272,37 +214,28 @@ files:
272
214
  - test/timed_playlist_test.rb
273
215
  homepage: http://github.com/bassnode/mreko
274
216
  licenses: []
275
-
276
217
  post_install_message:
277
- rdoc_options:
218
+ rdoc_options:
278
219
  - --charset=UTF-8
279
- require_paths:
220
+ require_paths:
280
221
  - lib
281
- required_ruby_version: !ruby/object:Gem::Requirement
222
+ required_ruby_version: !ruby/object:Gem::Requirement
282
223
  none: false
283
- requirements:
284
- - - ">="
285
- - !ruby/object:Gem::Version
286
- hash: 3
287
- segments:
288
- - 0
289
- version: "0"
290
- required_rubygems_version: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - ! '>='
226
+ - !ruby/object:Gem::Version
227
+ version: '0'
228
+ required_rubygems_version: !ruby/object:Gem::Requirement
291
229
  none: false
292
- requirements:
293
- - - ">="
294
- - !ruby/object:Gem::Version
295
- hash: 3
296
- segments:
297
- - 0
298
- version: "0"
230
+ requirements:
231
+ - - ! '>='
232
+ - !ruby/object:Gem::Version
233
+ version: '0'
299
234
  requirements: []
300
-
301
235
  rubyforge_project: mr_eko
302
- rubygems_version: 1.8.11
236
+ rubygems_version: 1.8.10
303
237
  signing_key:
304
238
  specification_version: 2
305
239
  summary: Catalogs music file data and exposes a playlist interface
306
240
  test_files: []
307
-
308
241
  has_rdoc: