mr_eko 0.2.4.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/mr_eko/song.rb CHANGED
@@ -1,109 +1,180 @@
1
+ # TODO: Refactor this so everything's not in class methods
1
2
  class MrEko::Song < Sequel::Model
2
3
  include MrEko::Core
3
4
  plugin :validation_helpers
4
5
  many_to_many :playlists
5
6
 
6
- # IDEA: This probably won't work since it's creating a new file,
7
- # but could try uploading a sample of the song (faster).
8
- # ffmpeg -y -i mogwai.mp3 -ar 22050 -ac 1 -ss 30 -t 30 output.mp3
9
- # or
10
- # sox mogwai.mp3 output.mp3 30 60
7
+ REQUIRED_ID3_TAGS = [:artist, :title]
11
8
 
12
- # Using the Echonest Musical Fingerprint lib in the hopes
13
- # of sidestepping the mp3 upload process.
14
- def self.enmfp_data(filename, md5)
15
- unless File.exists?(fp_location(md5))
16
- log 'Running ENMFP'
17
- `#{File.join(MrEko::HOME_DIR, 'ext', 'enmfp', enmfp_binary)} "#{File.expand_path(filename)}" > #{fp_location(md5)}`
18
- end
9
+ class EnmfpError < Exception; end
19
10
 
20
- File.read fp_location(md5)
21
- end
11
+ # A wrapper which gets called by the bin file.
12
+ # By default will try to extract the needed song info from the ID3 tags and
13
+ # if fails, will analyze via ENMFP/upload.
14
+ #
15
+ # @param [String] file path of the MP3
16
+ # @param [Hash] options hash
17
+ # @option options [Boolean] :tags_only If passed, skip ENMFP
18
+ # @return [MrEko::Song]
19
+ def self.create_from_file!(filename, opts={})
20
+ md5 = MrEko.md5(filename)
21
+ existing = where(:md5 => md5).first
22
+ return existing unless existing.nil?
23
+
24
+ if song = catalog_via_tags(filename, :md5 => md5)
25
+ song
26
+ elsif !opts[:tags_only]
27
+ catalog_via_enmfp(filename, :md5 => md5)
28
+ end
22
29
 
23
- # Return the file path of the EN fingerprint JSON file
24
- def self.fp_location(md5)
25
- File.expand_path File.join(MrEko::FINGERPRINTS_DIR, "#{md5}.json")
26
30
  end
27
31
 
28
- # Use the platform-specific binary.
29
- def self.enmfp_binary
30
- case RUBY_PLATFORM
31
- when /darwin/
32
- 'codegen.Darwin'
33
- when /686/
34
- 'codegen.Linux-i686'
35
- when /x86/
36
- 'codegen.Linux-x86_64'
32
+ # Run local analysis (ENMFP) on the passed file, send that identifier code
33
+ # to EN and store the returned details in our DB.
34
+ # If the local analysis fails, upload the MP3 to EN for server-side analysis.
35
+ #
36
+ # @param [String] location of the audio file
37
+ # @param [Hash] opts
38
+ # @option opts [String] :md5 pre-calculated MD5 of file
39
+ # @return [MrEko::Song] the created Song
40
+ def self.catalog_via_enmfp(filename, opts={})
41
+ 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}"
37
46
  else
38
- 'codegen.windows.exe'
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
69
+ end
70
+
71
+ create do |song|
72
+ song.filename = File.expand_path(filename)
73
+ song.md5 = md5
74
+ song.code = fingerprint_json.code
75
+ song.tempo = analysis.tempo
76
+ song.duration = analysis.duration
77
+ song.fade_in = analysis.end_of_fade_in
78
+ song.fade_out = analysis.start_of_fade_out
79
+ song.key = analysis.key
80
+ song.mode = analysis.mode
81
+ song.loudness = analysis.loudness
82
+ song.time_signature = analysis.time_signature
83
+ song.echonest_id = profile.id
84
+ song.bitrate = profile.bitrate
85
+ song.title = profile.title
86
+ song.artist = profile.artist || profile.artist_name
87
+ song.album = profile.release
88
+ song.danceability = profile.audio_summary? ? profile.audio_summary.danceability : analysis.danceability
89
+ song.energy = profile.audio_summary? ? profile.audio_summary.energy : analysis.energy
39
90
  end
40
91
  end
41
92
 
42
- # Returns the analysis and profile data from Echonest for the given track.
43
- def self.get_datapoints_by_filename(filename)
44
- analysis = MrEko.nest.track.analysis(filename)
45
- profile = MrEko.nest.track.profile(:md5 => MrEko.md5(filename)).body.track
93
+ # Parses the file's ID3 tags and converts and strange encoding.
94
+ #
95
+ # @param [String] The file path
96
+ # @return [ID3Lib::Tag]
97
+ def self.parse_id3_tags(filename)
98
+ log "Parsing ID3 tags"
46
99
 
47
- return [analysis, profile]
100
+ clean_tags ID3Lib::Tag.new(filename, ID3Lib::V_ALL)
48
101
  end
49
102
 
50
- # TODO: Cleanup - This method is prety ugly now.
51
- def self.create_from_file!(filename)
52
- md5 = MrEko.md5(filename)
53
- existing = where(:md5 => md5).first
54
- return existing unless existing.nil?
55
103
 
56
- fingerprint_data = enmfp_data(filename, md5)
57
- fingerprint_json_data = Hashie::Mash.new(JSON.parse(fingerprint_data).first)
104
+ # Uses ID3 tags to query Echonest and then store the resultant data.
105
+ #
106
+ # @see Song.catalog_via_enmfp for options
107
+ # @return [MrEko::Song]
108
+ def self.catalog_via_tags(filename, opts={})
109
+ tags = parse_id3_tags(filename)
110
+ return unless has_required_tags? tags
111
+
112
+ 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
117
+
118
+ create do |song|
119
+ song.filename = File.expand_path(filename)
120
+ song.md5 = md5
121
+ song.tempo = analysis.audio_summary.tempo
122
+ song.duration = analysis.audio_summary.duration
123
+ song.key = analysis.audio_summary.key
124
+ song.mode = analysis.audio_summary.mode
125
+ song.loudness = analysis.audio_summary.loudness
126
+ song.time_signature = analysis.audio_summary.time_signature
127
+ song.echonest_id = analysis.id
128
+ song.title = tags.title
129
+ song.artist = tags.artist
130
+ song.danceability = analysis.audio_summary.danceability
131
+ song.energy = analysis.audio_summary.energy
132
+ # XXX: Won't have these from tags - worth getting from EN?
133
+ # song.code = fingerprint_json.code
134
+ # song.album = album
135
+ # song.fade_in = analysis.end_of_fade_in
136
+ # song.fade_out = analysis.start_of_fade_out
137
+ # XXX: ID3Lib doesn't return these - worth parsing?
138
+ # song.bitrate = profile.bitrate
139
+ end if analysis
140
+ end
58
141
 
59
- if fingerprint_json_data.keys.include?('error')
60
- analysis, profile = get_datapoints_by_filename(filename)
61
- else
62
- log "Identifying with ENMFP code"
63
- identify_options = {:code => fingerprint_data}
64
- identify_options[:artist] = fingerprint_json_data.metadata.artist if fingerprint_json_data.metadata.artist
65
- identify_options[:title] = fingerprint_json_data.metadata.title if fingerprint_json_data.metadata.title
66
- identify_options[:release] = fingerprint_json_data.metadata.release if fingerprint_json_data.metadata.release
67
- profile = MrEko.nest.song.identify(identify_options)
68
-
69
- if profile.songs.empty?
70
- # ENMFP wasn't recognized, so upload.
71
- log "ENMP returned nothing, uploading"
72
- analysis, profile = get_datapoints_by_filename(filename)
73
- else
74
- begin
75
- profile = profile.songs.first
76
- analysis = MrEko.nest.song.profile(:id => profile.id, :bucket => 'audio_summary').songs.first.audio_summary
77
- rescue Exception => e
78
- log "Issues using ENMP data, uploading \"(#{e})\""
79
- analysis, profile = get_datapoints_by_filename(filename)
80
- end
81
- end
142
+ def self.has_required_tags?(tags)
143
+ found = REQUIRED_ID3_TAGS.inject([]) do |present, meth|
144
+ present << tags.send(meth)
145
+ end
146
+
147
+ found.compact.size == REQUIRED_ID3_TAGS.size ? true : false
148
+ end
149
+
150
+ # Using the Echonest Musical Fingerprint lib in the hopes
151
+ # of sidestepping the mp3 upload process.
152
+ #
153
+ # @param [String] file path of the MP3
154
+ # @param [String] MD5 hash of the file
155
+ # @return [Hash] data from the ENMFP process
156
+ def self.enmfp_data(filename, md5)
157
+ unless File.exists?(fp_location(md5))
158
+ log 'Running ENMFP'
159
+ `#{File.join(MrEko::HOME_DIR, 'ext', 'enmfp', MrEko.enmfp_binary)} "#{File.expand_path(filename)}" > #{fp_location(md5)}`
82
160
  end
83
161
 
84
- # TODO: add ruby-mp3info as fallback for parsing ID3 tags
85
- # since Echonest seems a bit flaky in that dept.
86
- song = new()
87
- song.filename = File.expand_path(filename)
88
- song.md5 = md5
89
- song.code = fingerprint_json_data.code
90
- song.tempo = analysis.tempo
91
- song.duration = analysis.duration
92
- song.fade_in = analysis.end_of_fade_in
93
- song.fade_out = analysis.start_of_fade_out
94
- song.key = analysis.key
95
- song.mode = analysis.mode
96
- song.loudness = analysis.loudness
97
- song.time_signature = analysis.time_signature
98
- song.echonest_id = profile.id
99
- song.bitrate = profile.bitrate
100
- song.title = profile.title
101
- song.artist = profile.artist || profile.artist_name
102
- song.album = profile.release
103
- song.danceability = profile.audio_summary? ? profile.audio_summary.danceability : analysis.danceability
104
- song.energy = profile.audio_summary? ? profile.audio_summary.energy : analysis.energy
105
-
106
- song.save
162
+ raw_json = File.read fp_location(md5)
163
+ hash = Hashie::Mash.new(JSON.parse(raw_json).first)
164
+ hash.raw_data = raw_json
165
+ hash
166
+ end
167
+
168
+ # Returns the analysis and profile data from Echonest for the given track.
169
+ #
170
+ # @param [String] file path of the MP3
171
+ # @return [Array] Analysis and profile data from EN
172
+ def self.get_datapoints_by_upload(filename)
173
+ log "Uploading data to EN for analysis"
174
+ analysis = MrEko.nest.track.analysis(filename)
175
+ profile = MrEko.nest.track.profile(:md5 => MrEko.md5(filename), :bucket => 'audio_summary').body.track
176
+
177
+ return [analysis, profile]
107
178
  end
108
179
 
109
180
  def validate
@@ -117,6 +188,31 @@ class MrEko::Song < Sequel::Model
117
188
  self.md5 ||= MrEko.md5(filename)
118
189
  end
119
190
 
191
+ # Return the file path of the EN fingerprint JSON file
192
+ #
193
+ # @param [String] MD5 hash of the file
194
+ # @return [String] full path of file with passed MP5
195
+ def self.fp_location(md5)
196
+ File.expand_path File.join(MrEko::FINGERPRINTS_DIR, "#{md5}.json")
197
+ end
198
+
199
+ # @param [ID3Lib::Tag]
200
+ # @return [ID3Lib::Tag]
201
+ def self.clean_tags(tags)
202
+ ic = Iconv.new("utf-8", "ucs-2")
203
+
204
+ REQUIRED_ID3_TAGS.each do |rt|
205
+ decoded = begin
206
+ ic.iconv(tags.send(rt))
207
+ rescue Iconv::InvalidCharacter
208
+ tags.send(rt)
209
+ end
210
+ decoded = nil if decoded.blank?
211
+ tags.send("#{rt}=", decoded)
212
+ end
213
+
214
+ tags
215
+ end
120
216
  end
121
217
 
122
218
  MrEko::Song.plugin :timestamps
@@ -0,0 +1,149 @@
1
+ class MrEko::TimedPlaylist
2
+
3
+ #
4
+ attr_reader :songs
5
+
6
+ # The number of seconds the playlist should be.
7
+ attr_reader :length
8
+
9
+ attr_reader :name
10
+
11
+ # The hash which holds all the controlling parameters for the Playlist.
12
+ attr_reader :attributes
13
+
14
+ # Hash keyed by the attribute w/ a value of the number of steps to reach the
15
+ # final setting.
16
+ attr_reader :step_map
17
+
18
+ class InvalidAttributes < Exception; end
19
+
20
+
21
+ def initialize(opts={})
22
+ @attributes = Hash.new{ |hsh, key| hsh[key] = {} }
23
+ @step_map = Hash.new
24
+ @songs = []
25
+
26
+ handle_opts(opts)
27
+
28
+ yield self if block_given?
29
+
30
+ end
31
+
32
+ def save
33
+ validate_attributes
34
+ determine_steps
35
+ find_songs
36
+
37
+ self
38
+ end
39
+
40
+ def initial(opt, value)
41
+ add_attribute(:initial, opt, value)
42
+ end
43
+
44
+ def final(opt, value)
45
+ add_attribute(:final, opt, value)
46
+ end
47
+
48
+ def static(opt, value)
49
+ add_attribute(:static, opt, value)
50
+ end
51
+
52
+
53
+ private
54
+
55
+ def handle_opts(opts)
56
+ @length = opts.delete(:length)
57
+ @name = opts.delete(:name)
58
+ end
59
+
60
+ def add_attribute(att_type, opt, value)
61
+ attributes[att_type][opt] = value
62
+ end
63
+
64
+ def validate_attributes
65
+ init_atts = attributes[:initial]
66
+ final_atts = attributes[:final]
67
+
68
+ unless init_atts.keys.map(&:to_s).sort == final_atts.keys.map(&:to_s).sort
69
+ raise InvalidAttributes, "You must provide values for both the initial and final settings, not just one."
70
+ end
71
+ end
72
+
73
+ def determine_steps
74
+
75
+ attributes[:initial].each_pair do |attr, val|
76
+
77
+ denominator = case attr
78
+ when :tempo, :loudness
79
+ attributes[:final][attr] - attributes[:initial][attr]
80
+ when :danceability, :energy
81
+ ( ( attributes[:final][attr] - attributes[:initial][attr] ) * 10 ).round
82
+ when :mode
83
+ 2
84
+ when :key
85
+ MrEko.key_lookup(attributes[:final][attr]) - MrEko.key_lookup(attributes[:initial][attr])
86
+ end
87
+
88
+ step_length = @length.to_f / denominator
89
+ step_length = 4.minutes if step_length.in_minutes < 4
90
+
91
+ step_map[attr] = [denominator, step_length.round]
92
+ end
93
+
94
+ step_map
95
+ end
96
+
97
+ # XXX Just sketching this part out at the moment...
98
+ # needs tests (and complete logic!)
99
+ def find_songs
100
+ step_count, step_length = step_map[:tempo]
101
+ return unless step_count && step_length
102
+ direction = step_count > 0 ? :asc : :desc
103
+ sorted_tempos = [attributes[:initial][:tempo], attributes[:final][:tempo]].sort
104
+ tempo_range = Range.new(*sorted_tempos)
105
+ all_songs = MrEko::Song.where(:tempo => tempo_range).order("tempo #{direction}".lit).all
106
+
107
+ songs_to_examine_per_step = step_count > all_songs.size ? 1 : all_songs.size / step_count
108
+
109
+ overall_seconds_used = 0
110
+ all_songs.each_slice(songs_to_examine_per_step).each do |songs|
111
+ break if overall_seconds_used >= @length
112
+
113
+ song_length_proximity = 0
114
+ length_map = songs.inject({}) do |hsh, song|
115
+ song_length_proximity = (song.duration - step_length).abs
116
+ hsh[song_length_proximity] = song
117
+ hsh
118
+ end
119
+
120
+ step_seconds_used = 0
121
+ length_map.sort_by{ |key, song| key }.each do |length, song|
122
+ @songs << song
123
+ step_seconds_used += song.duration
124
+ overall_seconds_used += song.duration
125
+ break if step_seconds_used >= step_length
126
+ end
127
+
128
+ end
129
+ # Might need to make a cluster map here instead of just choosing enough
130
+ # songs to fulfill the step_length. This is because the over
131
+ # Playlist#length can be fulfilled even before we reach the target/final
132
+ # target. I think a better rule would be to pluck a song having the
133
+ # initial and final values and then try to evenly spread out the remaining
134
+ # time with the songs in the middle...hence the map of the clusters of
135
+ # songs. Then we can make selections more intelliegently.
136
+
137
+ @songs
138
+ end
139
+ end
140
+
141
+ # @length = 3600 # 1hr
142
+ # tempo range 20bpm
143
+ #
144
+ # get count of all songs with the params, eg: tempo => 120..140
145
+ # => 100
146
+
147
+ # so take 100songs / 20steps = 5 songs per step
148
+ # out of the first 5 songs, select 3min worth using the first
149
+
data/lib/mr_eko.rb CHANGED
@@ -7,7 +7,12 @@ require "sequel"
7
7
  require "logger"
8
8
  require "hashie"
9
9
  require "digest/md5"
10
+ require 'id3lib'
10
11
  require "echonest"
12
+ begin
13
+ require 'ruby-debug'
14
+ rescue LoadError
15
+ end
11
16
 
12
17
  STDOUT.sync = true
13
18
 
@@ -15,12 +20,13 @@ EKO_ENV = ENV['EKO_ENV'] || 'development'
15
20
  Sequel.default_timezone = :utc
16
21
 
17
22
  module MrEko
18
- VERSION = '0.2.4.1'
23
+ VERSION = '0.3.0'
19
24
  USER_DIR = File.join(ENV['HOME'], ".mreko")
20
25
  FINGERPRINTS_DIR = File.join(USER_DIR, 'fingerprints')
26
+ LOG_DIR = File.join(USER_DIR, 'logs')
21
27
  HOME_DIR = File.join(File.dirname(__FILE__), '..')
22
28
 
23
- MODES = %w(minor major)
29
+ MODES = %w(minor major).freeze
24
30
  CHROMATIC_SCALE = %w(C C# D D# E F F# G G# A A# B).freeze
25
31
 
26
32
  class << self
@@ -43,15 +49,22 @@ module MrEko
43
49
  end
44
50
 
45
51
  def setup!
46
- @logger ||= Logger.new(STDOUT)
47
52
  setup_directories!
53
+ setup_logger!
48
54
  setup_db!
49
55
  setup_echonest!
50
56
  end
51
57
 
58
+ # Output to STDOUT in development, otherwise, save to logfile
59
+ def setup_logger!
60
+ out = env == 'development' ? STDOUT : File.join(LOG_DIR, "#{env}.log")
61
+ @logger ||= Logger.new(out)
62
+ end
63
+
52
64
  def setup_directories!
53
65
  Dir.mkdir(USER_DIR) unless File.directory?(USER_DIR)
54
66
  Dir.mkdir(FINGERPRINTS_DIR) unless File.directory?(FINGERPRINTS_DIR)
67
+ Dir.mkdir(LOG_DIR) unless File.directory?(LOG_DIR)
55
68
  end
56
69
 
57
70
  def setup_db!
@@ -65,7 +78,7 @@ module MrEko
65
78
  end
66
79
 
67
80
  def db_name
68
- env == 'test' ? 'db/eko_test.db' : 'db/eko.db'
81
+ env == 'test' ? File.join('db', 'eko_test.db') : File.join('db', 'eko.db')
69
82
  end
70
83
 
71
84
  def api_key
@@ -77,7 +90,7 @@ module MrEko
77
90
 
78
91
  # Takes 'minor' or 'major' and returns its integer representation.
79
92
  def mode_lookup(mode)
80
- MODES.index(mode.downcase)
93
+ MODES.index(mode.to_s.downcase)
81
94
  end
82
95
 
83
96
  # Takes a chromatic key (eg: G#) and returns its integer representation.
@@ -89,13 +102,35 @@ module MrEko
89
102
  def key_letter(key)
90
103
  CHROMATIC_SCALE[key]
91
104
  end
105
+
106
+ # Use the platform-specific binary.
107
+ def enmfp_binary
108
+ case ruby_platform
109
+ when /darwin/
110
+ 'codegen.Darwin'
111
+ when /686/
112
+ 'codegen.Linux-i686'
113
+ when /x86/
114
+ 'codegen.Linux-x86_64'
115
+ else
116
+ 'codegen.windows.exe'
117
+ end
118
+ end
119
+
120
+ def ruby_platform
121
+ RUBY_PLATFORM
122
+ end
123
+
92
124
  end
93
125
  end
94
126
 
95
127
 
96
128
  MrEko.setup!
97
129
 
130
+ require "lib/mr_eko/ext/numeric"
131
+ require "lib/mr_eko/ext/object"
98
132
  require "lib/mr_eko/core"
99
133
  require "lib/mr_eko/presets"
100
134
  require "lib/mr_eko/playlist"
135
+ require "lib/mr_eko/timed_playlist"
101
136
  require "lib/mr_eko/song"
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.2.4.1'
17
- s.date = '2011-02-08'
16
+ s.version = '0.3.0'
17
+ s.date = '2011-11-30'
18
18
  s.rubyforge_project = 'mr_eko'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -49,15 +49,18 @@ Gem::Specification.new do |s|
49
49
  s.add_dependency('sqlite3-ruby', "~> 1.3")
50
50
  s.add_dependency('hashie')
51
51
  s.add_dependency('httpclient', "~> 2.1")
52
+ s.add_dependency('bassnode-ruby-echonest')
52
53
  s.add_dependency('json', "= 1.4.6")
54
+ s.add_dependency('id3lib-ruby')
53
55
 
54
56
  ## List your development dependencies here. Development dependencies are
55
57
  ## those that are only needed during development
56
58
  s.add_development_dependency('mocha', "= 0.9.8")
57
59
  s.add_development_dependency('shoulda', "~> 2.11")
58
60
  s.add_development_dependency('test-unit', "~> 2.1")
59
- s.add_development_dependency("ruby-debug", "~> 0.10.3")
60
-
61
+ s.add_development_dependency("ruby-debug")
62
+ s.add_development_dependency("autotest")
63
+
61
64
  ## Leave this section as-is. It will be automatically generated from the
62
65
  ## contents of your Git repository via the gemspec task. DO NOT REMOVE
63
66
  ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
@@ -79,15 +82,24 @@ Gem::Specification.new do |s|
79
82
  ext/enmfp/codegen.Linux-i686
80
83
  ext/enmfp/codegen.Linux-x86_64
81
84
  ext/enmfp/codegen.windows.exe
85
+ ext/enmfp/old/codegen.Darwin
86
+ ext/enmfp/old/codegen.Linux-i686
87
+ ext/enmfp/old/codegen.Linux-x86_64
88
+ ext/enmfp/old/codegen.windows.exe
82
89
  lib/mr_eko.rb
83
90
  lib/mr_eko/core.rb
91
+ lib/mr_eko/ext/numeric.rb
92
+ lib/mr_eko/ext/object.rb
84
93
  lib/mr_eko/playlist.rb
85
94
  lib/mr_eko/presets.rb
86
95
  lib/mr_eko/song.rb
96
+ lib/mr_eko/timed_playlist.rb
87
97
  mr_eko.gemspec
88
98
  test/mr_eko_test.rb
89
99
  test/playlist_test.rb
100
+ test/song_test.rb
90
101
  test/test.rb
102
+ test/timed_playlist_test.rb
91
103
  ]
92
104
  # = MANIFEST =
93
105
 
data/test/mr_eko_test.rb CHANGED
@@ -1,25 +1,54 @@
1
- class MrEkoTest < Test::Unit::TestCase
1
+ class MrEkoTest < Test::Unit::TestCase
2
2
 
3
3
  context "the module" do
4
-
4
+
5
5
  should "return an Echonest API instance for nest" do
6
6
  assert_instance_of Echonest::Api, MrEko.nest
7
7
  end
8
-
8
+
9
9
  should "return a Sequel instance for connection" do
10
10
  assert_instance_of Sequel::SQLite::Database, MrEko.connection
11
11
  end
12
-
13
- # should "raise an error when there is no api.key found" do
14
- # File.expects(:exists?).with(File.join(MrEko::USER_DIR, 'echonest_api.key')).returns(false)
15
- # File.expects(:exists?).with(File.join(MrEko::HOME_DIR, 'echonest_api.key')).returns(false)
16
- # assert_raise(RuntimeError){ MrEko.setup_echonest! }
17
- # end
18
-
12
+
19
13
  should "return the MD5 of the passed filename" do
20
14
  md5 = Digest::MD5.hexdigest(open(__FILE__).read)
21
15
  assert_equal md5, MrEko.md5(__FILE__)
22
16
  end
17
+
18
+ context 'db_name' do
19
+
20
+ should 'return the test DB when in that env' do
21
+ assert_equal 'db/eko_test.db', MrEko.db_name
22
+ end
23
+
24
+ should 'return the main DB when not in the test env' do
25
+ MrEko.stubs(:env).returns('development')
26
+ assert_equal 'db/eko.db', MrEko.db_name
27
+ end
28
+ end
29
+
30
+ context 'enmfp_binary' do
31
+
32
+ should 'return proper Darwin bin' do
33
+ MrEko.stubs(:ruby_platform).returns("i686-darwin10.6.0")
34
+ assert_equal 'codegen.Darwin', MrEko.enmfp_binary
35
+ end
36
+
37
+ should 'return proper Windows bin' do
38
+ MrEko.stubs(:ruby_platform).returns("Win32")
39
+ assert_equal 'codegen.windows.exe', MrEko.enmfp_binary
40
+ end
41
+
42
+ should 'return proper 686 bin' do
43
+ MrEko.stubs(:ruby_platform).returns("i686-linux")
44
+ assert_equal 'codegen.Linux-i686', MrEko.enmfp_binary
45
+ end
46
+
47
+ should 'return proper x86 bin' do
48
+ MrEko.stubs(:ruby_platform).returns("x86_64-linux")
49
+ assert_equal 'codegen.Linux-x86_64', MrEko.enmfp_binary
50
+ end
51
+ end
23
52
  end
24
53
 
25
- end
54
+ end