mr_eko 0.2.4.1 → 0.3.0

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/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