diff --git a/admin/app/views/admin/jam_tracks/_form.html.slim b/admin/app/views/admin/jam_tracks/_form.html.slim index 2eb08fd27..a95275b82 100644 --- a/admin/app/views/admin/jam_tracks/_form.html.slim +++ b/admin/app/views/admin/jam_tracks/_form.html.slim @@ -6,22 +6,25 @@ = f.input :plan_code, :label=>'Recurly Plan Code', :required=>true, :hint => 'Must match plan code in Recurly' = f.input :version, :label => 'Version', :hint => 'Increment this value whenever you invalidate (update) the media in the JamTrack. Changing JMEP does not count as a version change; changing anything about a track (audio, instrument, part) does.' //= f.input :initial_play_silence, :label => 'Initial Play Silence (seconds)' - = f.input :time_signature, collection: JamRuby::JamTrack::TIME_SIGNATURES, include_blank: false + = f.input :time_signature, collection: JamRuby::JamTrack::TIME_SIGNATURES, include_blank: true = f.input :status, collection: JamRuby::JamTrack::STATUS, include_blank: false, hint: 'Only set to Production when end users should be able to purchase this JamTrack' = f.input :recording_type, collection: JamRuby::JamTrack::RECORDING_TYPE, include_blank: false = f.input :original_artist, :input_html => { :rows=>1, :maxlength=>1000 } = f.input :songwriter, :input_html => { :rows=>1, :maxlength=>1000 } = f.input :publisher, :input_html => { :rows=>1, :maxlength=>1000 } - = f.input :licensor, collection: JamRuby::JamTrackLicensor.all, include_blank: false - = f.input :pro, collection: JamRuby::JamTrack::PRO, include_blank: false + = f.input :licensor, collection: JamRuby::JamTrackLicensor.all, include_blank: true = f.input :genre, collection: JamRuby::Genre.all, include_blank: false = f.input :sales_region, collection: JamRuby::JamTrack::SALES_REGION, include_blank: false - = f.input :price, :required=>true, :input_html=>{type:'numeric'} + = f.input :price, :required => true, :input_html => {type: 'numeric'} + = f.input :pro_ascap, :label => 'ASCAP royalties due?' + = f.input :pro_bmi, :label => 'BMI royalties due?' + = f.input :pro_sesac, :label => 'SESAC royalties due?' = f.input :reproduction_royalty, :label => 'Reproduction Royalty' = f.input :public_performance_royalty, :label => 'Public Performance Royalty' = f.input :reproduction_royalty_amount, :required=>true, :input_html=>{type:'numeric'} = f.input :licensor_royalty_amount, :required=>true, :input_html=>{type:'numeric'} - = f.input :pro_royalty_amount, :required=>true, :input_html=>{type:'numeric'} + = f.input :preview_start_time_raw, :label=>'Preview Start Time', :hint => 'MM:SS:MLS', :as => :string + //= f.input :url, :as => :file, :label => 'Audio File' = f.input :jmep_text, :as => :text, :label => "JMEP Text", :input_html => {:rows => 5 }, :hint => 'Tap-Ins & Lead Silence. Examples: https://jamkazam.atlassian.net/wiki/pages/viewpage.action?pageId=39289025#JamKazamMeta-EventProcessor(JMEP)-CommonExamples' = f.input :jmep_json, :as => :text, :label => "JMEP Json", :input_html => {:rows => 5, :readonly => true }, :hint => 'Readonly field. This is shown here just so you can see what your JMEP got converted to readily' diff --git a/admin/config/initializers/jam_tracks.rb b/admin/config/initializers/jam_tracks.rb index c88fc6a2c..c1c0063bf 100644 --- a/admin/config/initializers/jam_tracks.rb +++ b/admin/config/initializers/jam_tracks.rb @@ -2,8 +2,11 @@ class JamRuby::JamTrack # add a custom validation + attr_accessor :preview_generate_error + before_save :jmep_json_generate validate :jmep_text_validate + validate :preview def jmep_text_validate begin @@ -14,6 +17,11 @@ class JamRuby::JamTrack end def jmep_json_generate + self.genre_id = nil if self.genre_id == '' + self.licensor_id = nil if self.licensor_id == '' + self.jmep_json = nil if self.jmep_json == '' + self.time_signature = nil if self.time_signature == '' + begin self[:jmep_json] = JmepManager.execute(self.jmep_text) rescue ArgumentError => err @@ -21,5 +29,96 @@ class JamRuby::JamTrack end end + def preview + if preview_generate_error + errors.add(:preview_url, preview_generate_error) + end + end + + + + # this is used by active admin/jam-admin + def preview_start_time_raw + if self.preview_start_time.nil? || self.preview_start_time.nil? + '' + else + seconds = self.preview_start_time.to_f/1000 + time = Time.at(seconds) + time.strftime("%M:%S:#{(self.preview_start_time % 1000).to_s.rjust(3, '0')}") + end + end + + # this is used by active admin/jam-admin + def preview_start_time_raw=(new_value) + + value = nil + if new_value == nil || new_value == '' + value = nil + else + if new_value && new_value.kind_of?(String) && new_value.include?(':') + bits = new_value.split(':') + if bits.length != 3 + raise "format of preview start time must be MM:SS:MLS" + end + + value = (bits[0].to_i * 60000) + (bits[1].to_i * 1000) + (bits[2].to_i) + + else + raise "format of preview start time must be MM:SS:MLS" + end + end + + if !value.nil? && value != self.preview_start_time + self.preview_start_time = value + generate_preview + else + self.preview_start_time = value + end + end + + def generate_preview + + begin + Dir.mktmpdir do |tmp_dir| + + input = File.join(tmp_dir, 'in.ogg') + output = File.join(tmp_dir, 'out.ogg') + + start = self.preview_start_time.to_f / 1000 + stop = start + 20 + + master_track = self.master_track + + raise 'no master track' unless master_track + + s3_manager.download(master_track.url_by_sample_rate(44), input) + + command = "sox \"#{input}\" \"#{output}\" trim #{start} #{stop}" + + @@log.debug("trimming using: " + command) + + sox_output = `#{command}` + + result_code = $?.to_i + + if result_code != 0 + @preview_generate_error = "unable to execute cut command #{sox_output}" + else + @@log.debug("uploading preview to #{self.preview_filename}") + + s3_manager.upload(self.preview_filename, output) + + # and finally update the JamTrackTrack with the new info + self["preview_url"] = self.preview_filename + self["preview_md5"] = ::Digest::MD5.file(output).hexdigest + self["preview_length"] = File.new(output).size + self.save! + end + end + rescue Exception => e + @preview_generate_error = e.to_s + end + + end end diff --git a/db/manifest b/db/manifest index f744571eb..6d9f1f63d 100755 --- a/db/manifest +++ b/db/manifest @@ -258,4 +258,5 @@ jam_track_version.sql recorded_jam_track_tracks.sql jam_track_jmep_data.sql add_jam_track_bitrates.sql -jam_track_importer.sql \ No newline at end of file +jam_track_importer.sql +jam_track_pro_licensing_update.sql \ No newline at end of file diff --git a/db/up/jam_track_pro_licensing_update.sql b/db/up/jam_track_pro_licensing_update.sql new file mode 100644 index 000000000..006483f44 --- /dev/null +++ b/db/up/jam_track_pro_licensing_update.sql @@ -0,0 +1,12 @@ +ALTER TABLE jam_tracks ADD COLUMN pro_ascap BOOLEAN DEFAULT FALSE NOT NULL; +ALTER TABLE jam_tracks ADD COLUMN pro_bmi BOOLEAN DEFAULT FALSE NOT NULL; +ALTER TABLE jam_tracks ADD COLUMN pro_sesac BOOLEAN DEFAULT FALSE NOT NULL; +UPDATE jam_tracks SET pro_ascap = TRUE WHERE pro = 'ASCAP'; +UPDATE jam_tracks SET pro_bmi = TRUE WHERE pro = 'BMI'; +UPDATE jam_tracks SET pro_sesac = TRUE WHERE pro = 'SESAC'; +ALTER TABLE jam_tracks DROP COLUMN pro; +ALTER TABLE jam_tracks DROP Column pro_royalty_amount; +ALTER TABLE jam_tracks ADD COLUMN preview_start_time INTEGER; +ALTER TABLE jam_tracks RENAME COLUMN url TO preview_url; +ALTER TABLE jam_tracks RENAME COLUMN md5 TO preview_md5; +ALTER TABLE jam_tracks RENAME COLUMN length TO preview_length; diff --git a/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb b/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb index 98e555e3d..9ec9a97ec 100644 --- a/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb +++ b/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb @@ -11,7 +11,7 @@ class JamTrackUploader < CarrierWave::Uploader::Base # Add a white list of extensions which are allowed to be uploaded. def extension_white_list - %w(jkz) + %w(ogg) end def store_dir @@ -23,6 +23,6 @@ class JamTrackUploader < CarrierWave::Uploader::Base end def filename - "#{model.store_dir}/#{model.filename}" if model.id + "#{model.preview_filename}" if model.id && model.uploading_preview end end diff --git a/ruby/lib/jam_ruby/jam_track_importer.rb b/ruby/lib/jam_ruby/jam_track_importer.rb index 39da44bb0..8874a66fe 100644 --- a/ruby/lib/jam_ruby/jam_track_importer.rb +++ b/ruby/lib/jam_ruby/jam_track_importer.rb @@ -102,7 +102,6 @@ module JamRuby jam_track.price = 1.99 jam_track.reproduction_royalty_amount = 0 jam_track.licensor_royalty_amount = 0 - jam_track.pro_royalty_amount = 0 jam_track.sales_region = 'United States' jam_track.recording_type = 'Cover' jam_track.description = "This is a JamTrack audio file for use exclusively with the JamKazam service. This JamTrack is a high quality cover of the #{jam_track.original_artist} song \"#{jam_track.name}\"." diff --git a/ruby/lib/jam_ruby/models/jam_track.rb b/ruby/lib/jam_ruby/models/jam_track.rb index 73e1634c3..9660f7a4f 100644 --- a/ruby/lib/jam_ruby/models/jam_track.rb +++ b/ruby/lib/jam_ruby/models/jam_track.rb @@ -12,35 +12,37 @@ module JamRuby @@log = Logging.logger[JamTrack] - mount_uploader :url, JamTrackUploader + mount_uploader :preview_url, JamTrackUploader + attr_accessor :uploading_preview attr_accessible :name, :description, :bpm, :time_signature, :status, :recording_type, :original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genre, :genre_id, :sales_region, :price, :reproduction_royalty, :public_performance_royalty, :reproduction_royalty_amount, :licensor_royalty_amount, :pro_royalty_amount, :plan_code, :initial_play_silence, :jam_track_tracks_attributes, - :jam_track_tap_ins_attributes, :version, :jmep_json, :jmep_text, as: :admin + :jam_track_tap_ins_attributes, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :preview_start_time_raw, as: :admin validates :name, presence: true, uniqueness: true, length: {maximum: 200} validates :plan_code, presence: true, uniqueness: true, length: {maximum: 50 } validates :description, length: {maximum: 1000} - validates :time_signature, inclusion: {in: [nil] + TIME_SIGNATURES} + validates :time_signature, inclusion: {in: [nil] + [''] + TIME_SIGNATURES} # the empty string is needed because of activeadmin validates :status, inclusion: {in: [nil] + STATUS} validates :recording_type, inclusion: {in: [nil] + RECORDING_TYPE} validates :original_artist, length: {maximum: 200} validates :songwriter, length: {maximum: 1000} validates :publisher, length: {maximum: 1000} - validates :pro, inclusion: {in: [nil] + PRO} validates :sales_region, inclusion: {in: [nil] + SALES_REGION} validates_format_of :price, with: /^\d+\.*\d{0,2}$/ validates :version, presence: true - + validates :preview_start_time, numericality: {only_integer: true}, length: {in: 1..1000}, :allow_nil => true + validates :pro_ascap, inclusion: {in: [true, false]} + validates :pro_bmi, inclusion: {in: [true, false]} + validates :pro_sesac, inclusion: {in: [true, false]} + validates :public_performance_royalty, inclusion: {in: [nil, true, false]} validates :reproduction_royalty, inclusion: {in: [nil, true, false]} validates :public_performance_royalty, inclusion: {in: [nil, true, false]} + validates_format_of :reproduction_royalty_amount, with: /^\d+\.*\d{0,3}$/ validates_format_of :licensor_royalty_amount, with: /^\d+\.*\d{0,3}$/ - validates_format_of :pro_royalty_amount, with: /^\d+\.*\d{0,3}$/ - - before_save :sanitize_active_admin belongs_to :genre, class_name: "JamRuby::Genre" belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id' @@ -91,15 +93,10 @@ module JamRuby end end end - - # create storage directory that will house this jam_track, as well as - def store_dir - "jam_tracks/#{id}" - end # create name of the file - def filename - "#{name}.jkz" + def preview_filename + "jam_track_previews/#{self.original_artist}/#{self.name}/preview-44100.ogg" end # creates a short-lived URL that has access to the object. @@ -107,9 +104,12 @@ module JamRuby # we would verify their rights (can_download?), and generates a URL in response to the click so that they can download # but the url is short lived enough so that it wouldn't be easily shared def sign_url(expiration_time = 120) - s3_manager.sign_url(self[:url], {:expires => expiration_time, :response_content_type => 'audio/jkz', :secure => false}) + s3_manager.sign_url(self[:preview_url], {:expires => expiration_time, :response_content_type => 'audio/ogg', :secure => false}) end + def master_track + JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Master').first + end def can_download?(user) owners.include?(user) @@ -118,50 +118,5 @@ module JamRuby def right_for_user(user) jam_track_rights.where("user_id=?", user).first end - - def self.list_downloads(user, limit = 100, since = 0, bitrate = 48) - since = 0 unless since || since == '' # guard against nil - downloads = [] - - user.jam_track_rights - .limit(limit) - .where('jam_track_rights.id > ?', since) - .each do |jam_track_right| - download = { - :type => "jam_track", - :id => jam_track_right.id.to_s, - :jam_track_id => jam_track_right.jam_track_id, - :created_at => jam_track_right.created_at, - :next => jam_track_right.id - } - if(bitrate==48) - download[:length] = jam_track_right.length_48 - download[:md5] = jam_track_right.md5_48 - download[:url] = jam_track_right.url_48 - else - download[:length] = jam_track_right.length_44 - download[:md5] = jam_track_right.md5_44 - download[:url] = jam_track_right.url_44 - end - downloads << download - end - - next_id = downloads[-1][:next] if downloads.length > 0 - next_id = since if next_id.nil? # echo back to the client the same value they passed in, if there are no results - - { - 'downloads' => downloads, - 'next' => next_id.to_s - } - end - - - private - - def sanitize_active_admin - self.genre_id = nil if self.genre_id == '' - self.licensor_id = nil if self.licensor_id == '' - self.jmep_json = nil if self.jmep_json = '' - end end end diff --git a/ruby/lib/jam_ruby/models/jam_track_tap_in.rb b/ruby/lib/jam_ruby/models/jam_track_tap_in.rb index 0bddeb8c6..fa0e63eda 100644 --- a/ruby/lib/jam_ruby/models/jam_track_tap_in.rb +++ b/ruby/lib/jam_ruby/models/jam_track_tap_in.rb @@ -35,7 +35,6 @@ module JamRuby else raise "format of offset time must be MM:SS:MLS" end - end end end \ No newline at end of file diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 322b206bd..18dbeb8bf 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -730,14 +730,12 @@ FactoryGirl.define do sequence(:original_artist) { |n| "original-artist-#{n}" } sequence(:songwriter) { |n| "songwriter-#{n}" } sequence(:publisher) { |n| "publisher-#{n}" } - pro 'ASCAP' sales_region 'United States' price 1.99 reproduction_royalty true public_performance_royalty true reproduction_royalty_amount 0.999 licensor_royalty_amount 0.999 - pro_royalty_amount 0.999 sequence(:plan_code) { |n| "jamtrack-#{n}" } genre JamRuby::Genre.first diff --git a/ruby/spec/jam_ruby/jam_track_importer_spec.rb b/ruby/spec/jam_ruby/jam_track_importer_spec.rb index eda890d1f..5a0a89cf4 100644 --- a/ruby/spec/jam_ruby/jam_track_importer_spec.rb +++ b/ruby/spec/jam_ruby/jam_track_importer_spec.rb @@ -57,7 +57,6 @@ describe JamTrackImporter do jam_track.original_artist.should eq('Artist 1') jam_track.songwriter.should be_nil jam_track.publisher.should be_nil - jam_track.pro.should be_nil jam_track.sales_region.should eq('United States') jam_track.price.should eq(1.99) end diff --git a/ruby/spec/jam_ruby/models/jam_track_right_spec.rb b/ruby/spec/jam_ruby/models/jam_track_right_spec.rb index ee7497f40..bd8e48196 100644 --- a/ruby/spec/jam_ruby/models/jam_track_right_spec.rb +++ b/ruby/spec/jam_ruby/models/jam_track_right_spec.rb @@ -17,14 +17,6 @@ describe JamTrackRight do end - it "lists" do - jam_track_right = FactoryGirl.create(:jam_track_right) - jam_tracks = JamTrack.list_downloads(jam_track_right.user) - jam_tracks.should have_key('downloads') - jam_tracks.should have_key('next') - jam_tracks['downloads'].should have(1).items - end - describe "validations" do it "one purchase per user/jam_track combo" do user = FactoryGirl.create(:user) diff --git a/ruby/spec/jam_ruby/models/jam_track_spec.rb b/ruby/spec/jam_ruby/models/jam_track_spec.rb index af4980f7b..0f41e5f25 100644 --- a/ruby/spec/jam_ruby/models/jam_track_spec.rb +++ b/ruby/spec/jam_ruby/models/jam_track_spec.rb @@ -119,9 +119,9 @@ describe JamTrack do end describe "upload/download" do - JKA_NAME = 'blah.jkz' + PREVIEW_NAME = 'blah.ogg' - in_directory_with_file(JKA_NAME) + in_directory_with_file(PREVIEW_NAME) before(:all) do original_storage = JamTrackUploader.storage = :fog @@ -137,17 +137,17 @@ describe JamTrack do it "uploads to s3 with correct name, and then downloads via signed URL" do jam_track = FactoryGirl.create(:jam_track) - uploader = JamTrackUploader.new(jam_track, :url) - uploader.store!(File.open(JKA_NAME)) # uploads file + uploader = JamTrackUploader.new(jam_track, :preview_url) + uploader.store!(File.open(PREVIEW_NAME)) # uploads file jam_track.save! # verify that the uploader stores the correct path - jam_track[:url].should == jam_track.store_dir + '/' + jam_track.filename + jam_track[:preview_url].should == jam_track.preview_filename # verify it's on S3 s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key) - s3.exists?(jam_track[:url]).should be_true - s3.length(jam_track[:url]).should == 'abc'.length + s3.exists?(jam_track[:preview_url]).should be_true + s3.length(jam_track[:preview_url]).should == 'abc'.length # download it via signed URL, and check contents url = jam_track.sign_url diff --git a/web/app/controllers/api_jam_tracks_controller.rb b/web/app/controllers/api_jam_tracks_controller.rb index b2322c4a5..eff17afa0 100644 --- a/web/app/controllers/api_jam_tracks_controller.rb +++ b/web/app/controllers/api_jam_tracks_controller.rb @@ -23,15 +23,6 @@ class ApiJamTracksController < ApiController render "api_jam_tracks/purchased", :layout => nil end - def downloads - sample_rate = params[:sample_rate].nil? ? nil : params[:sample_rate].to_i - begin - render :json => JamTrack.list_downloads(current_user, params[:limit], params[:since], sample_rate), :status => 200 - rescue - render :json => { :message => "could not produce list of files" }, :status => 403 - end - end - def download if @jam_track_right.valid? sample_rate = params[:sample_rate].nil? ? nil : params[:sample_rate].to_i diff --git a/web/config/routes.rb b/web/config/routes.rb index 00fd25f38..d21ed3861 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -206,7 +206,6 @@ SampleApp::Application.routes.draw do # Jamtracks match '/jamtracks' => 'api_jam_tracks#index', :via => :get, :as => 'api_jam_tracks_list' match '/jamtracks/purchased' => 'api_jam_tracks#purchased', :via => :get, :as => 'api_jam_tracks_purchased' - match '/jamtracks/downloads' => 'api_jam_tracks#downloads', :via => :get, :as => 'api_jam_tracks_downloads' match '/jamtracks/download/:id' => 'api_jam_tracks#download', :via => :get, :as => 'api_jam_tracks_download' match '/jamtracks/enqueue/:id' => 'api_jam_tracks#enqueue', :via => :post, :as => 'api_jam_tracks_enqueue' match '/jamtracks/rights/:id' => 'api_jam_tracks#show_jam_track_right', :via => :get, :as => 'api_jam_tracks_show_right' diff --git a/web/spec/factories.rb b/web/spec/factories.rb index 1912f434d..59a8089fa 100644 --- a/web/spec/factories.rb +++ b/web/spec/factories.rb @@ -717,14 +717,12 @@ FactoryGirl.define do sequence(:original_artist) { |n| "original-artist-#{n}" } sequence(:songwriter) { |n| "songwriter-#{n}" } sequence(:publisher) { |n| "publisher-#{n}" } - pro 'ASCAP' sales_region 'United States' price 1.99 reproduction_royalty true public_performance_royalty true reproduction_royalty_amount 0.999 licensor_royalty_amount 0.999 - pro_royalty_amount 0.999 sequence(:plan_code) { |n| "jamtrack-#{n}" } ignore do make_track true