merged from master
This commit is contained in:
commit
fb24f3beae
|
|
@ -44,7 +44,7 @@ gem 'rails3-jquery-autocomplete'
|
||||||
gem 'activeadmin' #, github: 'activeadmin', branch: '0-6-stable'
|
gem 'activeadmin' #, github: 'activeadmin', branch: '0-6-stable'
|
||||||
gem 'mime-types', '1.25'
|
gem 'mime-types', '1.25'
|
||||||
gem 'meta_search'
|
gem 'meta_search'
|
||||||
gem 'fog', "~> 1.32.0"
|
gem 'fog'
|
||||||
gem 'unf', '0.1.3' #optional fog dependency
|
gem 'unf', '0.1.3' #optional fog dependency
|
||||||
gem 'country-select'
|
gem 'country-select'
|
||||||
gem 'aasm', '3.0.16'
|
gem 'aasm', '3.0.16'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
ActiveAdmin.register JamRuby::AffiliateQuarterlyPayment, :as => 'Affiliate Quarterly Payments' do
|
||||||
|
|
||||||
|
menu :label => 'Quarterly Reports', :parent => 'Affiliates'
|
||||||
|
|
||||||
|
config.sort_order = 'due_amount_in_cents DESC'
|
||||||
|
config.batch_actions = false
|
||||||
|
config.clear_action_items!
|
||||||
|
config.filters = true
|
||||||
|
config.per_page = 50
|
||||||
|
config.paginate = true
|
||||||
|
|
||||||
|
filter :affiliate_partner
|
||||||
|
filter :year
|
||||||
|
filter :quarter
|
||||||
|
filter :closed
|
||||||
|
filter :paid
|
||||||
|
|
||||||
|
form :partial => 'form'
|
||||||
|
|
||||||
|
index do
|
||||||
|
|
||||||
|
# default_actions # use this for all view/edit/delete links
|
||||||
|
|
||||||
|
column 'Year' do |oo| oo.year end
|
||||||
|
column 'Quarter' do |oo| oo.quarter end
|
||||||
|
column 'Partner' do |oo| link_to(oo.affiliate_partner.display_name, oo.affiliate_partner.admin_url, {:title => oo.affiliate_partner.display_name}) end
|
||||||
|
column "Due (\u00A2)" do |oo| oo.due_amount_in_cents end
|
||||||
|
column 'JamTracks Sold' do |oo| oo.jamtracks_sold end
|
||||||
|
column 'Paid' do |oo| oo.paid end
|
||||||
|
column 'Closed' do |oo| oo.paid end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
controller do
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
ActiveAdmin.register JamRuby::AffiliateTrafficTotal, :as => 'Affiliate Daily Stats' do
|
||||||
|
|
||||||
|
menu :label => 'Daily Stats', :parent => 'Affiliates'
|
||||||
|
|
||||||
|
config.sort_order = 'referral_user_count DESC'
|
||||||
|
config.batch_actions = false
|
||||||
|
config.clear_action_items!
|
||||||
|
config.filters = true
|
||||||
|
config.per_page = 50
|
||||||
|
config.paginate = true
|
||||||
|
|
||||||
|
filter :affiliate_partner
|
||||||
|
filter :day
|
||||||
|
filter :signups
|
||||||
|
filter :visits
|
||||||
|
|
||||||
|
form :partial => 'form'
|
||||||
|
|
||||||
|
scope("Active", default: true) { |scope| scope.where('visits != 0 or signups != 0').order('day desc') }
|
||||||
|
|
||||||
|
index do
|
||||||
|
|
||||||
|
# default_actions # use this for all view/edit/delete links
|
||||||
|
|
||||||
|
column 'Day' do |oo| oo.day end
|
||||||
|
column 'Partner' do |oo| link_to(oo.affiliate_partner.display_name, oo.affiliate_partner.admin_url, {:title => oo.affiliate_partner.display_name}) end
|
||||||
|
column 'Signups' do |oo| oo.signups end
|
||||||
|
column 'Visits' do |oo| oo.visits end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
controller do
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -2,15 +2,18 @@ ActiveAdmin.register JamRuby::User, :as => 'Referrals' do
|
||||||
|
|
||||||
menu :label => 'Referrals', :parent => 'Affiliates'
|
menu :label => 'Referrals', :parent => 'Affiliates'
|
||||||
|
|
||||||
|
config.sort_order = 'created_at DESC'
|
||||||
config.batch_actions = false
|
config.batch_actions = false
|
||||||
config.clear_action_items!
|
config.clear_action_items!
|
||||||
config.filters = false
|
config.filters = true
|
||||||
|
|
||||||
|
filter :affiliate_referral
|
||||||
|
|
||||||
index do
|
index do
|
||||||
column 'User' do |oo| link_to(oo.name, "http://www.jamkazam.com/client#/profile/#{oo.id}", {:title => oo.name}) end
|
column 'User' do |oo| link_to(oo.name, oo.admin_url, {:title => oo.name}) end
|
||||||
column 'Email' do |oo| oo.email end
|
column 'Email' do |oo| oo.email end
|
||||||
column 'Created' do |oo| oo.created_at end
|
column 'Created' do |oo| oo.created_at end
|
||||||
column 'Partner' do |oo| oo.affiliate_referral.partner_name end
|
column 'Partner' do |oo| oo.affiliate_referral.display_name end
|
||||||
end
|
end
|
||||||
|
|
||||||
controller do
|
controller do
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,12 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
|
||||||
config.batch_actions = false
|
config.batch_actions = false
|
||||||
# config.clear_action_items!
|
# config.clear_action_items!
|
||||||
config.filters = false
|
config.filters = false
|
||||||
|
config.per_page = 50
|
||||||
|
config.paginate = true
|
||||||
|
|
||||||
form :partial => 'form'
|
form :partial => 'form'
|
||||||
|
|
||||||
scope("Active", default: true) { |scope| scope.where('partner_user_id IS NOT NULL') }
|
scope("Active", default: true) { |scope| scope.where('partner_user_id IS NOT NULL').order('referral_user_count desc') }
|
||||||
scope("Unpaid") { |partner| partner.unpaid }
|
scope("Unpaid") { |partner| partner.unpaid }
|
||||||
|
|
||||||
index do
|
index do
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,27 @@
|
||||||
ActiveAdmin.register JamRuby::CrashDump, :as => 'Crash Dump' do
|
ActiveAdmin.register JamRuby::CrashDump, :as => 'Crash Dump' do
|
||||||
# Note: a lame thing is it's not obvious how to make it search on email instead of user_id.
|
# Note: a lame thing is it's not obvious how to make it search on email instead of user_id.
|
||||||
filter :timestamp
|
filter :timestamp
|
||||||
filter :user_email, :as => :string
|
|
||||||
filter :client_id
|
filter :client_id
|
||||||
|
filter :user_id
|
||||||
|
|
||||||
menu :parent => 'Misc'
|
menu :parent => 'Misc'
|
||||||
|
|
||||||
|
config.sort_order = 'created_at DESC'
|
||||||
|
|
||||||
index do
|
index do
|
||||||
|
column 'User' do |oo| oo.user ? link_to(oo.user.email, oo.user.admin_url, {:title => oo.user.email}) : '' end
|
||||||
|
column "Client Version", :client_version
|
||||||
|
column "Client Type", :client_type
|
||||||
|
column "Download" do |post|
|
||||||
|
link_to 'Link', post.sign_url
|
||||||
|
end
|
||||||
column "Timestamp" do |post|
|
column "Timestamp" do |post|
|
||||||
(post.timestamp || post.created_at).strftime('%b %d %Y, %H:%M')
|
(post.timestamp || post.created_at).strftime('%b %d %Y, %H:%M')
|
||||||
end
|
end
|
||||||
column "Client Type", :client_type
|
column "Description" do |post|
|
||||||
column "Dump URL" do |post|
|
post.description
|
||||||
link_to post.uri, post.uri
|
|
||||||
end
|
end
|
||||||
|
|
||||||
column "User ID", :user_id
|
|
||||||
|
|
||||||
# FIXME (?): This isn't performant (though it likely doesn't matter). Could probably do a join.
|
|
||||||
column "User Email" do |post|
|
|
||||||
unless post.user_id.nil?
|
|
||||||
post.user_email
|
|
||||||
end
|
|
||||||
end
|
|
||||||
column "Client ID", :client_id
|
|
||||||
actions
|
actions
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
ActiveAdmin.register JamRuby::DownloadTracker, :as => 'DownloadTrackers' do
|
||||||
|
|
||||||
|
menu :label => 'Download Trackers', :parent => 'JamTracks'
|
||||||
|
|
||||||
|
config.batch_actions = false
|
||||||
|
config.filters = true
|
||||||
|
config.per_page = 50
|
||||||
|
|
||||||
|
filter :remote_ip
|
||||||
|
|
||||||
|
index do
|
||||||
|
column 'User' do |oo| oo.user ? link_to(oo.user.email, oo.user.admin_url, {:title => oo.user.email}) : '' end
|
||||||
|
column 'Created' do |oo| oo.created_at end
|
||||||
|
column 'JamTrack' do |oo| oo.jam_track end
|
||||||
|
column 'Paid' do |oo| oo.paid end
|
||||||
|
column 'Blacklisted?' do |oo| IpBlacklist.listed(oo.remote_ip) ? 'Yes' : 'No' end
|
||||||
|
column 'Remote IP' do |oo| oo.remote_ip end
|
||||||
|
column "" do |oo|
|
||||||
|
link_to 'Blacklist This IP', "download_trackers/#{oo.id}/blacklist_by_ip"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
member_action :blacklist_by_ip, :method => :get do
|
||||||
|
tracker = DownloadTracker.find(params[:id])
|
||||||
|
|
||||||
|
if !IpBlacklist.listed(tracker.remote_ip)
|
||||||
|
ip = IpBlacklist.new
|
||||||
|
ip.remote_ip = tracker.remote_ip
|
||||||
|
ip.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_download_trackers_path, :notice => "IP address #{tracker.remote_ip} blacklisted."
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -32,6 +32,7 @@ ActiveAdmin.register_page "Fake Purchaser" do
|
||||||
jam_track_right.user = user
|
jam_track_right.user = user
|
||||||
jam_track_right.jam_track = jam_track
|
jam_track_right.jam_track = jam_track
|
||||||
jam_track_right.is_test_purchase = true
|
jam_track_right.is_test_purchase = true
|
||||||
|
jam_track_right.version = jam_track.version
|
||||||
jam_track_right.save!
|
jam_track_right.save!
|
||||||
count = count + 1
|
count = count + 1
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
ActiveAdmin.register_page "Giftcarduploads" do
|
||||||
|
|
||||||
|
menu :label => 'Gift Cards Upload', :parent => 'JamTracks'
|
||||||
|
|
||||||
|
page_action :upload_giftcards, :method => :post do
|
||||||
|
GiftCard.transaction do
|
||||||
|
|
||||||
|
puts params
|
||||||
|
|
||||||
|
file = params[:jam_ruby_gift_card][:csv]
|
||||||
|
array_of_arrays = CSV.read(file.tempfile.path)
|
||||||
|
array_of_arrays.each do |row|
|
||||||
|
if row.length != 1
|
||||||
|
raise "UKNONWN CSV FORMAT! Must be 1 column"
|
||||||
|
end
|
||||||
|
|
||||||
|
code = row[0]
|
||||||
|
|
||||||
|
gift_card = GiftCard.new
|
||||||
|
gift_card.code = code
|
||||||
|
gift_card.card_type = params[:jam_ruby_gift_card][:card_type]
|
||||||
|
gift_card.origin = file .original_filename
|
||||||
|
gift_card.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_giftcarduploads_path, :notice => "Created #{array_of_arrays.length} gift cards!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
content do
|
||||||
|
semantic_form_for GiftCard.new, :url => admin_giftcarduploads_upload_giftcards_path, :builder => ActiveAdmin::FormBuilder do |f|
|
||||||
|
f.inputs "Upload Gift Cards" do
|
||||||
|
f.input :csv, as: :file, required: true, :label => "A single column CSV that contains ONE type of gift card (5 JamTrack, 10 JamTrack, etc)"
|
||||||
|
f.input :card_type, required:true, as: :select, :collection => JamRuby::GiftCard::CARD_TYPES
|
||||||
|
end
|
||||||
|
f.actions
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
ActiveAdmin.register JamRuby::GiftCard, :as => 'GiftCards' do
|
||||||
|
|
||||||
|
menu :label => 'Gift Cards', :parent => 'JamTracks'
|
||||||
|
|
||||||
|
config.batch_actions = false
|
||||||
|
config.filters = true
|
||||||
|
config.per_page = 50
|
||||||
|
|
||||||
|
scope("Redeemed Most Recently", default: true) { |scope| scope.where('user_id IS NOT NULL').order('updated_at DESC') }
|
||||||
|
scope("Available") { |scope| scope.where('user_id is NULL') }
|
||||||
|
|
||||||
|
filter :card_type
|
||||||
|
filter :origin
|
||||||
|
filter :code
|
||||||
|
|
||||||
|
index do
|
||||||
|
column 'User' do |oo| oo.user ? link_to(oo.user.email, oo.user.admin_url, {:title => oo.user.email}) : '' end
|
||||||
|
column 'Code' do |oo| oo.code end
|
||||||
|
column 'Card Type' do |oo| oo.card_type end
|
||||||
|
column 'Origin' do |oo| oo.origin end
|
||||||
|
column 'Created' do |oo| oo.created_at end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
ActiveAdmin.register JamRuby::IpBlacklist, :as => 'IP Blacklist' do
|
||||||
|
|
||||||
|
menu :label => 'IP Blacklist', :parent => 'Operations'
|
||||||
|
|
||||||
|
config.sort_order = 'created_at desc'
|
||||||
|
config.batch_actions = false
|
||||||
|
|
||||||
|
index do
|
||||||
|
column :remote_ip
|
||||||
|
column :notes
|
||||||
|
column :created_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -5,13 +5,16 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
|
||||||
config.sort_order = 'name_asc'
|
config.sort_order = 'name_asc'
|
||||||
config.batch_actions = false
|
config.batch_actions = false
|
||||||
|
|
||||||
|
filter :name
|
||||||
|
filter :original_artist
|
||||||
filter :genres
|
filter :genres
|
||||||
filter :status, :as => :select, collection: JamRuby::JamTrack::STATUS
|
filter :status, :as => :select, collection: JamRuby::JamTrack::STATUS
|
||||||
|
|
||||||
scope("Default", default: true) { |scope| scope }
|
scope("Default", default: true) { |scope| scope }
|
||||||
scope("Onboarding TODO") { |scope| scope.where('onboarding_exceptions is not null') }
|
scope("Onboarding TODO") { |scope| scope.where('onboarding_exceptions is not null') }
|
||||||
scope("Tency Only") { |scope| scope.joins('INNER JOIN jam_track_licensors as licensors ON jam_tracks.licensor_id = licensors.id').where("licensors.name = 'Tency Music'") }
|
scope("Tency Only") { |scope| scope.joins('INNER JOIN jam_track_licensors as licensors ON jam_tracks.licensor_id = licensors.id').where("licensors.name = 'Tency Music'") }
|
||||||
scope("Onboarding TODO w/ Tency Only") { |scope| scope.joins('INNER JOIN jam_track_licensors as licensors ON jam_tracks.licensor_id = licensors.id').where("licensors.name = 'Tency Music'").where('onboarding_exceptions is not null') }
|
scope("TimTracks Only") { |scope| scope.joins('INNER JOIN jam_track_licensors as licensors ON jam_tracks.licensor_id = licensors.id').where("licensors.name = 'Tim Waurick'") }
|
||||||
|
# scope("Onboarding TODO w/ Tency Only") { |scope| scope.joins('INNER JOIN jam_track_licensors as licensors ON jam_tracks.licensor_id = licensors.id').where("licensors.name = 'Tency Music'").where('onboarding_exceptions is not null') }
|
||||||
|
|
||||||
form :partial => 'form'
|
form :partial => 'form'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
ActiveAdmin.register JamRuby::SaleLineItem, :as => 'Sale Line Items' do
|
||||||
|
|
||||||
|
menu :label => 'Line Items', :parent => 'Purchases'
|
||||||
|
|
||||||
|
config.sort_order = 'created_at DESC'
|
||||||
|
config.batch_actions = false
|
||||||
|
config.clear_action_items!
|
||||||
|
config.filters = true
|
||||||
|
config.per_page = 50
|
||||||
|
config.paginate = true
|
||||||
|
|
||||||
|
filter :affiliate_referral_id
|
||||||
|
filter :free
|
||||||
|
|
||||||
|
form :partial => 'form'
|
||||||
|
|
||||||
|
scope("Non Free", default: true) { |scope| scope.where(free: false).order('created_at desc') }
|
||||||
|
scope("Free") { |scope| scope.where(free: true).order('created_at desc') }
|
||||||
|
|
||||||
|
index do
|
||||||
|
# default_actions # use this for all view/edit/delete links
|
||||||
|
|
||||||
|
column 'Product' do |oo| oo.product end
|
||||||
|
column "Partner" do |oo|
|
||||||
|
link_to("#{oo.affiliate_referral.display_name} #{oo.affiliate_referral_fee_in_cents ? "#{oo.affiliate_referral_fee_in_cents}\u00A2" : ''}", oo.affiliate_referral.admin_url, {:title => oo.affiliate_referral.display_name}) if oo.affiliate_referral
|
||||||
|
end
|
||||||
|
column 'User' do |oo|
|
||||||
|
link_to(oo.sale.user.name, admin_user_path(oo.sale.user.id), {:title => oo.sale.user.name})
|
||||||
|
end
|
||||||
|
column 'When' do |oo|
|
||||||
|
oo.created_at
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
controller do
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
ActiveAdmin.register JamRuby::UserBlacklist, :as => 'User Blacklist' do
|
||||||
|
|
||||||
|
menu :label => 'User Blacklist', :parent => 'Operations'
|
||||||
|
|
||||||
|
config.sort_order = 'created_at desc'
|
||||||
|
config.batch_actions = false
|
||||||
|
|
||||||
|
index do
|
||||||
|
column :user
|
||||||
|
column :notes
|
||||||
|
column :created_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -9,7 +9,7 @@ ActiveAdmin.register JamRuby::User, :as => 'User Progression' do
|
||||||
config.filters = false
|
config.filters = false
|
||||||
|
|
||||||
index do
|
index do
|
||||||
column :email do |user| link_to(truncate(user.email, {:length => 12}), resource_path(user), {:title => "#{user.first_name} #{user.last_name} (#{user.email})"}) end
|
column :email do |user| link_to(truncate(user.email, {:length => 12}), resource_path(user), {:title => "#{user.name} (#{user.email})"}) end
|
||||||
column :updated_at do |uu| uu.updated_at.strftime(PROGRESSION_DATE) end
|
column :updated_at do |uu| uu.updated_at.strftime(PROGRESSION_DATE) end
|
||||||
column :created_at do |uu| uu.created_at.strftime(PROGRESSION_DATE) end
|
column :created_at do |uu| uu.created_at.strftime(PROGRESSION_DATE) end
|
||||||
column :city
|
column :city
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,10 @@ class EmailController < ApplicationController
|
||||||
headers['Content-Type'] ||= 'text/csv'
|
headers['Content-Type'] ||= 'text/csv'
|
||||||
|
|
||||||
@users = User.where(subscribe_email: true)
|
@users = User.where(subscribe_email: true)
|
||||||
|
|
||||||
|
# if specified, return only users that have redeemed or bought a JamTrack
|
||||||
|
if params[:any_jam_track]
|
||||||
|
@users = @users.select('DISTINCT users.id, email, first_name, last_name').joins(:sales => :sale_line_items).where("sale_line_items.product_type = 'JamTrack'")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
<%= f.semantic_errors *f.object.errors.keys %>
|
<%= f.semantic_errors *f.object.errors.keys %>
|
||||||
<%= f.inputs do %>
|
<%= f.inputs do %>
|
||||||
<%= f.input(:partner_name, :input_html => {:maxlength => 128}) %>
|
<%= f.input(:partner_name, :input_html => {:maxlength => 128}) %>
|
||||||
|
<%= f.input(:entity_type, :as => :select, :collection => AffiliatePartner::ENTITY_TYPES) %>
|
||||||
|
<%= f.input(:rate) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= f.actions %>
|
<%= f.actions %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
= f.input :publisher, :input_html => { :rows=>1, :maxlength=>1000 }
|
= f.input :publisher, :input_html => { :rows=>1, :maxlength=>1000 }
|
||||||
= f.input :licensor, collection: JamRuby::JamTrackLicensor.all, include_blank: true
|
= f.input :licensor, collection: JamRuby::JamTrackLicensor.all, include_blank: true
|
||||||
= f.input :genres
|
= f.input :genres
|
||||||
|
= f.input :year
|
||||||
= f.input :duration, hint: 'this should rarely need editing because it comes from the import process'
|
= f.input :duration, hint: 'this should rarely need editing because it comes from the import process'
|
||||||
= f.input :sales_region, collection: JamRuby::JamTrack::SALES_REGION, 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'}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
<%- headers = ['email', 'name', 'unsubscribe_token'] -%>
|
<%- headers = ['email', 'name', 'unsubscribe_token'] -%>
|
||||||
<%= CSV.generate_line headers %><%- @users.each do |user| -%><%= CSV.generate_line([user.email, user.first_name, user.unsubscribe_token]) %><%- end -%>
|
<%= CSV.generate_line headers %><%- @users.each do |user| -%><%= CSV.generate_line([user.email, user.anonymous? ? '-' : user.first_name, user.unsubscribe_token]) %><%- end -%>
|
||||||
|
|
@ -110,6 +110,7 @@ module JamAdmin
|
||||||
|
|
||||||
config.redis_host = "localhost:6379"
|
config.redis_host = "localhost:6379"
|
||||||
|
|
||||||
|
config.email_social_alias = 'social@jamkazam.com'
|
||||||
config.email_alerts_alias = 'alerts@jamkazam.com' # should be used for 'oh no' server down/service down sorts of emails
|
config.email_alerts_alias = 'alerts@jamkazam.com' # should be used for 'oh no' server down/service down sorts of emails
|
||||||
config.email_generic_from = 'nobody@jamkazam.com'
|
config.email_generic_from = 'nobody@jamkazam.com'
|
||||||
config.email_smtp_address = 'smtp.sendgrid.net'
|
config.email_smtp_address = 'smtp.sendgrid.net'
|
||||||
|
|
@ -153,5 +154,12 @@ module JamAdmin
|
||||||
config.jmep_dir = ENV['JMEP_DIR'] || File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "jmep"))
|
config.jmep_dir = ENV['JMEP_DIR'] || File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "jmep"))
|
||||||
|
|
||||||
config.email_dump_code = 'rcAUyC3TZCbgGx4YQpznBRbNnQMXW5iKTzf9NSBfzMLsnw9dRQ'
|
config.email_dump_code = 'rcAUyC3TZCbgGx4YQpznBRbNnQMXW5iKTzf9NSBfzMLsnw9dRQ'
|
||||||
|
|
||||||
|
config.admin_port = ENV['ADMIN_PORT'] || 3333
|
||||||
|
config.admin_root_url = "#{config.external_protocol}#{config.external_hostname}#{(config.admin_port == 80 || config.admin_port == 443) ? '' : ':' + config.admin_port.to_s}"
|
||||||
|
|
||||||
|
config.download_tracker_day_range = 30
|
||||||
|
config.max_user_ip_address = 10
|
||||||
|
config.max_multiple_users_same_ip = 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -46,4 +46,5 @@ JamAdmin::Application.configure do
|
||||||
|
|
||||||
config.email_generic_from = 'nobody-dev@jamkazam.com'
|
config.email_generic_from = 'nobody-dev@jamkazam.com'
|
||||||
config.email_alerts_alias = 'alerts-dev@jamkazam.com'
|
config.email_alerts_alias = 'alerts-dev@jamkazam.com'
|
||||||
|
config.email_social_alias = 'social-dev@jamkazam.com'
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
class JamRuby::GiftCard
|
||||||
|
|
||||||
|
attr_accessor :csv
|
||||||
|
|
||||||
|
|
||||||
|
def process_csv
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -2,28 +2,5 @@ class JamRuby::JamTrack
|
||||||
|
|
||||||
# add a custom validation
|
# add a custom validation
|
||||||
|
|
||||||
attr_accessor :preview_generate_error
|
|
||||||
|
|
||||||
before_save :jmep_json_generate
|
|
||||||
validate :jmep_text_validate
|
|
||||||
|
|
||||||
def jmep_text_validate
|
|
||||||
begin
|
|
||||||
JmepManager.execute(self.jmep_text)
|
|
||||||
rescue ArgumentError => err
|
|
||||||
errors.add(:jmep_text, err.to_s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def jmep_json_generate
|
|
||||||
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
|
|
||||||
#errors.add(:jmep_text, err.to_s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,3 @@ PLATFORMS
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
pg_migrate (= 0.1.13)!
|
pg_migrate (= 0.1.13)!
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
1.10.5
|
|
||||||
|
|
|
||||||
20
db/manifest
20
db/manifest
|
|
@ -298,11 +298,25 @@ musician_search.sql
|
||||||
enhance_band_profile.sql
|
enhance_band_profile.sql
|
||||||
alter_band_profile_rate_defaults.sql
|
alter_band_profile_rate_defaults.sql
|
||||||
repair_band_profile.sql
|
repair_band_profile.sql
|
||||||
profile_teacher.sql
|
|
||||||
jam_track_onboarding_enhancements.sql
|
jam_track_onboarding_enhancements.sql
|
||||||
jam_track_name_drop_unique.sql
|
jam_track_name_drop_unique.sql
|
||||||
populate_languages.sql
|
|
||||||
populate_subjects.sql
|
|
||||||
jam_track_searchability.sql
|
jam_track_searchability.sql
|
||||||
harry_fox_agency.sql
|
harry_fox_agency.sql
|
||||||
jam_track_slug.sql
|
jam_track_slug.sql
|
||||||
|
mixdown.sql
|
||||||
|
aac_master.sql
|
||||||
|
video_recording.sql
|
||||||
|
web_playable_jamtracks.sql
|
||||||
|
affiliate_partner_rate.sql
|
||||||
|
track_downloads.sql
|
||||||
|
jam_track_lang_idx.sql
|
||||||
|
giftcard.sql
|
||||||
|
add_description_to_crash_dumps.sql
|
||||||
|
acappella.sql
|
||||||
|
purchasable_gift_cards.sql
|
||||||
|
versionable_jamtracks.sql
|
||||||
|
session_controller.sql
|
||||||
|
jam_tracks_bpm.sql
|
||||||
|
profile_teacher.sql
|
||||||
|
populate_languages.sql
|
||||||
|
populate_subjects.sql
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE jam_track_tracks ADD COLUMN preview_aac_url VARCHAR;
|
||||||
|
ALTER TABLE jam_track_tracks ADD COLUMN preview_aac_md5 VARCHAR;
|
||||||
|
ALTER TABLE jam_track_tracks ADD COLUMN preview_aac_length bigint;
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
INSERT INTO genres (id, description) values ('acapella', 'A Capella');
|
||||||
|
ALTER TABLE jam_track_licensors ADD COLUMN slug VARCHAR UNIQUE;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE crash_dumps ADD COLUMN description VARCHAR(20000);
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE affiliate_partners ADD COLUMN rate NUMERIC(8,2) DEFAULT 0.10;
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
ALTER TABLE crash_dumps ADD COLUMN email VARCHAR(255);
|
||||||
|
ALTER TABLE crash_dumps ADD COLUMN description VARCHAR(10000);
|
||||||
|
ALTER TABLE crash_dumps ADD COLUMN os VARCHAR(100);
|
||||||
|
ALTER TABLE crash_dumps ADD COLUMN os_version VARCHAR(100);
|
||||||
|
ALTER TABLE crash_dumps DROP CONSTRAINT crash_dumps_user_id_fkey;
|
||||||
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
CREATE TABLE gift_cards (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
code VARCHAR(64) UNIQUE NOT NULL,
|
||||||
|
user_id VARCHAR (64) REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
card_type VARCHAR(64) NOT NULL,
|
||||||
|
origin VARCHAR(200),
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX gift_card_user_id_idx ON gift_cards(user_id);
|
||||||
|
|
||||||
|
ALTER TABLE users ADD COLUMN gifted_jamtracks INTEGER DEFAULT 0;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
CREATE INDEX ON jam_tracks(language);
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE jam_tracks ADD COLUMN bpm numeric(8,3);
|
||||||
|
INSERT INTO instruments (id, description) VALUES ('percussion', 'Percussion');
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
CREATE TABLE jam_track_mixdowns (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
jam_track_id VARCHAR(64) NOT NULL REFERENCES jam_tracks(id) ON DELETE CASCADE,
|
||||||
|
user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
settings JSON NOT NULL,
|
||||||
|
name VARCHAR(1000) NOT NULL,
|
||||||
|
description VARCHAR(1000),
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE jam_track_mixdown_packages (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
jam_track_mixdown_id VARCHAR(64) NOT NULL REFERENCES jam_track_mixdowns(id) ON DELETE CASCADE,
|
||||||
|
file_type VARCHAR NOT NULL ,
|
||||||
|
sample_rate INTEGER NOT NULL,
|
||||||
|
url VARCHAR(2048),
|
||||||
|
md5 VARCHAR,
|
||||||
|
length INTEGER,
|
||||||
|
downloaded_since_sign BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
last_step_at TIMESTAMP,
|
||||||
|
last_signed_at TIMESTAMP,
|
||||||
|
download_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
signed_at TIMESTAMP,
|
||||||
|
downloaded_at TIMESTAMP,
|
||||||
|
signing_queued_at TIMESTAMP,
|
||||||
|
error_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
error_reason VARCHAR,
|
||||||
|
error_detail VARCHAR,
|
||||||
|
should_retry BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
packaging_steps INTEGER,
|
||||||
|
current_packaging_step INTEGER,
|
||||||
|
private_key VARCHAR,
|
||||||
|
signed BOOLEAN,
|
||||||
|
signing_started_at TIMESTAMP,
|
||||||
|
first_downloaded TIMESTAMP,
|
||||||
|
signing BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
encrypt_type VARCHAR,
|
||||||
|
first_downloaded_at TIMESTAMP,
|
||||||
|
last_downloaded_at TIMESTAMP,
|
||||||
|
version VARCHAR NOT NULL DEFAULT '1',
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE jam_track_rights ADD COLUMN last_mixdown_id VARCHAR(64) REFERENCES jam_track_mixdowns(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
ALTER TABLE notifications ADD COLUMN jam_track_mixdown_package_id VARCHAR(64) REFERENCES jam_track_mixdown_packages(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE jam_track_mixdown_packages ADD COLUMN last_errored_at TIMESTAMP;
|
||||||
|
ALTER TABLE jam_track_mixdown_packages ADD COLUMN queued BOOLEAN DEFAULT FALSE;
|
||||||
|
ALTER TABLE jam_track_mixdown_packages ADD COLUMN speed_pitched BOOLEAN DEFAULT FALSE;
|
||||||
|
ALTER TABLE jam_track_rights ADD COLUMN queued BOOLEAN DEFAULT FALSE;
|
||||||
|
|
||||||
|
CREATE INDEX jam_track_rights_queued ON jam_track_rights(queued);
|
||||||
|
CREATE INDEX jam_track_rights_signing_queued ON jam_track_rights(signing_queued_at);
|
||||||
|
CREATE INDEX jam_track_rights_updated ON jam_track_rights(updated_at);
|
||||||
|
|
||||||
|
CREATE INDEX jam_track_mixdown_packages_queued ON jam_track_mixdown_packages(queued);
|
||||||
|
CREATE INDEX jam_track_mixdown_packages_signing_queued ON jam_track_mixdown_packages(signing_queued_at);
|
||||||
|
CREATE INDEX jam_track_mixdown_packages_updated ON jam_track_mixdown_packages(updated_at);
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE gift_card_types (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
card_type VARCHAR(64) NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO gift_card_types (id, card_type) VALUES ('jam_tracks_5', 'jam_tracks_5');
|
||||||
|
INSERT INTO gift_card_types (id, card_type) VALUES ('jam_tracks_10', 'jam_tracks_10');
|
||||||
|
|
||||||
|
CREATE TABLE gift_card_purchases (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE SET NULL,
|
||||||
|
gift_card_type_id VARCHAR(64) REFERENCES gift_card_types(id) ON DELETE SET NULL,
|
||||||
|
recurly_adjustment_uuid VARCHAR(500),
|
||||||
|
recurly_adjustment_credit_uuid VARCHAR(500),
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE sale_line_items ADD COLUMN gift_card_purchase_id VARCHAR(64) REFERENCES gift_card_purchases(id);
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE music_sessions ADD COLUMN session_controller_id VARCHAR(64) REFERENCES users(id);
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
CREATE TABLE download_trackers (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
remote_ip VARCHAR(400) NOT NULL,
|
||||||
|
jam_track_id VARCHAR (64) NOT NULL REFERENCES jam_tracks(id) ON DELETE CASCADE,
|
||||||
|
paid BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX index_download_trackers_on_user_id ON download_trackers USING btree (user_id);
|
||||||
|
CREATE INDEX index_download_trackers_on_remote_ip ON download_trackers USING btree (remote_ip);
|
||||||
|
CREATE INDEX index_download_trackers_on_created_at ON download_trackers USING btree (created_at, paid);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE ip_blacklists (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
remote_ip VARCHAR(400) UNIQUE NOT NULL,
|
||||||
|
notes VARCHAR,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE user_blacklists (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
user_id VARCHAR(64) UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
notes VARCHAR,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE jam_track_rights ADD COLUMN version VARCHAR NOT NULL DEFAULT '0';
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE recordings ADD video BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE user_authorizations ADD refresh_token VARCHAR;
|
||||||
|
ALTER TABLE recordings ADD external_video_id VARCHAR;
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
ALTER TABLE users ADD COLUMN first_opened_jamtrack_web_player TIMESTAMP;
|
||||||
|
ALTER TABLE jam_track_rights ADD COLUMN last_stem_id VARCHAR(64) REFERENCES jam_track_tracks(id) ON DELETE SET NULL;
|
||||||
|
ALTER TABLE jam_track_tracks ADD COLUMN url_mp3_48 VARCHAR;
|
||||||
|
ALTER TABLE jam_track_tracks ADD COLUMN md5_mp3_48 VARCHAR;
|
||||||
|
ALTER TABLE jam_track_tracks ADD COLUMN length_mp3_48 BIGINT;
|
||||||
|
ALTER TABLE jam_track_tracks ADD COLUMN url_aac_48 VARCHAR;
|
||||||
|
ALTER TABLE jam_track_tracks ADD COLUMN md5_aac_48 VARCHAR;
|
||||||
|
ALTER TABLE jam_track_tracks ADD COLUMN length_aac_48 BIGINT;
|
||||||
|
|
||||||
|
CREATE TABLE user_events (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
user_id VARCHAR(64) REFERENCES users(id) ON DELETE SET NULL,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
detail JSON,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
@ -82,6 +82,10 @@ message ClientMessage {
|
||||||
JAM_TRACK_SIGN_COMPLETE = 260;
|
JAM_TRACK_SIGN_COMPLETE = 260;
|
||||||
JAM_TRACK_SIGN_FAILED = 261;
|
JAM_TRACK_SIGN_FAILED = 261;
|
||||||
|
|
||||||
|
// jamtracks mixdown notifications
|
||||||
|
MIXDOWN_SIGN_COMPLETE = 270;
|
||||||
|
MIXDOWN_SIGN_FAILED = 271;
|
||||||
|
|
||||||
TEST_SESSION_MESSAGE = 295;
|
TEST_SESSION_MESSAGE = 295;
|
||||||
|
|
||||||
PING_REQUEST = 300;
|
PING_REQUEST = 300;
|
||||||
|
|
@ -188,6 +192,10 @@ message ClientMessage {
|
||||||
optional JamTrackSignComplete jam_track_sign_complete = 260;
|
optional JamTrackSignComplete jam_track_sign_complete = 260;
|
||||||
optional JamTrackSignFailed jam_track_sign_failed = 261;
|
optional JamTrackSignFailed jam_track_sign_failed = 261;
|
||||||
|
|
||||||
|
// jamtrack mixdown notification
|
||||||
|
optional MixdownSignComplete mixdown_sign_complete = 270;
|
||||||
|
optional MixdownSignFailed mixdown_sign_failed = 271;
|
||||||
|
|
||||||
|
|
||||||
// Client-Session messages (to/from)
|
// Client-Session messages (to/from)
|
||||||
optional TestSessionMessage test_session_message = 295;
|
optional TestSessionMessage test_session_message = 295;
|
||||||
|
|
@ -612,6 +620,15 @@ message JamTrackSignFailed {
|
||||||
required int32 jam_track_right_id = 1; // jam track right id
|
required int32 jam_track_right_id = 1; // jam track right id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message MixdownSignComplete {
|
||||||
|
required string mixdown_package_id = 1; // jam track mixdown package id
|
||||||
|
}
|
||||||
|
|
||||||
|
message MixdownSignFailed {
|
||||||
|
required string mixdown_package_id = 1; // jam track mixdown package id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
message SubscriptionMessage {
|
message SubscriptionMessage {
|
||||||
optional string type = 1; // the type of the subscription
|
optional string type = 1; // the type of the subscription
|
||||||
optional string id = 2; // data about what to subscribe to, specifically
|
optional string id = 2; // data about what to subscribe to, specifically
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ require "jam_ruby/resque/scheduled/jam_tracks_cleaner"
|
||||||
require "jam_ruby/resque/scheduled/stats_maker"
|
require "jam_ruby/resque/scheduled/stats_maker"
|
||||||
require "jam_ruby/resque/scheduled/tally_affiliates"
|
require "jam_ruby/resque/scheduled/tally_affiliates"
|
||||||
require "jam_ruby/resque/jam_tracks_builder"
|
require "jam_ruby/resque/jam_tracks_builder"
|
||||||
|
require "jam_ruby/resque/jam_track_mixdown_packager"
|
||||||
require "jam_ruby/resque/google_analytics_event"
|
require "jam_ruby/resque/google_analytics_event"
|
||||||
require "jam_ruby/resque/batch_email_job"
|
require "jam_ruby/resque/batch_email_job"
|
||||||
require "jam_ruby/resque/long_running"
|
require "jam_ruby/resque/long_running"
|
||||||
|
|
@ -105,10 +106,14 @@ require "jam_ruby/models/max_mind_release"
|
||||||
require "jam_ruby/models/genre_player"
|
require "jam_ruby/models/genre_player"
|
||||||
require "jam_ruby/models/genre"
|
require "jam_ruby/models/genre"
|
||||||
require "jam_ruby/models/user"
|
require "jam_ruby/models/user"
|
||||||
|
require "jam_ruby/models/user_event"
|
||||||
require "jam_ruby/models/anonymous_user"
|
require "jam_ruby/models/anonymous_user"
|
||||||
require "jam_ruby/models/signup_hint"
|
require "jam_ruby/models/signup_hint"
|
||||||
require "jam_ruby/models/machine_fingerprint"
|
require "jam_ruby/models/machine_fingerprint"
|
||||||
require "jam_ruby/models/machine_extra"
|
require "jam_ruby/models/machine_extra"
|
||||||
|
require "jam_ruby/models/download_tracker"
|
||||||
|
require "jam_ruby/models/ip_blacklist"
|
||||||
|
require "jam_ruby/models/user_blacklist"
|
||||||
require "jam_ruby/models/fraud_alert"
|
require "jam_ruby/models/fraud_alert"
|
||||||
require "jam_ruby/models/fingerprint_whitelist"
|
require "jam_ruby/models/fingerprint_whitelist"
|
||||||
require "jam_ruby/models/rsvp_request"
|
require "jam_ruby/models/rsvp_request"
|
||||||
|
|
@ -209,6 +214,8 @@ require "jam_ruby/models/jam_track_track"
|
||||||
require "jam_ruby/models/jam_track_right"
|
require "jam_ruby/models/jam_track_right"
|
||||||
require "jam_ruby/models/jam_track_tap_in"
|
require "jam_ruby/models/jam_track_tap_in"
|
||||||
require "jam_ruby/models/jam_track_file"
|
require "jam_ruby/models/jam_track_file"
|
||||||
|
require "jam_ruby/models/jam_track_mixdown"
|
||||||
|
require "jam_ruby/models/jam_track_mixdown_package"
|
||||||
require "jam_ruby/models/genre_jam_track"
|
require "jam_ruby/models/genre_jam_track"
|
||||||
require "jam_ruby/app/mailers/async_mailer"
|
require "jam_ruby/app/mailers/async_mailer"
|
||||||
require "jam_ruby/app/mailers/batch_mailer"
|
require "jam_ruby/app/mailers/batch_mailer"
|
||||||
|
|
@ -249,6 +256,10 @@ require "jam_ruby/models/language"
|
||||||
require "jam_ruby/models/subject"
|
require "jam_ruby/models/subject"
|
||||||
require "jam_ruby/models/band_search"
|
require "jam_ruby/models/band_search"
|
||||||
require "jam_ruby/import/tency_stem_mapping"
|
require "jam_ruby/import/tency_stem_mapping"
|
||||||
|
require "jam_ruby/models/jam_track_search"
|
||||||
|
require "jam_ruby/models/gift_card"
|
||||||
|
require "jam_ruby/models/gift_card_purchase"
|
||||||
|
require "jam_ruby/models/gift_card_type"
|
||||||
|
|
||||||
include Jampb
|
include Jampb
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
||||||
module JamRuby
|
module JamRuby
|
||||||
# sends out a boring ale
|
# sends out a boring ale
|
||||||
class AdminMailer < ActionMailer::Base
|
class AdminMailer < ActionMailer::Base
|
||||||
include SendGrid
|
include SendGrid
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -20,6 +20,14 @@ module JamRuby
|
||||||
subject: options[:subject])
|
subject: options[:subject])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def social(options)
|
||||||
|
mail(to: APP_CONFIG.email_social_alias,
|
||||||
|
from: APP_CONFIG.email_generic_from,
|
||||||
|
body: options[:body],
|
||||||
|
content_type: "text/plain",
|
||||||
|
subject: options[:subject])
|
||||||
|
end
|
||||||
|
|
||||||
def recurly_alerts(user, options)
|
def recurly_alerts(user, options)
|
||||||
|
|
||||||
body = options[:body]
|
body = options[:body]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
<% provide(:title, 'Confirm Email') %>
|
<% provide(:title, 'Welcome to JamKazam!') %>
|
||||||
|
|
||||||
<p>Welcome to JamKazam, <%= @user.first_name %>!</p>
|
<p>We’re delighted you have joined our community of 20,000+ musicians. We’d like to send you an orientation email with information and resource links that will help you get the most out of JamKazam. Please <a style="color: #ffcc00;" href="<%= @signup_confirm_url %>">click here to confirm this email</a> has reached you successfully and we will then send the orientation email.</p>
|
||||||
|
|
||||||
<p>To confirm this email address, please go to the <a style="color: #ffcc00;" href="<%= @signup_confirm_url %>">signup confirmation page</a>.</p>
|
<p>If you have received this email but aren’t familiar with JamKazam or JamTracks, then someone has registered at our website using your email address, and you can just ignore and delete this email.</p>
|
||||||
|
|
||||||
|
<p>Best Regards,<br/>
|
||||||
|
Team JamKazam
|
||||||
|
</p>
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
Welcome to JamKazam, <%= @user.first_name %>!
|
Welcome to JamKazam!
|
||||||
|
|
||||||
To confirm this email address, please go to the signup confirmation page at: <%= @signup_confirm_url %>.
|
We’re delighted you have joined our community of 20,000+ musicians. We’d like to send you an orientation email with information and resource links that will help you get the most out of JamKazam. Please click <%= @signup_confirm_url %> to confirm this email has reached you successfully and we will then send the orientation email.
|
||||||
|
|
||||||
|
If you have received this email but aren’t familiar with JamKazam or JamTracks, then someone has registered at our website using your email address, and you can just ignore and delete this email.
|
||||||
|
|
||||||
|
Best Regards,
|
||||||
|
Team JamKazam
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<% provide(:title, 'New Musicians You Should Check Out') %>
|
<% provide(:title, 'New Musicians You Should Check Out') %>
|
||||||
Hi <%= @user.first_name %>,
|
<% if !@user.anonymous? %>
|
||||||
|
<p>Hi <%= @user.first_name %>,</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<p>The following new musicians have joined JamKazam within the last week, and have Internet connections with low enough latency to you that you can have a good online session together. We'd suggest that you look through the new musicians listed below to see if any match your musical interests, and if so, click through to their profile page on the JamKazam website to send them a message or a request to connect as a JamKazam friend:
|
<p>The following new musicians have joined JamKazam within the last week, and have Internet connections with low enough latency to you that you can have a good online session together. We'd suggest that you look through the new musicians listed below to see if any match your musical interests, and if so, click through to their profile page on the JamKazam website to send them a message or a request to connect as a JamKazam friend:
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
New Musicians You Should Check Out
|
New Musicians You Should Check Out
|
||||||
|
|
||||||
|
<% if !@user.anonymous? %>
|
||||||
Hi <%= @user.first_name %>,
|
Hi <%= @user.first_name %>,
|
||||||
|
<% end %>
|
||||||
The following new musicians have joined JamKazam within the last week, and have Internet connections with low enough latency to you that you can have a good online session together. We'd suggest that you look through the new musicians listed below to see if any match your musical interests, and if so, click through to their profile page on the JamKazam website to send them a message or a request to connect as a JamKazam friend:
|
The following new musicians have joined JamKazam within the last week, and have Internet connections with low enough latency to you that you can have a good online session together. We'd suggest that you look through the new musicians listed below to see if any match your musical interests, and if so, click through to their profile page on the JamKazam website to send them a message or a request to connect as a JamKazam friend:
|
||||||
|
|
||||||
<% @new_musicians.each do |user| %>
|
<% @new_musicians.each do |user| %>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
<% provide(:title, @title) %>
|
<% provide(:title, @title) %>
|
||||||
|
|
||||||
<p>Hello <%= @user.first_name %> --
|
<% if !@user.anonymous? %>
|
||||||
</p>
|
<p>Hi <%= @user.first_name %>,</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<p>The following new sessions have been posted within the last 24 hours, and you have good or acceptable latency to the organizer of each session below. If a session looks interesting, click the Details link to see the session page. You can RSVP to a session from the session page, and you'll be notified if/when the session organizer approves your RSVP.</p>
|
<p>The following new sessions have been posted within the last 24 hours, and you have good or acceptable latency to the organizer of each session below. If a session looks interesting, click the Details link to see the session page. You can RSVP to a session from the session page, and you'll be notified if/when the session organizer approves your RSVP.</p>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
<% provide(:title, @title) %>
|
<% provide(:title, @title) %>
|
||||||
|
|
||||||
Hello <%= @user.first_name %> --
|
<% if !@user.anonymous? %>
|
||||||
|
Hi <%= @user.first_name %>,
|
||||||
|
<% end %>
|
||||||
The following new sessions have been posted within the last 24 hours, and you have good or acceptable latency to the organizer of each session below. If a session looks interesting, click the Details link to see the session page. You can RSVP to a session from the session page, and you'll be notified if/when the session organizer approves your RSVP.
|
The following new sessions have been posted within the last 24 hours, and you have good or acceptable latency to the organizer of each session below. If a session looks interesting, click the Details link to see the session page. You can RSVP to a session from the session page, and you'll be notified if/when the session organizer approves your RSVP.
|
||||||
|
|
||||||
GENRE | NAME | DESCRIPTION | LATENCY
|
GENRE | NAME | DESCRIPTION | LATENCY
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
<% provide(:title, 'JamKazam Session Reminder') %>
|
<% provide(:title, 'JamKazam Session Reminder') %>
|
||||||
|
|
||||||
|
|
||||||
<div>
|
<% if !@user.anonymous? %>
|
||||||
|
<p>
|
||||||
Hi <%= @user.first_name %>,
|
Hi <%= @user.first_name %>,
|
||||||
</div>
|
</p>
|
||||||
|
<% end %>
|
||||||
<br/>
|
<br/>
|
||||||
<div>
|
<p>
|
||||||
<span>This is a reminder that your JamKazam session</span>
|
<span>This is a reminder that your JamKazam session</span>
|
||||||
<a href='<%=@session_url%>'><%= @session_name %></a>
|
<a href='<%=@session_url%>'><%= @session_name %></a>
|
||||||
<span>is scheduled for tomorrow. We hope you have fun!</span>
|
<span>is scheduled for tomorrow. We hope you have fun!</span>
|
||||||
</div>
|
</p>
|
||||||
<br/>
|
<br/>
|
||||||
<div>
|
<p>
|
||||||
Best Regards,
|
Best Regards,
|
||||||
<br/>
|
<br/>
|
||||||
Team JamKazam
|
Team JamKazam
|
||||||
</div>
|
</p>
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
<% if !@user.anonymous? %>
|
||||||
Hi <%= @user.first_name %>,
|
Hi <%= @user.first_name %>,
|
||||||
|
<% end %>
|
||||||
This is a reminder that your JamKazam session <%=@session_name%> is scheduled for tomorrow. We hope you have fun!
|
This is a reminder that your JamKazam session <%=@session_name%> is scheduled for tomorrow. We hope you have fun!
|
||||||
|
|
||||||
Best Regards,
|
Best Regards,
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
<% provide(:title, 'Your JamKazam session starts in 1 hour!') %>
|
<% provide(:title, 'Your JamKazam session starts in 1 hour!') %>
|
||||||
|
|
||||||
<div>
|
<% if !@user.anonymous? %>
|
||||||
|
<p>
|
||||||
Hi <%= @user.first_name %>,
|
Hi <%= @user.first_name %>,
|
||||||
</div>
|
</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<div>
|
<p>
|
||||||
<span>This is a reminder that your JamKazam session</span>
|
<span>This is a reminder that your JamKazam session</span>
|
||||||
<a href='<%=@session_url%>'><%= @session_name %></a>
|
<a href='<%=@session_url%>'><%= @session_name %></a>
|
||||||
<span>starts in 1 hour. We hope you have fun!</span>
|
<span>starts in 1 hour. We hope you have fun!</span>
|
||||||
</div>
|
</p>
|
||||||
<br/>
|
<br/>
|
||||||
<div>
|
<p>
|
||||||
Best Regards,
|
Best Regards,
|
||||||
<br/>
|
<br/>
|
||||||
Team JamKazam
|
Team JamKazam
|
||||||
</div>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
<% if !@user.anonymous? %>
|
||||||
Hi <%= @user.first_name %>,
|
Hi <%= @user.first_name %>,
|
||||||
|
<% end %>
|
||||||
|
|
||||||
This is a reminder that your JamKazam session
|
This is a reminder that your JamKazam session
|
||||||
<%=@session_name%>
|
<%=@session_name%>
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,118 @@
|
||||||
<% provide(:title, 'Welcome to JamKazam!') %>
|
<% provide(:title, 'Welcome to JamKazam!') %>
|
||||||
|
|
||||||
|
|
||||||
|
<% if !@user.anonymous? %>
|
||||||
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
|
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
|
||||||
</p>
|
</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
<p> We're delighted that you have decided to try the JamKazam service,
|
<p> We're delighted you have decided to join the JamKazam community of musicians, and we hope
|
||||||
and we hope that you will enjoy using JamKazam to play
|
|
||||||
music with others.
|
you will enjoy using JamKazam and JamTracks to play more music. Following are some
|
||||||
Following are some resources that can help you get oriented and get the most out of JamKazam.
|
|
||||||
|
resources and some things you can do to get the most out of JamKazam.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p><b style="color: white;">Playing With JamTracks</b><br/>
|
||||||
|
|
||||||
|
JamTracks are the best way to play along with your favorite songs. Far better and different than
|
||||||
|
|
||||||
|
traditional backing tracks, our JamTracks are complete multi-track professional recordings, with
|
||||||
|
|
||||||
|
fully isolated tracks for each part of the music. And our free app and Internet service are packed
|
||||||
|
|
||||||
|
with features that give you unmatched creative freedom to learn, practice, record, play with
|
||||||
|
|
||||||
|
others, and share your performances. Here are some great JamTracks resources:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li><a style="color:#fc0" href="https://www.youtube.com/watch?v=07zJC7C2ICA">JamTracks Overview Video</a> - See all the great things you can do with JamTracks.</li>
|
||||||
|
<li><a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/articles/2124663-playing-with-jamtracks">JamTracks User Guide</a> - A set of articles that explain how to use all the JamTracks
|
||||||
|
|
||||||
|
features.</li>
|
||||||
|
<li><a style="color:#fc0" href="https://www.jamkazam.com/client#/jamtrack">Get a JamTrack Free</a> - A web page you can visit to search our catalog of JamTracks.
|
||||||
|
|
||||||
|
When you find a song you like, click the Get It Free button, and your first one is free! If
|
||||||
|
|
||||||
|
you already redeemed a free JamTrack or purchased JamTracks, you can also access
|
||||||
|
|
||||||
|
them on this page from your web browser.</li>
|
||||||
|
<li><a style="color:#fc0" href="https://www.jamkazam.com/downloads">JamKazam Application</a> - A web page where you can download our free Mac or Windows
|
||||||
|
|
||||||
|
app. The app lets you do a lot more with JamTracks than you can do in a browser.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p><b style="color: white;">Play Live With Others from Different Locations on JamKazam</b><br/>
|
||||||
|
JamKazam’s free app lets musicians play together live and in sync from different locations over
|
||||||
|
|
||||||
|
the Internet. Kind of like Skype on super audio steroids, with ultra low latency and terrific audio
|
||||||
|
|
||||||
|
quality. You can set up online sessions that are public or private, for you alone or for others to
|
||||||
|
|
||||||
|
join. You can find and join others’ sessions, use backing tracks and loops in sessions, make
|
||||||
|
|
||||||
|
audio and video recordings of session performances, and more. <a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/topics/673198-tutorials-on-major-features/articles">Click here for a set of tutorial
|
||||||
|
|
||||||
|
videos that show how to use these features</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b style="color: white;">Teach or Take Online Music Lessons</b><br/>
|
||||||
|
If you teach music lessons and have tried to give lessons using Skype, you’ll know how
|
||||||
|
|
||||||
|
unsatisfactory that experience is. Audio quality is poor, and latency prohibits teacher and
|
||||||
|
|
||||||
|
student playing together at all. JamKazam is a terrific service for teaching and taking online
|
||||||
|
|
||||||
|
music lessons. If you want to use JamKazam for lessons, we’ll be happy to support both you and
|
||||||
|
|
||||||
|
your students in getting set up and ready to go.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b style="color: white;">Complete Your Profile</b><br/>
|
||||||
|
Every member of our community has a profile. It’s a great way to share a little bit about who
|
||||||
|
|
||||||
|
you are as a musician, as well as your musical interests. For example, what instruments do you
|
||||||
|
|
||||||
|
play? What musical genres do you like best? Are you interested in getting into a virtual/online
|
||||||
|
|
||||||
|
or a real-world band? And so on. Filling out your profile will help you connect with others with
|
||||||
|
|
||||||
|
common interests. To do this, go to <a style="color:#fc0" href="https://www.jamkazam.com/client">www.jamkazam.com</a> or launch the JamKazam app. Then
|
||||||
|
|
||||||
|
click on the Profile tile, and click the Edit Profile button.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b style="color: white;">Invite Your Friends</b><br/>
|
||||||
|
Have friends who are musicians? Invite them to join you to play together on JamKazam. To do
|
||||||
|
|
||||||
|
this, go to <a style="color:#fc0" href="https://www.jamkazam.com/client">www.jamkazam.com</a> or launch the JamKazam app. Then move your mouse over the
|
||||||
|
|
||||||
|
user icon in the top right corner of the screen. A menu will be displayed. Click Invite Friends in
|
||||||
|
|
||||||
|
this menu, and you can then use the options to invite your friends using their email addresses,
|
||||||
|
|
||||||
|
or via Facebook, or using your Google contacts.
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
<p><b style="color: white;">Getting Started</b><br/>
|
<p><b style="color: white;">Get Help</b><br/>
|
||||||
There are basically three kinds of setups you can use to play on JamKazam.<br/>
|
|
||||||
<ul>
|
|
||||||
<li><b style="color: white;">Built-In Audio on Your Computer</b> - You can use a Windows or Mac computer, and just use the built-in mic and headphone jack to
|
|
||||||
handle your audio. This is cheap and easy, but your audio quality will suffer, and it will also process audio very slowly,
|
|
||||||
creating problems with latency, or lag, in your sessions. Still, you can at least start experimenting with JamKazam in this way.</li>
|
|
||||||
<li><b style="color: white;">Computer with External Audio Interface</b> - You can use a Windows or Mac computer with an external audio interface that you
|
|
||||||
already own and use for recording, if you happen to have one already. If you are going to do this, or use the built-in mic/headphones on your computer, please refer
|
|
||||||
to our <a style="color: #ffcc00;" href="https://jamkazam.desk.com/customer/portal/articles/1288274-minimum-system-requirements">Minimum System Requirements</a>
|
|
||||||
to make sure your computer will work. These requirements were on the download page for the app, but you may have sped by them. Also, we'd recommend watching our
|
|
||||||
<a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=DBo--aj_P1w">Getting Started Video</a> to learn more about your options here.</li>
|
|
||||||
<li><b style="color: white;">The JamBlaster</b> - JamKazam has designed a new product from the ground up to be the best way to play music online in real time. It's called the JamBlaster.
|
|
||||||
It processes audio faster than any of the thousands of combinations of computers and interfaces in use on JamKazam today, which means you can play with musicians
|
|
||||||
who are farther away from you, and closer sessions will feel/sound tighter. The JamBlaster is both a computer and an audio interface, so it also eliminates the
|
|
||||||
system requirements worries, and it "just works" so you don't have to be an audio and computer genius to get it working. This is a great product - available only
|
|
||||||
through a Kickstarter program running during a 30-day window during parts of February and March 2015. You can watch the
|
|
||||||
<a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=gAJAIHMyois">JamBlaster Video</a> to learn more about this amazing new product.</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p><b style="color: white;">JamKazam Features</b><br/>
|
If you run into trouble and need help, please reach out to us. We will be glad to do everything
|
||||||
JamKazam offers a very robust and exciting set of features for playing online and sharing your performances with others. Here are some videos you can watch
|
|
||||||
to easily get up to speed on some of the things you can do with JamKazam:<br/>
|
|
||||||
<ul>
|
|
||||||
<li><a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=EZZuGcDUoWk">Creating a Session</a></li>
|
|
||||||
<li><a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=xWponSJo-GU">Finding a Session</a></li>
|
|
||||||
<li><a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=zJ68hA8-fLA">Playing in a Session</a></li>
|
|
||||||
<li><a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=4KWklSZZxRc">Connecting with Other Musicians</a></li>
|
|
||||||
<li><a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=Gn-dOqnNLoY">Working with Recordings</a></li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p><b style="color: white;">Getting Help</b><br/>
|
we can to answer your questions and get you up and running. You can visit our <a style="color:#fc0" href="https://jamkazam.desk.com/">Support Portal</a>
|
||||||
If you run into trouble and need help, please reach out to us. We will be glad to do everything we can to answer your questions and get you up and running.
|
|
||||||
You can visit our
|
to find knowledge base articles and post questions that have not already been answered. You
|
||||||
<a style="color: #ffcc00;" href="https://jamkazam.desk.com/">Support Portal</a>
|
|
||||||
to find knowledge base articles and post questions that have
|
can email us at <a style="color:#fc0" href="mailto:support@jamkazam.com">support@jamkazam.com</a>. And if you just want to chat, share tips and war
|
||||||
not already been answered. You can email us at support@jamkazam.com. And if you just want to chat, share tips and war stories, and hang out with fellow JamKazamers,
|
|
||||||
you can visit our <a style="color: #ffcc00;" href="http://forums.jamkazam.com/">Community Forum</a>
|
stories, and hang out with fellow JamKazamers, you can visit our <a style="color:#fc0" href="http://forums.jamkazam.com/">Community Forum</a>.
|
||||||
.
|
<p>
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Again, welcome to JamKazam, and we look forward to seeing – and hearing – you online soon!
|
<br/>
|
||||||
|
<br/>
|
||||||
|
Again, welcome to JamKazam, and we hope you have a great time here!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>Best Regards,<br/>
|
<p>Best Regards,<br/>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
|
<% if !@user.anonymous? %>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --<% end %>
|
||||||
|
|
||||||
We're delighted that you have decided to try the JamKazam service, and we hope that you will enjoy using JamKazam to play music with others. Following are some resources that can help you get oriented and get the most out of JamKazam.
|
We're delighted that you have decided to try the JamKazam service, and we hope that you will enjoy using JamKazam to play music with others. Following are some resources that can help you get oriented and get the most out of JamKazam.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,13 @@ module JamRuby
|
||||||
|
|
||||||
##### TODO: refactored to notification.rb but left here for backwards compatibility w/ connection_manager_spec.rb
|
##### TODO: refactored to notification.rb but left here for backwards compatibility w/ connection_manager_spec.rb
|
||||||
def gather_friends(connection, user_id)
|
def gather_friends(connection, user_id)
|
||||||
friend_ids = []
|
friend_ids = []
|
||||||
connection.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results|
|
connection.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results|
|
||||||
friend_results.each do |friend_result|
|
friend_results.each do |friend_result|
|
||||||
friend_ids.push(friend_result['friend_id'])
|
friend_ids.push(friend_result['friend_id'])
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return friend_ids
|
end
|
||||||
|
return friend_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
# this simulates music_session destroy callbacks with activerecord
|
# this simulates music_session destroy callbacks with activerecord
|
||||||
|
|
@ -42,7 +42,7 @@ module JamRuby
|
||||||
music_session = ActiveMusicSession.find_by_id(music_session_id)
|
music_session = ActiveMusicSession.find_by_id(music_session_id)
|
||||||
music_session.before_destroy if music_session
|
music_session.before_destroy if music_session
|
||||||
end
|
end
|
||||||
|
|
||||||
# reclaim the existing connection, if ip_address is not nil then perhaps a new address as well
|
# reclaim the existing connection, if ip_address is not nil then perhaps a new address as well
|
||||||
def reconnect(conn, channel_id, reconnect_music_session_id, ip_address, connection_stale_time, connection_expire_time, udp_reachable, gateway)
|
def reconnect(conn, channel_id, reconnect_music_session_id, ip_address, connection_stale_time, connection_expire_time, udp_reachable, gateway)
|
||||||
music_session_id = nil
|
music_session_id = nil
|
||||||
|
|
@ -65,11 +65,19 @@ module JamRuby
|
||||||
|
|
||||||
isp = JamIsp.lookup(addr)
|
isp = JamIsp.lookup(addr)
|
||||||
#puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============")
|
#puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============")
|
||||||
if isp.nil? then ispid = 0 else ispid = isp.coid end
|
if isp.nil? then
|
||||||
|
ispid = 0
|
||||||
|
else
|
||||||
|
ispid = isp.coid
|
||||||
|
end
|
||||||
|
|
||||||
block = GeoIpBlocks.lookup(addr)
|
block = GeoIpBlocks.lookup(addr)
|
||||||
#puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============")
|
#puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============")
|
||||||
if block.nil? then locid = 0 else locid = block.locid end
|
if block.nil? then
|
||||||
|
locid = 0
|
||||||
|
else
|
||||||
|
locid = block.locid
|
||||||
|
end
|
||||||
|
|
||||||
location = GeoIpLocations.find_by_locid(locid)
|
location = GeoIpLocations.find_by_locid(locid)
|
||||||
if location.nil? || isp.nil? || block.nil?
|
if location.nil? || isp.nil? || block.nil?
|
||||||
|
|
@ -183,11 +191,19 @@ SQL
|
||||||
|
|
||||||
isp = JamIsp.lookup(addr)
|
isp = JamIsp.lookup(addr)
|
||||||
#puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============")
|
#puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============")
|
||||||
if isp.nil? then ispid = 0 else ispid = isp.coid end
|
if isp.nil? then
|
||||||
|
ispid = 0
|
||||||
|
else
|
||||||
|
ispid = isp.coid
|
||||||
|
end
|
||||||
|
|
||||||
block = GeoIpBlocks.lookup(addr)
|
block = GeoIpBlocks.lookup(addr)
|
||||||
#puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============")
|
#puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============")
|
||||||
if block.nil? then locid = 0 else locid = block.locid end
|
if block.nil? then
|
||||||
|
locid = 0
|
||||||
|
else
|
||||||
|
locid = block.locid
|
||||||
|
end
|
||||||
|
|
||||||
location = GeoIpLocations.find_by_locid(locid)
|
location = GeoIpLocations.find_by_locid(locid)
|
||||||
if location.nil? || isp.nil? || block.nil?
|
if location.nil? || isp.nil? || block.nil?
|
||||||
|
|
@ -199,11 +215,11 @@ SQL
|
||||||
lock_connections(conn)
|
lock_connections(conn)
|
||||||
|
|
||||||
conn.exec("INSERT INTO connections (user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, aasm_state, stale_time, expire_time, udp_reachable, gateway) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
|
conn.exec("INSERT INTO connections (user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, aasm_state, stale_time, expire_time, udp_reachable, gateway) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
|
||||||
[user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, Connection::CONNECT_STATE.to_s, connection_stale_time, connection_expire_time, udp_reachable, gateway]).clear
|
[user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, Connection::CONNECT_STATE.to_s, connection_stale_time, connection_expire_time, udp_reachable, gateway]).clear
|
||||||
|
|
||||||
# we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends
|
# we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends
|
||||||
conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result|
|
conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result|
|
||||||
count = result.getvalue(0, 0) .to_i
|
count = result.getvalue(0, 0).to_i
|
||||||
# we're passing all this stuff so that the user record might be updated as well...
|
# we're passing all this stuff so that the user record might be updated as well...
|
||||||
blk.call(conn, count) unless blk.nil?
|
blk.call(conn, count) unless blk.nil?
|
||||||
end
|
end
|
||||||
|
|
@ -291,7 +307,7 @@ SQL
|
||||||
|
|
||||||
# destroy the music_session if it's empty
|
# destroy the music_session if it's empty
|
||||||
num_participants = nil
|
num_participants = nil
|
||||||
conn.exec("SELECT count(*) FROM connections WHERE music_session_id = $1",
|
conn.exec("SELECT count(*) FROM connections WHERE music_session_id = $1",
|
||||||
[previous_music_session_id]) do |result|
|
[previous_music_session_id]) do |result|
|
||||||
num_participants = result.getvalue(0, 0).to_i
|
num_participants = result.getvalue(0, 0).to_i
|
||||||
end
|
end
|
||||||
|
|
@ -324,11 +340,65 @@ SQL
|
||||||
|
|
||||||
conn.exec("UPDATE active_music_sessions set jam_track_id = NULL, jam_track_initiator_id = NULL where jam_track_initiator_id = $1 and id = $2",
|
conn.exec("UPDATE active_music_sessions set jam_track_id = NULL, jam_track_initiator_id = NULL where jam_track_initiator_id = $1 and id = $2",
|
||||||
[user_id, previous_music_session_id])
|
[user_id, previous_music_session_id])
|
||||||
|
|
||||||
|
update_session_controller(previous_music_session_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_session_controller(music_session_id)
|
||||||
|
active_music_session = ActiveMusicSession.find(music_session_id)
|
||||||
|
|
||||||
|
if active_music_session
|
||||||
|
music_session = active_music_session.music_session
|
||||||
|
if music_session.session_controller_id && !active_music_session.users.exists?(music_session.session_controller)
|
||||||
|
# find next in line, because the current 'session controller' is not part of the session
|
||||||
|
next_in_line(music_session, active_music_session)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# determine who should be session controller after someone leaves
|
||||||
|
def next_in_line(music_session, active_music_session)
|
||||||
|
session_users = active_music_session.users
|
||||||
|
|
||||||
|
# check friends 1st
|
||||||
|
session_friends = music_session.creator.friends && session_users
|
||||||
|
if session_friends.length > 0
|
||||||
|
music_session.session_controller = session_friends[0]
|
||||||
|
if music_session.save
|
||||||
|
active_music_session.tick_track_changes
|
||||||
|
Notification.send_tracks_changed(active_music_session)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# check invited 2nd
|
||||||
|
invited = music_session.invited_musicians && session_users
|
||||||
|
if invited.length > 0
|
||||||
|
music_session.session_controller = invited[0]
|
||||||
|
if music_session.save
|
||||||
|
active_music_session.tick_track_changes
|
||||||
|
Notification.send_tracks_changed(active_music_session)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# go by who joined earliest
|
||||||
|
earliest = active_music_session.connections.order(:joined_session_at).first
|
||||||
|
|
||||||
|
if earliest
|
||||||
|
music_session.session_controller = earliest
|
||||||
|
if music_session.save
|
||||||
|
active_music_session.tick_track_changes
|
||||||
|
Notification.send_tracks_changed(active_music_session)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
music_session.creator
|
||||||
|
end
|
||||||
|
|
||||||
def join_music_session(user, client_id, music_session, as_musician, tracks, audio_latency, video_sources=nil)
|
def join_music_session(user, client_id, music_session, as_musician, tracks, audio_latency, video_sources=nil)
|
||||||
connection = nil
|
connection = nil
|
||||||
|
|
||||||
|
|
@ -349,7 +419,10 @@ SQL
|
||||||
|
|
||||||
if connection.errors.any?
|
if connection.errors.any?
|
||||||
raise ActiveRecord::Rollback
|
raise ActiveRecord::Rollback
|
||||||
|
else
|
||||||
|
update_session_controller(music_session.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
connection
|
connection
|
||||||
|
|
@ -383,6 +456,8 @@ SQL
|
||||||
if result.cmd_tuples == 1
|
if result.cmd_tuples == 1
|
||||||
@log.debug("disassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}")
|
@log.debug("disassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}")
|
||||||
|
|
||||||
|
update_session_controller(music_session.id)
|
||||||
|
|
||||||
JamRuby::MusicSessionUserHistory.removed_music_session(user_id, music_session_id)
|
JamRuby::MusicSessionUserHistory.removed_music_session(user_id, music_session_id)
|
||||||
session_checks(conn, previous_music_session_id, user_id)
|
session_checks(conn, previous_music_session_id, user_id)
|
||||||
blk.call() unless blk.nil?
|
blk.call() unless blk.nil?
|
||||||
|
|
|
||||||
|
|
@ -51,4 +51,7 @@ module NotificationTypes
|
||||||
JAM_TRACK_SIGN_COMPLETE = "JAM_TRACK_SIGN_COMPLETE"
|
JAM_TRACK_SIGN_COMPLETE = "JAM_TRACK_SIGN_COMPLETE"
|
||||||
JAM_TRACK_SIGN_FAILED = "JAM_TRACK_SIGN_FAILED"
|
JAM_TRACK_SIGN_FAILED = "JAM_TRACK_SIGN_FAILED"
|
||||||
|
|
||||||
|
MIXDOWN_SIGN_COMPLETE = "MIXDOWN_SIGN_COMPLETE"
|
||||||
|
MIXDOWN_SIGN_FAILED = "MIXDOWN_SIGN_FAILED"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -19,6 +19,8 @@ module ValidationMessages
|
||||||
# sessions
|
# sessions
|
||||||
SESSION_NOT_FOUND = "Session not found."
|
SESSION_NOT_FOUND = "Session not found."
|
||||||
|
|
||||||
|
NOT_FOUND = 'not found'
|
||||||
|
|
||||||
# genres
|
# genres
|
||||||
RECORDING_GENRE_LIMIT_EXCEEDED = "No more than 1 genre is allowed."
|
RECORDING_GENRE_LIMIT_EXCEEDED = "No more than 1 genre is allowed."
|
||||||
BAND_GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed."
|
BAND_GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed."
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -41,7 +41,7 @@ module JamRuby
|
||||||
jam_file_opts=""
|
jam_file_opts=""
|
||||||
jam_track.jam_track_tracks.each do |jam_track_track|
|
jam_track.jam_track_tracks.each do |jam_track_track|
|
||||||
|
|
||||||
next if jam_track_track.track_type != "Track" # master mixes do not go into the JKZ
|
next if jam_track_track.track_type == "Master" # master mixes do not go into the JKZ
|
||||||
|
|
||||||
# use the jam_track_track ID as the filename.ogg/.wav, because it's important metadata
|
# use the jam_track_track ID as the filename.ogg/.wav, because it's important metadata
|
||||||
nm = jam_track_track.id + File.extname(jam_track_track.url_by_sample_rate(sample_rate))
|
nm = jam_track_track.id + File.extname(jam_track_track.url_by_sample_rate(sample_rate))
|
||||||
|
|
@ -52,7 +52,8 @@ module JamRuby
|
||||||
step = bump_step(jam_track_right, step)
|
step = bump_step(jam_track_right, step)
|
||||||
|
|
||||||
copy_url_to_file(track_url, track_filename)
|
copy_url_to_file(track_url, track_filename)
|
||||||
jam_file_opts << " -i #{Shellwords.escape("#{track_filename}+#{jam_track_track.part}")}"
|
part = jam_track_track.track_type == 'Click' ? 'ClickTrack' : jam_track_track.part
|
||||||
|
jam_file_opts << " -i #{Shellwords.escape("#{track_filename}+#{part}")}"
|
||||||
end
|
end
|
||||||
#puts "LS + " + `ls -la '#{tmp_dir}'`
|
#puts "LS + " + `ls -la '#{tmp_dir}'`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,10 @@ module JamRuby
|
||||||
s3_bucket.objects[filename].exists?
|
s3_bucket.objects[filename].exists?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def object(filename)
|
||||||
|
s3_bucket.objects[filename]
|
||||||
|
end
|
||||||
|
|
||||||
def length(filename)
|
def length(filename)
|
||||||
s3_bucket.objects[filename].content_length
|
s3_bucket.objects[filename].content_length
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,29 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.mount_source_up_requested(mount)
|
def self.mount_source_up_requested(mount)
|
||||||
Notification.send_subscription_message('mount', mount.id, {change_type: IcecastSourceChange::CHANGE_TYPE_MOUNT_UP_REQUEST}.to_json )
|
Notification.send_subscription_message('mount', mount.id, {change_type: IcecastSourceChange::CHANGE_TYPE_MOUNT_UP_REQUEST}.to_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.mount_source_down_requested(mount)
|
def self.mount_source_down_requested(mount)
|
||||||
Notification.send_subscription_message('mount', mount.id, {change_type: IcecastSourceChange::CHANGE_TYPE_MOUNT_DOWN_REQUEST}.to_json )
|
Notification.send_subscription_message('mount', mount.id, {change_type: IcecastSourceChange::CHANGE_TYPE_MOUNT_DOWN_REQUEST}.to_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.jam_track_signing_job_change(jam_track_right)
|
def self.jam_track_signing_job_change(jam_track_right)
|
||||||
Notification.send_subscription_message('jam_track_right', jam_track_right.id.to_s, {signing_state: jam_track_right.signing_state, current_packaging_step: jam_track_right.current_packaging_step, packaging_steps: jam_track_right.packaging_steps}.to_json )
|
Notification.send_subscription_message('jam_track_right', jam_track_right.id.to_s,
|
||||||
|
{signing_state: jam_track_right.signing_state,
|
||||||
|
current_packaging_step: jam_track_right.current_packaging_step,
|
||||||
|
packaging_steps: jam_track_right.packaging_steps}.to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.mixdown_signing_job_change(jam_track_mixdown_package)
|
||||||
|
Notification.send_subscription_message('mixdown', jam_track_mixdown_package.id.to_s,
|
||||||
|
{signing_state: jam_track_mixdown_package.signing_state,
|
||||||
|
current_packaging_step: jam_track_mixdown_package.current_packaging_step,
|
||||||
|
packaging_steps: jam_track_mixdown_package.packaging_steps}.to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.test
|
||||||
|
Notification.send_subscription_message('some_key', '1', {field1: 'field1', field2: 'field2'}.to_json)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -736,6 +736,30 @@ module JamRuby
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def mixdown_sign_complete(receiver_id, mixdown_package_id)
|
||||||
|
signed = Jampb::MixdownSignComplete.new(
|
||||||
|
:mixdown_package_id => mixdown_package_id
|
||||||
|
)
|
||||||
|
|
||||||
|
Jampb::ClientMessage.new(
|
||||||
|
:type => ClientMessage::Type::MIXDOWN_SIGN_COMPLETE,
|
||||||
|
:route_to => USER_TARGET_PREFIX + receiver_id, #:route_to => CLIENT_TARGET,
|
||||||
|
:mixdown_sign_complete => signed
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def mixdown_sign_failed(receiver_id, mixdown_package_id)
|
||||||
|
signed = Jampb::MixdownSignFailed.new(
|
||||||
|
:mixdown_package_id => mixdown_package_id
|
||||||
|
)
|
||||||
|
|
||||||
|
Jampb::ClientMessage.new(
|
||||||
|
:type => ClientMessage::Type::MIXDOWN_SIGN_FAILED,
|
||||||
|
:route_to => USER_TARGET_PREFIX + receiver_id, #:route_to => CLIENT_TARGET,
|
||||||
|
:mixdown_sign_failed=> signed
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def recording_master_mix_complete(receiver_id, recording_id, claimed_recording_id, band_id, msg, notification_id, created_at)
|
def recording_master_mix_complete(receiver_id, recording_id, claimed_recording_id, band_id, msg, notification_id, created_at)
|
||||||
recording_master_mix_complete = Jampb::RecordingMasterMixComplete.new(
|
recording_master_mix_complete = Jampb::RecordingMasterMixComplete.new(
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
|
||||||
has_many :months, :class_name => 'JamRuby::AffiliateMonthlyPayment', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
|
has_many :months, :class_name => 'JamRuby::AffiliateMonthlyPayment', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
|
||||||
has_many :traffic_totals, :class_name => 'JamRuby::AffiliateTrafficTotal', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
|
has_many :traffic_totals, :class_name => 'JamRuby::AffiliateTrafficTotal', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
|
||||||
has_many :visits, :class_name => 'JamRuby::AffiliateReferralVisit', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
|
has_many :visits, :class_name => 'JamRuby::AffiliateReferralVisit', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
|
||||||
attr_accessible :partner_name, :partner_code, :partner_user_id
|
attr_accessible :partner_name, :partner_code, :partner_user_id, :entity_type, :rate, as: :admin
|
||||||
|
|
||||||
ENTITY_TYPES = %w{ Individual Sole\ Proprietor Limited\ Liability\ Company\ (LLC) Partnership Trust/Estate S\ Corporation C\ Corporation Other }
|
ENTITY_TYPES = %w{ Individual Sole\ Proprietor Limited\ Liability\ Company\ (LLC) Partnership Trust/Estate S\ Corporation C\ Corporation Other }
|
||||||
|
|
||||||
|
|
@ -50,6 +50,14 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
|
||||||
record.entity_type ||= ENTITY_TYPES.first
|
record.entity_type ||= ENTITY_TYPES.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def display_name
|
||||||
|
partner_name || (partner_user ? partner_user.name : 'abandoned')
|
||||||
|
end
|
||||||
|
|
||||||
|
def admin_url
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/affiliates/#{id}"
|
||||||
|
end
|
||||||
|
|
||||||
# used by admin
|
# used by admin
|
||||||
def self.create_with_params(params={})
|
def self.create_with_params(params={})
|
||||||
raise 'not supported'
|
raise 'not supported'
|
||||||
|
|
@ -111,18 +119,16 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def should_attribute_sale?(shopping_cart)
|
def should_attribute_sale?(shopping_cart)
|
||||||
if shopping_cart.is_jam_track?
|
|
||||||
if created_within_affiliate_window(shopping_cart.user, Time.now)
|
if created_within_affiliate_window(shopping_cart.user, Time.now)
|
||||||
product_info = shopping_cart.product_info
|
product_info = shopping_cart.product_info
|
||||||
# subtract the total quantity from the freebie quantity, to see how much we should attribute to them
|
# subtract the total quantity from the freebie quantity, to see how much we should attribute to them
|
||||||
real_quantity = product_info[:quantity].to_i - product_info[:marked_for_redeem].to_i
|
real_quantity = product_info[:quantity].to_i - product_info[:marked_for_redeem].to_i
|
||||||
{fee_in_cents: real_quantity * 20}
|
{fee_in_cents: (product_info[:price] * 100 * real_quantity * rate.to_f).round}
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
raise 'shopping cart type not implemented yet'
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def cumulative_earnings_in_dollars
|
def cumulative_earnings_in_dollars
|
||||||
|
|
@ -469,4 +475,8 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
|
||||||
def affiliate_query_params
|
def affiliate_query_params
|
||||||
AffiliatePartner::AFFILIATE_PARAMS + self.id.to_s
|
AffiliatePartner::AFFILIATE_PARAMS + self.id.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
display_name
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -15,20 +15,47 @@ module JamRuby
|
||||||
ShoppingCart.where(anonymous_user_id: @id).order('created_at DESC')
|
ShoppingCart.where(anonymous_user_id: @id).order('created_at DESC')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def destroy_all_shopping_carts
|
def destroy_all_shopping_carts
|
||||||
ShoppingCart.destroy_all(anonymous_user_id: @id)
|
ShoppingCart.destroy_all(anonymous_user_id: @id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def destroy_jam_track_shopping_carts
|
||||||
|
ShoppingCart.destroy_all(anonymous_user_id: @id, cart_type: JamTrack::PRODUCT_TYPE)
|
||||||
|
end
|
||||||
|
|
||||||
def admin
|
def admin
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_redeemable_jamtrack
|
def has_redeemable_jamtrack
|
||||||
|
raise "not a cookied anonymous user" if @cookies.nil?
|
||||||
|
|
||||||
APP_CONFIG.one_free_jamtrack_per_user && !@cookies[:redeemed_jamtrack]
|
APP_CONFIG.one_free_jamtrack_per_user && !@cookies[:redeemed_jamtrack]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def gifted_jamtracks
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def free_jamtracks
|
||||||
|
if has_redeemable_jamtrack
|
||||||
|
1
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_free_jamtrack?
|
||||||
|
ShoppingCart.user_has_redeemable_jam_track?(self)
|
||||||
|
end
|
||||||
|
|
||||||
def signup_hint
|
def signup_hint
|
||||||
SignupHint.where(anonymous_user_id: @id).where('expires_at > ?', Time.now).first
|
SignupHint.where(anonymous_user_id: @id).where('expires_at > ?', Time.now).first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reload
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -102,11 +102,19 @@ module JamRuby
|
||||||
def self.search_target_class
|
def self.search_target_class
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.genre_ids
|
||||||
|
@@genre_ids ||= Hash[ *Genre.pluck(:id).collect { |v| [ v, v ] }.flatten ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.instrument_ids
|
||||||
|
@@instrument_ids ||= Hash[ *Instrument.pluck(:id).collect { |v| [ v, v ] }.flatten ]
|
||||||
|
end
|
||||||
|
|
||||||
def _genres(rel, query_data=json)
|
def _genres(rel, query_data=json)
|
||||||
gids = query_data[KEY_GENRES]
|
gids = query_data[KEY_GENRES]
|
||||||
unless gids.blank?
|
unless gids.blank?
|
||||||
allgids = Genre.order(:id).pluck(:id)
|
allgids = self.class.genre_ids
|
||||||
gids = gids.select { |gg| allgids.index(gg).present? }
|
gids = gids.select { |gg| allgids.has_key?(gg) }
|
||||||
|
|
||||||
unless gids.blank?
|
unless gids.blank?
|
||||||
gidsql = gids.join("','")
|
gidsql = gids.join("','")
|
||||||
|
|
@ -119,8 +127,8 @@ module JamRuby
|
||||||
|
|
||||||
def _instruments(rel, query_data=json)
|
def _instruments(rel, query_data=json)
|
||||||
unless (instruments = query_data[KEY_INSTRUMENTS]).blank?
|
unless (instruments = query_data[KEY_INSTRUMENTS]).blank?
|
||||||
instrids = Instrument.order(:id).pluck(:id)
|
instrids = self.class.instrument_ids
|
||||||
instruments = instruments.select { |ii| instrids.index(ii['instrument_id']).present? }
|
instruments = instruments.select { |ii| instrids.has_key?(ii['instrument_id']) }
|
||||||
|
|
||||||
unless instruments.blank?
|
unless instruments.blank?
|
||||||
instsql = "SELECT player_id FROM musicians_instruments WHERE (("
|
instsql = "SELECT player_id FROM musicians_instruments WHERE (("
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
module JamRuby
|
module JamRuby
|
||||||
class CrashDump < ActiveRecord::Base
|
class CrashDump < ActiveRecord::Base
|
||||||
|
|
||||||
|
include JamRuby::S3ManagerMixin
|
||||||
|
|
||||||
self.table_name = "crash_dumps"
|
self.table_name = "crash_dumps"
|
||||||
|
|
||||||
self.primary_key = 'id'
|
self.primary_key = 'id'
|
||||||
|
|
@ -15,7 +17,7 @@ module JamRuby
|
||||||
before_validation(:on => :create) do
|
before_validation(:on => :create) do
|
||||||
self.created_at ||= Time.now
|
self.created_at ||= Time.now
|
||||||
self.id = SecureRandom.uuid
|
self.id = SecureRandom.uuid
|
||||||
self.uri = "dump/#{self.id}-#{self.created_at.to_i}"
|
self.uri = "dumps/#{created_at.strftime('%Y-%m-%d')}/#{self.id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_email
|
def user_email
|
||||||
|
|
@ -23,5 +25,8 @@ module JamRuby
|
||||||
self.user.email
|
self.user.email
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sign_url(expiration_time = 3600 * 24 * 7, secure=true)
|
||||||
|
s3_manager.sign_url(self[:ri], {:expires => expiration_time, :secure => secure})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
module JamRuby
|
||||||
|
class DownloadTracker < ActiveRecord::Base
|
||||||
|
|
||||||
|
@@log = Logging.logger[DownloadTracker]
|
||||||
|
|
||||||
|
belongs_to :user, :class_name => "JamRuby::User"
|
||||||
|
belongs_to :mixdown, :class_name => "JamRuby::JamTrackMixdownPackage", foreign_key: 'mixdown_id'
|
||||||
|
belongs_to :stem, :class_name => "JamRuby::JamTrackTrack", foreign_key: 'stem_id'
|
||||||
|
belongs_to :jam_track, :class_name => "JamRuby::JamTrack"
|
||||||
|
|
||||||
|
# one of mixdown or stem need to be specified. could validate this?
|
||||||
|
validates :user, presence:true
|
||||||
|
validates :remote_ip, presence: true
|
||||||
|
#validates :paid, presence: true
|
||||||
|
validates :jam_track, presence: :true
|
||||||
|
|
||||||
|
def self.create(user, remote_ip, target, owned)
|
||||||
|
dt = DownloadTracker.new
|
||||||
|
dt.user = user
|
||||||
|
dt.remote_ip = remote_ip
|
||||||
|
dt.paid = owned
|
||||||
|
if target.is_a?(JamTrackTrack)
|
||||||
|
dt.jam_track_id = target.jam_track_id
|
||||||
|
elsif target.is_a?(JamTrackMixdownPackage)
|
||||||
|
dt.jam_track_id = target.jam_track_mixdown.jam_track_id
|
||||||
|
end
|
||||||
|
if !dt.save
|
||||||
|
@@log.error("unable to create Download Tracker: #{dt.errors.inspect}")
|
||||||
|
end
|
||||||
|
dt
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.check(user, remote_ip, target, owned)
|
||||||
|
|
||||||
|
return unless APP_CONFIG.guard_against_browser_fraud
|
||||||
|
|
||||||
|
create(user, remote_ip, target, owned)
|
||||||
|
|
||||||
|
# let's check the following
|
||||||
|
alert_freebies_snarfer(remote_ip)
|
||||||
|
|
||||||
|
alert_user_sharer(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
# somebody who has shared account info with a large number of people
|
||||||
|
# high number of downloads of the same user from different IP addresses that were or were not paid for
|
||||||
|
# raw query created by this code:
|
||||||
|
# SELECT distinct(user_id), count(user_id) FROM "download_trackers" WHERE (created_at > NOW() - '30 days'::interval) GROUP BY user_id HAVING count(distinct(remote_ip)) >= 2
|
||||||
|
def self.check_user_sharer(max, user_id = nil)
|
||||||
|
query = DownloadTracker.select('distinct(user_id), count(user_id)')
|
||||||
|
query = query.where("created_at > NOW() - '#{APP_CONFIG.download_tracker_day_range} days'::interval")
|
||||||
|
if !user_id.nil?
|
||||||
|
query = query.where('user_id = ?', user_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
query.group(:user_id).having("count(distinct(remote_ip)) >= #{max}")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# somebody who has figured out how to bypass cookie based method of identity checking, and is getting lots of free JamTracks
|
||||||
|
# high number of downloads of different jam tracks from different users for the same IP address that weren't paid for
|
||||||
|
# raw query created by this code:
|
||||||
|
# SELECT distinct(remote_ip), count(remote_ip) FROM "download_trackers" WHERE (paid = false) AND (created_at > NOW() - '30 days'::interval) GROUP BY remote_ip HAVING count(distinct(jam_track_id)) >= 2
|
||||||
|
def self.check_freebie_snarfer(max, remote_ip = nil)
|
||||||
|
|
||||||
|
query = DownloadTracker.select('distinct(remote_ip), count(remote_ip)').where("paid = false")
|
||||||
|
query = query.where("created_at > NOW() - '#{APP_CONFIG.download_tracker_day_range} days'::interval")
|
||||||
|
if !remote_ip.nil?
|
||||||
|
query = query.where('remote_ip = ?', remote_ip)
|
||||||
|
end
|
||||||
|
query.group(:remote_ip).having("count(distinct(jam_track_id)) >= #{max}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.alert_user_sharer(user)
|
||||||
|
violation = check_user_sharer(APP_CONFIG.max_user_ip_address, user.id).first
|
||||||
|
|
||||||
|
if violation
|
||||||
|
body = "User has downloaded from too many IP addresses #{user.id}\n"
|
||||||
|
body << "Download Count: #{violation['count']}\n"
|
||||||
|
body << "User URL #{user.admin_url}\n"
|
||||||
|
body << "Add to blacklist: #{UserBlacklist.admin_url}"
|
||||||
|
|
||||||
|
AdminMailer.alerts({
|
||||||
|
subject:"Account IP Access Violation. USER: #{user.email}",
|
||||||
|
body:body
|
||||||
|
}).deliver
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.alert_freebies_snarfer(remote_ip)
|
||||||
|
|
||||||
|
violation = check_freebie_snarfer(APP_CONFIG.max_multiple_users_same_ip, remote_ip).first
|
||||||
|
|
||||||
|
if violation
|
||||||
|
body = "IP Address: #{remote_ip}\n"
|
||||||
|
body << "Download Count: #{violation['count']}\n"
|
||||||
|
body << "Add to blacklist: #{IpBlacklist.admin_url}"
|
||||||
|
body << "Check Activity: #{IpBlacklist.admin_activity_url(remote_ip)}"
|
||||||
|
|
||||||
|
AdminMailer.alerts({
|
||||||
|
subject:"Single IP Access Violation. IP:#{remote_ip}",
|
||||||
|
body:body
|
||||||
|
}).deliver
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def admin_url
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/download_trackers/" + id
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
if stem?
|
||||||
|
"stem:#{stem} #{remote_ip} #{user}"
|
||||||
|
else
|
||||||
|
"mixdown:#{mixdown} #{remote_ip} #{user}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -25,5 +25,13 @@ module JamRuby
|
||||||
def to_s
|
def to_s
|
||||||
description
|
description
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.jam_track_list
|
||||||
|
sql = "SELECT DISTINCT genre_id FROM genres_jam_tracks WHERE genre_id IS NOT NULL"
|
||||||
|
Genre.select("DISTINCT(genres.id), genres.*")
|
||||||
|
.where("genres.id IN (#{sql})")
|
||||||
|
.order('genres.description ASC, genres.id')
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ module JamRuby
|
||||||
class GenreJamTrack < ActiveRecord::Base
|
class GenreJamTrack < ActiveRecord::Base
|
||||||
|
|
||||||
self.table_name = 'genres_jam_tracks'
|
self.table_name = 'genres_jam_tracks'
|
||||||
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
|
|
||||||
belongs_to :genre, class_name: 'JamRuby::Genre'
|
attr_accessible :jam_track_id, :genre_id
|
||||||
|
|
||||||
|
belongs_to :jam_track, class_name: 'JamRuby::JamTrack', inverse_of: :genres_jam_tracks
|
||||||
|
belongs_to :genre, class_name: 'JamRuby::Genre', inverse_of: :genres_jam_tracks
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
# represents the gift card you hold in your hand
|
||||||
|
module JamRuby
|
||||||
|
class GiftCard < ActiveRecord::Base
|
||||||
|
|
||||||
|
@@log = Logging.logger[GiftCard]
|
||||||
|
|
||||||
|
JAM_TRACKS_5 = 'jam_tracks_5'
|
||||||
|
JAM_TRACKS_10 = 'jam_tracks_10'
|
||||||
|
CARD_TYPES =
|
||||||
|
[
|
||||||
|
JAM_TRACKS_5,
|
||||||
|
JAM_TRACKS_10
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
belongs_to :user, class_name: "JamRuby::User"
|
||||||
|
|
||||||
|
validates :card_type, presence: true, inclusion: {in: CARD_TYPES}
|
||||||
|
validates :code, presence: true, uniqueness: true
|
||||||
|
|
||||||
|
after_save :check_gifted
|
||||||
|
|
||||||
|
def check_gifted
|
||||||
|
if user && user_id_changed?
|
||||||
|
if card_type == JAM_TRACKS_5
|
||||||
|
user.gifted_jamtracks += 5
|
||||||
|
elsif card_type == JAM_TRACKS_10
|
||||||
|
user.gifted_jamtracks += 10
|
||||||
|
else
|
||||||
|
raise "unknown card type #{card_type}"
|
||||||
|
end
|
||||||
|
user.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
# reperesents the gift card you buy from the site (but physical gift card is modeled by GiftCard)
|
||||||
|
module JamRuby
|
||||||
|
class GiftCardPurchase < ActiveRecord::Base
|
||||||
|
|
||||||
|
@@log = Logging.logger[GiftCardPurchase]
|
||||||
|
|
||||||
|
attr_accessible :user, :gift_card_type
|
||||||
|
|
||||||
|
def name
|
||||||
|
gift_card_type.sale_display
|
||||||
|
end
|
||||||
|
|
||||||
|
# who purchased the card?
|
||||||
|
belongs_to :user, class_name: "JamRuby::User"
|
||||||
|
belongs_to :gift_card_type, class_name: "JamRuby::GiftCardType"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
# reperesents the gift card you buy from the site (but physical gift card is modeled by GiftCard)
|
||||||
|
module JamRuby
|
||||||
|
class GiftCardType < ActiveRecord::Base
|
||||||
|
|
||||||
|
@@log = Logging.logger[GiftCardType]
|
||||||
|
|
||||||
|
PRODUCT_TYPE = 'GiftCardType'
|
||||||
|
|
||||||
|
JAM_TRACKS_5 = 'jam_tracks_5'
|
||||||
|
JAM_TRACKS_10 = 'jam_tracks_10'
|
||||||
|
CARD_TYPES =
|
||||||
|
[
|
||||||
|
JAM_TRACKS_5,
|
||||||
|
JAM_TRACKS_10
|
||||||
|
]
|
||||||
|
|
||||||
|
validates :card_type, presence: true, inclusion: {in: CARD_TYPES}
|
||||||
|
|
||||||
|
def self.jam_track_5
|
||||||
|
GiftCardType.find(JAM_TRACKS_5)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.jam_track_10
|
||||||
|
GiftCardType.find(JAM_TRACKS_10)
|
||||||
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
sale_display
|
||||||
|
end
|
||||||
|
|
||||||
|
def price
|
||||||
|
if card_type == JAM_TRACKS_5
|
||||||
|
10.00
|
||||||
|
elsif card_type == JAM_TRACKS_10
|
||||||
|
20.00
|
||||||
|
else
|
||||||
|
raise "unknown card type #{card_type}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def sale_display
|
||||||
|
if card_type == JAM_TRACKS_5
|
||||||
|
'JamTracks Gift Card (5)'
|
||||||
|
elsif card_type == JAM_TRACKS_10
|
||||||
|
'JamTracks Gift Card (10)'
|
||||||
|
else
|
||||||
|
raise "unknown card type #{card_type}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def plan_code
|
||||||
|
if card_type == JAM_TRACKS_5
|
||||||
|
"jamtrack-giftcard-5"
|
||||||
|
elsif card_type == JAM_TRACKS_10
|
||||||
|
"jamtrack-giftcard-10"
|
||||||
|
else
|
||||||
|
raise "unknown card type #{card_type}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sales_region
|
||||||
|
'Worldwide'
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
sale_display
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -50,6 +50,12 @@ module JamRuby
|
||||||
return Instrument.where('instruments.popularity > 0').order('instruments.popularity DESC, instruments.description ASC')
|
return Instrument.where('instruments.popularity > 0').order('instruments.popularity DESC, instruments.description ASC')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.jam_track_list
|
||||||
|
sql = "SELECT DISTINCT instrument_id FROM jam_track_tracks WHERE instrument_id IS NOT NULL"
|
||||||
|
Instrument.where("instruments.id IN (#{sql})")
|
||||||
|
.order('instruments.description ASC')
|
||||||
|
end
|
||||||
|
|
||||||
def icon_name
|
def icon_name
|
||||||
MAP_ICON_NAME[self.id]
|
MAP_ICON_NAME[self.id]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
module JamRuby
|
||||||
|
class IpBlacklist < ActiveRecord::Base
|
||||||
|
|
||||||
|
attr_accessible :remote_ip, :notes, as: :admin
|
||||||
|
|
||||||
|
@@log = Logging.logger[IpBlacklist]
|
||||||
|
|
||||||
|
validates :remote_ip, presence:true, uniqueness:true
|
||||||
|
|
||||||
|
def self.listed(remote_ip)
|
||||||
|
IpBlacklist.count(:conditions => "remote_ip = '#{remote_ip}'") == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.admin_url
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/ip_blacklists/"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.admin_activity_url(remote_ip)
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/download_trackers?q[remote_ip_equals]=#{URI.escape(remote_ip)}&commit=Filter&order=id_desc"
|
||||||
|
end
|
||||||
|
|
||||||
|
def admin_url
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/ip_blacklists/" + id
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
remote_ip
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
module JamRuby
|
module JamRuby
|
||||||
class JamTrack < ActiveRecord::Base
|
class JamTrack < ActiveRecord::Base
|
||||||
include JamRuby::S3ManagerMixin
|
include JamRuby::S3ManagerMixin
|
||||||
|
|
@ -18,7 +19,7 @@ module JamRuby
|
||||||
:reproduction_royalty, :public_performance_royalty, :reproduction_royalty_amount,
|
:reproduction_royalty, :public_performance_royalty, :reproduction_royalty_amount,
|
||||||
:licensor_royalty_amount, :pro_royalty_amount, :plan_code, :initial_play_silence, :jam_track_tracks_attributes,
|
:licensor_royalty_amount, :pro_royalty_amount, :plan_code, :initial_play_silence, :jam_track_tracks_attributes,
|
||||||
:jam_track_tap_ins_attributes, :genre_ids, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :duration,
|
:jam_track_tap_ins_attributes, :genre_ids, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :duration,
|
||||||
:server_fixation_date, :hfa_license_status, :hfa_license_desired, :alternative_license_status, :hfa_license_number, :hfa_song_code, :album_title, as: :admin
|
:server_fixation_date, :hfa_license_status, :hfa_license_desired, :alternative_license_status, :hfa_license_number, :hfa_song_code, :album_title, :year, as: :admin
|
||||||
|
|
||||||
validates :name, presence: true, length: {maximum: 200}
|
validates :name, presence: true, length: {maximum: 200}
|
||||||
validates :plan_code, presence: true, uniqueness: true, length: {maximum: 50 }
|
validates :plan_code, presence: true, uniqueness: true, length: {maximum: 50 }
|
||||||
|
|
@ -52,7 +53,7 @@ module JamRuby
|
||||||
|
|
||||||
belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id', :inverse_of => :jam_tracks
|
belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id', :inverse_of => :jam_tracks
|
||||||
|
|
||||||
has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "jam_track_id"
|
has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "jam_track_id", inverse_of: :jam_track
|
||||||
has_many :genres, :through => :genres_jam_tracks, :class_name => "JamRuby::Genre", :source => :genre
|
has_many :genres, :through => :genres_jam_tracks, :class_name => "JamRuby::Genre", :source => :genre
|
||||||
|
|
||||||
has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'track_type ASC, position ASC, part ASC, instrument_id ASC'
|
has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'track_type ASC, position ASC, part ASC, instrument_id ASC'
|
||||||
|
|
@ -85,6 +86,10 @@ module JamRuby
|
||||||
after_save :sync_reproduction_royalty
|
after_save :sync_reproduction_royalty
|
||||||
after_save :sync_onboarding_exceptions
|
after_save :sync_onboarding_exceptions
|
||||||
|
|
||||||
|
def increment_version!
|
||||||
|
self.version = version.to_i + 1
|
||||||
|
save!
|
||||||
|
end
|
||||||
|
|
||||||
def sync_reproduction_royalty
|
def sync_reproduction_royalty
|
||||||
|
|
||||||
|
|
@ -154,6 +159,9 @@ module JamRuby
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sale_display
|
||||||
|
"JamTrack: " + name
|
||||||
|
end
|
||||||
def duplicate_positions?
|
def duplicate_positions?
|
||||||
counter = {}
|
counter = {}
|
||||||
jam_track_tracks.each do |track|
|
jam_track_tracks.each do |track|
|
||||||
|
|
@ -255,12 +263,12 @@ module JamRuby
|
||||||
limit = options[:limit]
|
limit = options[:limit]
|
||||||
limit ||= 20
|
limit ||= 20
|
||||||
limit = limit.to_i
|
limit = limit.to_i
|
||||||
|
per_page = limit
|
||||||
else
|
else
|
||||||
limit = per_page
|
limit = per_page
|
||||||
end
|
end
|
||||||
|
|
||||||
start = (page -1 )* per_page
|
start = (page -1 )* per_page
|
||||||
limit = per_page
|
|
||||||
else
|
else
|
||||||
limit = options[:limit]
|
limit = options[:limit]
|
||||||
limit ||= 20
|
limit ||= 20
|
||||||
|
|
@ -337,9 +345,18 @@ module JamRuby
|
||||||
query = query.where('genre_id = ? ', options[:genre])
|
query = query.where('genre_id = ? ', options[:genre])
|
||||||
end
|
end
|
||||||
|
|
||||||
query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}' and jam_track_tracks.track_type != 'Master'") unless options[:instrument].blank?
|
query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}' and jam_track_tracks.track_type = 'Track'") unless options[:instrument].blank?
|
||||||
query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?
|
query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?
|
||||||
|
|
||||||
|
# FIXME: n+1 queries for rights and genres
|
||||||
|
# query = query.includes([{ jam_track_tracks: :instrument },
|
||||||
|
# :jam_track_tap_ins,
|
||||||
|
# :jam_track_rights,
|
||||||
|
# :genres])
|
||||||
|
# { genres_jam_tracks: :genre },
|
||||||
|
# query = query.includes([{ jam_track_tracks: :instrument },
|
||||||
|
# { genres_jam_tracks: :genre }])
|
||||||
|
|
||||||
count = query.total_entries
|
count = query.total_entries
|
||||||
|
|
||||||
if count == 0
|
if count == 0
|
||||||
|
|
@ -421,13 +438,43 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def click_track_file
|
||||||
|
JamTrackFile.where(jam_track_id: self.id).where(file_type: 'ClickWav').first
|
||||||
|
end
|
||||||
|
|
||||||
|
def click_track
|
||||||
|
JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Click').first
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_count_in?
|
||||||
|
has_count_in = false
|
||||||
|
if jmep_json
|
||||||
|
jmep = JSON.parse(jmep_json)
|
||||||
|
|
||||||
|
if jmep["Events"]
|
||||||
|
events = jmep["Events"]
|
||||||
|
metronome = nil
|
||||||
|
events.each do |event|
|
||||||
|
if event.has_key?("metronome")
|
||||||
|
metronome = event["metronome"]
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if metronome
|
||||||
|
has_count_in = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
has_count_in
|
||||||
|
end
|
||||||
|
|
||||||
def master_track
|
def master_track
|
||||||
JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Master').first
|
JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Master').first
|
||||||
end
|
end
|
||||||
|
|
||||||
def stem_tracks
|
def stem_tracks
|
||||||
JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Track')
|
JamTrackTrack.where(jam_track_id: self.id).where("track_type = 'Track' or track_type = 'Click'")
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_download?(user)
|
def can_download?(user)
|
||||||
|
|
@ -438,6 +485,10 @@ module JamRuby
|
||||||
jam_track_rights.where("user_id=?", user).first
|
jam_track_rights.where("user_id=?", user).first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def mixdowns_for_user(user)
|
||||||
|
JamTrackMixdown.where(user_id: user.id).where(jam_track_id: self.id)
|
||||||
|
end
|
||||||
|
|
||||||
def short_plan_code
|
def short_plan_code
|
||||||
prefix = 'jamtrack-'
|
prefix = 'jamtrack-'
|
||||||
plan_code[prefix.length..-1]
|
plan_code[prefix.length..-1]
|
||||||
|
|
@ -450,7 +501,80 @@ module JamRuby
|
||||||
|
|
||||||
def generate_slug
|
def generate_slug
|
||||||
self.slug = sluggarize(original_artist) + '-' + sluggarize(name)
|
self.slug = sluggarize(original_artist) + '-' + sluggarize(name)
|
||||||
puts "Self.slug #{self.slug}"
|
|
||||||
|
if licensor && licensor.slug.present?
|
||||||
|
#raise "no slug on licensor #{licensor.id}" if licensor.slug.nil?
|
||||||
|
self.slug << "-" + licensor.slug
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def gen_plan_code
|
||||||
|
# remove all non-alphanumeric chars from artist as well as name
|
||||||
|
artist_code = original_artist.gsub(/[^0-9a-z]/i, '').downcase
|
||||||
|
name_code = name.gsub(/[^0-9a-z]/i, '').downcase
|
||||||
|
self.plan_code = "jamtrack-#{artist_code[0...20]}-#{name_code}"
|
||||||
|
|
||||||
|
if licensor && licensor.slug
|
||||||
|
raise "no slug on licensor #{licensor.id}" if licensor.slug.nil?
|
||||||
|
self.plan_code << "-" + licensor.slug
|
||||||
|
end
|
||||||
|
|
||||||
|
self.plan_code = self.plan_code[0...50] # make sure it's a max of 50 long
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{self.name} (#{self.original_artist})"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.latestPurchase(user_id)
|
||||||
|
jtx_created = JamTrackRight
|
||||||
|
.select('created_at')
|
||||||
|
.where(user_id: user_id)
|
||||||
|
.order('created_at DESC')
|
||||||
|
.limit(1)
|
||||||
|
jtx_created.first.created_at.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :preview_generate_error
|
||||||
|
|
||||||
|
before_save :jmep_json_generate
|
||||||
|
validate :jmep_text_validate
|
||||||
|
|
||||||
|
def jmep_text_validate
|
||||||
|
begin
|
||||||
|
JmepManager.execute(self.jmep_text)
|
||||||
|
rescue ArgumentError => err
|
||||||
|
errors.add(:jmep_text, err.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def jmep_json_generate
|
||||||
|
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
|
||||||
|
#errors.add(:jmep_text, err.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# used in mobile simulate purchase
|
||||||
|
def self.forsale(user)
|
||||||
|
sql =<<SQL
|
||||||
|
SELECT jt.* FROM jam_tracks jt
|
||||||
|
WHERE jt.id NOT IN (
|
||||||
|
SELECT jt.id
|
||||||
|
FROM jam_tracks jt
|
||||||
|
JOIN jam_track_rights AS jtr ON jtr.jam_track_id = jt.id
|
||||||
|
WHERE jtr.user_id = '#{user.id}'
|
||||||
|
)
|
||||||
|
LIMIT 1
|
||||||
|
SQL
|
||||||
|
self.find_by_sql(sql).first
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,18 @@ module JamRuby
|
||||||
"jam_track_files"
|
"jam_track_files"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def licensor_suffix
|
||||||
|
suffix = ''
|
||||||
|
if jam_track.licensor
|
||||||
|
raise "no licensor name" if jam_track.licensor.name.nil?
|
||||||
|
suffix = " - #{jam_track.licensor.name}"
|
||||||
|
end
|
||||||
|
suffix
|
||||||
|
end
|
||||||
|
|
||||||
# create name of the file
|
# create name of the file
|
||||||
def filename(original_name)
|
def filename(original_name)
|
||||||
"#{store_dir}/#{jam_track.original_artist}/#{jam_track.name}/#{original_name}"
|
"#{store_dir}/#{jam_track.original_artist}/#{jam_track.name}#{licensor_suffix}/#{original_name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def manually_uploaded_filename
|
def manually_uploaded_filename
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ module JamRuby
|
||||||
table_name = 'jam_track_licensors'
|
table_name = 'jam_track_licensors'
|
||||||
|
|
||||||
attr_accessible :name, :description, :attention, :address_line_1, :address_line_2,
|
attr_accessible :name, :description, :attention, :address_line_1, :address_line_2,
|
||||||
:city, :state, :zip_code, :contact, :email, :phone, as: :admin
|
:city, :state, :zip_code, :contact, :email, :phone, :slug, as: :admin
|
||||||
|
|
||||||
validates :name, presence: true, uniqueness: true, length: {maximum: 200}
|
validates :name, presence: true, uniqueness: true, length: {maximum: 200}
|
||||||
validates :description, length: {maximum: 1000}
|
validates :description, length: {maximum: 1000}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
module JamRuby
|
||||||
|
|
||||||
|
# describes what users have rights to which tracks
|
||||||
|
class JamTrackMixdown < ActiveRecord::Base
|
||||||
|
|
||||||
|
@@log = Logging.logger[JamTrackMixdown]
|
||||||
|
|
||||||
|
belongs_to :user, class_name: "JamRuby::User" # the owner, or purchaser of the jam_track
|
||||||
|
belongs_to :jam_track, class_name: "JamRuby::JamTrack"
|
||||||
|
has_many :jam_track_mixdown_packages, class_name: "JamRuby::JamTrackMixdownPackage", order: 'created_at DESC'
|
||||||
|
has_one :jam_track_right, class_name: 'JamRuby::JamTrackRight', foreign_key: 'last_mixdown_id', inverse_of: :last_mixdown
|
||||||
|
|
||||||
|
validates :name, presence: true, length: {maximum: 100}
|
||||||
|
validates :description, length: {maximum: 1000}
|
||||||
|
validates :user, presence: true
|
||||||
|
validates :jam_track, presence: true
|
||||||
|
validates :settings, presence: true
|
||||||
|
|
||||||
|
validates_uniqueness_of :name, scope: [:user_id, :jam_track_id]
|
||||||
|
|
||||||
|
validate :verify_settings
|
||||||
|
validate :verify_max_mixdowns
|
||||||
|
|
||||||
|
def self.index(params, user)
|
||||||
|
jam_track_id = params[:id]
|
||||||
|
|
||||||
|
limit = 20
|
||||||
|
|
||||||
|
query = JamTrackMixdown.where('jam_track_id = ?', jam_track_id).where('user_id = ?', user.id).order('created_at').paginate(page: 1, per_page: limit)
|
||||||
|
|
||||||
|
count = query.total_entries
|
||||||
|
|
||||||
|
if count == 0
|
||||||
|
[query, nil, count]
|
||||||
|
elsif query.length < limit
|
||||||
|
[query, nil, count]
|
||||||
|
else
|
||||||
|
[query, start + limit, count]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_max_mixdowns
|
||||||
|
if self.jam_track && self.user && self.jam_track.mixdowns_for_user(self.user).length >= 5
|
||||||
|
errors.add(:jam_track, 'allowed 5 mixes')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_settings
|
||||||
|
|
||||||
|
# the user has to specify at least at least one tweak to volume, speed, pitch, pan. otherwise there is nothing to do
|
||||||
|
|
||||||
|
parsed = JSON.parse(self.settings)
|
||||||
|
specified_track_count = parsed["tracks"] ? parsed["tracks"].length : 0
|
||||||
|
|
||||||
|
tweaked = false
|
||||||
|
all_quiet = jam_track.stem_tracks.length == 0 ? false : jam_track.stem_tracks.length == specified_track_count # we already say 'all_quiet is false' if the user did not specify as many tracks as there are on the JamTrack, because omission implies 'include this track'
|
||||||
|
|
||||||
|
|
||||||
|
if parsed["speed"]
|
||||||
|
tweaked = true
|
||||||
|
end
|
||||||
|
if parsed["pitch"]
|
||||||
|
tweaked = true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if parsed["tracks"]
|
||||||
|
parsed["tracks"].each do |track|
|
||||||
|
if track["mute"]
|
||||||
|
tweaked = true
|
||||||
|
end
|
||||||
|
if track["vol"] && track["vol"] != 0
|
||||||
|
tweaked = true
|
||||||
|
end
|
||||||
|
if track["pan"] && track["pan"] != 0
|
||||||
|
tweaked = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# there is at least one track with volume specified.
|
||||||
|
if !track["mute"] && track["vol"] != 0
|
||||||
|
all_quiet = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if parsed["count-in"]
|
||||||
|
all_quiet = false
|
||||||
|
tweaked = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if all_quiet
|
||||||
|
errors.add(:settings, 'are all muted')
|
||||||
|
end
|
||||||
|
if !tweaked && !parsed['full']
|
||||||
|
errors.add(:settings, 'have nothing specified')
|
||||||
|
end
|
||||||
|
|
||||||
|
if parsed["speed"] && !parsed["speed"].is_a?(Integer)
|
||||||
|
errors.add(:settings, 'has non-integer speed')
|
||||||
|
end
|
||||||
|
|
||||||
|
if parsed["pitch"] && !parsed["pitch"].is_a?(Integer)
|
||||||
|
errors.add(:settings, 'has non-integer pitch')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.create(name, description, user, jam_track, settings)
|
||||||
|
mixdown = JamTrackMixdown.new
|
||||||
|
mixdown.name = name
|
||||||
|
mixdown.description = description
|
||||||
|
mixdown.user = user
|
||||||
|
mixdown.jam_track = jam_track
|
||||||
|
mixdown.settings = settings.to_json # RAILS 4 CAN REMOVE .to_json
|
||||||
|
mixdown.save
|
||||||
|
mixdown
|
||||||
|
end
|
||||||
|
|
||||||
|
def will_pitch_shift?
|
||||||
|
self.settings["pitch"] != 0 || self.settings["speed"] != 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.mixdownChecksum(user_id, jam_track_id)
|
||||||
|
dates = self
|
||||||
|
.select('created_at')
|
||||||
|
.where(user_id: user_id, jam_track_id: jam_track_id)
|
||||||
|
.order(:id)
|
||||||
|
|
||||||
|
dates = dates.map do |date|
|
||||||
|
date.created_at.to_i.to_s
|
||||||
|
end.join('')
|
||||||
|
|
||||||
|
Digest::MD5.hexdigest(dates)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
@ -0,0 +1,253 @@
|
||||||
|
module JamRuby
|
||||||
|
|
||||||
|
# describes what users have rights to which tracks
|
||||||
|
class JamTrackMixdownPackage < ActiveRecord::Base
|
||||||
|
include JamRuby::S3ManagerMixin
|
||||||
|
|
||||||
|
@@log = Logging.logger[JamTrackMixdownPackage]
|
||||||
|
|
||||||
|
# these are used as extensions for the files stored in s3
|
||||||
|
FILE_TYPE_MP3 = 'mp3'
|
||||||
|
FILE_TYPE_OGG = 'ogg'
|
||||||
|
FILE_TYPE_AAC = 'aac'
|
||||||
|
FILE_TYPES = [FILE_TYPE_MP3, FILE_TYPE_OGG, FILE_TYPE_AAC]
|
||||||
|
|
||||||
|
SAMPLE_RATE_44 = 44
|
||||||
|
SAMPLE_RATE_48 = 48
|
||||||
|
SAMPLE_RATES = [SAMPLE_RATE_44, SAMPLE_RATE_48]
|
||||||
|
|
||||||
|
ENCRYPT_TYPE_JKZ = 'jkz'
|
||||||
|
ENCRYPT_TYPES = [ENCRYPT_TYPE_JKZ, nil]
|
||||||
|
|
||||||
|
default_scope { order('created_at desc') }
|
||||||
|
|
||||||
|
belongs_to :jam_track_mixdown, class_name: "JamRuby::JamTrackMixdown", dependent: :destroy
|
||||||
|
|
||||||
|
validates :jam_track_mixdown, presence: true
|
||||||
|
|
||||||
|
validates :file_type, inclusion: {in: FILE_TYPES}
|
||||||
|
validates :sample_rate, inclusion: {in: SAMPLE_RATES}
|
||||||
|
validates :encrypt_type, inclusion: {in: ENCRYPT_TYPES}
|
||||||
|
validates_uniqueness_of :file_type, scope: [:sample_rate, :encrypt_type, :jam_track_mixdown_id]
|
||||||
|
validates :signing, inclusion: {in: [true, false]}
|
||||||
|
validates :signed, inclusion: {in: [true, false]}
|
||||||
|
|
||||||
|
validate :verify_download_count
|
||||||
|
before_destroy :delete_s3_files
|
||||||
|
after_save :after_save
|
||||||
|
|
||||||
|
MAX_JAM_TRACK_DOWNLOADS = 1000
|
||||||
|
|
||||||
|
def self.estimated_queue_time
|
||||||
|
jam_track_signing_count = JamTrackRight.where(queued: true).count
|
||||||
|
mixdowns = JamTrackMixdownPackage.unscoped.select('count(CASE WHEN queued THEN 1 ELSE NULL END) as queue_count, count(CASE WHEN speed_pitched THEN 1 ELSE NULL END) as speed_pitch_count').where(queued: true).first
|
||||||
|
total_mixdowns = mixdowns['queue_count'].to_i
|
||||||
|
slow_mixdowns = mixdowns['speed_pitch_count'].to_i
|
||||||
|
fast_mixdowns = total_mixdowns - slow_mixdowns
|
||||||
|
|
||||||
|
guess = APP_CONFIG.estimated_jam_track_time * jam_track_signing_count + APP_CONFIG.estimated_fast_mixdown_time * fast_mixdowns + APP_CONFIG.estimated_slow_mixdown_time * slow_mixdowns
|
||||||
|
|
||||||
|
Stats.write('web.jam_track.queue_time', {value: guess / 60.0, jam_tracks: jam_track_signing_count, slow_mixdowns: slow_mixdowns, fast_mixdowns: fast_mixdowns})
|
||||||
|
guess
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_save
|
||||||
|
# try to catch major transitions:
|
||||||
|
|
||||||
|
# if just queue time changes, start time changes, or signed time changes, send out a notice
|
||||||
|
if signing_queued_at_was != signing_queued_at || signing_started_at_was != signing_started_at || last_signed_at_was != last_signed_at || current_packaging_step != current_packaging_step_was || packaging_steps != packaging_steps_was
|
||||||
|
SubscriptionMessage.mixdown_signing_job_change(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.create(mixdown, file_type, sample_rate, encrypt_type)
|
||||||
|
|
||||||
|
package = JamTrackMixdownPackage.new
|
||||||
|
package.speed_pitched = mixdown.will_pitch_shift?
|
||||||
|
package.jam_track_mixdown = mixdown
|
||||||
|
package.file_type = file_type
|
||||||
|
package.sample_rate = sample_rate
|
||||||
|
package.signed = false
|
||||||
|
package.signing = false
|
||||||
|
package.encrypt_type = encrypt_type
|
||||||
|
package.save
|
||||||
|
package
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_download_count
|
||||||
|
if (self.download_count < 0 || self.download_count > MAX_JAM_TRACK_DOWNLOADS) && !@current_user.admin
|
||||||
|
errors.add(:download_count, "must be less than or equal to #{MAX_JAM_TRACK_DOWNLOADS}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_pitch_speed_shifted?
|
||||||
|
mix_settings = JSON.parse(self.settings)
|
||||||
|
mix_settings["speed"] || mix_settings["pitch"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish_errored(error_reason, error_detail)
|
||||||
|
self.last_errored_at = Time.now
|
||||||
|
self.last_signed_at = Time.now
|
||||||
|
self.error_count = self.error_count + 1
|
||||||
|
self.error_reason = error_reason
|
||||||
|
self.error_detail = error_detail
|
||||||
|
self.should_retry = self.error_count < 5
|
||||||
|
self.signing = false
|
||||||
|
self.signing_queued_at = nil # if left set, throws off signing_state on subsequent signing attempts
|
||||||
|
|
||||||
|
if save
|
||||||
|
Notification.send_mixdown_sign_failed(self)
|
||||||
|
else
|
||||||
|
raise "Error sending notification #{self.errors}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish_sign(url, private_key, length, md5)
|
||||||
|
self.url = url
|
||||||
|
self.private_key = private_key
|
||||||
|
self.signing_queued_at = nil # if left set, throws off signing_state on subsequent signing attempts
|
||||||
|
self.downloaded_since_sign = false
|
||||||
|
self.last_signed_at = Time.now
|
||||||
|
self.length = length
|
||||||
|
self.md5 = md5
|
||||||
|
self.signed = true
|
||||||
|
self.signing = false
|
||||||
|
self.error_count = 0
|
||||||
|
self.error_reason = nil
|
||||||
|
self.error_detail = nil
|
||||||
|
self.should_retry = false
|
||||||
|
save!
|
||||||
|
end
|
||||||
|
|
||||||
|
def store_dir
|
||||||
|
"jam_track_mixdowns/#{created_at.strftime('%m-%d-%Y')}/#{self.jam_track_mixdown.user_id}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def filename
|
||||||
|
if encrypt_type
|
||||||
|
"#{id}.#{encrypt_type}"
|
||||||
|
else
|
||||||
|
"#{id}.#{file_type}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# creates a short-lived URL that has access to the object.
|
||||||
|
# the idea is that this is used when a user who has the rights to this tries to download this JamTrack
|
||||||
|
# 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, content_type = nil, response_content_disposition = nil)
|
||||||
|
options = {:expires => expiration_time, :secure => true}
|
||||||
|
options[:response_content_type] = content_type if content_type
|
||||||
|
options[:response_content_disposition] = response_content_disposition if response_content_disposition
|
||||||
|
s3_manager.sign_url(self['url'], options)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def enqueue
|
||||||
|
begin
|
||||||
|
self.signing_queued_at = Time.now
|
||||||
|
self.signing_started_at = nil
|
||||||
|
self.last_signed_at = nil
|
||||||
|
self.queued = true
|
||||||
|
self.save
|
||||||
|
|
||||||
|
queue_time = JamTrackMixdownPackage.estimated_queue_time
|
||||||
|
|
||||||
|
# is_pitch_speed_shifted?
|
||||||
|
Resque.enqueue(JamTrackMixdownPackager, self.id)
|
||||||
|
return queue_time
|
||||||
|
rescue Exception => e
|
||||||
|
puts "e: #{e}"
|
||||||
|
# implies redis is down. we don't update started_at by bailing out here
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# if the job is already signed, just queued up for signing, or currently signing, then don't enqueue... otherwise fire it off
|
||||||
|
def enqueue_if_needed
|
||||||
|
state = signing_state
|
||||||
|
if state == 'SIGNED' || state == 'SIGNING' || state == 'QUEUED'
|
||||||
|
false
|
||||||
|
else
|
||||||
|
return enqueue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ready?
|
||||||
|
self.signed && self.url.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
# returns easy to digest state field
|
||||||
|
# SIGNED - the package is ready to be downloaded
|
||||||
|
# ERROR - the package was built unsuccessfully
|
||||||
|
# SIGNING_TIMEOUT - the package was kicked off to be signed, but it seems to have hung
|
||||||
|
# SIGNING - the package is currently signing
|
||||||
|
# QUEUED_TIMEOUT - the package signing job (JamTrackBuilder) was queued, but never executed
|
||||||
|
# QUEUED - the package is queued to sign
|
||||||
|
# QUIET - the jam_track_right exists, but no job has been kicked off; a job needs to be enqueued
|
||||||
|
def signing_state
|
||||||
|
state = nil
|
||||||
|
|
||||||
|
if signed
|
||||||
|
state = 'SIGNED'
|
||||||
|
elsif signing_started_at && signing
|
||||||
|
# the maximum amount of time the packaging job can take is 10 seconds * num steps. For a 10 track song, this will be 110 seconds. It's a bit long.
|
||||||
|
if Time.now - signing_started_at > APP_CONFIG.signing_job_signing_max_time
|
||||||
|
state = 'SIGNING_TIMEOUT'
|
||||||
|
elsif Time.now - last_step_at > APP_CONFIG.mixdown_step_max_time
|
||||||
|
state = 'SIGNING_TIMEOUT'
|
||||||
|
else
|
||||||
|
state = 'SIGNING'
|
||||||
|
end
|
||||||
|
elsif signing_queued_at
|
||||||
|
if Time.now - signing_queued_at > APP_CONFIG.mixdown_job_queue_max_time
|
||||||
|
state = 'QUEUED_TIMEOUT'
|
||||||
|
else
|
||||||
|
state = 'QUEUED'
|
||||||
|
end
|
||||||
|
elsif error_count > 0
|
||||||
|
state = 'ERROR'
|
||||||
|
else
|
||||||
|
if Time.now - created_at > 60 # it should not take more than a minute to get QUIET out
|
||||||
|
state = 'QUIET_TIMEOUT'
|
||||||
|
else
|
||||||
|
state = 'QUIET' # needs to be poked to go build
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
state
|
||||||
|
end
|
||||||
|
|
||||||
|
def signed?
|
||||||
|
signed
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_download_count(count=1)
|
||||||
|
self.download_count = self.download_count + count
|
||||||
|
self.last_downloaded_at = Time.now
|
||||||
|
|
||||||
|
if self.signed
|
||||||
|
self.downloaded_since_sign = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def self.stats
|
||||||
|
stats = {}
|
||||||
|
|
||||||
|
result = JamTrackMixdownPackage.unscoped.select('count(id) as total, count(CASE WHEN signing THEN 1 ELSE NULL END) as signing_count')
|
||||||
|
|
||||||
|
stats['count'] = result[0]['total'].to_i
|
||||||
|
stats['signing_count'] = result[0]['signing_count'].to_i
|
||||||
|
stats
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def delete_s3_files
|
||||||
|
s3_manager.delete(self.url) if self.url && s3_manager.exists?(self.url)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
@ -11,7 +11,10 @@ module JamRuby
|
||||||
attr_accessible :url_48, :md5_48, :length_48, :url_44, :md5_44, :length_44
|
attr_accessible :url_48, :md5_48, :length_48, :url_44, :md5_44, :length_44
|
||||||
belongs_to :user, class_name: "JamRuby::User" # the owner, or purchaser of the jam_track
|
belongs_to :user, class_name: "JamRuby::User" # the owner, or purchaser of the jam_track
|
||||||
belongs_to :jam_track, class_name: "JamRuby::JamTrack"
|
belongs_to :jam_track, class_name: "JamRuby::JamTrack"
|
||||||
|
belongs_to :last_mixdown, class_name: 'JamRuby::JamTrackMixdown', foreign_key: 'last_mixdown_id', inverse_of: :jam_track_right
|
||||||
|
belongs_to :last_stem, class_name: 'JamRuby::JamTrackTrack', foreign_key: 'last_stem_id', inverse_of: :jam_track_right
|
||||||
|
|
||||||
|
validates :version, presence: true
|
||||||
validates :user, presence: true
|
validates :user, presence: true
|
||||||
validates :jam_track, presence: true
|
validates :jam_track, presence: true
|
||||||
validates :is_test_purchase, inclusion: {in: [true, false]}
|
validates :is_test_purchase, inclusion: {in: [true, false]}
|
||||||
|
|
@ -25,9 +28,16 @@ module JamRuby
|
||||||
mount_uploader :url_48, JamTrackRightUploader
|
mount_uploader :url_48, JamTrackRightUploader
|
||||||
mount_uploader :url_44, JamTrackRightUploader
|
mount_uploader :url_44, JamTrackRightUploader
|
||||||
before_destroy :delete_s3_files
|
before_destroy :delete_s3_files
|
||||||
|
before_create :create_private_keys
|
||||||
|
|
||||||
MAX_JAM_TRACK_DOWNLOADS = 1000
|
MAX_JAM_TRACK_DOWNLOADS = 1000
|
||||||
|
|
||||||
|
def create_private_keys
|
||||||
|
rsa_key = OpenSSL::PKey::RSA.new(1024)
|
||||||
|
key = rsa_key.to_pem()
|
||||||
|
self.private_key_44 = key
|
||||||
|
self.private_key_48 = key
|
||||||
|
end
|
||||||
def after_save
|
def after_save
|
||||||
# try to catch major transitions:
|
# try to catch major transitions:
|
||||||
|
|
||||||
|
|
@ -58,6 +68,7 @@ module JamRuby
|
||||||
|
|
||||||
def finish_errored(error_reason, error_detail, sample_rate)
|
def finish_errored(error_reason, error_detail, sample_rate)
|
||||||
self.last_signed_at = Time.now
|
self.last_signed_at = Time.now
|
||||||
|
self.queued = false
|
||||||
self.error_count = self.error_count + 1
|
self.error_count = self.error_count + 1
|
||||||
self.error_reason = error_reason
|
self.error_reason = error_reason
|
||||||
self.error_detail = error_detail
|
self.error_detail = error_detail
|
||||||
|
|
@ -77,6 +88,7 @@ module JamRuby
|
||||||
|
|
||||||
def finish_sign(length, md5, bitrate)
|
def finish_sign(length, md5, bitrate)
|
||||||
self.last_signed_at = Time.now
|
self.last_signed_at = Time.now
|
||||||
|
self.queued = false
|
||||||
if bitrate==48
|
if bitrate==48
|
||||||
self.length_48 = length
|
self.length_48 = length
|
||||||
self.md5_48 = md5
|
self.md5_48 = md5
|
||||||
|
|
@ -99,10 +111,10 @@ module JamRuby
|
||||||
# the idea is that this is used when a user who has the rights to this tries to download this JamTrack
|
# the idea is that this is used when a user who has the rights to this tries to download this JamTrack
|
||||||
# we would verify their rights (can_download?), and generates a URL in response to the click so that they can download
|
# 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
|
# but the url is short lived enough so that it wouldn't be easily shared
|
||||||
def sign_url(expiration_time = 120, bitrate=48, secure=true)
|
def sign_url(expiration_time = 120, bitrate=48, secure=true)
|
||||||
field_name = (bitrate==48) ? "url_48" : "url_44"
|
field_name = (bitrate==48) ? "url_48" : "url_44"
|
||||||
s3_manager.sign_url(self[field_name], {:expires => expiration_time, :secure => secure})
|
s3_manager.sign_url(self[field_name], {:expires => expiration_time, :secure => secure})
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_s3_files
|
def delete_s3_files
|
||||||
remove_url_48!
|
remove_url_48!
|
||||||
|
|
@ -112,7 +124,7 @@ module JamRuby
|
||||||
|
|
||||||
def enqueue(sample_rate=48)
|
def enqueue(sample_rate=48)
|
||||||
begin
|
begin
|
||||||
JamTrackRight.where(:id => self.id).update_all(:signing_queued_at => Time.now, :signing_started_at_44 => nil, :signing_started_at_48 => nil, :last_signed_at => nil)
|
JamTrackRight.where(:id => self.id).update_all(:signing_queued_at => Time.now, :signing_started_at_44 => nil, :signing_started_at_48 => nil, :last_signed_at => nil, :queued => true)
|
||||||
Resque.enqueue(JamTracksBuilder, self.id, sample_rate)
|
Resque.enqueue(JamTracksBuilder, self.id, sample_rate)
|
||||||
true
|
true
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
|
|
@ -122,8 +134,33 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cleanup_old_package!
|
||||||
|
if self.jam_track.version != self.version
|
||||||
|
delete_s3_files
|
||||||
|
self[:url_48] = nil
|
||||||
|
self[:url_44] = nil
|
||||||
|
self.signing_queued_at = nil
|
||||||
|
self.signing_started_at_48 = nil
|
||||||
|
self.signing_started_at_44 = nil
|
||||||
|
self.last_signed_at = nil
|
||||||
|
self.current_packaging_step = nil
|
||||||
|
self.packaging_steps = nil
|
||||||
|
self.should_retry = false
|
||||||
|
self.signing_44 = false
|
||||||
|
self.signing_48 = false
|
||||||
|
self.signed_44 = false
|
||||||
|
self.signed_48 = false
|
||||||
|
self.queued = false
|
||||||
|
self.version = self.jam_track.version
|
||||||
|
self.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
# if the job is already signed, just queued up for signing, or currently signing, then don't enqueue... otherwise fire it off
|
# if the job is already signed, just queued up for signing, or currently signing, then don't enqueue... otherwise fire it off
|
||||||
def enqueue_if_needed(sample_rate=48)
|
def enqueue_if_needed(sample_rate=48)
|
||||||
|
|
||||||
|
# delete any package that's out dated
|
||||||
|
cleanup_old_package!
|
||||||
|
|
||||||
state = signing_state(sample_rate)
|
state = signing_state(sample_rate)
|
||||||
if state == 'SIGNED' || state == 'SIGNING' || state == 'QUEUED'
|
if state == 'SIGNED' || state == 'SIGNING' || state == 'QUEUED'
|
||||||
false
|
false
|
||||||
|
|
@ -137,9 +174,9 @@ module JamRuby
|
||||||
# @return true if signed && file exists for the sample_rate specifed:
|
# @return true if signed && file exists for the sample_rate specifed:
|
||||||
def ready?(sample_rate=48)
|
def ready?(sample_rate=48)
|
||||||
if sample_rate==48
|
if sample_rate==48
|
||||||
self.signed_48 && self.url_48.present? && self.url_48.file.exists?
|
self.signed_48 && self.url_48.present? && self.url_48.file.exists? && self.version == self.jam_track.version
|
||||||
else
|
else
|
||||||
self.signed_44 && self.url_44.present? && self.url_44.file.exists?
|
self.signed_44 && self.url_44.present? && self.url_44.file.exists? && self.version == self.jam_track.version
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
module JamRuby
|
||||||
|
class JamTrackSearch < BaseSearch
|
||||||
|
|
||||||
|
cattr_accessor :jschema, :search_meta
|
||||||
|
attr_accessor :user_counters
|
||||||
|
|
||||||
|
KEY_QUERY = 'query'
|
||||||
|
KEY_SEARCH_STR = 'search_str'
|
||||||
|
KEY_RESULT_TYPES = 'result_types'
|
||||||
|
KEY_SONGS = 'songs'
|
||||||
|
KEY_ARTISTS = 'artists'
|
||||||
|
KEY_RESULTS = 'results'
|
||||||
|
KEY_RESULT_SETS = 'result_sets'
|
||||||
|
KEY_PAGE_NUM = 'page_num'
|
||||||
|
KEY_TOTAL_COUNT = 'total_count'
|
||||||
|
KEY_PAGE_COUNT = 'page_count'
|
||||||
|
KEY_PER_PAGE = 'per_page'
|
||||||
|
PER_PAGE = 'development'==Rails.env ? 8 : 20
|
||||||
|
KEY_GENRES = 'genres'
|
||||||
|
KEY_INSTRUMENTS = 'instruments'
|
||||||
|
KEY_LANGUAGE = 'language'
|
||||||
|
KEY_ORIGINAL_ARTIST = 'original_artist'
|
||||||
|
|
||||||
|
def self.json_schema
|
||||||
|
return @@jschema ||= {
|
||||||
|
KEY_QUERY => {
|
||||||
|
KEY_SEARCH_STR => '',
|
||||||
|
KEY_INSTRUMENTS => [],
|
||||||
|
KEY_GENRES => [],
|
||||||
|
KEY_LANGUAGE => '',
|
||||||
|
KEY_ORIGINAL_ARTIST => '',
|
||||||
|
KEY_RESULT_TYPES => [],
|
||||||
|
KEY_PAGE_NUM => 1,
|
||||||
|
KEY_PER_PAGE => PER_PAGE,
|
||||||
|
},
|
||||||
|
KEY_RESULT_SETS => {
|
||||||
|
KEY_SONGS => {
|
||||||
|
KEY_RESULTS => [],
|
||||||
|
KEY_PAGE_NUM => 1,
|
||||||
|
KEY_TOTAL_COUNT => 0,
|
||||||
|
KEY_PAGE_COUNT => 0,
|
||||||
|
},
|
||||||
|
KEY_ARTISTS => {
|
||||||
|
KEY_RESULTS => [],
|
||||||
|
KEY_PAGE_NUM => 1,
|
||||||
|
KEY_TOTAL_COUNT => 0,
|
||||||
|
KEY_PAGE_COUNT => 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.search_target_class
|
||||||
|
JamTrack
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_search(query)
|
||||||
|
rel = JamTrack.unscoped
|
||||||
|
|
||||||
|
unless (gids = query[KEY_GENRES]).blank?
|
||||||
|
allgids = self.class.genre_ids
|
||||||
|
gids = gids.select { |gg| allgids.has_key?(gg) }
|
||||||
|
|
||||||
|
unless gids.blank?
|
||||||
|
sqlstr = "'#{gids.join("','")}'"
|
||||||
|
rel = rel.joins(:genres_jam_tracks)
|
||||||
|
rel = rel.where("genres_jam_tracks.genre_id IN (#{sqlstr})")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
unless (instruments = query[KEY_INSTRUMENTS]).blank?
|
||||||
|
instrids = self.class.instrument_ids
|
||||||
|
instruments = instruments.select { |ii| instrids.has_key?(ii['instrument_id']) }
|
||||||
|
|
||||||
|
unless instruments.blank?
|
||||||
|
sqlstr = "'#{instruments.join("','")}'"
|
||||||
|
rel = rel.joins(:jam_track_tracks)
|
||||||
|
rel = rel.where("jam_track_tracks.instrument_id IN (#{sqlstr})")
|
||||||
|
rel = rel.where("jam_track_tracks.track_type = 'Track'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless (artist_name = query[KEY_ORIGINAL_ARTIST]).blank?
|
||||||
|
rel = rel.where(original_artist: artist_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
rel
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_results_page(query=nil)
|
||||||
|
filter = {
|
||||||
|
KEY_QUERY => query,
|
||||||
|
}
|
||||||
|
result_types = query[KEY_RESULT_TYPES]
|
||||||
|
if result_types
|
||||||
|
has_songs, has_artists = result_types.index(KEY_SONGS), result_types.index(KEY_ARTISTS)
|
||||||
|
else
|
||||||
|
has_songs, has_artists = true, true
|
||||||
|
end
|
||||||
|
result_sets = filter[KEY_RESULT_SETS] = self.class.json_schema[KEY_RESULT_SETS].clone
|
||||||
|
if has_songs
|
||||||
|
rel = do_search(query)
|
||||||
|
unless (val = query[KEY_SEARCH_STR]).blank?
|
||||||
|
tsquery = Search.create_tsquery(val)
|
||||||
|
rel = rel.where("(search_tsv @@ to_tsquery('jamenglish', ?))", tsquery) if tsquery
|
||||||
|
end
|
||||||
|
rel = rel.order(:name).includes(:genres)
|
||||||
|
|
||||||
|
pgnum = [query[KEY_PAGE_NUM].to_i, 1].max
|
||||||
|
rel = rel.paginate(:page => pgnum, :per_page => query[KEY_PER_PAGE])
|
||||||
|
|
||||||
|
results = rel.all.collect do |jt|
|
||||||
|
{
|
||||||
|
'id' => jt.id,
|
||||||
|
'name' => jt.name,
|
||||||
|
'artist' => jt.original_artist,
|
||||||
|
'genre' => jt.genres.map(&:description).join(', '),
|
||||||
|
'plan_code' => jt.plan_code,
|
||||||
|
'year' => jt.year
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
result_sets[KEY_SONGS] = {
|
||||||
|
KEY_RESULTS => results,
|
||||||
|
KEY_PAGE_NUM => pgnum,
|
||||||
|
KEY_TOTAL_COUNT => rel.total_entries,
|
||||||
|
KEY_PAGE_COUNT => rel.total_pages,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
if has_artists
|
||||||
|
rel = do_search(query)
|
||||||
|
counter = rel.select("DISTINCT(jam_tracks.original_artist)")
|
||||||
|
rel = rel.select("DISTINCT ON(jam_tracks.original_artist) jam_tracks.id, jam_tracks.original_artist")
|
||||||
|
|
||||||
|
unless (val = query[KEY_SEARCH_STR]).blank?
|
||||||
|
rel = rel.where("original_artist ILIKE ?","%#{val}%")
|
||||||
|
counter = counter.where("original_artist ILIKE ?","%#{val}%")
|
||||||
|
end
|
||||||
|
rel = rel.order(:original_artist)
|
||||||
|
|
||||||
|
pgnum = [query[KEY_PAGE_NUM].to_i, 1].max
|
||||||
|
rel = rel.paginate(:page => pgnum, :per_page => query[KEY_PER_PAGE])
|
||||||
|
|
||||||
|
results = rel.all.collect do |jt|
|
||||||
|
{ 'id' => jt.id, 'artist' => jt.original_artist }
|
||||||
|
end
|
||||||
|
|
||||||
|
artist_count = counter.count
|
||||||
|
|
||||||
|
result_sets[KEY_ARTISTS] = {
|
||||||
|
KEY_RESULTS => results,
|
||||||
|
KEY_PAGE_NUM => pgnum,
|
||||||
|
KEY_TOTAL_COUNT => artist_count,
|
||||||
|
KEY_PAGE_COUNT => (artist_count / query[KEY_PER_PAGE].to_f).ceil,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
filter
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.all_languages
|
||||||
|
JamTrack.select("SELECT DISTINCT(language)").order(:language).collect do |lang|
|
||||||
|
{ description: ISO_639.find_by_code(lang), id: lang }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -6,7 +6,7 @@ module JamRuby
|
||||||
include JamRuby::S3PublicManagerMixin
|
include JamRuby::S3PublicManagerMixin
|
||||||
|
|
||||||
# there should only be one Master per JamTrack, but there can be N Track per JamTrack
|
# there should only be one Master per JamTrack, but there can be N Track per JamTrack
|
||||||
TRACK_TYPE = %w{Track Master}
|
TRACK_TYPE = %w{Track Master Click}
|
||||||
|
|
||||||
@@log = Logging.logger[JamTrackTrack]
|
@@log = Logging.logger[JamTrackTrack]
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ module JamRuby
|
||||||
attr_accessible :jam_track_id, :track_type, :instrument, :instrument_id, :position, :part, as: :admin
|
attr_accessible :jam_track_id, :track_type, :instrument, :instrument_id, :position, :part, as: :admin
|
||||||
attr_accessible :url_44, :url_48, :md5_44, :md5_48, :length_44, :length_48, :preview_start_time_raw, as: :admin
|
attr_accessible :url_44, :url_48, :md5_44, :md5_48, :length_44, :length_48, :preview_start_time_raw, as: :admin
|
||||||
|
|
||||||
attr_accessor :original_audio_s3_path, :skip_uploader, :preview_generate_error
|
attr_accessor :original_audio_s3_path, :skip_uploader, :preview_generate_error, :wav_file, :tmp_duration, :skip_inst_part_uniq
|
||||||
|
|
||||||
before_destroy :delete_s3_files
|
before_destroy :delete_s3_files
|
||||||
|
|
||||||
|
|
@ -27,29 +27,44 @@ module JamRuby
|
||||||
validates :part, length: {maximum: 35}
|
validates :part, length: {maximum: 35}
|
||||||
validates :track_type, inclusion: {in: TRACK_TYPE }
|
validates :track_type, inclusion: {in: TRACK_TYPE }
|
||||||
validates :preview_start_time, numericality: {only_integer: true}, length: {in: 1..1000}, :allow_nil => true
|
validates :preview_start_time, numericality: {only_integer: true}, length: {in: 1..1000}, :allow_nil => true
|
||||||
validates_uniqueness_of :part, scope: [:jam_track_id, :instrument_id]
|
validates_uniqueness_of :part, scope: [:jam_track_id, :instrument_id], unless: :skip_inst_part_uniq
|
||||||
# validates :jam_track, presence: true
|
# validates :jam_track, presence: true
|
||||||
|
|
||||||
belongs_to :instrument, class_name: "JamRuby::Instrument"
|
belongs_to :instrument, class_name: "JamRuby::Instrument"
|
||||||
belongs_to :jam_track, class_name: "JamRuby::JamTrack"
|
belongs_to :jam_track, class_name: "JamRuby::JamTrack"
|
||||||
|
|
||||||
has_many :recorded_jam_track_tracks, :class_name => "JamRuby::RecordedJamTrackTrack", :foreign_key => :jam_track_track_id, :dependent => :destroy
|
has_many :recorded_jam_track_tracks, :class_name => "JamRuby::RecordedJamTrackTrack", :foreign_key => :jam_track_track_id, :dependent => :destroy
|
||||||
|
has_one :jam_track_right, class_name: 'JamRuby::JamTrackRight', foreign_key: 'last_stem_id', inverse_of: :last_stem
|
||||||
|
|
||||||
# create storage directory that will house this jam_track, as well as
|
# create storage directory that will house this jam_track, as well as
|
||||||
def store_dir
|
def store_dir
|
||||||
"jam_track_tracks"
|
"jam_track_tracks"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def licensor_suffix
|
||||||
|
suffix = ''
|
||||||
|
if jam_track.licensor
|
||||||
|
raise "no licensor name" if jam_track.licensor.name.nil?
|
||||||
|
suffix = " - #{jam_track.licensor.name}"
|
||||||
|
end
|
||||||
|
suffix
|
||||||
|
end
|
||||||
|
|
||||||
# create name of the file
|
# create name of the file
|
||||||
def filename(original_name)
|
def filename(original_name)
|
||||||
"#{store_dir}/#{jam_track.original_artist}/#{jam_track.name}/#{original_name}"
|
"#{store_dir}/#{jam_track.original_artist}/#{jam_track.name}#{licensor_suffix}/#{original_name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# create name of the preview file.
|
# create name of the preview file.
|
||||||
# md5-'ed because we cache forever
|
# md5-'ed because we cache forever
|
||||||
def preview_filename(md5, ext='ogg')
|
def preview_filename(md5, ext='ogg')
|
||||||
original_name = "#{File.basename(self["url_44"], ".ogg")}-preview-#{md5}.#{ext}"
|
original_name = "#{File.basename(self["url_44"], ".ogg")}-preview-#{md5}.#{ext}"
|
||||||
"jam_track_previews/#{jam_track.original_artist}/#{jam_track.name}/#{original_name}"
|
"#{preview_directory}/#{original_name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def preview_directory
|
||||||
|
"jam_track_previews/#{jam_track.original_artist}/#{jam_track.name}#{licensor_suffix}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_preview?
|
def has_preview?
|
||||||
|
|
@ -58,7 +73,16 @@ module JamRuby
|
||||||
|
|
||||||
# generates a URL that points to a public version of the preview
|
# generates a URL that points to a public version of the preview
|
||||||
def preview_public_url(media_type='ogg')
|
def preview_public_url(media_type='ogg')
|
||||||
url = media_type == 'ogg' ? self[:preview_url] : self[:preview_mp3_url]
|
case media_type
|
||||||
|
when 'ogg'
|
||||||
|
url = self[:preview_url]
|
||||||
|
when 'mp3'
|
||||||
|
url = self[:preview_mp3_url]
|
||||||
|
when 'aac'
|
||||||
|
url = self[:preview_aac_url]
|
||||||
|
else
|
||||||
|
raise "unknown media_type #{media_type}"
|
||||||
|
end
|
||||||
if url
|
if url
|
||||||
s3_public_manager.public_url(url,{ :secure => true})
|
s3_public_manager.public_url(url,{ :secure => true})
|
||||||
else
|
else
|
||||||
|
|
@ -66,6 +90,18 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def display_name
|
||||||
|
if track_type == 'Master'
|
||||||
|
'Master Mix'
|
||||||
|
else
|
||||||
|
display_part = ''
|
||||||
|
if part
|
||||||
|
display_part = "-(#{part})"
|
||||||
|
end
|
||||||
|
"#{instrument.description}#{display_part}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def manually_uploaded_filename(mounted_as)
|
def manually_uploaded_filename(mounted_as)
|
||||||
if track_type == 'Master'
|
if track_type == 'Master'
|
||||||
filename("Master Mix-#{mounted_as == :url_48 ? '48000' : '44100'}.ogg")
|
filename("Master Mix-#{mounted_as == :url_48 ? '48000' : '44100'}.ogg")
|
||||||
|
|
@ -89,6 +125,18 @@ module JamRuby
|
||||||
def sign_url(expiration_time = 120, sample_rate=48)
|
def sign_url(expiration_time = 120, sample_rate=48)
|
||||||
s3_manager.sign_url(url_by_sample_rate(sample_rate), {:expires => expiration_time, :response_content_type => 'audio/ogg', :secure => true})
|
s3_manager.sign_url(url_by_sample_rate(sample_rate), {:expires => expiration_time, :response_content_type => 'audio/ogg', :secure => true})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def web_download_sign_url(expiration_time = 120, type='mp3', content_type = nil, response_content_disposition = nil)
|
||||||
|
options = {:expires => expiration_time, :secure => true}
|
||||||
|
options[:response_content_type] = content_type if content_type
|
||||||
|
options[:response_content_disposition] = response_content_disposition if response_content_disposition
|
||||||
|
|
||||||
|
url_field = self['url_' + type + '_48']
|
||||||
|
url_field = self['url_48'] if type == 'ogg' # ogg has different column format in database
|
||||||
|
|
||||||
|
|
||||||
|
s3_manager.sign_url(url_field, options)
|
||||||
|
end
|
||||||
|
|
||||||
def can_download?(user)
|
def can_download?(user)
|
||||||
# I think we have to make a special case for 'previews', but maybe that's just up to the controller to not check can_download?
|
# I think we have to make a special case for 'previews', but maybe that's just up to the controller to not check can_download?
|
||||||
|
|
@ -157,6 +205,7 @@ module JamRuby
|
||||||
uuid = SecureRandom.uuid
|
uuid = SecureRandom.uuid
|
||||||
output = File.join(tmp_dir, "#{uuid}.ogg")
|
output = File.join(tmp_dir, "#{uuid}.ogg")
|
||||||
output_mp3 = File.join(tmp_dir, "#{uuid}.mp3")
|
output_mp3 = File.join(tmp_dir, "#{uuid}.mp3")
|
||||||
|
output_aac = File.join(tmp_dir, "#{uuid}.aac")
|
||||||
|
|
||||||
start = self.preview_start_time.to_f / 1000
|
start = self.preview_start_time.to_f / 1000
|
||||||
stop = start + 20
|
stop = start + 20
|
||||||
|
|
@ -176,7 +225,6 @@ module JamRuby
|
||||||
# now create mp3 off of ogg preview
|
# now create mp3 off of ogg preview
|
||||||
|
|
||||||
convert_mp3_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{output}\" -ab 192k \"#{output_mp3}\""
|
convert_mp3_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{output}\" -ab 192k \"#{output_mp3}\""
|
||||||
|
|
||||||
@@log.debug("converting to mp3 using: " + convert_mp3_cmd)
|
@@log.debug("converting to mp3 using: " + convert_mp3_cmd)
|
||||||
|
|
||||||
convert_output = `#{convert_mp3_cmd}`
|
convert_output = `#{convert_mp3_cmd}`
|
||||||
|
|
@ -187,35 +235,55 @@ module JamRuby
|
||||||
@@log.debug("fail #{result_code}")
|
@@log.debug("fail #{result_code}")
|
||||||
@preview_generate_error = "unable to execute mp3 convert command #{convert_output}"
|
@preview_generate_error = "unable to execute mp3 convert command #{convert_output}"
|
||||||
else
|
else
|
||||||
ogg_digest = ::Digest::MD5.file(output)
|
|
||||||
mp3_digest = ::Digest::MD5.file(output_mp3)
|
|
||||||
self["preview_md5"] = ogg_md5 = ogg_digest.hexdigest
|
|
||||||
self["preview_mp3_md5"] = mp3_md5 = mp3_digest.hexdigest
|
|
||||||
|
|
||||||
@@log.debug("uploading ogg preview to #{self.preview_filename('ogg')}")
|
convert_aac_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{output}\" -c:a libfdk_aac -b:a 192k \"#{output_aac}\""
|
||||||
s3_public_manager.upload(self.preview_filename(ogg_md5, 'ogg'), output, content_type: 'audio/ogg', content_md5: ogg_digest.base64digest)
|
@@log.debug("converting to aac using: " + convert_aac_cmd)
|
||||||
@@log.debug("uploading mp3 preview to #{self.preview_filename('mp3')}")
|
|
||||||
s3_public_manager.upload(self.preview_filename(mp3_md5, 'mp3'), output_mp3, content_type: 'audio/mpeg', content_md5: mp3_digest.base64digest)
|
|
||||||
|
|
||||||
self.skip_uploader = true
|
convert_output = `#{convert_aac_cmd}`
|
||||||
|
|
||||||
original_ogg_preview_url = self["preview_url"]
|
result_code = $?.to_i
|
||||||
original_mp3_preview_url = self["preview_mp3_url"]
|
|
||||||
|
|
||||||
# and finally update the JamTrackTrack with the new info
|
if result_code != 0
|
||||||
self["preview_url"] = self.preview_filename(ogg_md5, 'ogg')
|
@@log.debug("fail #{result_code}")
|
||||||
self["preview_length"] = File.new(output).size
|
@preview_generate_error = "unable to execute aac convert command #{convert_output}"
|
||||||
# and finally update the JamTrackTrack with the new info
|
else
|
||||||
self["preview_mp3_url"] = self.preview_filename(mp3_md5, 'mp3')
|
|
||||||
self["preview_mp3_length"] = File.new(output_mp3).size
|
|
||||||
self.save!
|
|
||||||
|
|
||||||
# if all that worked, now delete old previews, if present
|
ogg_digest = ::Digest::MD5.file(output)
|
||||||
begin
|
mp3_digest = ::Digest::MD5.file(output_mp3)
|
||||||
s3_public_manager.delete(original_ogg_preview_url) if original_ogg_preview_url && original_ogg_preview_url != self["preview_url"]
|
aac_digest = ::Digest::MD5.file(output_aac)
|
||||||
s3_public_manager.delete(original_mp3_preview_url) if original_mp3_preview_url && original_mp3_preview_url != track["preview_mp3_url"]
|
self["preview_md5"] = ogg_md5 = ogg_digest.hexdigest
|
||||||
rescue
|
self["preview_mp3_md5"] = mp3_md5 = mp3_digest.hexdigest
|
||||||
puts "UNABLE TO CLEANUP OLD PREVIEW URL"
|
self["preview_aac_md5"] = aac_md5 = mp3_digest.hexdigest
|
||||||
|
|
||||||
|
@@log.debug("uploading ogg preview to #{self.preview_filename('ogg')}")
|
||||||
|
s3_public_manager.upload(self.preview_filename(ogg_md5, 'ogg'), output, content_type: 'audio/ogg', content_md5: ogg_digest.base64digest)
|
||||||
|
@@log.debug("uploading mp3 preview to #{self.preview_filename('mp3')}")
|
||||||
|
s3_public_manager.upload(self.preview_filename(mp3_md5, 'mp3'), output_mp3, content_type: 'audio/mpeg', content_md5: mp3_digest.base64digest)
|
||||||
|
@@log.debug("uploading aac preview to #{self.preview_filename('aac')}")
|
||||||
|
s3_public_manager.upload(self.preview_filename(aac_md5, 'aac'), output_aac, content_type: 'audio/aac', content_md5: aac_digest.base64digest)
|
||||||
|
|
||||||
|
self.skip_uploader = true
|
||||||
|
|
||||||
|
original_ogg_preview_url = self["preview_url"]
|
||||||
|
original_mp3_preview_url = self["preview_mp3_url"]
|
||||||
|
original_aac_preview_url = self["preview_aac_url"]
|
||||||
|
|
||||||
|
self["preview_url"] = self.preview_filename(ogg_md5, 'ogg')
|
||||||
|
self["preview_length"] = File.new(output).size
|
||||||
|
self["preview_mp3_url"] = self.preview_filename(mp3_md5, 'mp3')
|
||||||
|
self["preview_mp3_length"] = File.new(output_mp3).size
|
||||||
|
self["preview_aac_url"] = self.preview_filename(aac_md5, 'aac')
|
||||||
|
self["preview_aac_length"] = File.new(output_aac).size
|
||||||
|
self.save!
|
||||||
|
|
||||||
|
# if all that worked, now delete old previews, if present
|
||||||
|
begin
|
||||||
|
s3_public_manager.delete(original_ogg_preview_url) if original_ogg_preview_url && original_ogg_preview_url != self["preview_url"]
|
||||||
|
s3_public_manager.delete(original_mp3_preview_url) if original_mp3_preview_url && original_mp3_preview_url != track["preview_mp3_url"]
|
||||||
|
s3_public_manager.delete(original_aac_preview_url) if original_aac_preview_url && original_aac_preview_url != track["preview_aac_url"]
|
||||||
|
rescue
|
||||||
|
puts "UNABLE TO CLEANUP OLD PREVIEW URL"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ module JamRuby
|
||||||
|
|
||||||
belongs_to :active_music_session, :class_name => 'JamRuby::ActiveMusicSession', foreign_key: :music_session_id
|
belongs_to :active_music_session, :class_name => 'JamRuby::ActiveMusicSession', foreign_key: :music_session_id
|
||||||
|
|
||||||
|
belongs_to :session_controller, :class_name => 'JamRuby::User', :foreign_key => :session_controller_id, :inverse_of => :controlled_sessions
|
||||||
|
|
||||||
has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id", :dependent => :delete_all
|
has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id", :dependent => :delete_all
|
||||||
has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id"
|
has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id"
|
||||||
has_many :session_info_comments, :class_name => "JamRuby::SessionInfoComment", :foreign_key => "music_session_id"
|
has_many :session_info_comments, :class_name => "JamRuby::SessionInfoComment", :foreign_key => "music_session_id"
|
||||||
|
|
@ -116,6 +118,7 @@ module JamRuby
|
||||||
new_session.open_rsvps = self.open_rsvps
|
new_session.open_rsvps = self.open_rsvps
|
||||||
new_session.is_unstructured_rsvp = self.is_unstructured_rsvp
|
new_session.is_unstructured_rsvp = self.is_unstructured_rsvp
|
||||||
new_session.legal_terms = true
|
new_session.legal_terms = true
|
||||||
|
new_session.session_controller = self.session_controller
|
||||||
|
|
||||||
# copy rsvp_slots, rsvp_requests, and rsvp_requests_rsvp_slots
|
# copy rsvp_slots, rsvp_requests, and rsvp_requests_rsvp_slots
|
||||||
RsvpSlot.find_each(:conditions => "music_session_id = '#{self.id}'") do |slot|
|
RsvpSlot.find_each(:conditions => "music_session_id = '#{self.id}'") do |slot|
|
||||||
|
|
@ -255,6 +258,30 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_session_controller(current_user, user)
|
||||||
|
|
||||||
|
# only allow update of session controller by the creator or the currently marked user
|
||||||
|
|
||||||
|
should_tick = false
|
||||||
|
|
||||||
|
if current_user != creator && current_user != self.session_controller
|
||||||
|
return should_tick
|
||||||
|
end
|
||||||
|
|
||||||
|
if active_music_session
|
||||||
|
if user
|
||||||
|
if active_music_session.users.exists?(user)
|
||||||
|
self.session_controller = user
|
||||||
|
should_tick = save
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.session_controller = nil
|
||||||
|
should_tick = save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
should_tick
|
||||||
|
end
|
||||||
|
|
||||||
def self.index(current_user, user_id, band_id = nil, genre = nil)
|
def self.index(current_user, user_id, band_id = nil, genre = nil)
|
||||||
hide_private = false
|
hide_private = false
|
||||||
if current_user.id != user_id
|
if current_user.id != user_id
|
||||||
|
|
@ -343,6 +370,7 @@ module JamRuby
|
||||||
ms.legal_terms = true
|
ms.legal_terms = true
|
||||||
ms.open_rsvps = options[:open_rsvps] if options[:open_rsvps]
|
ms.open_rsvps = options[:open_rsvps] if options[:open_rsvps]
|
||||||
ms.creator = user
|
ms.creator = user
|
||||||
|
ms.session_controller = user
|
||||||
ms.create_type = options[:create_type]
|
ms.create_type = options[:create_type]
|
||||||
ms.is_unstructured_rsvp = options[:isUnstructuredRsvp] if options[:isUnstructuredRsvp]
|
ms.is_unstructured_rsvp = options[:isUnstructuredRsvp] if options[:isUnstructuredRsvp]
|
||||||
ms.scheduled_start = parse_scheduled_start(options[:start], options[:timezone]) if options[:start] && options[:timezone]
|
ms.scheduled_start = parse_scheduled_start(options[:start], options[:timezone]) if options[:start] && options[:timezone]
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,10 @@ module JamRuby
|
||||||
.first
|
.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
user.name
|
||||||
|
end
|
||||||
|
|
||||||
def music_session
|
def music_session
|
||||||
@msh ||= JamRuby::MusicSession.find_by_music_session_id(self.music_session_id)
|
@msh ||= JamRuby::MusicSession.find_by_music_session_id(self.music_session_id)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ module JamRuby
|
||||||
belongs_to :music_session, :class_name => "JamRuby::MusicSession", :foreign_key => "music_session_id"
|
belongs_to :music_session, :class_name => "JamRuby::MusicSession", :foreign_key => "music_session_id"
|
||||||
belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id"
|
belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id"
|
||||||
belongs_to :jam_track_right, :class_name => "JamRuby::JamTrackRight", :foreign_key => "jam_track_right_id"
|
belongs_to :jam_track_right, :class_name => "JamRuby::JamTrackRight", :foreign_key => "jam_track_right_id"
|
||||||
|
belongs_to :jam_track_mixdown_package, :class_name => "JamRuby::JamTrackMixdownPackage", :foreign_key => "jam_track_mixdown_package_id"
|
||||||
|
|
||||||
validates :target_user, :presence => true
|
validates :target_user, :presence => true
|
||||||
validates :message, length: {minimum: 1, maximum: 400}, no_profanity: true, if: :text_message?
|
validates :message, length: {minimum: 1, maximum: 400}, no_profanity: true, if: :text_message?
|
||||||
|
|
@ -1255,7 +1256,7 @@ module JamRuby
|
||||||
def send_jam_track_sign_complete(jam_track_right)
|
def send_jam_track_sign_complete(jam_track_right)
|
||||||
|
|
||||||
notification = Notification.new
|
notification = Notification.new
|
||||||
notification.jam_track_right_id = jam_track_right.id
|
notification.jam_track_mixdown_package = jam_track_right.id
|
||||||
notification.description = NotificationTypes::JAM_TRACK_SIGN_COMPLETE
|
notification.description = NotificationTypes::JAM_TRACK_SIGN_COMPLETE
|
||||||
notification.target_user_id = jam_track_right.user_id
|
notification.target_user_id = jam_track_right.user_id
|
||||||
notification.save!
|
notification.save!
|
||||||
|
|
@ -1265,6 +1266,30 @@ module JamRuby
|
||||||
#@@mq_router.publish_to_all_clients(msg)
|
#@@mq_router.publish_to_all_clients(msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def send_mixdown_sign_failed(jam_track_mixdown_package)
|
||||||
|
|
||||||
|
notification = Notification.new
|
||||||
|
notification.jam_track_mixdown_package_id = jam_track_mixdown_package.id
|
||||||
|
notification.description = NotificationTypes::MIXDOWN_SIGN_FAILED
|
||||||
|
notification.target_user_id = jam_track_mixdown_package.jam_track_mixdown.user_id
|
||||||
|
notification.save!
|
||||||
|
|
||||||
|
msg = @@message_factory.mixdown_sign_failed(jam_track_mixdown_package.jam_track_mixdown.user_id, jam_track_mixdown_package.id)
|
||||||
|
@@mq_router.publish_to_user(jam_track_mixdown_package.jam_track_mixdown.user_id, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_mixdown_sign_complete(jam_track_mixdown_package)
|
||||||
|
|
||||||
|
notification = Notification.new
|
||||||
|
notification.jam_track_mixdown_package_id = jam_track_mixdown_package.id
|
||||||
|
notification.description = NotificationTypes::MIXDOWN_SIGN_COMPLETE
|
||||||
|
notification.target_user_id = jam_track_mixdown_package.jam_track_mixdown.user_id
|
||||||
|
notification.save!
|
||||||
|
|
||||||
|
msg = @@message_factory.mixdown_sign_complete(jam_track_mixdown_package.jam_track_mixdown.user_id, jam_track_mixdown_package.id)
|
||||||
|
@@mq_router.publish_to_user(jam_track_mixdown_package.jam_track_mixdown.user_id, msg)
|
||||||
|
end
|
||||||
|
|
||||||
def send_client_update(product, version, uri, size)
|
def send_client_update(product, version, uri, size)
|
||||||
msg = @@message_factory.client_update( product, version, uri, size)
|
msg = @@message_factory.client_update( product, version, uri, size)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
# Start recording a session.
|
# Start recording a session.
|
||||||
def self.start(music_session, owner)
|
def self.start(music_session, owner, record_video: false)
|
||||||
recording = nil
|
recording = nil
|
||||||
# Use a transaction and lock to avoid races.
|
# Use a transaction and lock to avoid races.
|
||||||
music_session.with_lock do
|
music_session.with_lock do
|
||||||
|
|
@ -213,6 +213,7 @@ module JamRuby
|
||||||
recording.music_session = music_session
|
recording.music_session = music_session
|
||||||
recording.owner = owner
|
recording.owner = owner
|
||||||
recording.band = music_session.band
|
recording.band = music_session.band
|
||||||
|
recording.video = record_video
|
||||||
|
|
||||||
if recording.save
|
if recording.save
|
||||||
GoogleAnalyticsEvent.report_band_recording(recording.band)
|
GoogleAnalyticsEvent.report_band_recording(recording.band)
|
||||||
|
|
@ -700,6 +701,10 @@ module JamRuby
|
||||||
self.save(:validate => false)
|
self.save(:validate => false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_video_data(data)
|
||||||
|
Recording.where(id: self.id).update_all(external_video_id: data[:video_id])
|
||||||
|
end
|
||||||
|
|
||||||
def add_timeline(timeline)
|
def add_timeline(timeline)
|
||||||
global = timeline["global"]
|
global = timeline["global"]
|
||||||
raise JamArgumentError, "global must be specified" unless global
|
raise JamArgumentError, "global must be specified" unless global
|
||||||
|
|
|
||||||
|
|
@ -92,53 +92,15 @@ module JamRuby
|
||||||
transaction.save!
|
transaction.save!
|
||||||
|
|
||||||
# now that we have the transaction saved, we also need to delete the jam_track_right if this is a refund, or voided
|
# now that we have the transaction saved, we also need to delete the jam_track_right if this is a refund, or voided
|
||||||
|
|
||||||
|
|
||||||
if transaction.transaction_type == 'refund' || transaction.transaction_type == 'void'
|
if transaction.transaction_type == 'refund' || transaction.transaction_type == 'void'
|
||||||
sale = Sale.find_by_recurly_invoice_id(transaction.invoice_id)
|
sale = Sale.find_by_recurly_invoice_id(transaction.invoice_id)
|
||||||
|
|
||||||
if sale && sale.is_jam_track_sale?
|
if sale
|
||||||
if sale.sale_line_items.length == 1
|
AdminMailer.recurly_alerts(transaction.user, {
|
||||||
if sale.recurly_total_in_cents == transaction.amount_in_cents
|
subject: "ACTION REQUIRED: #{transaction.user.email} has refund on invoice",
|
||||||
line_item = sale.sale_line_items[0]
|
body: "You will have to manually revoke any JamTrackRights in our database for the appropriate JamTracks"
|
||||||
jam_track = line_item.product
|
}).deliver
|
||||||
jam_track_right = jam_track.right_for_user(transaction.user) if jam_track
|
|
||||||
if jam_track_right
|
|
||||||
line_item.affiliate_refunded = true
|
|
||||||
line_item.affiliate_refunded_at = Time.now
|
|
||||||
line_item.save!
|
|
||||||
|
|
||||||
jam_track_right.destroy
|
|
||||||
|
|
||||||
# associate which JamTrack we assume this is related to in this one success case
|
|
||||||
transaction.jam_track = jam_track
|
|
||||||
transaction.save!
|
|
||||||
|
|
||||||
AdminMailer.recurly_alerts(transaction.user, {
|
|
||||||
subject: "NOTICE: #{transaction.user.email} has had JamTrack: #{jam_track.name} revoked",
|
|
||||||
body: "A #{transaction.transaction_type} event came from Recurly for sale with Recurly invoice ID #{sale.recurly_invoice_id}. We deleted their right to the track in our own database as a result."
|
|
||||||
}).deliver
|
|
||||||
else
|
|
||||||
AdminMailer.recurly_alerts(transaction.user, {
|
|
||||||
subject: "NOTICE: #{transaction.user.email} got a refund, but unable to find JamTrackRight to delete",
|
|
||||||
body: "This should just mean the user already has no rights to the JamTrackRight when the refund came in. Not a big deal, but sort of weird..."
|
|
||||||
}).deliver
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
AdminMailer.recurly_alerts(transaction.user, {
|
|
||||||
subject: "ACTION REQUIRED: #{transaction.user.email} got a refund it was not for total value of a JamTrack sale",
|
|
||||||
body: "We received a #{transaction.transaction_type} notice for an amount that was not the same as the original sale. So, no action was taken in the database. sale total: #{sale.recurly_total_in_cents}, refund amount: #{transaction.amount_in_cents}"
|
|
||||||
}).deliver
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
else
|
|
||||||
AdminMailer.recurly_alerts(transaction.user, {
|
|
||||||
subject: "ACTION REQUIRED: #{transaction.user.email} has refund on invoice with multiple JamTracks",
|
|
||||||
body: "You will have to manually revoke any JamTrackRights in our database for the appropriate JamTracks"
|
|
||||||
}).deliver
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
AdminMailer.recurly_alerts(transaction.user, {
|
AdminMailer.recurly_alerts(transaction.user, {
|
||||||
subject: "ACTION REQUIRED: #{transaction.user.email} has refund with no correlator to sales",
|
subject: "ACTION REQUIRED: #{transaction.user.email} has refund with no correlator to sales",
|
||||||
|
|
|
||||||
|
|
@ -69,27 +69,12 @@ module JamRuby
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.preview_invoice(current_user, shopping_carts)
|
|
||||||
|
|
||||||
line_items = {jam_tracks: []}
|
def self.ios_purchase(current_user, jam_track, receipt)
|
||||||
shopping_carts_jam_tracks = []
|
jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id) do |jam_track_right|
|
||||||
shopping_carts_subscriptions = []
|
jam_track_right.redeemed = false
|
||||||
shopping_carts.each do |shopping_cart|
|
jam_track_right.version = jam_track.version
|
||||||
|
|
||||||
if shopping_cart.is_jam_track?
|
|
||||||
shopping_carts_jam_tracks << shopping_cart
|
|
||||||
else
|
|
||||||
# XXX: this may have to be revisited when we actually have something other than JamTracks for puchase
|
|
||||||
shopping_carts_subscriptions << shopping_cart
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
jam_track_items = preview_invoice_jam_tracks(current_user, shopping_carts_jam_tracks)
|
|
||||||
line_items[:jam_tracks] = jam_track_items if jam_track_items
|
|
||||||
|
|
||||||
# TODO: process shopping_carts_subscriptions
|
|
||||||
|
|
||||||
line_items
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# place_order will create one or more sales based on the contents of shopping_carts for the current user
|
# place_order will create one or more sales based on the contents of shopping_carts for the current user
|
||||||
|
|
@ -99,19 +84,14 @@ module JamRuby
|
||||||
def self.place_order(current_user, shopping_carts)
|
def self.place_order(current_user, shopping_carts)
|
||||||
|
|
||||||
sales = []
|
sales = []
|
||||||
shopping_carts_jam_tracks = []
|
|
||||||
shopping_carts_subscriptions = []
|
|
||||||
shopping_carts.each do |shopping_cart|
|
|
||||||
|
|
||||||
if shopping_cart.is_jam_track?
|
|
||||||
shopping_carts_jam_tracks << shopping_cart
|
if Sale.is_mixed(shopping_carts)
|
||||||
else
|
# the controller checks this too; this is just an extra-level of sanity checking
|
||||||
# XXX: this may have to be revisited when we actually have something other than JamTracks for puchase
|
return sales
|
||||||
shopping_carts_subscriptions << shopping_cart
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
jam_track_sale = order_jam_tracks(current_user, shopping_carts_jam_tracks)
|
jam_track_sale = order_jam_tracks(current_user, shopping_carts)
|
||||||
sales << jam_track_sale if jam_track_sale
|
sales << jam_track_sale if jam_track_sale
|
||||||
|
|
||||||
# TODO: process shopping_carts_subscriptions
|
# TODO: process shopping_carts_subscriptions
|
||||||
|
|
@ -119,22 +99,52 @@ module JamRuby
|
||||||
sales
|
sales
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.preview_invoice_jam_tracks(current_user, shopping_carts_jam_tracks)
|
|
||||||
### XXX TODO;
|
|
||||||
|
|
||||||
# we currently use a fake plan in Recurly to estimate taxes using the Pricing.Attach metod in Recurly.js
|
def self.is_only_freebie(shopping_carts)
|
||||||
|
free = true
|
||||||
|
shopping_carts.each do |cart|
|
||||||
|
free = cart.product_info[:free]
|
||||||
|
|
||||||
# if we were to implement this the right way (ensure adjustments are on the account as necessary), then it would be better (more correct)
|
if !free
|
||||||
# just a pain to implement
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
free
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.is_only_freebie(shopping_carts_jam_tracks)
|
# we don't allow mixed shopping carts :/
|
||||||
shopping_carts_jam_tracks.length == 1 && shopping_carts_jam_tracks[0].product_info[:free]
|
def self.is_mixed(shopping_carts)
|
||||||
|
free = false
|
||||||
|
non_free = false
|
||||||
|
shopping_carts.each do |cart|
|
||||||
|
if cart.product_info[:free]
|
||||||
|
free = true
|
||||||
|
else
|
||||||
|
non_free = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
free && non_free
|
||||||
end
|
end
|
||||||
|
|
||||||
# this method will either return a valid sale, or throw a RecurlyClientError or ActiveRecord validation error (save! failed)
|
# this method will either return a valid sale, or throw a RecurlyClientError or ActiveRecord validation error (save! failed)
|
||||||
# it may return an nil sale if the JamTrack(s) specified by the shopping carts are already owned
|
# it may return an nil sale if the JamTrack(s) specified by the shopping carts are already owned
|
||||||
def self.order_jam_tracks(current_user, shopping_carts_jam_tracks)
|
def self.order_jam_tracks(current_user, shopping_carts)
|
||||||
|
|
||||||
|
shopping_carts_jam_tracks = []
|
||||||
|
shopping_carts_subscriptions = []
|
||||||
|
shopping_carts_gift_cards = []
|
||||||
|
|
||||||
|
shopping_carts.each do |shopping_cart|
|
||||||
|
if shopping_cart.is_jam_track?
|
||||||
|
shopping_carts_jam_tracks << shopping_cart
|
||||||
|
elsif shopping_cart.is_gift_card?
|
||||||
|
shopping_carts_gift_cards << shopping_cart
|
||||||
|
else
|
||||||
|
# XXX: this may have to be revisited when we actually have something other than JamTracks for puchase
|
||||||
|
raise "unknown shopping cart type #{shopping_cart.cart_type}"
|
||||||
|
shopping_carts_subscriptions << shopping_cart
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
client = RecurlyClient.new
|
client = RecurlyClient.new
|
||||||
|
|
||||||
|
|
@ -143,8 +153,8 @@ module JamRuby
|
||||||
sale = create_jam_track_sale(current_user)
|
sale = create_jam_track_sale(current_user)
|
||||||
|
|
||||||
if sale.valid?
|
if sale.valid?
|
||||||
if is_only_freebie(shopping_carts_jam_tracks)
|
if is_only_freebie(shopping_carts)
|
||||||
sale.process_jam_tracks(current_user, shopping_carts_jam_tracks, nil)
|
sale.process_shopping_carts(current_user, shopping_carts, nil)
|
||||||
|
|
||||||
sale.recurly_subtotal_in_cents = 0
|
sale.recurly_subtotal_in_cents = 0
|
||||||
sale.recurly_tax_in_cents = 0
|
sale.recurly_tax_in_cents = 0
|
||||||
|
|
@ -159,11 +169,13 @@ module JamRuby
|
||||||
return sale
|
return sale
|
||||||
end
|
end
|
||||||
|
|
||||||
sale_line_item = sale.sale_line_items[0]
|
sale.sale_line_items.each do |sale_line_item|
|
||||||
sale_line_item.recurly_tax_in_cents = 0
|
sale_line_item = sale.sale_line_items[0]
|
||||||
sale_line_item.recurly_total_in_cents = 0
|
sale_line_item.recurly_tax_in_cents = 0
|
||||||
sale_line_item.recurly_currency = 'USD'
|
sale_line_item.recurly_total_in_cents = 0
|
||||||
sale_line_item.recurly_discount_in_cents = 0
|
sale_line_item.recurly_currency = 'USD'
|
||||||
|
sale_line_item.recurly_discount_in_cents = 0
|
||||||
|
end
|
||||||
sale.save
|
sale.save
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
@ -173,7 +185,7 @@ module JamRuby
|
||||||
|
|
||||||
purge_pending_adjustments(account)
|
purge_pending_adjustments(account)
|
||||||
|
|
||||||
created_adjustments = sale.process_jam_tracks(current_user, shopping_carts_jam_tracks, account)
|
created_adjustments = sale.process_shopping_carts(current_user, shopping_carts, account)
|
||||||
|
|
||||||
# now invoice the sale ... almost done
|
# now invoice the sale ... almost done
|
||||||
|
|
||||||
|
|
@ -229,13 +241,13 @@ module JamRuby
|
||||||
sale
|
sale
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_jam_tracks(current_user, shopping_carts_jam_tracks, account)
|
def process_shopping_carts(current_user, shopping_carts, account)
|
||||||
|
|
||||||
created_adjustments = []
|
created_adjustments = []
|
||||||
|
|
||||||
begin
|
begin
|
||||||
shopping_carts_jam_tracks.each do |shopping_cart|
|
shopping_carts.each do |shopping_cart|
|
||||||
process_jam_track(current_user, shopping_cart, account, created_adjustments)
|
process_shopping_cart(current_user, shopping_cart, account, created_adjustments)
|
||||||
end
|
end
|
||||||
rescue Recurly::Error, NoMethodError => x
|
rescue Recurly::Error, NoMethodError => x
|
||||||
# rollback any adjustments created if error
|
# rollback any adjustments created if error
|
||||||
|
|
@ -251,7 +263,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def process_jam_track(current_user, shopping_cart, account, created_adjustments)
|
def process_shopping_cart(current_user, shopping_cart, account, created_adjustments)
|
||||||
recurly_adjustment_uuid = nil
|
recurly_adjustment_uuid = nil
|
||||||
recurly_adjustment_credit_uuid = nil
|
recurly_adjustment_credit_uuid = nil
|
||||||
|
|
||||||
|
|
@ -259,15 +271,20 @@ module JamRuby
|
||||||
shopping_cart.reload
|
shopping_cart.reload
|
||||||
|
|
||||||
# get the JamTrack in this shopping cart
|
# get the JamTrack in this shopping cart
|
||||||
jam_track = shopping_cart.cart_product
|
cart_product = shopping_cart.cart_product
|
||||||
|
|
||||||
if jam_track.right_for_user(current_user)
|
if shopping_cart.is_jam_track?
|
||||||
# if the user already owns the JamTrack, we should just skip this cart item, and destroy it
|
jam_track = cart_product
|
||||||
# if this occurs, we have to reload every shopping_cart as we iterate. so, we do at the top of the loop
|
if jam_track.right_for_user(current_user)
|
||||||
ShoppingCart.remove_jam_track_from_cart(current_user, shopping_cart)
|
# if the user already owns the JamTrack, we should just skip this cart item, and destroy it
|
||||||
return
|
# if this occurs, we have to reload every shopping_cart as we iterate. so, we do at the top of the loop
|
||||||
|
ShoppingCart.remove_jam_track_from_cart(current_user, shopping_cart)
|
||||||
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if account
|
if account
|
||||||
# ask the shopping cart to create the correct Recurly adjustment attributes for a JamTrack
|
# ask the shopping cart to create the correct Recurly adjustment attributes for a JamTrack
|
||||||
adjustments = shopping_cart.create_adjustment_attributes(current_user)
|
adjustments = shopping_cart.create_adjustment_attributes(current_user)
|
||||||
|
|
@ -300,38 +317,70 @@ module JamRuby
|
||||||
|
|
||||||
# if the sale line item is invalid, blow up the transaction
|
# if the sale line item is invalid, blow up the transaction
|
||||||
unless sale_line_item.valid?
|
unless sale_line_item.valid?
|
||||||
@log.error("sale item invalid! #{sale_line_item.errors.inspect}")
|
@@log.error("sale item invalid! #{sale_line_item.errors.inspect}")
|
||||||
puts("sale item invalid! #{sale_line_item.errors.inspect}")
|
puts("sale item invalid! #{sale_line_item.errors.inspect}")
|
||||||
Stats.write('web.recurly.purchase.sale_invalid', {message: sale_line_item.errors.to_s, value: 1})
|
Stats.write('web.recurly.purchase.sale_invalid', {message: sale_line_item.errors.to_s, value: 1})
|
||||||
raise RecurlyClientError.new(sale_line_item.errors)
|
raise RecurlyClientError.new(sale_line_item.errors)
|
||||||
end
|
end
|
||||||
|
|
||||||
# create a JamTrackRight (this needs to be in a transaction too to make sure we don't make these by accident)
|
if shopping_cart.is_jam_track?
|
||||||
jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id) do |jam_track_right|
|
jam_track = cart_product
|
||||||
jam_track_right.redeemed = shopping_cart.free?
|
|
||||||
end
|
|
||||||
|
|
||||||
# also if the purchase was a free one, then update the user record to no longer allow redeemed jamtracks
|
# create a JamTrackRight (this needs to be in a transaction too to make sure we don't make these by accident)
|
||||||
if shopping_cart.free?
|
jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id) do |jam_track_right|
|
||||||
User.where(id: current_user.id).update_all(has_redeemable_jamtrack: false)
|
jam_track_right.redeemed = shopping_cart.free?
|
||||||
current_user.has_redeemable_jamtrack = false # make sure model reflects the truth
|
jam_track_right.version = jam_track.version
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# this can't go in the block above, as it's here to fix bad subscription UUIDs in an update path
|
|
||||||
if jam_track_right.recurly_adjustment_uuid != recurly_adjustment_uuid
|
|
||||||
jam_track_right.recurly_adjustment_uuid = recurly_adjustment_uuid
|
|
||||||
jam_track_right.recurly_adjustment_credit_uuid = recurly_adjustment_credit_uuid
|
|
||||||
unless jam_track_right.save
|
|
||||||
raise RecurlyClientError.new(jam_track_right.errors)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# also if the purchase was a free one, then:
|
||||||
|
# first, mark the free has_redeemable_jamtrack field if that's still true
|
||||||
|
# and if still they have more free things, then redeem the giftable_jamtracks
|
||||||
|
if shopping_cart.free?
|
||||||
|
if user.has_redeemable_jamtrack
|
||||||
|
User.where(id: current_user.id).update_all(has_redeemable_jamtrack: false)
|
||||||
|
current_user.has_redeemable_jamtrack = false
|
||||||
|
else
|
||||||
|
User.where(id: current_user.id).update_all(gifted_jamtracks: current_user.gifted_jamtracks - 1)
|
||||||
|
current_user.gifted_jamtracks = current_user.gifted_jamtracks - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# this can't go in the block above, as it's here to fix bad subscription UUIDs in an update path
|
||||||
|
if jam_track_right.recurly_adjustment_uuid != recurly_adjustment_uuid
|
||||||
|
jam_track_right.recurly_adjustment_uuid = recurly_adjustment_uuid
|
||||||
|
jam_track_right.recurly_adjustment_credit_uuid = recurly_adjustment_credit_uuid
|
||||||
|
unless jam_track_right.save
|
||||||
|
raise RecurlyClientError.new(jam_track_right.errors)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# blow up the transaction if the JamTrackRight did not get created
|
||||||
|
raise RecurlyClientError.new(jam_track_right.errors) if jam_track_right.errors.any?
|
||||||
|
|
||||||
|
elsif shopping_cart.is_gift_card?
|
||||||
|
gift_card_type = cart_product
|
||||||
|
raise "gift card is null" if gift_card_type.nil?
|
||||||
|
raise if current_user.nil?
|
||||||
|
|
||||||
|
shopping_cart.quantity.times do |item|
|
||||||
|
gift_card_purchase = GiftCardPurchase.new(
|
||||||
|
{
|
||||||
|
user: current_user,
|
||||||
|
gift_card_type: gift_card_type
|
||||||
|
})
|
||||||
|
|
||||||
|
unless gift_card_purchase.save
|
||||||
|
raise RecurlyClientError.new(gift_card_purchase.errors)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
raise 'unknown shopping cart type: ' + shopping_cart.cart_type
|
||||||
end
|
end
|
||||||
|
|
||||||
# delete the shopping cart; it's been dealt with
|
# delete the shopping cart; it's been dealt with
|
||||||
shopping_cart.destroy if shopping_cart
|
shopping_cart.destroy if shopping_cart
|
||||||
|
|
||||||
# blow up the transaction if the JamTrackRight did not get created
|
|
||||||
raise RecurlyClientError.new(jam_track_right.errors) if jam_track_right.errors.any?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -361,7 +410,7 @@ module JamRuby
|
||||||
def self.create_jam_track_sale(user)
|
def self.create_jam_track_sale(user)
|
||||||
sale = Sale.new
|
sale = Sale.new
|
||||||
sale.user = user
|
sale.user = user
|
||||||
sale.sale_type = JAMTRACK_SALE
|
sale.sale_type = JAMTRACK_SALE # gift cards and jam tracks are sold with this type of sale
|
||||||
sale.order_total = 0
|
sale.order_total = 0
|
||||||
sale.save
|
sale.save
|
||||||
sale
|
sale
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,16 @@ module JamRuby
|
||||||
JAMBLASTER = 'JamBlaster'
|
JAMBLASTER = 'JamBlaster'
|
||||||
JAMCLOUD = 'JamCloud'
|
JAMCLOUD = 'JamCloud'
|
||||||
JAMTRACK = 'JamTrack'
|
JAMTRACK = 'JamTrack'
|
||||||
|
GIFTCARD = 'GiftCardType'
|
||||||
|
|
||||||
belongs_to :sale, class_name: 'JamRuby::Sale'
|
belongs_to :sale, class_name: 'JamRuby::Sale'
|
||||||
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
|
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
|
||||||
belongs_to :jam_track_right, class_name: 'JamRuby::JamTrackRight'
|
belongs_to :jam_track_right, class_name: 'JamRuby::JamTrackRight'
|
||||||
|
belongs_to :gift_card, class_name: 'JamRuby::GiftCard'
|
||||||
belongs_to :affiliate_referral, class_name: 'JamRuby::AffiliatePartner', foreign_key: :affiliate_referral_id
|
belongs_to :affiliate_referral, class_name: 'JamRuby::AffiliatePartner', foreign_key: :affiliate_referral_id
|
||||||
has_many :recurly_transactions, class_name: 'JamRuby::RecurlyTransactionWebHook', inverse_of: :sale_line_item, foreign_key: 'subscription_id', primary_key: 'recurly_subscription_uuid'
|
has_many :recurly_transactions, class_name: 'JamRuby::RecurlyTransactionWebHook', inverse_of: :sale_line_item, foreign_key: 'subscription_id', primary_key: 'recurly_subscription_uuid'
|
||||||
|
|
||||||
validates :product_type, inclusion: {in: [JAMBLASTER, JAMCLOUD, JAMTRACK]}
|
validates :product_type, inclusion: {in: [JAMBLASTER, JAMCLOUD, JAMTRACK, GIFTCARD]}
|
||||||
validates :unit_price, numericality: {only_integer: false}
|
validates :unit_price, numericality: {only_integer: false}
|
||||||
validates :quantity, numericality: {only_integer: true}
|
validates :quantity, numericality: {only_integer: true}
|
||||||
validates :free, numericality: {only_integer: true}
|
validates :free, numericality: {only_integer: true}
|
||||||
|
|
@ -21,9 +23,19 @@ module JamRuby
|
||||||
validates :recurly_plan_code, presence:true
|
validates :recurly_plan_code, presence:true
|
||||||
validates :sale, presence:true
|
validates :sale, presence:true
|
||||||
|
|
||||||
|
def is_jam_track?
|
||||||
|
product_type == JAMTRACK
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_gift_card?
|
||||||
|
product_type == GIFTCARD
|
||||||
|
end
|
||||||
|
|
||||||
def product
|
def product
|
||||||
if product_type == JAMTRACK
|
if product_type == JAMTRACK
|
||||||
JamTrack.find_by_id(product_id)
|
JamTrack.find_by_id(product_id)
|
||||||
|
elsif product_type == GIFTCARD
|
||||||
|
GiftCardType.find_by_id(product_id)
|
||||||
else
|
else
|
||||||
raise 'unsupported product type'
|
raise 'unsupported product type'
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ module JamRuby
|
||||||
|
|
||||||
attr_accessible :quantity, :cart_type, :product_info
|
attr_accessible :quantity, :cart_type, :product_info
|
||||||
|
|
||||||
|
attr_accessor :skip_mix_check
|
||||||
|
|
||||||
validates_uniqueness_of :cart_id, scope: [:cart_type, :user_id, :anonymous_user_id]
|
validates_uniqueness_of :cart_id, scope: [:cart_type, :user_id, :anonymous_user_id]
|
||||||
|
|
||||||
belongs_to :user, :inverse_of => :shopping_carts, :class_name => "JamRuby::User", :foreign_key => "user_id"
|
belongs_to :user, :inverse_of => :shopping_carts, :class_name => "JamRuby::User", :foreign_key => "user_id"
|
||||||
|
|
@ -20,12 +22,13 @@ module JamRuby
|
||||||
validates :cart_type, presence: true
|
validates :cart_type, presence: true
|
||||||
validates :cart_class_name, presence: true
|
validates :cart_class_name, presence: true
|
||||||
validates :marked_for_redeem, numericality: {only_integer: true}
|
validates :marked_for_redeem, numericality: {only_integer: true}
|
||||||
|
validate :not_mixed
|
||||||
|
|
||||||
default_scope order('created_at DESC')
|
default_scope order('created_at DESC')
|
||||||
|
|
||||||
def product_info
|
def product_info
|
||||||
product = self.cart_product
|
product = self.cart_product
|
||||||
{name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, real_price: real_price(product), total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem, free: free?, sales_region: product.sales_region} unless product.nil?
|
{type: cart_type, name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, real_price: real_price(product), total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem, free: free?, sales_region: product.sales_region, sale_display:product.sale_display} unless product.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
# multiply quantity by price
|
# multiply quantity by price
|
||||||
|
|
@ -38,6 +41,31 @@ module JamRuby
|
||||||
(quantity - marked_for_redeem) * product.price
|
(quantity - marked_for_redeem) * product.price
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def not_mixed
|
||||||
|
|
||||||
|
return if @skip_mix_check
|
||||||
|
existing_carts = []
|
||||||
|
this_user = any_user()
|
||||||
|
|
||||||
|
if this_user
|
||||||
|
existing_carts = this_user.shopping_carts
|
||||||
|
end
|
||||||
|
|
||||||
|
existing_carts = existing_carts.to_a
|
||||||
|
existing_carts << self
|
||||||
|
|
||||||
|
if Sale.is_mixed(existing_carts)
|
||||||
|
if free?
|
||||||
|
errors.add(:base, "You can not add a free JamTrack to a cart with non-free items. Please clear out your cart.")
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
errors.add(:base, "You can not add a non-free JamTrack to a cart containing free items. Please clear out your cart.")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def cart_product
|
def cart_product
|
||||||
self.cart_class_name.classify.constantize.find_by_id(self.cart_id) unless self.cart_class_name.blank?
|
self.cart_class_name.classify.constantize.find_by_id(self.cart_id) unless self.cart_class_name.blank?
|
||||||
|
|
@ -51,7 +79,18 @@ module JamRuby
|
||||||
marked_for_redeem == quantity
|
marked_for_redeem == quantity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def any_user
|
||||||
|
if user
|
||||||
|
user
|
||||||
|
elsif anonymous_user_id
|
||||||
|
AnonymousUser.new(anonymous_user_id, nil)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.create user, product, quantity = 1, mark_redeem = false
|
def self.create user, product, quantity = 1, mark_redeem = false
|
||||||
|
|
||||||
cart = ShoppingCart.new
|
cart = ShoppingCart.new
|
||||||
if user.is_a?(User)
|
if user.is_a?(User)
|
||||||
cart.user = user
|
cart.user = user
|
||||||
|
|
@ -72,39 +111,42 @@ module JamRuby
|
||||||
cart_type == JamTrack::PRODUCT_TYPE
|
cart_type == JamTrack::PRODUCT_TYPE
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def is_gift_card?
|
||||||
|
cart_type == GiftCardType::PRODUCT_TYPE
|
||||||
|
end
|
||||||
|
|
||||||
# returns an array of adjustments for the shopping cart
|
# returns an array of adjustments for the shopping cart
|
||||||
def create_adjustment_attributes(current_user)
|
def create_adjustment_attributes(current_user)
|
||||||
raise "not a jam track" unless is_jam_track?
|
raise "not a jam track or gift card" unless is_jam_track? || is_gift_card?
|
||||||
|
|
||||||
info = self.product_info
|
info = self.product_info
|
||||||
|
|
||||||
if free?
|
if free?
|
||||||
|
|
||||||
# create the credit, then the pseudo charge
|
# create the credit, then the pseudo charge
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
accounting_code: PURCHASE_FREE_CREDIT,
|
accounting_code: PURCHASE_FREE_CREDIT,
|
||||||
currency: 'USD',
|
currency: 'USD',
|
||||||
unit_amount_in_cents: -(info[:total_price] * 100).to_i,
|
unit_amount_in_cents: -(info[:total_price] * 100).to_i,
|
||||||
description: "JamTrack: " + info[:name] + " (Credit)",
|
description: info[:sale_display] + " (Credit)",
|
||||||
tax_exempt: true
|
tax_exempt: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accounting_code: PURCHASE_FREE,
|
accounting_code: PURCHASE_FREE,
|
||||||
currency: 'USD',
|
currency: 'USD',
|
||||||
unit_amount_in_cents: (info[:total_price] * 100).to_i,
|
unit_amount_in_cents: (info[:total_price] * 100).to_i,
|
||||||
description: "JamTrack: " + info[:name],
|
description: info[:sale_display],
|
||||||
tax_exempt: true
|
tax_exempt: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
else
|
else
|
||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
accounting_code: PURCHASE_NORMAL,
|
accounting_code: PURCHASE_NORMAL,
|
||||||
currency: 'USD',
|
currency: 'USD',
|
||||||
unit_amount_in_cents: (info[:total_price] * 100).to_i,
|
unit_amount_in_cents: (info[:total_price] * 100).to_i,
|
||||||
description: "JamTrack: " + info[:name],
|
description: info[:sale_display],
|
||||||
tax_exempt: false
|
tax_exempt: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -113,8 +155,13 @@ module JamRuby
|
||||||
|
|
||||||
def self.move_to_user(user, anonymous_user, shopping_carts)
|
def self.move_to_user(user, anonymous_user, shopping_carts)
|
||||||
shopping_carts.each do |shopping_cart|
|
shopping_carts.each do |shopping_cart|
|
||||||
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(user)
|
if shopping_cart.is_jam_track?
|
||||||
cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, mark_redeem)
|
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(user)
|
||||||
|
cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, mark_redeem)
|
||||||
|
else
|
||||||
|
cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, false)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
anonymous_user.destroy_all_shopping_carts
|
anonymous_user.destroy_all_shopping_carts
|
||||||
|
|
@ -134,28 +181,32 @@ module JamRuby
|
||||||
# if no shpping carts have been marked, then mark it redeemable
|
# if no shpping carts have been marked, then mark it redeemable
|
||||||
# should be wrapped in a TRANSACTION
|
# should be wrapped in a TRANSACTION
|
||||||
def self.user_has_redeemable_jam_track?(any_user)
|
def self.user_has_redeemable_jam_track?(any_user)
|
||||||
mark_redeem = false
|
|
||||||
if APP_CONFIG.one_free_jamtrack_per_user && any_user.has_redeemable_jamtrack
|
if any_user.has_redeemable_jamtrack || any_user.gifted_jamtracks > 0
|
||||||
mark_redeem = true # start out assuming we can redeem...
|
|
||||||
|
free_in_cart = 0
|
||||||
any_user.shopping_carts.each do |shopping_cart|
|
any_user.shopping_carts.each do |shopping_cart|
|
||||||
# but if we find any shopping cart item already marked for redeem, then back out of mark_redeem=true
|
# but if we find any shopping cart item already marked for redeem, then back out of mark_redeem=true
|
||||||
if shopping_cart.cart_type == JamTrack::PRODUCT_TYPE && shopping_cart.marked_for_redeem > 0
|
if shopping_cart.cart_type == JamTrack::PRODUCT_TYPE
|
||||||
mark_redeem = false
|
free_in_cart += shopping_cart.marked_for_redeem
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
any_user.free_jamtracks > free_in_cart
|
||||||
|
else
|
||||||
|
false
|
||||||
end
|
end
|
||||||
mark_redeem
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# adds a jam_track to cart, checking for promotions
|
# adds a jam_track to cart, checking for promotions
|
||||||
def self.add_jam_track_to_cart(any_user, jam_track)
|
def self.add_jam_track_to_cart(any_user, jam_track, clear:false)
|
||||||
cart = nil
|
cart = nil
|
||||||
ShoppingCart.transaction do
|
ShoppingCart.transaction do
|
||||||
|
|
||||||
if any_user.has_redeemable_jamtrack
|
if clear
|
||||||
# if you still have a freebie available to you, or if you are an anonymous user, we make sure there is nothing else in your shopping cart
|
# if you are an anonymous user, we make sure there is nothing else in your shopping cart ... keep it clean for the 'new user rummaging around for a freebie scenario'
|
||||||
any_user.destroy_all_shopping_carts
|
any_user.destroy_jam_track_shopping_carts
|
||||||
|
any_user.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
|
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
|
||||||
|
|
@ -164,23 +215,66 @@ module JamRuby
|
||||||
cart
|
cart
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.add_item_to_cart(any_user, item)
|
||||||
|
cart = nil
|
||||||
|
ShoppingCart.transaction do
|
||||||
|
cart = ShoppingCart.create(any_user, item, 1, false)
|
||||||
|
end
|
||||||
|
cart
|
||||||
|
end
|
||||||
|
|
||||||
# deletes a jam track from the shopping cart, updating redeem flag as necessary
|
# deletes a jam track from the shopping cart, updating redeem flag as necessary
|
||||||
def self.remove_jam_track_from_cart(any_user, cart)
|
def self.remove_jam_track_from_cart(any_user, cart)
|
||||||
ShoppingCart.transaction do
|
ShoppingCart.transaction do
|
||||||
cart.destroy
|
cart.destroy
|
||||||
# check if we should move the redemption
|
|
||||||
|
# so that user.shopping_carts reflects truth
|
||||||
|
any_user.reload
|
||||||
|
|
||||||
|
# check if we should move the redemption around automatically
|
||||||
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
|
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
|
||||||
|
|
||||||
carts = any_user.shopping_carts
|
carts = any_user.shopping_carts
|
||||||
|
|
||||||
# if we find any carts on the account, mark one redeemable
|
# if we find any carts on the account that are not redeemed, mark first one redeemable
|
||||||
if mark_redeem && carts.length > 0
|
if mark_redeem && carts.length > 0
|
||||||
carts[0].redeem(mark_redeem)
|
carts.each do |cart|
|
||||||
carts[0].save
|
if cart.marked_for_redeem == 0
|
||||||
|
if cart.quantity > 1
|
||||||
|
raise 'unknown situation for redeemption juggling'
|
||||||
|
end
|
||||||
|
cart.redeem(mark_redeem)
|
||||||
|
cart.save
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.remove_item_from_cart(any_user, cart)
|
||||||
|
ShoppingCart.transaction do
|
||||||
|
cart.destroy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# if the number of items in the shopping cart is less than gifted_jamtracks on the user, then fix them all up
|
||||||
|
def self.apply_gifted_jamtracks(user)
|
||||||
|
jam_track_carts = user.shopping_carts.where(cart_type:JamTrack::PRODUCT_TYPE)
|
||||||
|
|
||||||
|
if jam_track_carts.count > user.gifted_jamtracks
|
||||||
|
# just whack everything in their shopping cart
|
||||||
|
user.destroy_all_shopping_carts
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
jam_track_carts.each do |cart|
|
||||||
|
cart.skip_mix_check = true
|
||||||
|
cart.marked_for_redeem = 1
|
||||||
|
cart.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def port(user, anonymous_user)
|
def port(user, anonymous_user)
|
||||||
|
|
||||||
ShoppingCart.transaction do
|
ShoppingCart.transaction do
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,12 @@ module JamRuby
|
||||||
attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :birth_date, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_large_fpfile, :cropped_s3_path, :cropped_large_s3_path, :photo_url, :large_photo_url, :crop_selection
|
attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :birth_date, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_large_fpfile, :cropped_s3_path, :cropped_large_s3_path, :photo_url, :large_photo_url, :crop_selection
|
||||||
|
|
||||||
# updating_password corresponds to a lost_password
|
# updating_password corresponds to a lost_password
|
||||||
attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field, :mods_json
|
attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field, :mods_json, :expecting_gift_card
|
||||||
|
|
||||||
belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id'
|
belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id'
|
||||||
|
|
||||||
|
has_many :controlled_sessions, :class_name=> "JamRuby::MusicSession", inverse_of: :session_controller, foreign_key: :session_controller_id
|
||||||
|
|
||||||
# authorizations (for facebook, etc -- omniauth)
|
# authorizations (for facebook, etc -- omniauth)
|
||||||
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"
|
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"
|
||||||
|
|
||||||
|
|
@ -149,6 +151,11 @@ module JamRuby
|
||||||
# events
|
# events
|
||||||
has_many :event_sessions, :class_name => "JamRuby::EventSession"
|
has_many :event_sessions, :class_name => "JamRuby::EventSession"
|
||||||
|
|
||||||
|
# gift cards
|
||||||
|
has_many :gift_cards, :class_name=> "JamRuby::GiftCard"
|
||||||
|
has_many :gift_card_purchases, :class_name=> "JamRuby::GiftCardPurchase"
|
||||||
|
|
||||||
|
|
||||||
# affiliate_partner
|
# affiliate_partner
|
||||||
has_one :affiliate_partner, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :partner_user_id, inverse_of: :partner_user
|
has_one :affiliate_partner, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :partner_user_id, inverse_of: :partner_user
|
||||||
belongs_to :affiliate_referral, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :affiliate_referral_id, :counter_cache => :referral_user_count
|
belongs_to :affiliate_referral, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :affiliate_referral_id, :counter_cache => :referral_user_count
|
||||||
|
|
@ -177,11 +184,13 @@ module JamRuby
|
||||||
has_one :musician_search, :class_name => 'JamRuby::MusicianSearch'
|
has_one :musician_search, :class_name => 'JamRuby::MusicianSearch'
|
||||||
has_one :band_search, :class_name => 'JamRuby::BandSearch'
|
has_one :band_search, :class_name => 'JamRuby::BandSearch'
|
||||||
|
|
||||||
|
before_save :default_anonymous_names
|
||||||
before_save :create_remember_token, :if => :should_validate_password?
|
before_save :create_remember_token, :if => :should_validate_password?
|
||||||
before_save :stringify_avatar_info , :if => :updating_avatar
|
before_save :stringify_avatar_info , :if => :updating_avatar
|
||||||
|
|
||||||
validates :first_name, presence: true, length: {maximum: 50}, no_profanity: true
|
validates :first_name, length: {maximum: 50}, no_profanity: true
|
||||||
validates :last_name, presence: true, length: {maximum: 50}, no_profanity: true
|
validates :last_name, length: {maximum: 50}, no_profanity: true
|
||||||
|
validates :last_name, length: {maximum: 50}, no_profanity: true
|
||||||
validates :biography, length: {maximum: 4000}, no_profanity: true
|
validates :biography, length: {maximum: 4000}, no_profanity: true
|
||||||
validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}
|
validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}
|
||||||
validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, :if => :updating_email
|
validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, :if => :updating_email
|
||||||
|
|
@ -193,6 +202,7 @@ module JamRuby
|
||||||
validates :terms_of_service, :acceptance => {:accept => true, :on => :create, :allow_nil => false }
|
validates :terms_of_service, :acceptance => {:accept => true, :on => :create, :allow_nil => false }
|
||||||
validates :reuse_card, :inclusion => {:in => [true, false]}
|
validates :reuse_card, :inclusion => {:in => [true, false]}
|
||||||
validates :has_redeemable_jamtrack, :inclusion => {:in => [true, false]}
|
validates :has_redeemable_jamtrack, :inclusion => {:in => [true, false]}
|
||||||
|
validates :gifted_jamtracks, presence: true, :numericality => { :less_than_or_equal_to => 100 }
|
||||||
validates :subscribe_email, :inclusion => {:in => [nil, true, false]}
|
validates :subscribe_email, :inclusion => {:in => [nil, true, false]}
|
||||||
validates :musician, :inclusion => {:in => [true, false]}
|
validates :musician, :inclusion => {:in => [true, false]}
|
||||||
validates :show_whats_next, :inclusion => {:in => [nil, true, false]}
|
validates :show_whats_next, :inclusion => {:in => [nil, true, false]}
|
||||||
|
|
@ -213,6 +223,7 @@ module JamRuby
|
||||||
validate :email_case_insensitive_uniqueness
|
validate :email_case_insensitive_uniqueness
|
||||||
validate :update_email_case_insensitive_uniqueness, :if => :updating_email
|
validate :update_email_case_insensitive_uniqueness, :if => :updating_email
|
||||||
validate :validate_mods
|
validate :validate_mods
|
||||||
|
validate :presence_gift_card, :if => :expecting_gift_card
|
||||||
|
|
||||||
scope :musicians, where(:musician => true)
|
scope :musicians, where(:musician => true)
|
||||||
scope :fans, where(:musician => false)
|
scope :fans, where(:musician => false)
|
||||||
|
|
@ -232,6 +243,18 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def has_any_free_jamtracks
|
||||||
|
has_redeemable_jamtrack || gifted_jamtracks > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def free_jamtracks
|
||||||
|
(has_redeemable_jamtrack ? 1 : 0) + gifted_jamtracks
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_free_jamtrack?
|
||||||
|
ShoppingCart.user_has_redeemable_jam_track?(self)
|
||||||
|
end
|
||||||
|
|
||||||
def failed_qualification(reason)
|
def failed_qualification(reason)
|
||||||
self.last_failed_certified_gear_at = DateTime.now
|
self.last_failed_certified_gear_at = DateTime.now
|
||||||
self.last_failed_certified_gear_reason = reason
|
self.last_failed_certified_gear_reason = reason
|
||||||
|
|
@ -254,6 +277,12 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def presence_gift_card
|
||||||
|
if self.gift_cards.length == 0
|
||||||
|
errors.add(:gift_card, ValidationMessages::NOT_FOUND)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def validate_current_password
|
def validate_current_password
|
||||||
# checks if the user put in their current password (used when changing your email, for instance)
|
# checks if the user put in their current password (used when changing your email, for instance)
|
||||||
errors.add(:current_password, ValidationMessages::NOT_YOUR_PASSWORD) if should_confirm_existing_password? && !valid_password?(self.current_password)
|
errors.add(:current_password, ValidationMessages::NOT_YOUR_PASSWORD) if should_confirm_existing_password? && !valid_password?(self.current_password)
|
||||||
|
|
@ -296,8 +325,16 @@ module JamRuby
|
||||||
online?
|
online?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def anonymous?
|
||||||
|
first_name == 'Anonymous' && last_name == 'Anonymous'
|
||||||
|
end
|
||||||
|
|
||||||
def name
|
def name
|
||||||
"#{first_name} #{last_name}"
|
if anonymous?
|
||||||
|
'Anonymous'
|
||||||
|
else
|
||||||
|
"#{first_name} #{last_name}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def location
|
def location
|
||||||
|
|
@ -576,9 +613,7 @@ module JamRuby
|
||||||
def to_s
|
def to_s
|
||||||
return email unless email.nil?
|
return email unless email.nil?
|
||||||
|
|
||||||
if !first_name.nil? && !last_name.nil?
|
return name unless name.nil?
|
||||||
return first_name + ' ' + last_name
|
|
||||||
end
|
|
||||||
|
|
||||||
id
|
id
|
||||||
end
|
end
|
||||||
|
|
@ -1018,6 +1053,7 @@ module JamRuby
|
||||||
reuse_card = options[:reuse_card]
|
reuse_card = options[:reuse_card]
|
||||||
signup_hint = options[:signup_hint]
|
signup_hint = options[:signup_hint]
|
||||||
affiliate_partner = options[:affiliate_partner]
|
affiliate_partner = options[:affiliate_partner]
|
||||||
|
gift_card = options[:gift_card]
|
||||||
|
|
||||||
user = User.new
|
user = User.new
|
||||||
|
|
||||||
|
|
@ -1029,6 +1065,9 @@ module JamRuby
|
||||||
user.terms_of_service = terms_of_service
|
user.terms_of_service = terms_of_service
|
||||||
user.musician = musician
|
user.musician = musician
|
||||||
user.reuse_card unless reuse_card.nil?
|
user.reuse_card unless reuse_card.nil?
|
||||||
|
user.gifted_jamtracks = 0
|
||||||
|
user.has_redeemable_jamtrack = true
|
||||||
|
|
||||||
|
|
||||||
# FIXME: Setting random password for social network logins. This
|
# FIXME: Setting random password for social network logins. This
|
||||||
# is because we have validations all over the place on this.
|
# is because we have validations all over the place on this.
|
||||||
|
|
@ -1133,8 +1172,22 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
found_gift_card = nil
|
||||||
|
|
||||||
|
# if a gift card value was passed in, then try to find that gift card and apply it to user
|
||||||
|
if gift_card
|
||||||
|
user.expecting_gift_card = true
|
||||||
|
found_gift_card = GiftCard.where(code:gift_card).where(user_id:nil).first
|
||||||
|
user.gift_cards << found_gift_card if found_gift_card
|
||||||
|
end
|
||||||
|
|
||||||
user.save
|
user.save
|
||||||
|
|
||||||
|
if found_gift_card
|
||||||
|
user.reload
|
||||||
|
ShoppingCart.apply_gifted_jamtracks(user)
|
||||||
|
end
|
||||||
|
|
||||||
# if the user has just one, free jamtrack in their shopping cart, and it matches the signup hint, then auto-buy it
|
# if the user has just one, free jamtrack in their shopping cart, and it matches the signup hint, then auto-buy it
|
||||||
# only_freebie_in_cart =
|
# only_freebie_in_cart =
|
||||||
# signup_hint &&
|
# signup_hint &&
|
||||||
|
|
@ -1174,6 +1227,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
user.reload if user.id# gift card adding gifted_jamtracks doesn't reflect here until reload
|
||||||
user
|
user
|
||||||
end # def signup
|
end # def signup
|
||||||
|
|
||||||
|
|
@ -1629,6 +1683,11 @@ module JamRuby
|
||||||
ShoppingCart.where("user_id=?", self).destroy_all
|
ShoppingCart.where("user_id=?", self).destroy_all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def destroy_jam_track_shopping_carts
|
||||||
|
ShoppingCart.destroy_all(anonymous_user_id: @id, cart_type: JamTrack::PRODUCT_TYPE)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def unsubscribe_token
|
def unsubscribe_token
|
||||||
self.class.create_access_token(self)
|
self.class.create_access_token(self)
|
||||||
end
|
end
|
||||||
|
|
@ -1681,14 +1740,17 @@ module JamRuby
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
private
|
private
|
||||||
def create_remember_token
|
def create_remember_token
|
||||||
self.remember_token = SecureRandom.urlsafe_base64
|
self.remember_token = SecureRandom.urlsafe_base64
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def default_anonymous_names
|
||||||
|
self.first_name = 'Anonymous' if self.first_name.nil?
|
||||||
|
self.last_name = 'Anonymous' if self.last_name.nil?
|
||||||
|
end
|
||||||
|
|
||||||
def stringify_avatar_info
|
def stringify_avatar_info
|
||||||
# fpfile comes in as a hash, which is a easy-to-use and validate form. However, we store it as a VARCHAR,
|
# fpfile comes in as a hash, which is a easy-to-use and validate form. However, we store it as a VARCHAR,
|
||||||
# so we need t oconvert it to JSON before storing it (otherwise it gets serialized as a ruby object)
|
# so we need t oconvert it to JSON before storing it (otherwise it gets serialized as a ruby object)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
module JamRuby
|
module JamRuby
|
||||||
class UserAuthorization < ActiveRecord::Base
|
class UserAuthorization < ActiveRecord::Base
|
||||||
|
|
||||||
attr_accessible :provider, :uid, :token, :token_expiration, :secret, :user
|
attr_accessible :provider, :uid, :token, :token_expiration, :secret, :user, :refresh_token
|
||||||
|
|
||||||
self.table_name = "user_authorizations"
|
self.table_name = "user_authorizations"
|
||||||
|
|
||||||
|
|
@ -12,6 +12,36 @@ module JamRuby
|
||||||
validates_uniqueness_of :uid, scope: :provider
|
validates_uniqueness_of :uid, scope: :provider
|
||||||
# token, secret, token_expiration can be missing
|
# token, secret, token_expiration can be missing
|
||||||
|
|
||||||
|
def self.refreshing_google_auth(user)
|
||||||
|
auth = self.where(:user_id => user.id)
|
||||||
|
.where(:provider => 'google_login')
|
||||||
|
.limit(1).first
|
||||||
|
|
||||||
|
# if we have an auth that will expire in less than 10 minutes
|
||||||
|
if auth && auth.refresh_token && auth.token_expiration < Time.now - 60 * 10
|
||||||
|
|
||||||
|
begin
|
||||||
|
oauth_client = OAuth2::Client.new(
|
||||||
|
Rails.application.config.google_client_id, Rails.application.config.google_secret,
|
||||||
|
:site => "https://accounts.google.com",
|
||||||
|
:token_url => "/o/oauth2/token",
|
||||||
|
:authorize_url => "/o/oauth2/auth")
|
||||||
|
access_token = OAuth2::AccessToken.from_hash(oauth_client, {:refresh_token => auth.refresh_token})
|
||||||
|
access_token = access_token.refresh!
|
||||||
|
|
||||||
|
auth.token = access_token.token
|
||||||
|
auth.token_expiration = Time.now + access_token.expires_in
|
||||||
|
auth.save
|
||||||
|
return auth
|
||||||
|
rescue Exception => e
|
||||||
|
# couldn't refresh; probably the user has revoked the app's rights
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
auth
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.google_auth(user)
|
def self.google_auth(user)
|
||||||
self
|
self
|
||||||
.where(:user_id => user.id)
|
.where(:user_id => user.id)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
module JamRuby
|
||||||
|
class UserBlacklist < ActiveRecord::Base
|
||||||
|
|
||||||
|
attr_accessible :user_id, :notes, as: :admin
|
||||||
|
@@log = Logging.logger[UserBlacklist]
|
||||||
|
|
||||||
|
belongs_to :user, :class_name => "JamRuby::User"
|
||||||
|
|
||||||
|
validates :user, presence:true, uniqueness: true
|
||||||
|
|
||||||
|
def self.listed(user)
|
||||||
|
UserBlacklist.count(:conditions => "user_id= '#{user.id}'") == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.admin_url
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/user_blacklists/"
|
||||||
|
end
|
||||||
|
|
||||||
|
def admin_url
|
||||||
|
APP_CONFIG.admin_root_url + "/admin/user_blacklists/" + id
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
module JamRuby
|
||||||
|
class UserEvent < ActiveRecord::Base
|
||||||
|
|
||||||
|
belongs_to :user, class_name: 'JamRuby::User'
|
||||||
|
|
||||||
|
validates :name, presence: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,771 @@
|
||||||
|
require 'json'
|
||||||
|
require 'resque'
|
||||||
|
require 'resque-retry'
|
||||||
|
require 'net/http'
|
||||||
|
require 'digest/md5'
|
||||||
|
|
||||||
|
module JamRuby
|
||||||
|
class JamTrackMixdownPackager
|
||||||
|
extend JamRuby::ResqueStats
|
||||||
|
|
||||||
|
include JamRuby::S3ManagerMixin
|
||||||
|
|
||||||
|
TAP_IN_PADDING = 2
|
||||||
|
|
||||||
|
MAX_PAN = 90
|
||||||
|
MIN_PAN = -90
|
||||||
|
KNOCK_SECONDS = 0.035
|
||||||
|
|
||||||
|
attr_accessor :mixdown_package_id, :settings, :mixdown_package, :mixdown, :step
|
||||||
|
@queue = :jam_track_mixdown_packager
|
||||||
|
|
||||||
|
def log
|
||||||
|
@log || Logging.logger[JamTrackMixdownPackager]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.perform(mixdown_package_id, bitrate=48)
|
||||||
|
jam_track_builder = JamTrackMixdownPackager.new()
|
||||||
|
jam_track_builder.mixdown_package_id = mixdown_package_id
|
||||||
|
jam_track_builder.run
|
||||||
|
end
|
||||||
|
|
||||||
|
def compute_steps
|
||||||
|
@step = 0
|
||||||
|
number_downloads = @track_settings.length
|
||||||
|
number_volume_adjustments = (@track_settings.select { |track| should_alter_volume? track }).length
|
||||||
|
|
||||||
|
pitch_shift_steps = @mixdown.will_pitch_shift? ? 1 : 0
|
||||||
|
mix_steps = 1
|
||||||
|
package_steps = 1
|
||||||
|
|
||||||
|
number_downloads + number_volume_adjustments + pitch_shift_steps + mix_steps + package_steps
|
||||||
|
end
|
||||||
|
|
||||||
|
def run
|
||||||
|
begin
|
||||||
|
log.info("Mixdown job starting. mixdown_packager_id #{mixdown_package_id}")
|
||||||
|
begin
|
||||||
|
@mixdown_package = JamTrackMixdownPackage.find(mixdown_package_id)
|
||||||
|
|
||||||
|
|
||||||
|
# bailout check
|
||||||
|
if @mixdown_package.signed?
|
||||||
|
log.debug("package is already signed. bailing")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
@mixdown = @mixdown_package.jam_track_mixdown
|
||||||
|
@settings = JSON.parse(@mixdown.settings)
|
||||||
|
|
||||||
|
process_jmep
|
||||||
|
|
||||||
|
track_settings
|
||||||
|
|
||||||
|
# compute the step count
|
||||||
|
total_steps = compute_steps
|
||||||
|
|
||||||
|
# track that it's started ( and avoid db validations )
|
||||||
|
signing_started_at = Time.now
|
||||||
|
last_step_at = Time.now
|
||||||
|
#JamTrackMixdownPackage.where(:id => @mixdown_package.id).update_all(:signing_started_at => signing_started_at, :should_retry => false, packaging_steps: total_steps, current_packaging_step: 0, last_step_at: last_step_at, :signing => true)
|
||||||
|
|
||||||
|
# because we are skipping 'after_save', we have to keep the model current for the notification. A bit ugly...
|
||||||
|
|
||||||
|
@mixdown_package.current_packaging_step = 0
|
||||||
|
@mixdown_package.packaging_steps = total_steps
|
||||||
|
@mixdown_package.signing_started_at = signing_started_at
|
||||||
|
@mixdown_package.signing = true
|
||||||
|
@mixdown_package.should_retry = false
|
||||||
|
@mixdown_package.last_step_at = last_step_at
|
||||||
|
@mixdown_package.queued = false
|
||||||
|
@mixdown_package.save
|
||||||
|
|
||||||
|
SubscriptionMessage.mixdown_signing_job_change(@mixdown_package)
|
||||||
|
|
||||||
|
package
|
||||||
|
|
||||||
|
log.info "Signed mixdown package to #{@mixdown_package[:url]}"
|
||||||
|
|
||||||
|
rescue Exception => e
|
||||||
|
# record the error in the database
|
||||||
|
post_error(e)
|
||||||
|
|
||||||
|
#SubscriptionMessage.mixdown_signing_job_change(@mixdown_package)
|
||||||
|
# and let the job fail, alerting ops too
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def should_alter_volume? track
|
||||||
|
|
||||||
|
# short cut is possible if vol = 1.0 and pan = 0
|
||||||
|
vol = track[:vol]
|
||||||
|
pan = track[:pan]
|
||||||
|
|
||||||
|
vol != 1.0 || pan != 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_jmep
|
||||||
|
@start_points = []
|
||||||
|
@initial_padding = 0.0
|
||||||
|
@tap_in_initial_silence = 0
|
||||||
|
|
||||||
|
speed = @settings['speed'] || 0
|
||||||
|
|
||||||
|
@speed_factor = 1.0 + (-speed.to_f / 100.0)
|
||||||
|
@inverse_speed_factor = 1 - (-speed.to_f / 100)
|
||||||
|
|
||||||
|
log.info("speed factor #{@speed_factor}")
|
||||||
|
|
||||||
|
jmep = @mixdown.jam_track.jmep_json
|
||||||
|
if jmep
|
||||||
|
jmep = JSON.parse(jmep)
|
||||||
|
end
|
||||||
|
|
||||||
|
if jmep.nil?
|
||||||
|
log.debug("no jmep")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
events = jmep["Events"]
|
||||||
|
|
||||||
|
return if events.nil? || events.length == 0
|
||||||
|
|
||||||
|
metronome = nil
|
||||||
|
events.each do |event|
|
||||||
|
if event.has_key?("metronome")
|
||||||
|
metronome = event["metronome"]
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if metronome.nil? || metronome.length == 0
|
||||||
|
log.debug("no metronome events for jmep", jmep)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
@start_points = metronome.select { |x| puts x.inspect; x["action"] == "start" }
|
||||||
|
|
||||||
|
log.debug("found #{@start_points.length} metronome start points")
|
||||||
|
|
||||||
|
start_point = @start_points[0]
|
||||||
|
|
||||||
|
if start_point
|
||||||
|
start_time = parse_time(start_point["ts"])
|
||||||
|
|
||||||
|
if start_time < 2.0
|
||||||
|
padding = start_time - 2.0
|
||||||
|
@initial_padding = padding.abs
|
||||||
|
@initial_tap_in = start_time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @speed_factor != 1.0
|
||||||
|
metronome.length.times do |count|
|
||||||
|
|
||||||
|
# we expect to find metronome start/stop grouped
|
||||||
|
if count % 2 == 0
|
||||||
|
|
||||||
|
start = metronome[count]
|
||||||
|
stop = metronome[count + 1]
|
||||||
|
|
||||||
|
if start["action"] != "start" || stop["action"] != "stop"
|
||||||
|
# bail out
|
||||||
|
log.error("found de-coupled metronome events #{start.to_json} | #{stop.to_json}")
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
bpm = start["bpm"].to_f
|
||||||
|
stop_time = parse_time(stop['ts'])
|
||||||
|
ticks = stop['ticks'].to_i
|
||||||
|
|
||||||
|
|
||||||
|
new_bpm = bpm * @inverse_speed_factor
|
||||||
|
new_stop_time = stop_time * @speed_factor
|
||||||
|
new_start_time = new_stop_time - (60.0/new_bpm * ticks)
|
||||||
|
|
||||||
|
log.info("original bpm:#{bpm} start: #{parse_time(start["ts"])} stop: #{stop_time}")
|
||||||
|
log.info("updated bpm:#{new_bpm} start: #{new_start_time} stop: #{new_stop_time}")
|
||||||
|
|
||||||
|
stop["ts"] = new_stop_time
|
||||||
|
start["ts"] = new_start_time
|
||||||
|
start["bpm"] = new_bpm
|
||||||
|
stop["bpm"] = new_bpm
|
||||||
|
|
||||||
|
@tap_in_initial_silence = (@initial_tap_in + @initial_padding) * @speed_factor
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@start_points = metronome.select { |x| puts x.inspect; x["action"] == "start" }
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# format like: "-0:00:02:820"
|
||||||
|
def parse_time(ts)
|
||||||
|
|
||||||
|
if ts.is_a?(Float)
|
||||||
|
return ts
|
||||||
|
end
|
||||||
|
|
||||||
|
time = 0.0
|
||||||
|
negative = false
|
||||||
|
|
||||||
|
if ts.start_with?('-')
|
||||||
|
negative = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# parse time_format
|
||||||
|
bits = ts.split(':').reverse
|
||||||
|
|
||||||
|
bit_position = 0
|
||||||
|
bits.each do |bit|
|
||||||
|
if bit_position == 0
|
||||||
|
# milliseconds
|
||||||
|
milliseconds = bit.to_f
|
||||||
|
time += milliseconds/1000
|
||||||
|
elsif bit_position == 1
|
||||||
|
# seconds
|
||||||
|
time += bit.to_f
|
||||||
|
elsif bit_position == 2
|
||||||
|
# minutes
|
||||||
|
time += 60 * bit.to_f
|
||||||
|
elsif bit_position == 3
|
||||||
|
# hours
|
||||||
|
# not bothering
|
||||||
|
end
|
||||||
|
|
||||||
|
bit_position += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if negative
|
||||||
|
time = 0.0 - time
|
||||||
|
end
|
||||||
|
|
||||||
|
time
|
||||||
|
end
|
||||||
|
|
||||||
|
def path_to_resources
|
||||||
|
File.join(File.dirname(File.expand_path(__FILE__)), '../../../lib/jam_ruby/app/assets/sounds')
|
||||||
|
end
|
||||||
|
|
||||||
|
def knock_file
|
||||||
|
if long_sample_rate == 44100
|
||||||
|
knock = File.join(path_to_resources, 'knock44.wav')
|
||||||
|
else
|
||||||
|
knock = File.join(path_to_resources, 'knock48.wav')
|
||||||
|
end
|
||||||
|
knock
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_silence(tmp_dir, segment_count, duration)
|
||||||
|
file = File.join(tmp_dir, "#{segment_count}.wav")
|
||||||
|
|
||||||
|
# -c 2 means stereo
|
||||||
|
cmd("sox -n -r #{long_sample_rate} -c 2 #{file} trim 0.0 #{duration}", "silence")
|
||||||
|
|
||||||
|
file
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_tapin_track(tmp_dir)
|
||||||
|
|
||||||
|
return nil if @start_points.length == 0
|
||||||
|
|
||||||
|
segment_count = 0
|
||||||
|
|
||||||
|
|
||||||
|
#initial_silence = @initial_tap_in + @initial_padding
|
||||||
|
|
||||||
|
initial_silence = @tap_in_initial_silence
|
||||||
|
|
||||||
|
#log.info("tapin data: initial_tap_in: #{@initial_tap_in}, initial_padding: #{@initial_padding}, initial_silence: #{initial_silence}")
|
||||||
|
|
||||||
|
time_points = []
|
||||||
|
files = []
|
||||||
|
if initial_silence > 0
|
||||||
|
|
||||||
|
files << create_silence(tmp_dir, segment_count, initial_silence)
|
||||||
|
|
||||||
|
time_points << {type: :silence, ts: initial_silence}
|
||||||
|
segment_count += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
time_cursor = nil
|
||||||
|
@start_points.each do |start_point|
|
||||||
|
tap_time = parse_time(start_point["ts"])
|
||||||
|
if !time_cursor.nil?
|
||||||
|
between_silence = tap_time - time_cursor
|
||||||
|
files << create_silence(tmp_dir, segment_count, between_silence)
|
||||||
|
time_points << {type: :silence, ts: between_silence}
|
||||||
|
end
|
||||||
|
time_cursor = tap_time
|
||||||
|
bpm = start_point["bpm"].to_f
|
||||||
|
|
||||||
|
tick_silence = 60.0/bpm - KNOCK_SECONDS
|
||||||
|
|
||||||
|
ticks = start_point["ticks"].to_i
|
||||||
|
|
||||||
|
ticks.times do |tick|
|
||||||
|
files << knock_file
|
||||||
|
files << create_silence(tmp_dir, segment_count, tick_silence)
|
||||||
|
time_points << {type: :knock, ts: KNOCK_SECONDS}
|
||||||
|
time_points << {type: :silence, ts: tick_silence}
|
||||||
|
time_cursor + 60.0/bpm
|
||||||
|
segment_count += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
log.info("time points for tap-in: #{time_points.inspect}")
|
||||||
|
# do we need to pad with time? not sure
|
||||||
|
|
||||||
|
sequence_cmd = "sox "
|
||||||
|
files.each do |file|
|
||||||
|
sequence_cmd << "\"#{file}\" "
|
||||||
|
end
|
||||||
|
|
||||||
|
count_in = File.join(tmp_dir, "count-in.wav")
|
||||||
|
sequence_cmd << "\"#{count_in}\""
|
||||||
|
|
||||||
|
cmd(sequence_cmd, "count_in")
|
||||||
|
@count_in_file = count_in
|
||||||
|
count_in
|
||||||
|
end
|
||||||
|
|
||||||
|
# creates a list of tracks to actually mix
|
||||||
|
def track_settings
|
||||||
|
altered_tracks = @settings["tracks"] || []
|
||||||
|
|
||||||
|
@track_settings = []
|
||||||
|
|
||||||
|
#void slider2Pan(int i, float *f);
|
||||||
|
|
||||||
|
stems = @mixdown.jam_track.stem_tracks
|
||||||
|
@track_count = stems.length
|
||||||
|
|
||||||
|
@include_count_in = @settings["count-in"] && @start_points.length > 0 && @mixdown_package.encrypt_type.nil?
|
||||||
|
|
||||||
|
# temp
|
||||||
|
# @include_count_in = true
|
||||||
|
|
||||||
|
if @include_count_in
|
||||||
|
@track_count += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
stems.each do |stem|
|
||||||
|
|
||||||
|
vol = 1.0
|
||||||
|
pan = 0
|
||||||
|
match = false
|
||||||
|
skipped = false
|
||||||
|
# is this stem in the altered_tracks list?
|
||||||
|
altered_tracks.each do |alteration|
|
||||||
|
|
||||||
|
if alteration["id"] == stem.id
|
||||||
|
if alteration["mute"] || alteration["vol"] == 0
|
||||||
|
log.debug("leaving out track because muted or 0 volume #{alteration.inspect}")
|
||||||
|
skipped = true
|
||||||
|
next
|
||||||
|
else
|
||||||
|
vol = alteration["vol"] || vol
|
||||||
|
pan = alteration["pan"] || pan
|
||||||
|
end
|
||||||
|
@track_settings << {stem: stem, vol: vol, pan: pan}
|
||||||
|
match = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# if we didn't deliberately skip this one, and if there was no 'match' (meaning user did not specify), then we leave this in unchanged
|
||||||
|
if !skipped && !match
|
||||||
|
@track_settings << {stem: stem, vol: vol, pan: pan}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @include_count_in
|
||||||
|
@track_settings << {count_in: true, vol: 1.0, pan: 0}
|
||||||
|
end
|
||||||
|
|
||||||
|
@track_settings
|
||||||
|
end
|
||||||
|
|
||||||
|
def slider_to_pan(pan)
|
||||||
|
# transpose MIN_PAN to MAX_PAN to
|
||||||
|
# 0-1.0 range
|
||||||
|
#assumes abs(MIN_PAN) == abs(MAX_PAN)
|
||||||
|
# k = f(i) = (i)/(2*MAX_PAN) + 0.5
|
||||||
|
# so f(MIN_PAN) = -0.5 + 0.5 = 0
|
||||||
|
|
||||||
|
k = ((pan * (1.0))/ (2.0 * MAX_PAN)) + 0.5
|
||||||
|
l, r = 0
|
||||||
|
|
||||||
|
if k == 0
|
||||||
|
l = 0.0
|
||||||
|
r = 1.0
|
||||||
|
else
|
||||||
|
l = Math.sqrt(k)
|
||||||
|
r = Math.sqrt(1-k)
|
||||||
|
end
|
||||||
|
|
||||||
|
[l, r]
|
||||||
|
end
|
||||||
|
|
||||||
|
def package
|
||||||
|
|
||||||
|
log.info("Settings: #{@settings.to_json}")
|
||||||
|
|
||||||
|
Dir.mktmpdir do |tmp_dir|
|
||||||
|
|
||||||
|
# download all files
|
||||||
|
@track_settings.each do |track|
|
||||||
|
|
||||||
|
if track[:count_in]
|
||||||
|
file = create_tapin_track(tmp_dir)
|
||||||
|
bump_step(@mixdown_package)
|
||||||
|
else
|
||||||
|
jam_track_track = track[:stem]
|
||||||
|
|
||||||
|
file = File.join(tmp_dir, jam_track_track.id + '.ogg')
|
||||||
|
|
||||||
|
bump_step(@mixdown_package)
|
||||||
|
|
||||||
|
# download each track needed
|
||||||
|
s3_manager.download(jam_track_track.url_by_sample_rate(@mixdown_package.sample_rate), file)
|
||||||
|
end
|
||||||
|
|
||||||
|
track[:file] = file
|
||||||
|
end
|
||||||
|
|
||||||
|
audio_process tmp_dir
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def audio_process(tmp_dir)
|
||||||
|
# use sox remix to apply mute, volume, pan settings
|
||||||
|
|
||||||
|
|
||||||
|
# step 1: apply pan and volume per track. mute and vol of 0 has already been handled, by virtue of those tracks not being present in @track_settings
|
||||||
|
# step 2: mix all tracks into single track, dividing by constant number of jam tracks, which is same as done by client backend
|
||||||
|
# step 3: apply pitch and speed (if applicable)
|
||||||
|
# step 4: encrypt with jkz (if applicable)
|
||||||
|
|
||||||
|
apply_vol_and_pan tmp_dir
|
||||||
|
|
||||||
|
create_silence_padding tmp_dir
|
||||||
|
|
||||||
|
mix tmp_dir
|
||||||
|
|
||||||
|
pitch_speed tmp_dir
|
||||||
|
|
||||||
|
final_packaging tmp_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
# output is :volumed_file in each track in @track_settings
|
||||||
|
def apply_vol_and_pan(tmp_dir)
|
||||||
|
@track_settings.each do |track|
|
||||||
|
|
||||||
|
jam_track_track = track[:stem]
|
||||||
|
count_in = track[:count_in]
|
||||||
|
file = track[:file]
|
||||||
|
|
||||||
|
unless should_alter_volume? track
|
||||||
|
track[:volumed_file] = file
|
||||||
|
else
|
||||||
|
pan_l, pan_r = slider_to_pan(track[:pan])
|
||||||
|
|
||||||
|
vol = track[:vol]
|
||||||
|
|
||||||
|
# short
|
||||||
|
channel_l = pan_l * vol
|
||||||
|
channel_r = pan_r * vol
|
||||||
|
|
||||||
|
bump_step(@mixdown_package)
|
||||||
|
|
||||||
|
# sox claps.wav claps-remixed.wav remix 1v1.0 2v1.0
|
||||||
|
|
||||||
|
if count_in
|
||||||
|
volumed_file = File.join(tmp_dir, 'count-in' + '-volumed.ogg')
|
||||||
|
else
|
||||||
|
volumed_file = File.join(tmp_dir, jam_track_track.id + '-volumed.ogg')
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd("sox \"#{file}\" \"#{volumed_file}\" remix 1v#{channel_r} 2v#{channel_l}", 'vol_pan')
|
||||||
|
|
||||||
|
track[:volumed_file] = volumed_file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_silence_padding(tmp_dir)
|
||||||
|
if @initial_padding > 0 && @include_count_in
|
||||||
|
|
||||||
|
@padding_file = File.join(tmp_dir, "initial_padding.ogg")
|
||||||
|
|
||||||
|
# -c 2 means stereo
|
||||||
|
cmd("sox -n -r #{long_sample_rate} -c 2 #{@padding_file} trim 0.0 #{@initial_padding}", "initial_padding")
|
||||||
|
|
||||||
|
@track_settings.each do |track|
|
||||||
|
|
||||||
|
next if track[:count_in]
|
||||||
|
|
||||||
|
input = track[:volumed_file]
|
||||||
|
output = input[0..-5] + '-padded.ogg'
|
||||||
|
|
||||||
|
padd_cmd = "sox '#{@padding_file}' '#{input}' '#{output}'"
|
||||||
|
|
||||||
|
cmd(padd_cmd, "pad_track_with_silence")
|
||||||
|
track[:volumed_file] = output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# output is @mix_file
|
||||||
|
def mix(tmp_dir)
|
||||||
|
|
||||||
|
bump_step(@mixdown_package)
|
||||||
|
|
||||||
|
@mix_file = File.join(tmp_dir, "mix.ogg")
|
||||||
|
|
||||||
|
|
||||||
|
pitch = @settings['pitch'] || 0
|
||||||
|
speed = @settings['speed'] || 0
|
||||||
|
|
||||||
|
|
||||||
|
real_count = @track_settings.count
|
||||||
|
real_count -= 1 if @include_count_in
|
||||||
|
|
||||||
|
# if there is only one track to mix, we need to skip mixing (sox will barf if you try to mix one file), but still divide by number of tracks
|
||||||
|
if real_count <= 1
|
||||||
|
mix_divide = 1.0/@track_count
|
||||||
|
cmd = "sox -v #{mix_divide} \"#{@track_settings[0][:volumed_file]}\" \"#{@mix_file}\""
|
||||||
|
cmd(cmd, 'volume_adjust')
|
||||||
|
else
|
||||||
|
# sox -m will divide by number of inputs by default. But we purposefully leave out tracks that are mute/no volume (to save downloading/processing time in this job)
|
||||||
|
# so we need to tell sox to divide by how many tracks there are as a constant, because this is how the client works today
|
||||||
|
#sox -m -v 1/n file1 -v 1/n file2 out
|
||||||
|
cmd = "sox -m"
|
||||||
|
mix_divide = 1.0/@track_count
|
||||||
|
@track_settings.each do |track|
|
||||||
|
|
||||||
|
# if pitch/shifted, we lay the tap-in after pitch/speed shift
|
||||||
|
# next if (pitch != 0 || speed != 0) && track[:count_in]
|
||||||
|
next if track[:count_in]
|
||||||
|
|
||||||
|
volumed_file = track[:volumed_file]
|
||||||
|
cmd << " -v #{mix_divide} \"#{volumed_file}\""
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
cmd << " \"#{@mix_file}\""
|
||||||
|
cmd(cmd, 'mix_adjust')
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def long_sample_rate
|
||||||
|
sample_rate = 48000
|
||||||
|
if @mixdown_package.sample_rate != 48
|
||||||
|
sample_rate = 44100
|
||||||
|
end
|
||||||
|
sample_rate
|
||||||
|
end
|
||||||
|
|
||||||
|
# output is @speed_mix_file
|
||||||
|
def pitch_speed tmp_dir
|
||||||
|
|
||||||
|
# # usage
|
||||||
|
# This app will take an ogg, wav, or mp3 file (for the uploads) as its input and output an ogg file.
|
||||||
|
# Usage:
|
||||||
|
# sbsms path-to-input.ogg path-to-output.ogg TimeStrech PitchShift
|
||||||
|
|
||||||
|
# input is @mix_file, created by mix()
|
||||||
|
# output is @speed_mix_file
|
||||||
|
|
||||||
|
pitch = @settings['pitch'] || 0
|
||||||
|
speed = @settings['speed'] || 0
|
||||||
|
|
||||||
|
# if pitch and speed are 0, we do nothing here
|
||||||
|
if pitch == 0 && speed == 0
|
||||||
|
@speed_mix_file = @mix_file
|
||||||
|
else
|
||||||
|
bump_step(@mixdown_package)
|
||||||
|
|
||||||
|
@speed_mix_file = File.join(tmp_dir, "speed_mix_file.ogg")
|
||||||
|
|
||||||
|
# usage: sbsms infile<.wav|.aif|.mp3|.ogg> outfile<.ogg> rate[0.01:100] halfsteps[-48:48] outSampleRateInHz
|
||||||
|
|
||||||
|
sample_rate = long_sample_rate
|
||||||
|
|
||||||
|
# rate comes in as a percent (like 5, -5 for 5%, -5%). We need to change that to 1.05/
|
||||||
|
sbsms_speed = speed/100.0
|
||||||
|
sbsms_speed = 1.0 + sbsms_speed
|
||||||
|
|
||||||
|
sbsms_pitch = pitch
|
||||||
|
cmd("sbsms \"#{@mix_file}\" \"#{@speed_mix_file}\" #{sbsms_speed} #{sbsms_pitch} #{sample_rate}", 'speed_pitch_shift')
|
||||||
|
end
|
||||||
|
|
||||||
|
if @include_count_in
|
||||||
|
# lay the tap-ins over the recording
|
||||||
|
layered = File.join(tmp_dir, "layered_speed_mix.ogg")
|
||||||
|
cmd("sox -m '#{@count_in_file}' '#{@speed_mix_file}' '#{layered}'", "layer_tap_in")
|
||||||
|
@speed_mix_file = layered
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def final_packaging tmp_dir
|
||||||
|
|
||||||
|
bump_step(@mixdown_package)
|
||||||
|
|
||||||
|
url = nil
|
||||||
|
private_key = nil
|
||||||
|
md5 = nil
|
||||||
|
length = 0
|
||||||
|
output = nil
|
||||||
|
|
||||||
|
if @mixdown_package.encrypt_type
|
||||||
|
output, private_key = encrypt_jkz tmp_dir
|
||||||
|
else
|
||||||
|
# create output file to correct output format
|
||||||
|
output = convert tmp_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
# upload output to S3
|
||||||
|
s3_url = "#{@mixdown_package.store_dir}/#{@mixdown_package.filename}"
|
||||||
|
s3_manager.upload(s3_url, output)
|
||||||
|
|
||||||
|
length = File.size(output)
|
||||||
|
computed_md5 = Digest::MD5.new
|
||||||
|
File.open(output, 'rb').each { |line| computed_md5.update(line) }
|
||||||
|
md5 = computed_md5.to_s
|
||||||
|
|
||||||
|
@mixdown_package.finish_sign(s3_url, private_key, length, md5.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
# returns output destination, converting if necessary
|
||||||
|
def convert(tmp_dir)
|
||||||
|
# if the file already ends with the desired file type, call it a win
|
||||||
|
if @speed_mix_file.end_with?(@mixdown_package.file_type)
|
||||||
|
@speed_mix_file
|
||||||
|
else
|
||||||
|
# otherwise we need to convert from lastly created file to correct
|
||||||
|
output = File.join(tmp_dir, "output.#{@mixdown_package.file_type}")
|
||||||
|
|
||||||
|
cmd("#{APP_CONFIG.normalize_ogg_path} --bitrate 192 \"#{@speed_mix_file}\"", 'normalize')
|
||||||
|
|
||||||
|
if @mixdown_package.file_type == JamTrackMixdownPackage::FILE_TYPE_AAC
|
||||||
|
cmd("ffmpeg -i \"#{@speed_mix_file}\" -c:a libfdk_aac -b:a 128k \"#{output}\"", 'convert_aac')
|
||||||
|
elsif @mixdown_package.file_type == JamTrackMixdownPackage::FILE_TYPE_MP3
|
||||||
|
cmd("ffmpeg -i \"#{@speed_mix_file}\" -ab 192k \"#{output}\"", 'convert_mp3')
|
||||||
|
else
|
||||||
|
raise 'unknown file_type'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def encrypt_jkz(tmp_dir)
|
||||||
|
py_root = APP_CONFIG.jamtracks_dir
|
||||||
|
step = 0
|
||||||
|
|
||||||
|
private_key = nil
|
||||||
|
# we need to make the id of the custom mix be the name of the file (ID.ogg)
|
||||||
|
custom_mix_name = File.join(tmp_dir, "#{@mixdown.id}.ogg")
|
||||||
|
FileUtils.mv(@speed_mix_file, custom_mix_name)
|
||||||
|
jam_file_opts = ""
|
||||||
|
jam_file_opts << " -i #{Shellwords.escape("#{custom_mix_name}+mixdown")}"
|
||||||
|
|
||||||
|
sku = @mixdown_package.id
|
||||||
|
title = @mixdown.name
|
||||||
|
output = File.join(tmp_dir, "#{title.parameterize}.jkz")
|
||||||
|
py_file = File.join(py_root, "jkcreate.py")
|
||||||
|
version = @mixdown_package.version
|
||||||
|
|
||||||
|
right = @mixdown.jam_track.right_for_user(@mixdown.user)
|
||||||
|
|
||||||
|
if @mixdown_package.sample_rate == 48
|
||||||
|
private_key = right.private_key_48
|
||||||
|
else
|
||||||
|
private_key = right.private_key_44
|
||||||
|
end
|
||||||
|
|
||||||
|
unless private_key
|
||||||
|
@error_reason = 'no_private_key'
|
||||||
|
@error_detail = 'user needs to generate JamTrack for given sample rate'
|
||||||
|
raise @error_reason
|
||||||
|
end
|
||||||
|
|
||||||
|
private_key_file = File.join(tmp_dir, 'skey.pem')
|
||||||
|
File.open(private_key_file, 'w') { |f| f.write(private_key) }
|
||||||
|
|
||||||
|
log.debug("PRIVATE KEY")
|
||||||
|
log.debug(private_key)
|
||||||
|
log.info "Executing python source in #{py_file}, outputting to #{tmp_dir} (#{output})"
|
||||||
|
|
||||||
|
cli = "python #{py_file} -D -k #{sku} -p #{Shellwords.escape(tmp_dir)}/pkey.pem -s #{Shellwords.escape(tmp_dir)}/skey.pem #{jam_file_opts} -o #{Shellwords.escape(output)} -t #{Shellwords.escape(title)} -V #{Shellwords.escape(version)}"
|
||||||
|
|
||||||
|
Open3.popen3(cli) do |stdin, stdout, stderr, wait_thr|
|
||||||
|
pid = wait_thr.pid
|
||||||
|
exit_status = wait_thr.value
|
||||||
|
err = stderr.read(1000)
|
||||||
|
out = stdout.read(1000)
|
||||||
|
#puts "stdout: #{out}, stderr: #{err}"
|
||||||
|
raise ArgumentError, "Error calling python script: #{err}" if err.present?
|
||||||
|
raise ArgumentError, "Error calling python script: #{out}" if out && (out.index("No track files specified") || out.index("Cannot find file"))
|
||||||
|
|
||||||
|
private_key = File.read(private_key_file)
|
||||||
|
end
|
||||||
|
return output, private_key
|
||||||
|
end
|
||||||
|
|
||||||
|
def cmd(cmd, type)
|
||||||
|
|
||||||
|
log.debug("executing #{cmd}")
|
||||||
|
|
||||||
|
output = `#{cmd}`
|
||||||
|
|
||||||
|
result_code = $?.to_i
|
||||||
|
|
||||||
|
if result_code == 0
|
||||||
|
output
|
||||||
|
else
|
||||||
|
@error_reason = type + "_fail"
|
||||||
|
@error_detail = "#{cmd}, #{output}"
|
||||||
|
raise "command `#{cmd}` failed."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# increment the step, which causes a notification to be sent to the client so it can keep the UI fresh as the packaging step goes on
|
||||||
|
def bump_step(mixdown_package)
|
||||||
|
step = @step
|
||||||
|
last_step_at = Time.now
|
||||||
|
mixdown_package.current_packaging_step = step
|
||||||
|
mixdown_package.last_step_at = last_step_at
|
||||||
|
JamTrackMixdownPackage.where(:id => mixdown_package.id).update_all(last_step_at: last_step_at, current_packaging_step: step)
|
||||||
|
SubscriptionMessage.mixdown_signing_job_change(mixdown_package)
|
||||||
|
|
||||||
|
@step = step + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# set @error_reason before you raise an exception, and it will be sent back as the error reason
|
||||||
|
# otherwise, the error_reason will be unhandled-job-exception
|
||||||
|
def post_error(e)
|
||||||
|
begin
|
||||||
|
# if error_reason is null, assume this is an unhandled error
|
||||||
|
unless @error_reason
|
||||||
|
@error_reason = "unhandled-job-exception"
|
||||||
|
@error_detail = e.to_s
|
||||||
|
end
|
||||||
|
@mixdown_package.finish_errored(@error_reason, @error_detail)
|
||||||
|
|
||||||
|
rescue Exception => e
|
||||||
|
log.error "unable to post back to the database the error #{e}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -42,7 +42,7 @@ module JamRuby
|
||||||
signing_started_model_symbol = bitrate == 48 ? :signing_started_at_48 : :signing_started_at_44
|
signing_started_model_symbol = bitrate == 48 ? :signing_started_at_48 : :signing_started_at_44
|
||||||
signing_state_symbol = bitrate == 48 ? :signing_48 : :signing_44
|
signing_state_symbol = bitrate == 48 ? :signing_48 : :signing_44
|
||||||
last_step_at = Time.now
|
last_step_at = Time.now
|
||||||
JamTrackRight.where(:id => @jam_track_right.id).update_all(signing_started_model_symbol => signing_started_at, :should_retry => false, packaging_steps: total_steps, current_packaging_step: 0, last_step_at: last_step_at, signing_state_symbol => true)
|
JamTrackRight.where(:id => @jam_track_right.id).update_all(signing_started_model_symbol => signing_started_at, :should_retry => false, packaging_steps: total_steps, current_packaging_step: 0, last_step_at: last_step_at, signing_state_symbol => true, queued: false)
|
||||||
# because we are skipping 'after_save', we have to keep the model current for the notification. A bit ugly...
|
# because we are skipping 'after_save', we have to keep the model current for the notification. A bit ugly...
|
||||||
@jam_track_right.current_packaging_step = 0
|
@jam_track_right.current_packaging_step = 0
|
||||||
@jam_track_right.packaging_steps = total_steps
|
@jam_track_right.packaging_steps = total_steps
|
||||||
|
|
@ -50,6 +50,7 @@ module JamRuby
|
||||||
@jam_track_right[signing_state_symbol] = true
|
@jam_track_right[signing_state_symbol] = true
|
||||||
@jam_track_right.should_retry = false
|
@jam_track_right.should_retry = false
|
||||||
@jam_track_right.last_step_at = Time.now
|
@jam_track_right.last_step_at = Time.now
|
||||||
|
@jam_track_right.queued = false
|
||||||
SubscriptionMessage.jam_track_signing_job_change(@jam_track_right)
|
SubscriptionMessage.jam_track_signing_job_change(@jam_track_right)
|
||||||
JamRuby::JamTracksManager.save_jam_track_right_jkz(@jam_track_right, self.bitrate)
|
JamRuby::JamTracksManager.save_jam_track_right_jkz(@jam_track_right, self.bitrate)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,11 @@ module JamRuby
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
# this needs more testing
|
# this needs more testing
|
||||||
|
|
||||||
|
# let's make sure jobs don't stay falsely queued for too long. 1 hour seems more than enough
|
||||||
|
JamTrackRight.where("queued = true AND (NOW() - signing_queued_at > '1 hour'::INTERVAL OR NOW() - updated_at > '1 hour'::INTERVAL)").update_all(queued:false)
|
||||||
|
JamTrackMixdownPackage.unscoped.where("queued = true AND (NOW() - signing_queued_at > '1 hour'::INTERVAL OR NOW() - updated_at > '1 hour'::INTERVAL)").update_all(queued:false)
|
||||||
|
|
||||||
return
|
return
|
||||||
#JamTrackRight.ready_to_clean.each do |jam_track_right|
|
#JamTrackRight.ready_to_clean.each do |jam_track_right|
|
||||||
# log.debug("deleting files for jam_track_right #{jam_track_right.id}")
|
# log.debug("deleting files for jam_track_right #{jam_track_right.id}")
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue