VRFS-3007 : Merge with latest develop

This commit is contained in:
Steven Miers 2015-04-28 16:43:38 -05:00
commit 2260d21fc1
257 changed files with 4044 additions and 1251 deletions

View File

@ -1,20 +1,29 @@
require 'jam_ruby/recurly_client'
ActiveAdmin.register JamRuby::JamTrackRight, :as => 'JamTrackRights' do
menu :label => 'Purchased JamTracks', :parent => 'JamTracks'
menu :label => 'Purchased JamTracks', :parent => 'Purchases'
config.sort_order = 'updated_at DESC'
config.sort_order = 'created_at DESC'
config.batch_actions = false
#form :partial => 'form'
filter :user_id
filter :user_id,
:label => "USER ID", :required => false,
:wrapper_html => { :style => "list-style: none" }
filter :jam_track
index do
default_actions
column "Order" do |right|
link_to("Place", order_admin_jam_track_right_path(right)) + " | " +
link_to("Refund", refund_admin_jam_track_right_path(right))
end
#column "Order" do |right|
#link_to("Place", order_admin_jam_track_right_path(right)) + " | " +
# link_to("Refund", refund_admin_jam_track_right_path(right))
#end
column "Last Name" do |right|
right.user.last_name
@ -23,13 +32,15 @@ ActiveAdmin.register JamRuby::JamTrackRight, :as => 'JamTrackRights' do
right.user.first_name
end
column "Jam Track" do |right|
link_to(right.jam_track.name, admin_jam_track_right_path(right.jam_track))
link_to(right.jam_track.name, admin_jam_track_path(right.jam_track))
# right.jam_track
end
column "Plan Code" do |right|
right.jam_track.plan_code
end
column "Redeemed" do |right|
right.redeemed ? 'Y' : 'N'
end
end
@ -42,6 +53,9 @@ ActiveAdmin.register JamRuby::JamTrackRight, :as => 'JamTrackRights' do
f.actions
end
=begin
member_action :order, :method => :get do
right = JamTrackRight.where("id=?",params[:id]).first
user = right.user
@ -84,4 +98,5 @@ ActiveAdmin.register JamRuby::JamTrackRight, :as => 'JamTrackRights' do
redirect_to admin_jam_track_rights_path, notice: "Issued full refund on #{right.jam_track} for #{right.user.to_s}"
end
end
=end
end

View File

@ -0,0 +1,40 @@
ActiveAdmin.register JamRuby::RecurlyTransactionWebHook, :as => 'RecurlyHooks' do
menu :label => 'Recurly Transaction Hooks', :parent => 'Purchases'
config.sort_order = 'created_at DESC'
config.batch_actions = false
actions :all, :except => [:destroy]
#form :partial => 'form'
filter :transaction_type, :as => :select, :collection => JamRuby::RecurlyTransactionWebHook::HOOK_TYPES
filter :user_id,
:label => "USER ID", :required => false,
:wrapper_html => { :style => "list-style: none" }
filter :invoice_id
form :partial => 'form'
index do
default_actions
column :transaction_type
column :transaction_at
column :amount_in_cents
column 'Transaction' do |hook| link_to('Go to Recurly', Rails.application.config.recurly_root_url + "/transactions/#{hook.recurly_transaction_id}") end
column 'Invoice' do |hook| link_to(hook.invoice_number, Rails.application.config.recurly_root_url + "/invoices/#{hook.invoice_number}") end
column :admin_description
column 'User' do |hook| link_to("#{hook.user.email} (#{hook.user.name})", admin_user_path(hook.user.id)) end
#column "Order" do |right|
#link_to("Place", order_admin_jam_track_right_path(right)) + " | " +
# link_to("Refund", refund_admin_jam_track_right_path(right))
#end
end
end

View File

@ -0,0 +1,6 @@
= semantic_form_for([:admin, resource], :html => {:multipart => true}, :url => resource.new_record? ? admin_recurly_transaction_web_hooks_path : "#{ENV['RAILS_RELATIVE_URL_ROOT']}/admin/recurly_hooks/#{resource.id}") do |f|
= f.semantic_errors *f.object.errors.keys
= f.inputs name: 'Recurly Web Hook fields' do
= f.input :admin_description, :input_html => { :rows=>1, :maxlength=>200, }, hint: "this will display on the user's payment history page"
= f.input :jam_track, collection: JamRuby::JamTrack.all, include_blank: true, hint: "Please indicate which JamTrack this refund for, if not set"
= f.actions

View File

@ -83,6 +83,7 @@ module JamAdmin
config.external_port = ENV['EXTERNAL_PORT'] || 3000
config.external_protocol = ENV['EXTERNAL_PROTOCOL'] || 'http://'
config.external_root_url = "#{config.external_protocol}#{config.external_hostname}#{(config.external_port == 80 || config.external_port == 443) ? '' : ':' + config.external_port.to_s}"
config.recurly_root_url = 'https://jamkazam-development.recurly.com'
# where is rabbitmq?
config.rabbitmq_host = "localhost"
@ -116,7 +117,7 @@ module JamAdmin
config.email_smtp_domain = 'www.jamkazam.com'
config.email_smtp_authentication = :plain
config.email_smtp_user_name = 'jamkazam'
config.email_smtp_password = 'jamjamblueberryjam'
config.email_smtp_password = 'snorkeltoesniffyfarce1'
config.email_smtp_starttls_auto = true
config.facebook_app_id = ENV['FACEBOOK_APP_ID'] || '468555793186398'

View File

@ -43,4 +43,7 @@ JamAdmin::Application.configure do
# Show the logging configuration on STDOUT
config.show_log_configuration = true
config.email_generic_from = 'nobody-dev@jamkazam.com'
config.email_alerts_alias = 'alerts-dev@jamkazam.com'
end

View File

@ -282,3 +282,7 @@ add_genre_type.sql
add_description_to_perf_samples.sql
alter_genre_player_unique_constraint.sql
musician_search.sql
signup_hints.sql
packaging_notices.sql
first_played_jamtrack_at.sql
payment_history.sql

View File

@ -0,0 +1 @@
ALTER TABLE users ADD COLUMN first_played_jamtrack_at TIMESTAMP;

View File

@ -1 +1,9 @@
ALTER TABLE jam_tracks ADD COLUMN duration INTEGER;
DO $$
BEGIN
BEGIN
ALTER TABLE jam_tracks ADD COLUMN duration INTEGER;
EXCEPTION
WHEN duplicate_column THEN RAISE NOTICE 'column duration already exists in jam_tracks.';
END;
END;
$$ LANGUAGE plpgsql;

View File

@ -0,0 +1,3 @@
ALTER TABLE jam_track_rights ADD COLUMN packaging_steps INTEGER;
ALTER TABLE jam_track_rights ADD COLUMN current_packaging_step INTEGER;
ALTER TABLE jam_track_rights ADD COLUMN last_step_at TIMESTAMP;

17
db/up/payment_history.sql Normal file
View File

@ -0,0 +1,17 @@
ALTER TABLE recurly_transaction_web_hooks ADD COLUMN admin_description VARCHAR;
ALTER TABLE recurly_transaction_web_hooks ADD COLUMN jam_track_id VARCHAR(64) REFERENCES jam_tracks(id);
CREATE VIEW payment_histories AS
SELECT id AS sale_id,
CAST(NULL as VARCHAR) AS recurly_transaction_web_hook_id,
user_id,
created_at,
'sale' AS transaction_type
FROM sales s
UNION ALL
SELECT CAST(NULL as VARCHAR) AS sale_id,
id AS recurly_transaction_web_hook_id,
user_id,
transaction_at AS created_at,
transaction_type
FROM recurly_transaction_web_hooks;

View File

@ -1,4 +1,11 @@
ALTER TABLE jam_track_tracks ADD COLUMN preview_mp3_url VARCHAR;
ALTER TABLE jam_track_tracks ADD COLUMN preview_mp3_md5 VARCHAR;
ALTER TABLE jam_track_tracks ADD COLUMN preview_mp3_length BIGINT;
UPDATE jam_track_tracks SET preview_url = NULL where track_type = 'Master';
DO $$
BEGIN
BEGIN
ALTER TABLE jam_track_tracks ADD COLUMN preview_mp3_url VARCHAR;
ALTER TABLE jam_track_tracks ADD COLUMN preview_mp3_md5 VARCHAR;
ALTER TABLE jam_track_tracks ADD COLUMN preview_mp3_length BIGINT;
EXCEPTION
WHEN duplicate_column THEN RAISE NOTICE 'preview mp3 columns already exist in jam_tracks';
END;
END;
$$ LANGUAGE plpgsql;

12
db/up/signup_hints.sql Normal file
View File

@ -0,0 +1,12 @@
CREATE TABLE signup_hints (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
anonymous_user_id VARCHAR(64) UNIQUE,
redirect_location VARCHAR,
want_jamblaster BOOLEAN NOT NULL DEFAULT FALSE,
user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE,
expires_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE users ADD COLUMN want_jamblaster BOOLEAN NOT NULL DEFAULT FALSE;

1
ruby/jt_metadata.json Normal file
View File

@ -0,0 +1 @@
{"container_file": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150428-83245-1alsyg1/jam-track-19.jkz", "version": "0", "coverart": null, "rsa_priv_file": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150428-83245-1alsyg1/skey.pem", "tracks": [{"name": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150428-83245-1alsyg1/9599b114-0f66-4140-ae3a-078a4e0c0767.ogg", "trackName": "track_00"}], "rsa_pub_file": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150428-83245-1alsyg1/pkey.pem", "jamktrack_info": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/tmp9PHU_9"}

View File

@ -20,11 +20,12 @@ require 'resque_mailer'
require 'rest-client'
require 'zip'
require 'csv'
require 'tzinfo'
require "jam_ruby/constants/limits"
require "jam_ruby/constants/notification_types"
require "jam_ruby/constants/validation_messages"
require "jam_ruby/errors/permission_error"
require "jam_ruby/errors/jam_permission_error"
require "jam_ruby/errors/state_error"
require "jam_ruby/errors/jam_argument_error"
require "jam_ruby/errors/conflict_error"
@ -100,6 +101,7 @@ require "jam_ruby/models/genre_player"
require "jam_ruby/models/genre"
require "jam_ruby/models/user"
require "jam_ruby/models/anonymous_user"
require "jam_ruby/models/signup_hint"
require "jam_ruby/models/rsvp_request"
require "jam_ruby/models/rsvp_slot"
require "jam_ruby/models/rsvp_request_rsvp_slot"
@ -205,6 +207,7 @@ require "jam_ruby/models/generic_state"
require "jam_ruby/models/score_history"
require "jam_ruby/models/jam_company"
require "jam_ruby/models/user_sync"
require "jam_ruby/models/payment_history"
require "jam_ruby/models/video_source"
require "jam_ruby/models/text_message"
require "jam_ruby/models/sale"

View File

@ -1,6 +1,6 @@
module JamRuby
# sends out a boring ale
class AdminMailer < ActionMailer::Base
class AdminMailer < ActionMailer::Base
include SendGrid
@ -14,9 +14,24 @@ module JamRuby
def alerts(options)
mail(to: APP_CONFIG.email_alerts_alias,
from: APP_CONFIG.email_generic_from,
body: options[:body],
content_type: "text/plain",
subject: options[:subject])
end
def recurly_alerts(user, options)
body = options[:body]
body << "\n\n"
body << "User " << user.admin_url + "\n"
body << "User's JamTracks " << user.jam_track_rights_admin_url + "\n"
mail(to: APP_CONFIG.email_recurly_notice,
from: APP_CONFIG.email_generic_from,
body: body,
content_type: "text/plain",
subject: options[:subject])
end
end
end

View File

@ -4,8 +4,8 @@
<%= yield %>
<% end %>
<% unless @suppress_user_has_account_footer == true %>
This email was sent to you because you have an account at JamKazam / http://www.jamkazam.com. Visit your profile page to unsubscribe: http://www.jamkazam.com/unsubscribe/<%=@user.unsubscribe_token%>.
<% unless @user.nil? || @suppress_user_has_account_footer == true %>
This email was sent to you because you have an account at JamKazam / http://www.jamkazam.com. To unsubscribe: http://www.jamkazam.com/unsubscribe/<%=@user.unsubscribe_token%>.
<% end %>
Copyright <%= Time.now.year %> JamKazam, Inc. All rights reserved.

View File

@ -0,0 +1,5 @@
module JamRuby
class JamPermissionError < Exception
end
end

View File

@ -1,5 +0,0 @@
module JamRuby
class PermissionError < Exception
end
end

View File

@ -91,13 +91,15 @@ module JamRuby
true
end
def synchronize_metadata(jam_track, metadata, metalocation, original_artist, name)
def synchronize_metadata(jam_track, metadata, metalocation, original_artist, name, options)
metadata ||= {}
self.name = metadata["name"] || name
if jam_track.new_record?
jam_track.id = "#{JamTrack.count + 1}" # default is UUID, but the initial import was based on auto-increment ID, so we'll maintain that
latest_jamtrack = JamTrack.order('created_at desc').first
id = latest_jamtrack.nil? ? 1 : latest_jamtrack.id.to_i + 1
jam_track.id = "#{id}" # default is UUID, but the initial import was based on auto-increment ID, so we'll maintain that
jam_track.status = 'Staging'
jam_track.metalocation = metalocation
jam_track.original_artist = metadata["original_artist"] || original_artist
@ -107,13 +109,20 @@ module JamRuby
jam_track.price = 1.99
jam_track.reproduction_royalty_amount = 0
jam_track.licensor_royalty_amount = 0
jam_track.sales_region = 'United States'
jam_track.sales_region = 'Worldwide'
jam_track.recording_type = 'Cover'
jam_track.description = "This is a JamTrack audio file for use exclusively with the JamKazam service. This JamTrack is a high quality cover of the #{jam_track.original_artist} song \"#{jam_track.name}\"."
else
#@@log.debug("#{self.name} skipped because it already exists in database")
finish("jam_track_exists", "")
return false
if !options[:resync_audio]
#@@log.debug("#{self.name} skipped because it already exists in database")
finish("jam_track_exists", "")
return false
else
# jamtrack exists, leave it be
return true
end
end
saved = jam_track.save
@ -318,24 +327,31 @@ module JamRuby
def set_custom_weight(track)
weight = 5
case track.instrument_id
when 'electric guitar'
weight = 1
when 'acoustic guitar'
weight = 2
when 'drums'
weight = 3
when 'keys'
weight = 4
when 'computer'
weight = 10
else
weight = 5
end
if track.track_type == 'Master'
weight = 1000
# if there are any persisted tracks, do not sort from scratch; just stick new stuff at the end
if track.persisted?
weight = track.position
else
case track.instrument_id
when 'electric guitar'
weight = 100
when 'acoustic guitar'
weight = 200
when 'drums'
weight = 300
when 'keys'
weight = 400
when 'computer'
weight = 600
else
weight = 500
end
if track.track_type == 'Master'
weight = 1000
end
end
weight
end
@ -346,10 +362,19 @@ module JamRuby
a_weight <=> b_weight
end
# default to 1, but if there are any persisted tracks, this will get manipulated to be +1 the highest persisted track
position = 1
sorted_tracks.each do |track|
track.position = position
position = position + 1
if track.persisted?
# persisted tracks should be sorted at the beginning of the sorted_tracks,
# so this just keeps moving the 'position builder' up to +1 of the last persisted track
position = track.position + 1
else
track.position = position
position = position + 1
end
end
sorted_tracks[sorted_tracks.length - 1].position = 1000
@ -359,11 +384,40 @@ module JamRuby
def synchronize_audio(jam_track, metadata, s3_path, skip_audio_upload)
attempt_to_match_existing_tracks = true
# find all wav files in the JamTracks s3 bucket
wav_files = fetch_wav_files(s3_path)
tracks = []
wav_files.each do |wav_file|
if attempt_to_match_existing_tracks
# try to find a matching track from the JamTrack based on the name of the 44.1 path
basename = File.basename(wav_file)
ogg_44100_filename = File.basename(basename, ".wav") + "-44100.ogg"
found_track = nil
jam_track.jam_track_tracks.each do |jam_track_track|
if jam_track_track["url_44"] && jam_track_track["url_44"].end_with?(ogg_44100_filename)
# found a match!
found_track = jam_track_track
break
end
end
if found_track
@@log.debug("found a existing track to reuse")
found_track.original_audio_s3_path = wav_file
tracks << found_track
next
end
end
@@log.debug("no existing track found; creating a new one")
track = JamTrackTrack.new
track.original_audio_s3_path = wav_file
@ -388,6 +442,15 @@ module JamRuby
tracks << track
end
jam_track.jam_track_tracks.each do |jam_track_track|
# delete all jam_track_tracks not in the tracks array
unless tracks.include?(jam_track_track)
@@log.info("destroying removed JamTrackTrack #{jam_track_track.inspect}")
jam_track_track.destroy # should also delete s3 files associated with this jamtrack
end
end
@@log.info("sorting tracks")
tracks = sort_tracks(tracks)
jam_track.jam_track_tracks = tracks
@ -481,15 +544,16 @@ module JamRuby
track["length_48"] = File.new(ogg_48000).size
synchronize_duration(jam_track, ogg_44100)
jam_track.save!
# convert entire master ogg file to mp3, and push both to public destination
preview_succeeded = synchronize_master_preview(track, tmp_dir, ogg_44100, ogg_44100_digest) if track.track_type == 'Master'
if track.track_type == 'Master'
preview_succeeded = synchronize_master_preview(track, tmp_dir, ogg_44100, ogg_44100_digest)
if !preview_succeeded
return false
if !preview_succeeded
return false
end
end
end
track.save!
@ -596,7 +660,7 @@ module JamRuby
original_artist = parsed_metalocation[1]
name = parsed_metalocation[2]
success = synchronize_metadata(jam_track, metadata, metalocation, original_artist, name)
success = synchronize_metadata(jam_track, metadata, metalocation, original_artist, name, options)
return unless success
@ -614,8 +678,8 @@ module JamRuby
def synchronize_recurly(jam_track)
begin
recurly = RecurlyClient.new
# no longer create JamTrack plans: VRFS-3028
# recurly.create_jam_track_plan(jam_track) unless recurly.find_jam_track_plan(jam_track)
# no longer create JamTrack plans: VRFS-3028
# recurly.create_jam_track_plan(jam_track) unless recurly.find_jam_track_plan(jam_track)
rescue RecurlyClientError => x
finish('recurly_create_plan', x.errors.to_s)
return false

View File

@ -22,9 +22,21 @@ module JamRuby
save_jam_track_right_jkz(jam_track_right, sample_rate)
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(jam_track_right, step)
last_step_at = Time.now
jam_track_right.current_packaging_step = step
jam_track_right.last_step_at = Time.now
JamTrackRight.where(:id => jam_track_right.id).update_all(last_step_at: last_step_at, current_packaging_step: step)
SubscriptionMessage.jam_track_signing_job_change(jam_track_right)
step = step + 1
step
end
def save_jam_track_right_jkz(jam_track_right, sample_rate=48)
jam_track = jam_track_right.jam_track
py_root = APP_CONFIG.jamtracks_dir
step = 0
Dir.mktmpdir do |tmp_dir|
jam_file_opts=""
jam_track.jam_track_tracks.each do |jam_track_track|
@ -36,6 +48,9 @@ module JamRuby
track_filename = File.join(tmp_dir, nm)
track_url = jam_track_track.sign_url(120, sample_rate)
@@log.info("downloading #{track_url} to #{track_filename}")
step = bump_step(jam_track_right, step)
copy_url_to_file(track_url, track_filename)
jam_file_opts << " -i #{Shellwords.escape("#{track_filename}+#{jam_track_track.part}")}"
end
@ -48,6 +63,8 @@ module JamRuby
version = jam_track.version
@@log.info "Executing python source in #{py_file}, outputting to #{tmp_dir} (#{output_jkz})"
step = bump_step(jam_track_right, step)
# From http://stackoverflow.com/questions/690151/getting-output-of-system-calls-in-ruby/5970819#5970819:
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_jkz)} -t #{Shellwords.escape(title)} -V #{Shellwords.escape(version)}"
Open3.popen3(cli) do |stdin, stdout, stderr, wait_thr|

View File

@ -22,7 +22,7 @@ module JamRuby
end
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}.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
end
end

View File

@ -7,7 +7,7 @@ module JamRuby
self.table_name = 'active_music_sessions'
attr_accessor :legal_terms, :max_score, :opening_jam_track, :opening_recording, :opening_backing_track, :opening_metronome
attr_accessor :legal_terms, :max_score, :opening_jam_track, :opening_recording, :opening_backing_track, :opening_metronome, :jam_track_id
belongs_to :claimed_recording, :class_name => "JamRuby::ClaimedRecording", :foreign_key => "claimed_recording_id", :inverse_of => :playing_sessions
belongs_to :claimed_recording_initiator, :class_name => "JamRuby::User", :inverse_of => :playing_claimed_recordings, :foreign_key => "claimed_recording_initiator_id"

View File

@ -1,15 +0,0 @@
module JamRuby
class AfterSignupHint < ActiveRecord::Base
before_create :generate_lookup_id
def self.delete_old
FacebookSignup.where("created_at < :week", {:week => 1.week.ago}).delete_all
end
private
def generate_lookup_id
self.lookup_id = SecureRandom.urlsafe_base64
end
end
end

View File

@ -11,7 +11,7 @@ module JamRuby
end
def shopping_carts
ShoppingCart.where(anonymous_user_id: @id)
ShoppingCart.where(anonymous_user_id: @id).order('created_at DESC')
end
def destroy_all_shopping_carts

View File

@ -163,14 +163,14 @@ module JamRuby
# ensure person creating this Band is a Musician
unless user.musician?
raise PermissionError, "must be a musician"
raise JamPermissionError, "must be a musician"
end
band = id.blank? ? Band.new : Band.find(id)
# ensure user updating Band details is a Band member
unless band.new_record? || band.users.exists?(user)
raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR
raise JamPermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR
end
band.name = params[:name] if params.has_key?(:name)

View File

@ -41,7 +41,7 @@ module JamRuby
# params is a hash, and everything is optional
def update_fields(user, params)
if user != self.user
raise PermissionError, "user doesn't own claimed_recording"
raise JamPermissionError, "user doesn't own claimed_recording"
end
self.name = params[:name]
@ -53,7 +53,7 @@ module JamRuby
def discard(user)
if user != self.user
raise PermissionError, "user doesn't own claimed_recording"
raise JamPermissionError, "user doesn't own claimed_recording"
end
ClaimedRecording.where(:id => id).update_all(:discarded => true )
@ -96,11 +96,11 @@ module JamRuby
target_user = params[:user]
raise PermissionError, "must specify current user" unless user
raise JamPermissionError, "must specify current user" unless user
raise "user must be specified" unless target_user
if target_user != user.id
raise PermissionError, "unable to view another user's favorites"
raise JamPermissionError, "unable to view another user's favorites"
end
query = ClaimedRecording.limit(limit).order('created_at DESC').offset(start)

View File

@ -60,7 +60,10 @@ module JamRuby
# has_many :plays, :class_name => "JamRuby::PlayablePlay", :foreign_key => :jam_track_id, :dependent => :destroy
# VRFS-2916 jam_tracks.id is varchar: ADD
has_many :plays, :class_name => "JamRuby::PlayablePlay", :as => :playable, :dependent => :destroy
# when we know what JamTrack this refund is related to, these are associated
belongs_to :recurly_transactions, class_name: 'JamRuby::RecurlyTransactionWebHook'
accepts_nested_attributes_for :jam_track_tracks, allow_destroy: true
accepts_nested_attributes_for :jam_track_tap_ins, allow_destroy: true
@ -161,21 +164,75 @@ module JamRuby
query = query.where("original_artist=?", options[:artist])
end
if options[:id].present?
query = query.where("jam_tracks.id=?", options[:id])
end
if options[:group_artist]
query = query.select("original_artist, array_agg(jam_tracks.id) AS id, MIN(name) AS name, MIN(description) AS description, MIN(recording_type) AS recording_type, MIN(original_artist) AS original_artist, MIN(songwriter) AS songwriter, MIN(publisher) AS publisher, MIN(sales_region) AS sales_region, MIN(price) AS price, MIN(version) AS version, MIN(genre_id) AS genre_id")
query = query.group("original_artist")
query = query.order('jam_tracks.original_artist')
else
query = query.group("jam_tracks.id")
query = query.order('jam_tracks.name')
query = query.group("jam_tracks.id")
query = query.order('jam_tracks.original_artist, jam_tracks.name')
end
query = query.where("jam_tracks.status = ?", 'Production') unless user.admin
query = query.where("jam_tracks.genre_id = '#{options[:genre]}'") unless options[:genre].blank?
query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}'") 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?
if query.length == 0
[query, nil]
elsif query.length < limit
[query, nil]
else
[query, start + limit]
end
end
# provides artist names and how many jamtracks are available for each
def artist_index(options, user)
if options[:page]
page = options[:page].to_i
per_page = options[:per_page].to_i
if per_page == 0
# try and see if limit was specified
limit = options[:limit]
limit ||= 100
limit = limit.to_i
else
limit = per_page
end
start = (page -1 )* per_page
limit = per_page
else
limit = options[:limit]
limit ||= 100
limit = limit.to_i
start = options[:start].presence
start = start.to_i || 0
page = 1 + start/limit
per_page = limit
end
query = JamTrack.paginate(page: page, per_page: per_page)
query = query.select("original_artist, count(original_artist) AS song_count")
query = query.group("original_artist")
query = query.order('jam_tracks.original_artist')
query = query.where("jam_tracks.status = ?", 'Production') unless user.admin
query = query.where("jam_tracks.genre_id = '#{options[:genre]}'") unless options[:genre].blank?
query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}'") unless options[:instrument].blank?
query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?
if query.length == 0
[query, nil]
@ -192,6 +249,10 @@ module JamRuby
JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Master').first
end
def stem_tracks
JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Track')
end
def can_download?(user)
owners.include?(user)
end

View File

@ -29,7 +29,7 @@ module JamRuby
# 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
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.jam_track_signing_job_change(self)
end
end
@ -148,7 +148,11 @@ module JamRuby
if signed
state = 'SIGNED'
elsif signing_started_at
if Time.now - signing_started_at > APP_CONFIG.signing_job_run_max_time
# 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.
signing_job_run_max_time = packaging_steps * 10
if Time.now - signing_started_at > signing_job_run_max_time
state = 'SIGNING_TIMEOUT'
elsif Time.now - last_step_at > APP_CONFIG.signing_step_max_time
state = 'SIGNING_TIMEOUT'
else
state = 'SIGNING'
@ -169,6 +173,10 @@ module JamRuby
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.list_keys(user, jamtracks)

View File

@ -10,6 +10,7 @@ module JamRuby
@@log = Logging.logger[JamTrackTrack]
before_destroy :delete_s3_files
# Because JamTrackImporter imports audio files now, and because also the mere presence of this causes serious issues when updating the model (because reset of url_44 to something bogus), I've removed these
#mount_uploader :url_48, JamTrackTrackUploader
@ -20,6 +21,8 @@ module JamRuby
attr_accessor :original_audio_s3_path, :skip_uploader
before_destroy :delete_s3_files
validates :position, presence: true, numericality: {only_integer: true}, length: {in: 1..1000}
validates :part, length: {maximum: 25}
validates :track_type, inclusion: {in: TRACK_TYPE }
@ -120,6 +123,13 @@ module JamRuby
end
end
def delete_s3_files
s3_manager.delete(self[:url_44]) if self[:url_44] && s3_manager.exists?(self[:url_44])
s3_manager.delete(self[:url_48]) if self[:url_48] && s3_manager.exists?(self[:url_48])
s3_public_manager.delete(self[:preview_url]) if self[:preview_url] && s3_public_manager.exists?(self[:preview_url])
s3_public_manager.delete(self[:preview_mp3_url]) if self[:preview_mp3_url] && s3_public_manager.exists?(self[:preview_mp3_url])
end
private
def normalize_position
parent = self.jam_track

View File

@ -4,6 +4,8 @@ module JamRuby
MAX_MIX_TIME = 7200 # 2 hours
@@log = Logging.logger[Mix]
before_destroy :delete_s3_files
self.primary_key = 'id'
@ -137,15 +139,67 @@ module JamRuby
one_day = 60 * 60 * 24
jam_track_offset = 0
jam_track_seek = 0
was_jamtrack_played = false
if recording.timeline
recording_timeline_data = JSON.parse(recording.timeline)
# did the jam track play at all?
jam_track_isplaying = recording_timeline_data["jam_track_isplaying"]
recording_start_time = recording_timeline_data["recording_start_time"]
jam_track_play_start_time = recording_timeline_data["jam_track_play_start_time"]
jam_track_recording_start_play_offset = recording_timeline_data["jam_track_recording_start_play_offset"]
jam_track_offset = -jam_track_recording_start_play_offset
if jam_track_play_start_time != 0
was_jamtrack_played = true
# how long did the JamTrack play? not needed because we limit on the input tracks, which represents how long the recording is, too
jam_track_play_time = recording_timeline_data["jam_track_play_time"]
offset = jam_track_play_start_time - recording_start_time
@@log.debug("base offset = #{offset}")
if offset >= 0
# jamtrack started after recording, so buffer with silence as necessary\
if jam_track_recording_start_play_offset < 0
@@log.info("prelude captured. offsetting further by #{-jam_track_recording_start_play_offset}")
# a negative jam_track_recording_start_play_offset indicates prelude, i.e., silence
# so add it to the offset to add more silence as necessary
offset = offset + -jam_track_recording_start_play_offset
jam_track_offset = offset
else
@@log.info("positive jamtrack offset; seeking into jamtrack by #{jam_track_recording_start_play_offset}")
# a positive jam_track_recording_start_play_offset means we need to cut into the jamtrack
jam_track_seek = jam_track_recording_start_play_offset
jam_track_offset = offset
end
else
# jamtrack started before recording, so we can seek into it to make up for the missing parts
if jam_track_recording_start_play_offset < 0
@@log.info("partial prelude captured. offset becomes jamtrack offset#{-jam_track_recording_start_play_offset}")
# a negative jam_track_recording_start_play_offset indicates prelude, i.e., silence
# so add it to the offset to add more silence as necessary
jam_track_offset = -jam_track_recording_start_play_offset
else
@@log.info("no prelude captured. offset becomes jamtrack offset=#{jam_track_recording_start_play_offset}")
jam_track_offset = 0
jam_track_seek = jam_track_recording_start_play_offset
end
# also, ignore jam_track_recording_start_play_offset - it simply matches the offset in this case
end
@@log.info("computed values. jam_track_offset=#{jam_track_offset} jam_track_seek=#{jam_track_seek}")
end
end
manifest = { "files" => [], "timeline" => [] }
@ -154,7 +208,7 @@ module JamRuby
# this 'pick limiter' logic will ensure that we set a limiter on the 1st recorded_track we come across.
pick_limiter = false
if recording.is_jamtrack_recording?
if was_jamtrack_played
# we only use the limiter feature if this is a JamTrack recording
# by setting this to true, the 1st recorded_track in the database will be the limiter
pick_limiter = true
@ -171,27 +225,29 @@ module JamRuby
mix_params << { "level" => 1.0, "balance" => 0 }
end
recording.recorded_jam_track_tracks.each do |recorded_jam_track_track|
manifest["files"] << { "filename" => recorded_jam_track_track.jam_track_track.sign_url(one_day), "codec" => "vorbis", "offset" => jam_track_offset }
# let's look for level info from the client
level = 1.0 # default value - means no effect
if recorded_jam_track_track.timeline
if was_jamtrack_played
recording.recorded_jam_track_tracks.each do |recorded_jam_track_track|
manifest["files"] << { "filename" => recorded_jam_track_track.jam_track_track.sign_url(one_day, sample_rate=44), "codec" => "vorbis", "offset" => jam_track_offset, "seek" => jam_track_seek }
# let's look for level info from the client
level = 1.0 # default value - means no effect
if recorded_jam_track_track.timeline
timeline_data = JSON.parse(recorded_jam_track_track.timeline)
timeline_data = JSON.parse(recorded_jam_track_track.timeline)
# always take the 1st entry for now
first = timeline_data[0]
# always take the 1st entry for now
first = timeline_data[0]
if first["mute"]
# mute equates to no noise
level = 0.0
else
# otherwise grab the left channel...
level = first["vol_l"]
if first["mute"]
# mute equates to no noise
level = 0.0
else
# otherwise grab the left channel...
level = first["vol_l"]
end
end
end
mix_params << { "level" => level, "balance" => 0 }
mix_params << { "level" => level, "balance" => 0 }
end
end
manifest["timeline"] << { "timestamp" => 0, "mix" => mix_params }

View File

@ -210,7 +210,7 @@ module JamRuby
return "New message about session."
when NotificationTypes::JAM_TRACK_SIGN_COMPLETE
return "Jam Track is ready for download."
return "JamTrack is ready for download."
# recording notifications
when NotificationTypes::MUSICIAN_RECORDING_SAVED

View File

@ -0,0 +1,39 @@
module JamRuby
class PaymentHistory < ActiveRecord::Base
self.table_name = 'payment_histories'
belongs_to :sale
belongs_to :recurly_transaction_web_hook
def self.index(user, params = {})
limit = params[:per_page]
limit ||= 20
limit = limit.to_i
query = PaymentHistory.limit(limit)
.includes(sale: [:sale_line_items], recurly_transaction_web_hook:[])
.where(user_id: user.id)
.where("transaction_type = 'sale' OR transaction_type = 'refund' OR transaction_type = 'void'")
.order('created_at DESC')
current_page = params[:page].nil? ? 1 : params[:page].to_i
next_page = current_page + 1
# will_paginate gem
query = query.paginate(:page => current_page, :per_page => limit)
if query.length == 0 # no more results
{ query: query, next_page: nil}
elsif query.length < limit # no more results
{ query: query, next_page: nil}
else
{ query: query, next_page: next_page }
end
end
end
end

View File

@ -3,7 +3,7 @@ module JamRuby
@@log = Logging.logger[Recording]
attr_accessible :owner, :owner_id, :band, :band_id, :recorded_tracks_attributes, :mixes_attributes, :claimed_recordings_attributes, :name, :description, :genre, :is_public, :duration, as: :admin
attr_accessible :owner, :owner_id, :band, :band_id, :recorded_tracks_attributes, :mixes_attributes, :claimed_recordings_attributes, :name, :description, :genre, :is_public, :duration, :jam_track_id, as: :admin
has_many :users, :through => :recorded_tracks, :class_name => "JamRuby::User"
has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
@ -21,6 +21,7 @@ module JamRuby
belongs_to :owner, :class_name => "JamRuby::User", :inverse_of => :owned_recordings, :foreign_key => 'owner_id'
belongs_to :band, :class_name => "JamRuby::Band", :inverse_of => :recordings
belongs_to :music_session, :class_name => "JamRuby::ActiveMusicSession", :inverse_of => :recordings, foreign_key: :music_session_id
belongs_to :non_active_music_session, :class_name => "JamRuby::MusicSession", foreign_key: :music_session_id
belongs_to :jam_track, :class_name => "JamRuby::JamTrack", :inverse_of => :recordings, :foreign_key => 'jam_track_id'
belongs_to :jam_track_initiator, :class_name => "JamRuby::User", :inverse_of => :initiated_jam_track_recordings, :foreign_key => 'jam_track_initiator_id'
@ -50,7 +51,11 @@ module JamRuby
end
def is_jamtrack_recording?
!jam_track_id.nil?
!jam_track_id.nil? && parsed_timeline['jam_track_isplaying']
end
def parsed_timeline
timeline ? JSON.parse(timeline) : {}
end
def high_quality_mix?
@ -182,21 +187,21 @@ module JamRuby
def recorded_tracks_for_user(user)
unless self.users.exists?(user)
raise PermissionError, "user was not in this session"
raise JamPermissionError, "user was not in this session"
end
recorded_tracks.where(:user_id => user.id)
end
def recorded_backing_tracks_for_user(user)
unless self.users.exists?(user)
raise PermissionError, "user was not in this session"
raise JamPermissionError, "user was not in this session"
end
recorded_backing_tracks.where(:user_id => user.id)
end
def has_access?(user)
users.exists?(user)
users.exists?(user) || plays.where("player_id=?", user).count != 0
end
# Start recording a session.
@ -264,7 +269,7 @@ module JamRuby
def claim(user, name, description, genre, is_public, upload_to_youtube=false)
upload_to_youtube = !!upload_to_youtube # Correct where nil is borking save
unless self.users.exists?(user)
raise PermissionError, "user was not in this session"
raise JamPermissionError, "user was not in this session"
end
claimed_recording = ClaimedRecording.new
@ -710,23 +715,26 @@ module JamRuby
end
end
def self.popular_recordings(limit = 100)
Recording.select('recordings.id').joins('inner join claimed_recordings ON claimed_recordings.recording_id = recordings.id AND claimed_recordings.is_public = TRUE').where(all_discarded: false).where(is_done: true).where(deleted: false).order('play_count DESC').limit(limit).group('recordings.id')
end
private
def self.validate_user_is_band_member(user, band)
unless band.users.exists? user
raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR
raise JamPermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR
end
end
def self.validate_user_is_creator(user, creator)
unless user.id == creator.id
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
end
end
def self.validate_user_is_musician(user)
unless user.musician?
raise PermissionError, ValidationMessages::USER_NOT_MUSICIAN_VALIDATION_ERROR
raise JamPermissionError, ValidationMessages::USER_NOT_MUSICIAN_VALIDATION_ERROR
end
end

View File

@ -1,10 +1,15 @@
module JamRuby
class RecurlyTransactionWebHook < ActiveRecord::Base
class RecurlyTransactionWebHook < ActiveRecord::Base
attr_accessible :admin_description, :jam_track_id, as: :admin
belongs_to :user, class_name: 'JamRuby::User'
belongs_to :sale_line_item, class_name: 'JamRuby::SaleLineItem', foreign_key: 'subscription_id', primary_key: 'recurly_subscription_uuid', inverse_of: :recurly_transactions
belongs_to :sale, class_name: 'JamRuby::Sale', foreign_key: 'invoice_id', primary_key: 'recurly_invoice_id', inverse_of: :recurly_transactions
# when we know what JamTrack this refund is related to, we set this value
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
validates :recurly_transaction_id, presence: true
validates :action, presence: true
validates :status, presence: true
@ -17,6 +22,9 @@ module JamRuby
REFUND = 'refund'
VOID = 'void'
HOOK_TYPES = [SUCCESSFUL_PAYMENT, FAILED_PAYMENT, REFUND, VOID]
def is_credit_type?
transaction_type == REFUND || transaction_type == VOID
end
@ -46,6 +54,10 @@ module JamRuby
end
end
def admin_url
APP_CONFIG.admin_root_url + "/admin/recurly_hooks/" + id
end
# see spec for examples of XML
def self.create_from_xml(document)
@ -81,6 +93,7 @@ module JamRuby
# 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'
sale = Sale.find_by_recurly_invoice_id(transaction.invoice_id)
@ -91,33 +104,44 @@ module JamRuby
jam_track_right = jam_track.right_for_user(transaction.user) if jam_track
if jam_track_right
jam_track_right.destroy
AdminMailer.alerts({
subject:"NOTICE: #{transaction.user.email} has had JamTrack: #{jam_track.name} revoked",
body: "A void 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
# 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.alerts({
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
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.alerts({
subject:"ACTION REQUIRED: #{transaction.user.email} got a refund it was not for total value of a JamTrack sale",
body: "We received a refund 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
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.alerts({
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
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
AdminMailer.recurly_alerts(transaction.user, {
subject: "ACTION REQUIRED: #{transaction.user.email} has refund with no correlator to sales",
body: "You will have to manually revoke any JamTrackRights in our database for the appropriate JamTracks"
}).deliver
end
end
transaction
end

View File

@ -62,7 +62,7 @@ module JamRuby
invitation = Invitation.where("music_session_id = ? AND receiver_id = ?", music_session.id, user.id)
if invitation.first.nil? && !music_session.open_rsvps && music_session.creator.id != user.id
raise PermissionError, "Only a session invitee can create an RSVP for this session."
raise JamPermissionError, "Only a session invitee can create an RSVP for this session."
end
RsvpRequest.transaction do
@ -154,7 +154,7 @@ module JamRuby
# authorize the user attempting to respond to the RSVP request
if music_session.creator.id != user.id
raise PermissionError, "Only the session organizer can accept or decline an RSVP request."
raise JamPermissionError, "Only the session organizer can accept or decline an RSVP request."
end
rsvp_request = RsvpRequest.find_by_id(rsvp_request_id)
@ -249,7 +249,7 @@ module JamRuby
rsvp_request = RsvpRequest.find(params[:id])
if music_session.creator.id != user.id && rsvp_request.user_id != user.id
raise PermissionError, "Only the session organizer or RSVP creator can cancel the RSVP."
raise JamPermissionError, "Only the session organizer or RSVP creator can cancel the RSVP."
end
RsvpRequest.transaction do

View File

@ -12,7 +12,7 @@ module JamRuby
attr_accessible :quantity, :cart_type, :product_info
validates_uniqueness_of :cart_id, scope: :cart_type
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"
@ -25,7 +25,7 @@ module JamRuby
def product_info
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} unless product.nil?
{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?
end
# multiply quantity by price
@ -111,6 +111,15 @@ module JamRuby
end
end
def self.move_to_user(user, anonymous_user, shopping_carts)
shopping_carts.each do |shopping_cart|
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(user)
cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, mark_redeem)
end
anonymous_user.destroy_all_shopping_carts
end
def self.is_product_purchase?(adjustment)
(adjustment[:accounting_code].include?(PURCHASE_FREE) || adjustment[:accounting_code].include?(PURCHASE_NORMAL)) && !adjustment[:accounting_code].include?(PURCHASE_FREE_CREDIT)
end

View File

@ -0,0 +1,36 @@
module JamRuby
# some times someone comes to signup as a new user, but there is context to preserve.
# the AnyUser cookie is one way that we can track the user from pre-signup to post-signup
# anyway, once the signup is done, we check to see if there is a SignupHint, and if so,
# we use it to figure out what to do with the user after they signup
class SignupHint < ActiveRecord::Base
belongs_to :user, class_name: 'JamRuby::User'
validates :redirect_location, length: {maximum: 1000}
validates :want_jamblaster, inclusion: {in: [nil, true, false]}
def self.refresh_by_anoymous_user(anonymous_user, options = {})
hint = SignupHint.find_by_anonymous_user_id(anonymous_user.id)
unless hint
hint = SignupHint.new
end
hint.anonymous_user_id = anonymous_user.id
hint.redirect_location = options[:redirect_location] if options.has_key?(:redirect_location)
hint.want_jamblaster = options[:want_jamblaster] if options.has_key?(:want_jamblaster)
hint.expires_at = 15.minutes.from_now
hint.save
hint
end
def self.delete_old
SignupHint.where("created_at < :week", {:week => 1.week.ago}).delete_all
end
end
end

View File

@ -113,6 +113,7 @@ module JamRuby
result = {}
backing_tracks = [] unless backing_tracks
tracks = [] unless tracks
Track.transaction do
connection = Connection.find_by_client_id!(clientId)

View File

@ -209,7 +209,7 @@ module JamRuby
scope :email_opt_in, where(:subscribe_email => true)
def user_progression_fields
@user_progression_fields ||= Set.new ["first_downloaded_client_at", "first_ran_client_at", "first_music_session_at", "first_real_music_session_at", "first_good_music_session_at", "first_certified_gear_at", "first_invited_at", "first_friended_at", "first_recording_at", "first_social_promoted_at" ]
@user_progression_fields ||= Set.new ["first_downloaded_client_at", "first_ran_client_at", "first_music_session_at", "first_real_music_session_at", "first_good_music_session_at", "first_certified_gear_at", "first_invited_at", "first_friended_at", "first_recording_at", "first_social_promoted_at", "first_played_jamtrack_at" ]
end
def update_progression_field(field_name, time = DateTime.now)
@ -812,7 +812,7 @@ module JamRuby
end
if user.id != updater_id
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
end
user.easy_save(first_name, last_name, email, password, password_confirmation, musician, gender,
@ -1046,7 +1046,16 @@ module JamRuby
user.photo_url = photo_url
# copy over the shopping cart to the new user, if a shopping cart is provided
user.shopping_carts = any_user.shopping_carts if any_user
if any_user
user.shopping_carts = any_user.shopping_carts
if user.shopping_carts
user.shopping_carts.each do |shopping_cart|
shopping_cart.anonymous_user_id = nil # nil out the anonymous user ID; required for uniqeness constraint on ShoppingCart
end
end
end
unless fb_signup.nil?
user.update_fb_authorization(fb_signup)
@ -1596,6 +1605,15 @@ module JamRuby
verifier.generate(user.id)
end
# URL to jam-admin
def admin_url
APP_CONFIG.admin_root_url + "/admin/users/" + id
end
def jam_track_rights_admin_url
APP_CONFIG.admin_root_url + "/admin/jam_track_rights?q[user_id_equals]=#{id}&commit=Filter&order=created_at DESC"
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64

View File

@ -17,7 +17,7 @@ class MQRouter
end
if !music_session.access? user
raise PermissionError, 'not allowed to join the specified session'
raise JamPermissionError, 'not allowed to join the specified session'
end
return music_session

View File

@ -263,6 +263,7 @@ module JamRuby
@@log.debug("manifest")
@@log.debug("--------")
@@log.debug(JSON.pretty_generate(@manifest))
@@log.debug("--------")
@manifest[:mix_id] = mix_id # slip in the mix_id so that the job can add it to the ogg comments

View File

@ -101,7 +101,9 @@ module JamRuby
el: 'data',
ev: data.to_s
}
RestClient.post(APP_CONFIG.ga_endpoint, params: params, timeout: 8, open_timeout: 8)
@@log.info("done (#{category}, #{action})")
end

View File

@ -34,8 +34,20 @@ module JamRuby
return
end
# compute the step count
total_steps = @jam_track_right.jam_track.stem_tracks.count + 1 # the '1' represents the jkz.py invocation
# track that it's started ( and avoid db validations )
JamTrackRight.where(:id => @jam_track_right.id).update_all(:signing_started_at => Time.now, :should_retry => false)
signing_started_at = Time.now
last_step_at = Time.now
JamTrackRight.where(:id => @jam_track_right.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)
# because we are skiping 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.packaging_steps = total_steps
@jam_track_right.signing_started_at = signing_started_at
@jam_track_right.should_retry = false
@jam_track_right.last_step_at = Time.now
SubscriptionMessage.jam_track_signing_job_change(@jam_track_right)
JamRuby::JamTracksManager.save_jam_track_right_jkz(@jam_track_right, self.bitrate)
# If bitrate is 48 (the default), use that URL. Otherwise, use 44kHz:

View File

@ -11,6 +11,7 @@ module JamRuby
@@log.debug("waking up")
FacebookSignup.delete_old
SignupHint.delete_old
@@log.debug("done")
end

View File

@ -46,7 +46,7 @@ describe JamTrackImporter do
let(:options) {{ skip_audio_upload:true }}
it "bare minimum specification" do
importer.synchronize_metadata(jam_track, minimum_meta, metalocation, 'Artist 1', 'Song 1')
importer.synchronize_metadata(jam_track, minimum_meta, metalocation, 'Artist 1', 'Song 1', options)
jam_track.plan_code.should eq('jamtrack-artist1-song1')
jam_track.name.should eq("Song 1")
@ -57,7 +57,7 @@ describe JamTrackImporter do
jam_track.original_artist.should eq('Artist 1')
jam_track.songwriter.should be_nil
jam_track.publisher.should be_nil
jam_track.sales_region.should eq('United States')
jam_track.sales_region.should eq('Worldwide')
jam_track.price.should eq(1.99)
end
end

View File

@ -134,12 +134,12 @@ describe JamTrackRight do
end
it "signing" do
right = FactoryGirl.create(:jam_track_right, signing_started_at: Time.now)
right = FactoryGirl.create(:jam_track_right, signing_started_at: Time.now, packaging_steps: 3, current_packaging_step:0, last_step_at:Time.now)
right.signing_state.should eq('SIGNING')
end
it "signing timeout" do
right = FactoryGirl.create(:jam_track_right, signing_started_at: Time.now - (APP_CONFIG.signing_job_run_max_time + 1))
right = FactoryGirl.create(:jam_track_right, signing_started_at: Time.now - 100, packaging_steps: 3, current_packaging_step:0, last_step_at:Time.now)
right.signing_state.should eq('SIGNING_TIMEOUT')
end

View File

@ -38,6 +38,41 @@ describe JamTrack do
end
end
describe "artist_index" do
before :each do
JamTrack.delete_all
end
it "empty query" do
query, pager = JamTrack.artist_index({}, user)
query.size.should == 0
end
it "groups" do
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, original_artist: 'artist', name: 'a')
jam_track2 = FactoryGirl.create(:jam_track_with_tracks, original_artist: 'artist', name: 'b')
query, pager = JamTrack.artist_index({}, user)
query.size.should == 1
query[0].original_artist.should eq('artist')
query[0]['song_count'].should eq('2')
end
it "sorts by name" do
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, original_artist: 'blartist', name: 'a')
jam_track2 = FactoryGirl.create(:jam_track_with_tracks, original_artist: 'artist', name: 'b')
query, pager = JamTrack.artist_index({}, user)
query.size.should == 2
query[0].original_artist.should eq('artist')
query[0]['song_count'].should eq('1')
query[1].original_artist.should eq('blartist')
query[1]['song_count'].should eq('1')
end
end
describe "index" do
it "empty query" do
query, pager = JamTrack.index({}, user)
@ -45,8 +80,8 @@ describe JamTrack do
end
it "sorts by name" do
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, name: 'a')
jam_track2 = FactoryGirl.create(:jam_track_with_tracks, name: 'b')
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, original_artist: 'artist', name: 'a')
jam_track2 = FactoryGirl.create(:jam_track_with_tracks, original_artist: 'artist', name: 'b')
query, pager = JamTrack.index({}, user)
query.size.should == 2
@ -141,5 +176,4 @@ describe JamTrack do
end
end
end
end
end

View File

@ -0,0 +1,48 @@
require 'spec_helper'
describe PaymentHistory do
let(:user) {FactoryGirl.create(:user)}
let(:user2) {FactoryGirl.create(:user)}
let(:jam_track) {FactoryGirl.create(:jam_track)}
before(:each) do
end
describe "index" do
it "empty" do
result = PaymentHistory.index(user)
result[:query].length.should eq(0)
result[:next].should eq(nil)
end
it "one" do
sale = Sale.create_jam_track_sale(user)
shopping_cart = ShoppingCart.create(user, jam_track)
sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, 'some_adjustment_uuid', nil)
result = PaymentHistory.index(user)
result[:query].length.should eq(1)
result[:next].should eq(nil)
end
it "user filtered correctly" do
sale = Sale.create_jam_track_sale(user)
shopping_cart = ShoppingCart.create(user, jam_track)
sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, nil, 'some_adjustment_uuid', nil)
result = PaymentHistory.index(user)
result[:query].length.should eq(1)
result[:next].should eq(nil)
sale2 = Sale.create_jam_track_sale(user2)
shopping_cart = ShoppingCart.create(user2, jam_track)
sale_line_item2 = SaleLineItem.create_from_shopping_cart(sale2, shopping_cart, nil, 'some_adjustment_uuid', nil)
result = PaymentHistory.index(user)
result[:query].length.should eq(1)
result[:next].should eq(nil)
end
end
end

View File

@ -15,6 +15,29 @@ describe Recording do
@track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument)
end
describe "popular_recordings" do
it "empty" do
Recording.popular_recordings.length.should eq(0)
end
it "one public recording" do
claim = FactoryGirl.create(:claimed_recording)
claim.recording.is_done = true
claim.recording.save!
recordings = Recording.popular_recordings
recordings.length.should eq(1)
recordings[0].id.should eq(claim.recording.id)
end
it "one private recording" do
claim = FactoryGirl.create(:claimed_recording, is_public: true)
recordings = Recording.popular_recordings
recordings.length.should eq(0)
end
end
describe "cleanup_excessive_storage" do
sample_audio='sample.file'

View File

@ -104,7 +104,7 @@ describe RsvpRequest do
it "should not allow non-invitee to RSVP to session with closed RSVPs" do
@music_session.open_rsvps = false
@music_session.save!
expect {RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)}.to raise_error(JamRuby::PermissionError)
expect {RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)}.to raise_error(JamRuby::JamPermissionError)
end
it "should allow RSVP creation with autoapprove option for open RSVP sessions" do
@ -216,7 +216,7 @@ describe RsvpRequest do
# attempt to approve with non-organizer
rs1 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot1.id)
rs2 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot2.id)
expect {RsvpRequest.update({:id => rsvp.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs1.id, :accept => true}, {:request_slot_id => rs2.id, :accept => true}]}, @session_invitee)}.to raise_error(PermissionError)
expect {RsvpRequest.update({:id => rsvp.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs1.id, :accept => true}, {:request_slot_id => rs2.id, :accept => true}]}, @session_invitee)}.to raise_error(JamPermissionError)
# approve with organizer
rs1 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot1.id)
@ -402,7 +402,7 @@ describe RsvpRequest do
comment.comment.should == "Let's Jam!"
# cancel
expect {RsvpRequest.cancel({:id => rsvp.id, :session_id => @music_session.id, :cancelled => "all", :message => "I'm gonna cancel all your RSVPs"}, user)}.to raise_error(PermissionError)
expect {RsvpRequest.cancel({:id => rsvp.id, :session_id => @music_session.id, :cancelled => "all", :message => "I'm gonna cancel all your RSVPs"}, user)}.to raise_error(JamPermissionError)
end
end

View File

@ -0,0 +1,24 @@
require 'spec_helper'
describe SignupHint do
let(:user) {AnonymousUser.new(SecureRandom.uuid)}
describe "refresh_by_anoymous_user" do
it "creates" do
hint = SignupHint.refresh_by_anoymous_user(user, {redirect_location: 'abc'})
hint.errors.any?.should be_false
hint.redirect_location.should eq('abc')
hint.want_jamblaster.should be_false
end
it "updated" do
SignupHint.refresh_by_anoymous_user(user, {redirect_location: 'abc'})
hint = SignupHint.refresh_by_anoymous_user(user, {redirect_location: nil, want_jamblaster: true})
hint.errors.any?.should be_false
hint.redirect_location.should be_nil
hint.want_jamblaster.should be_true
end
end
end

View File

@ -3,10 +3,18 @@ JAMKAZAM_TESTING_BUCKET = 'jamkazam-testing' # cuz i'm not comfortable using aws
def app_config
klass = Class.new do
def admin_root_url
'http://localhost:3333'
end
def email_alerts_alias
'alerts@jamkazam.com'
end
def email_recurly_notice
'recurly-alerts@jamkazam.com'
end
def email_generic_from
'nobody@jamkazam.com'
end
@ -166,8 +174,8 @@ def app_config
false
end
def signing_job_run_max_time
60 # 1 minute
def signing_step_max_time
40 # 40 seconds
end
def signing_job_queue_max_time
@ -182,6 +190,9 @@ def app_config
'foobar'
end
def unsubscribe_token
'blah'
end
private

1
web/.gitignore vendored
View File

@ -23,6 +23,7 @@ doc/
.idea
*.iml
jt_metadata.json
artifacts

View File

@ -87,6 +87,7 @@ gem 'recurly'
gem 'guard', '2.7.3'
gem 'influxdb', '0.1.8'
gem 'influxdb-rails', '0.1.10'
gem 'sitemap_generator'
group :development, :test do
gem 'rspec-rails', '2.14.2'

View File

@ -6,6 +6,7 @@
#require 'resque/scheduler/tasks'
require 'resque/tasks'
require 'resque/scheduler/tasks'
require 'sitemap_generator/tasks'
require File.expand_path('../config/application', __FILE__)
SampleApp::Application.load_tasks

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -8,8 +8,10 @@
var rest = context.JK.Rest();
var userId;
var user = {};
function beforeShow(data) {
console.log("beforeShow", data)
userId = data.id;
}
@ -43,6 +45,19 @@
var invalidProfiles = prettyPrintAudioProfiles(context.JK.getBadConfigMap());
var sessionSummary = summarizeSession(userDetail);
if(gon.global.video_available && gon.global.video_available!="none" ) {
var webcamName;
var webcam = context.jamClient.FTUECurrentSelectedVideoDevice()
if (webcam == null || typeof(webcam) == "undefined" || Object.keys(webcam).length == 0) {
webcamName = "None Configured"
} else {
webcamName = _.values(webcam)[0]
}
}
else {
webcamName = 'video unavailable'
}
var $template = $(context._.template($('#template-account-main').html(), {
email: userDetail.email,
name: userDetail.name,
@ -56,6 +71,7 @@
invalidProfiles : invalidProfiles,
isNativeClient: gon.isNativeClient,
musician: context.JK.currentUserMusician,
webcamName: webcamName,
sales_count: userDetail.sales_count
} , { variable: 'data' }));
@ -65,8 +81,9 @@
$('#account-scheduled-sessions-link').show();
} else {
$('#account-scheduled-sessions-link').hide();
}
}
}
}// function
function prettyPrintAudioProfiles(profileMap) {
var profiles = "";
@ -110,6 +127,7 @@
$('#account-content-scroller').on('click', '#account-edit-subscriptions-link', function(evt) { evt.stopPropagation(); navToEditSubscriptions(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-payments-link', function(evt) { evt.stopPropagation(); navToEditPayments(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-audio-link', function(evt) { evt.stopPropagation(); navToEditAudio(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-video-link', function(evt) { evt.stopPropagation(); navToEditVideo(); return false; } );
$('#account-content-scroller').on('avatar_changed', '#profile-avatar', function(evt, newAvatarUrl) { evt.stopPropagation(); updateAvatar(newAvatarUrl); return false; })
// License dialog:
@ -158,6 +176,11 @@
window.location = "/client#/account/audio"
}
function navToEditVideo() {
resetForm()
window.location = "/client#/account/video"
}
function navToPaymentHistory() {
window.location = '/client#/account/paymentHistory'
}
@ -178,6 +201,7 @@
}
function initialize() {
var screenBindings = {
'beforeShow': beforeShow,
'afterShow': afterShow

View File

@ -163,6 +163,13 @@
function handleConfigureAudioProfile(audioProfileId) {
if(!gearUtils.canBeConfigured(audioProfileId)) {
context.JK.Banner.showAlert("The System Default (Playback Only) profile can not currently be configured.");
return;
}
if(audioProfileId == gearUtils.GearUtil)
if(audioProfileId != context.jamClient.FTUEGetMusicProfileName()) {
logger.debug("activating " + audioProfileId);
var result = context.jamClient.FTUELoadAudioConfiguration(audioProfileId);

View File

@ -51,41 +51,33 @@ context.JK.AccountPaymentHistoryScreen = class AccountPaymentHistoryScreen
renderPayments:(response) =>
if response.entries? && response.entries.length > 0
for sale in response.entries
amt = sale.recurly_total_in_cents
amt = 0 if !amt?
original_total = sale.state.original_total
refund_total = sale.state.refund_total
refund_state = null
if original_total != 0 # the enclosed logic does not work for free purchases
if refund_total == original_total
refund_state = 'refunded'
else if refund_total != 0 and refund_total < original_total
refund_state = 'partial refund'
displayAmount = (amt/100).toFixed(2)
status = 'paid'
if sale.state.voided
status = 'voided'
displayAmount = (0).toFixed(2)
else if refund_state?
status = refund_state
displayAmount = (amt/100).toFixed(2) + " (refunded: #{(refund_total/100).toFixed(2)})"
description = []
for line_item in sale.line_items
description.push(line_item.product_info?.name)
for paymentHistory in response.entries
if paymentHistory.sale?
# this is a sale
sale = paymentHistory.sale
amt = sale.recurly_total_in_cents
status = 'paid'
displayAmount = ' $' + (amt/100).toFixed(2)
date = context.JK.formatDate(sale.created_at, true)
items = []
for line_item in sale.line_items
items.push(line_item.product_info?.name)
description = items.join(', ')
else
# this is a recurly webhook
transaction = paymentHistory.transaction
amt = transaction.amount_in_cents
status = transaction.transaction_type
displayAmount = '($' + (amt/100).toFixed(2) + ')'
date = context.JK.formatDate(transaction.transaction_at, true)
description = transaction.admin_description
payment = {
date: context.JK.formatDate(sale.created_at, true)
date: date
amount: displayAmount
status: status
payment_method: 'Credit Card',
description: description.join(', ')
payment_method: 'Credit Card'
description: description
}
tr = $(context._.template(@rowTemplate, payment, { variable: 'data' }));
@ -98,10 +90,9 @@ context.JK.AccountPaymentHistoryScreen = class AccountPaymentHistoryScreen
# Turn in to HTML rows and append:
#@tbody.html("")
console.log("response.next", response)
@next = response.next_page
@next = response.next
@renderPayments(response)
if response.next_page == null
if response.next == null
# if we less results than asked for, end searching
@scroller.infinitescroll 'pause'
@logger.debug("end of history")
@ -147,7 +138,7 @@ context.JK.AccountPaymentHistoryScreen = class AccountPaymentHistoryScreen
msg: $('<div class="infinite-scroll-loader">Loading ...</div>')
img: '/assets/shared/spinner.gif'
path: (page) =>
'/api/sales?' + $.param(that.buildQuery())
'/api/payment_histories?' + $.param(that.buildQuery())
}, (json, opts) =>
this.salesHistoryDone(json)

View File

@ -0,0 +1,32 @@
(function (context, $) {
"use strict";
context.JK = context.JK || {};
context.JK.AccountVideoProfile = function (app) {
var $webcamViewer = new context.JK.WebcamViewer()
function initialize() {
var screenBindings = {
'beforeShow': beforeShow,
'beforeHide':beforeHide
};
app.bindScreen('account/video', screenBindings);
$webcamViewer.init($(".webcam-container"))
}
function beforeShow() {
$webcamViewer.beforeShow()
}
function beforeHide() {
$webcamViewer.setVideoOff()
}
this.beforeShow = beforeShow
this.beforeHide = beforeHide
this.initialize = initialize
return this;
};
})(window, jQuery);

View File

@ -44,6 +44,7 @@
//= require globals
//= require AAB_message_factory
//= require jam_rest
//= require ga
//= require utils
//= require subscription_utils
//= require custom_controls

View File

@ -30,15 +30,16 @@
var $orderPrompt = null;
var $emptyCartPrompt = null;
var $noAccountInfoPrompt = null;
var $downloadApplicationLink = null;
function beforeShow() {
beforeShowOrder();
}
function afterShow(data) {
beforeShowOrder();
}
@ -56,11 +57,13 @@
}
function beforeShowOrder() {
$purchasedJamTracks.empty()
$orderPrompt.addClass('hidden')
$emptyCartPrompt.addClass('hidden')
$noAccountInfoPrompt.addClass('hidden')
$orderPanel.removeClass("hidden")
$thanksPanel.addClass("hidden")
$purchasedJamTrackHeader.attr('status', 'in-progress')
$screen.find(".place-order").addClass('disabled').off('click', placeOrder)
$("#order_error").text('').addClass("hidden")
step = 3;
@ -225,6 +228,7 @@
$orderPanel.addClass("hidden")
$thanksPanel.removeClass("hidden")
jamTrackUtils.checkShoppingCart()
app.refreshUser()
handleJamTracksPurchased(purchaseResponse.jam_tracks)
}
@ -237,6 +241,11 @@
}
else {
$jamTrackInBrowser.removeClass('hidden');
app.user().done(function(user) {
if(!user.first_downloaded_client_at) {
$downloadApplicationLink.removeClass('hidden')
}
})
}
}
}
@ -297,7 +306,7 @@
}
else {
logger.debug("done iterating over purchased JamTracks")
$purchasedJamTrackHeader.text('All purchased JamTracks have been downloaded successfully! You can now play them in a session.')
$purchasedJamTrackHeader.attr('status', 'done')
}
}
@ -349,6 +358,7 @@
$emptyCartPrompt = $screen.find('.empty-cart-prompt');
$noAccountInfoPrompt = $screen.find('.no-account-info-prompt');
$orderContent = $orderPanel.find(".order-content");
$downloadApplicationLink = $screen.find('.download-jamkazam-wrapper');
if ($screen.length == 0) throw "$screen must be specified";
if ($navigation.length == 0) throw "$navigation must be specified";

View File

@ -5,6 +5,7 @@
context.JK.CheckoutSignInScreen = function(app) {
var logger = context.JK.logger;
var rest = context.JK.Rest();
var $screen = null;
var $navigation = null;
@ -17,6 +18,7 @@
var $inputElements = null;
var $contentHolder = null;
var $btnNext = null;
var $btnFacebook = null;
function beforeShow(data) {
renderNavigation();
@ -44,6 +46,7 @@
$signinForm.on('submit', login);
$signinBtn.on('click', login);
$btnNext.on('click', moveNext);
$btnFacebook.on('click', facebookSignup);
}
function reset() {
@ -55,6 +58,31 @@
return false;
}
function facebookSignup() {
var $btn = $(this);
if($btn.is('.disabled')) {
logger.debug("ignoring fast attempt at facebook signup")
return false;
}
$btn.addClass('disabled')
rest.createSignupHint({redirect_location: '/client#/checkoutPayment'})
.done(function() {
// send the user on to facebook signin
window.location = $btn.attr('href');
})
.fail(function() {
app.notify({text:"Facebook Signup is not working properly"});
})
.always(function() {
$btn.removeClass('disabled')
})
return false;
}
function login() {
if($signinBtn.is('.disabled')) {
return false;
@ -117,6 +145,7 @@
$inputElements = $signinForm.find('.input-elements');
$contentHolder = $screen.find('.content-holder');
$btnNext = $screen.find('.btnNext');
$btnFacebook = $screen.find('.signin-facebook')
if($screen.length == 0) throw "$screen must be specified";
if($navigation.length == 0) throw "$navigation must be specified";

View File

@ -16,7 +16,7 @@ class CheckoutUtils
setPreserveBillingInfo:() =>
date = new Date();
minutes = 1;
minutes = 10;
date.setTime(date.getTime() + (minutes * 60 * 1000))
$.removeCookie(@cookie_name, { path: '/' })
$.cookie(@cookie_name, "jam", { expires: date, path: '/' })

View File

@ -5,7 +5,7 @@
//= require globals
//= require jamkazam
//= require utils
//= require ga
//= require jam_rest
//= require ga
//= require corp/init
//= require_directory ../corp

View File

@ -135,7 +135,11 @@
if(!button.name) throw "button.name must be specified";
if(!button.click) throw "button.click must be specified";
var buttonStyle = options.buttons.length == i + 1 ? 'button-orange' : 'button-grey';
var buttonStyle = button.buttonStyle;
if(!buttonStyle) {
buttonStyle = options.buttons.length == i + 1 ? 'button-orange' : 'button-grey';
}
var $btn = $('<a class="' + buttonStyle + ' user-btn">' + button.name + '</a>');
$btn.click(function() {

View File

@ -21,6 +21,7 @@
var autostart = $autoStartField.find('.icheckbox_minimal').is('.checked');
context.jamClient.SetAutoStart(autostart);
app.layout.closeDialog('client-preferences-dialog')
context.jamClient.SaveSettings();
return false;
})
}
@ -54,4 +55,4 @@
this.initialize = initialize;
}
})(window, jQuery);
})(window, jQuery);

View File

@ -50,7 +50,7 @@
$browserJamTrackBtn.click(function() {
app.layout.closeDialog('getting-started')
window.location = '/client#/jamtrack'
window.location = '/client#/jamtrackBrowse'
return false;
})

View File

@ -49,7 +49,6 @@
function getBackingTracks(page) {
var result = context.jamClient.getBackingTrackList();
console.log("result", result)
var backingTracks = result.backing_tracks;
if (!backingTracks || backingTracks.length == 0) {
@ -89,8 +88,7 @@
rest.openBackingTrack({id: context.JK.CurrentSessionModel.id(), backing_track_path: backingTrack.name})
.done(function(response) {
var result = context.jamClient.SessionOpenBackingTrackFile(backingTrack.name, false);
console.log("BackingTrackPlay response: %o", result);
// TODO: Possibly actually check the result. Investigate
// what real client returns:
// // if(result) {

View File

@ -10,10 +10,10 @@
var $dialog = null;
function resetForm() {
// remove all display errors
$('#recording-finished-dialog form .error-text').remove()
$('#recording-finished-dialog form .error').removeClass("error")
removeGoogleLoginErrors()
}
function beforeShow() {
@ -130,11 +130,47 @@
return false;
}
function startGoogleLogin(e) {
e.preventDefault()
logger.debug("Starting google login")
window._oauth_win = window.open("/auth/google_login", "Log In to Google", "height=500,width=500,menubar=no,resizable=no,status=no");
window._oauth_callback = function() {
window._oauth_win.close()
setGoogleAuthState()
}
return false;
}
function claimRecording(e) {
resetForm();
registerClaimRecordingHandlers(false);
registerClaimRecordingHandlers(false)
var upload_to_youtube = $('#recording-finished-dialog form input[name=upload_to_youtube]').is(':checked')
if (upload_to_youtube) {
$.ajax({
type: "GET",
dataType: "json",
url: "/auth/has_google_auth"
}).success(function(data) {
if(data.has_google_auth) {
performClaim()
} else {
var error_ul = $('<ul class="error-text upload_to_youtube"><li>You must sign in to YouTube</li></ul>')
$('#recording-finished-dialog form [purpose=upload_to_youtube]').addClass('error').append(error_ul)
}
}).always(function () {
registerClaimRecordingHandlers(true);
})
} else {
performClaim()
}
return false;
}
function performClaim() {
var name = $('#recording-finished-dialog form input[name=name]').val();
var description = $('#recording-finished-dialog form textarea[name=description]').val();
var genre = $('#recording-finished-dialog form select[name=genre]').val();
@ -151,59 +187,53 @@
save_video: save_video,
upload_to_youtube: upload_to_youtube
})
.done(function () {
$dialog.data('result', {keep:true});
app.layout.closeDialog('recordingFinished');
context.JK.GA.trackMakeRecording();
})
.fail(function (jqXHR) {
if (jqXHR.status == 422) {
var errors = JSON.parse(jqXHR.responseText);
.done(function () {
$dialog.data('result', {keep:true});
app.layout.closeDialog('recordingFinished');
context.JK.GA.trackMakeRecording();
})
.fail(function (jqXHR) {
if (jqXHR.status == 422) {
var errors = JSON.parse(jqXHR.responseText);
var $name_errors = context.JK.format_errors('name', errors);
if ($name_errors) $('#recording-finished-dialog form input[name=name]').closest('div.field').addClass('error').end().after($name_errors);
var $name_errors = context.JK.format_errors('name', errors);
if ($name_errors) $('#recording-finished-dialog form input[name=name]').closest('div.field').addClass('error').end().after($name_errors);
var $description_errors = context.JK.format_errors('description', errors);
if ($description_errors) $('#recording-finished-dialog form input[name=description]').closest('div.field').addClass('error').end().after($description_errors);
var $description_errors = context.JK.format_errors('description', errors);
if ($description_errors) $('#recording-finished-dialog form input[name=description]').closest('div.field').addClass('error').end().after($description_errors);
var $genre_errors = context.JK.format_errors('genre', errors);
if ($genre_errors) $('#recording-finished-dialog form select[name=genre]').closest('div.field').addClass('error').end().after($genre_errors);
var $genre_errors = context.JK.format_errors('genre', errors);
if ($genre_errors) $('#recording-finished-dialog form select[name=genre]').closest('div.field').addClass('error').end().after($genre_errors);
var $is_public_errors = context.JK.format_errors('is_public', errors);
if ($is_public_errors) $('#recording-finished-dialog form input[name=is_public]').closest('div.field').addClass('error').end().after($is_public_errors);
var $is_public_errors = context.JK.format_errors('is_public', errors);
if ($is_public_errors) $('#recording-finished-dialog form input[name=is_public]').closest('div.field').addClass('error').end().after($is_public_errors);
var $save_video_errors = context.JK.format_errors('save_video', errors);
if ($save_video_errors) $('#recording-finished-dialog form input[name=save_video]').closest('div.field').addClass('error').end().after($save_video_errors);
var $save_video_errors = context.JK.format_errors('save_video', errors);
if ($save_video_errors) $('#recording-finished-dialog form input[name=save_video]').closest('div.field').addClass('error').end().after($save_video_errors);
var recording_error = context.JK.get_first_error('recording_id', errors);
var $upload_to_youtube_errors = context.JK.format_errors('upload_to_youtube', errors);
if ($upload_to_youtube_errors) $('#recording-finished-dialog form input[name=upload_to_youtube]').closest('div.field').addClass('error').end().after($upload_to_youtube_errors);
if (recording_error) context.JK.showErrorDialog(app, "Unable to claim recording.", recording_error);
}
else {
logger.error("unable to claim recording %o", arguments);
var recording_error = context.JK.get_first_error('recording_id', errors);
if (recording_error) context.JK.showErrorDialog(app, "Unable to claim recording.", recording_error);
}
else {
logger.error("unable to claim recording %o", arguments);
context.JK.showErrorDialog(app, "Unable to claim recording.", jqXHR.responseText);
}
})
.always(function () {
registerClaimRecordingHandlers(true);
});
return false;
context.JK.showErrorDialog(app, "Unable to claim recording.", jqXHR.responseText);
}
})
.always(function () {
registerClaimRecordingHandlers(true);
});
}
function registerClaimRecordingHandlers(onOff) {
$('#keep-session-recording').off('click', claimRecording)
$('#recording-finished-dialog form').off('submit', claimRecording);
if (onOff) {
$('#keep-session-recording').on('click', claimRecording);
$('#recording-finished-dialog form').on('submit', claimRecording);
}
else {
$('#keep-session-recording').off('click', claimRecording)
$('#recording-finished-dialog form').off('submit', claimRecording);
}
}
function registerDiscardRecordingHandlers(onOff) {
@ -216,6 +246,11 @@
}
function onPause() {
logger.debug("calling jamClient.SessionPausePlay");
context.jamClient.SessionPausePlay();
}
function onStop() {
logger.debug("calling jamClient.SessionStopPlay");
context.jamClient.SessionStopPlay();
}
@ -234,9 +269,39 @@
registerClaimRecordingHandlers(true);
registerDiscardRecordingHandlers(true);
$(playbackControls)
.on('stop', onStop)
.on('pause', onPause)
.on('play', onPlay)
.on('change-position', onChangePlayPosition);
$dialog.find(".google_login_button").on('click', startGoogleLogin);
// Check for google authorization using AJAX and show/hide the
// google login button / "signed in" label as appropriate:
$(window).on('focus', function() {
setGoogleAuthState();
});
}
function setGoogleAuthState() {
$.ajax({
type: "GET",
dataType: "json",
url: "/auth/has_google_auth"
}).success(function(data) {
if(data.has_google_auth) {
$("input.google_login_button").addClass("hidden")
$("span.signed_in_to_google").removeClass("hidden")
removeGoogleLoginErrors()
} else {
$("span.signed_in_to_google").addClass("hidden")
$("input.google_login_button").removeClass("hidden")
}
})
}
function removeGoogleLoginErrors() {
$("ul.error-text.upload_to_youtube").remove()
$('#recording-finished-dialog form div[purpose=upload_to_youtube]').removeClass('error')
}
function setRecording(recordingData) {
@ -267,7 +332,6 @@
playbackControls = new context.JK.PlaybackControls($('#recording-finished-dialog .recording-controls'));
registerStaticEvents();
initializeButtons();
};

View File

@ -42,7 +42,7 @@ context.JK.SinglePlayerProfileGuardDialog = class SinglePlayerProfileGuardDialog
latency = '?'
if canPlayWithOthers.audioLatency?
latency = canPlayWithOthers.audioLatency
latency = canPlayWithOthers.audioLatency.toFixed(2)
@audioLatency.text("#{latency} milliseconds.")

View File

@ -48,6 +48,8 @@ context.JK.DownloadJamTrack = class DownloadJamTrack
@tracked = false
@ajaxEnqueueAborted = false
@ajaxGetJamTrackRightAborted = false
@currentPackagingStep = null
@totalSteps = null
throw "no JamTrack specified" unless @jamTrack?
throw "invalid size" if @size != 'large' && @size != 'small'
@ -174,6 +176,7 @@ context.JK.DownloadJamTrack = class DownloadJamTrack
if @state == @states.errored
data.result = 'error'
data.detail = @errorReason
@rest.createAlert("JamTrack Sync failed for #{context.JK.currentUserName}", data)
else
data.result = 'success'
@ -187,7 +190,10 @@ context.JK.DownloadJamTrack = class DownloadJamTrack
showDownloading: () =>
@logger.debug("showing #{@state.name}")
# while downloading, we don't run the transition timer, because the download API is guaranteed to call success, or failure, eventually
context.jamClient.JamTrackDownload(@jamTrack.id, this.makeDownloadSuccessCallback(), this.makeDownloadFailureCallback())
context.jamClient.JamTrackDownload(@jamTrack.id, context.JK.currentUserId,
this.makeDownloadProgressCallback(),
this.makeDownloadSuccessCallback(),
this.makeDownloadFailureCallback())
showKeying: () =>
@logger.debug("showing #{@state.name}")
@ -374,8 +380,22 @@ context.JK.DownloadJamTrack = class DownloadJamTrack
.done(this.processJamTrackRight)
.fail(this.processJamTrackRightFail)
# update progress indicator for packaging step
updateSteps: () =>
if @currentPackagingStep? and @totalSteps?
progress = "#{Math.round(@currentPackagingStep/@totalSteps * 100)}%"
else
progress = '...'
@root.find('.state-packaging .progress').text(progress)
processSigningState: (jamTrackRight) =>
signingState = jamTrackRight.signing_state
@totalSteps = jamTrackRight.packaging_steps
@currentPackagingStep = jamTrackRight.current_packaging_step
@updateSteps()
processSigningState: (signingState) =>
@logger.debug("DownloadJamTrack: processSigningState: " + signingState)
switch signingState
@ -435,8 +455,9 @@ context.JK.DownloadJamTrack = class DownloadJamTrack
processJamTrackRight: (myJamTrack) =>
@logger.debug("processJamTrackRight", myJamTrack)
unless @ajaxGetJamTrackRightAborted
this.processSigningState(myJamTrack.signing_state)
this.processSigningState(myJamTrack)
else
@logger.debug("DownloadJamTrack: ignoring processJamTrackRight response")
@ -459,16 +480,26 @@ context.JK.DownloadJamTrack = class DownloadJamTrack
@logger.debug("DownloadJamTrack: ignoring processEnqueueJamTrackFail response")
onJamTrackRightEvent: (e, data) =>
@logger.debug("DownloadJamTrack: subscription notification received: type:" + data.type)
@logger.debug("DownloadJamTrack: subscription notification received: type:" + data.type, data)
this.expectTransition()
this.processSigningState(data.body.signing_state)
this.processSigningState(data.body)
downloadProgressCallback: (bytesReceived, bytesTotal, downloadSpeedMegSec, timeRemaining) =>
bytesReceived = Number(bytesReceived)
bytesTotal = Number(bytesTotal)
# bytesTotal from Qt is not trust worthy; trust server's answer instead
#progressWidth = ((bytesReceived / updateSize) * 100).toString() + "%";
# $('#progress-bar').width(progressWidth)
updateDownloadProgress: () =>
if @bytesReceived? and @bytesTotal?
progress = "#{Math.round(@bytesReceived/@bytesTotal * 100)}%"
else
progress = '0%'
@root.find('.state-downloading .progress').text(progress)
downloadProgressCallback: (bytesReceived, bytesTotal) =>
@logger.debug("download #{bytesReceived}/#{bytesTotal}")
@bytesReceived = Number(bytesReceived)
@bytesTotal = Number(bytesTotal)
setTimeout(this.updateDownloadProgress, 100)
downloadSuccessCallback: (updateLocation) =>
# is the package loadable yet?

View File

@ -20,6 +20,7 @@
var stun = null;
var rest = context.JK.Rest();
$(document).on('JAMKAZAM_CONSTRUCTED', function(e, data) {
var app = data.app;
@ -31,6 +32,8 @@
updateScoringIntervals();
initializeInfluxDB();
trackNewUser();
})
$(document).on('JAMKAZAM_READY', function() {
@ -204,9 +207,9 @@
var user = app.user()
if(user) {
user.done(function(userProfile) {
if (userProfile.show_whats_next && userProfile.show_whats_next_count < 10 &&
if (!userProfile.show_jamtrack_guide && userProfile.show_whats_next && userProfile.show_whats_next_count < 10 &&
window.location.pathname.indexOf(gon.client_path) == 0 &&
window.location.pathname.indexOf('/checkout') == -1 &&
window.location.hash.indexOf('/checkout') == -1 &&
!app.layout.isDialogShowing('getting-started'))
{
app.layout.showDialog('getting-started');
@ -219,4 +222,26 @@
context.JK.JamTrackUtils.checkShoppingCart();
}
function trackNewUser() {
var cookie = $.cookie('new_user')
if(cookie) {
try {
cookie = JSON.parse(cookie)
context.JK.signup = {}
context.JK.signup = cookie
$(function() {
// ga() object isn't ready until the page is loaded
$.removeCookie('new_user')
context.JK.GA.trackRegister(cookie.musician, cookie.registrationType);
});
}
catch(e) {
logger.error("unable to deserialize new_user cookie")
}
}
}
})(window, jQuery);

View File

@ -21,6 +21,7 @@
var frameSize = 2.5;
var fakeJamClientRecordings = null;
var p2pCallbacks = null;
var videoShared = false;
var metronomeActive=false;
var metronomeBPM=false;
var metronomeSound=false;
@ -59,6 +60,45 @@
function FTUESetMusicProfileName() {
}
function FTUESelectVideoCaptureDevice(device, settings) {
}
function FTUESetVideoEncodeResolution(resolution) {
}
function FTUEGetVideoCaptureDeviceNames() {
return ["Built-in Webcam HD"]
}
function FTUECurrentSelectedVideoDevice() {
return {"xy323ss": "Built-in Webcam HD"}
}
function FTUEGetAvailableEncodeVideoResolutions() {
return {
1: "1024x768",
2: "800x600"
}
}
function FTUEGetVideoCaptureDeviceCapabilities() {
return {}
}
function isSessVideoShared() {
return videoShared;
}
function SessStopVideoSharing() {
videoShared=false;
}
function SessStartVideoSharing(bitrate) {
if (!bitrate || typeof(bitrate)=="undefined") {
bitrate = 0
}
videoShared=true;
}
function FTUEGetInputLatency() {
dbg("FTUEGetInputLatency");
return 2;
@ -403,6 +443,14 @@
}
// Session Functions
function SessionCurrrentJamTrackPlayPosMs() {
return 0;
}
function SessionGetJamTracksPlayDurationMs() {
return 60000;
}
function SessionAddTrack() {}
function SessionAudioResync() {
@ -931,6 +979,7 @@
function LeaveSessionAndMinimize() {}
function SetAutoStart() {}
function GetAutoStart() { return true; }
function SaveSettings() {}
// Javascript Bridge seems to camel-case
// Set the instance functions:
@ -1031,6 +1080,8 @@
// Session
this.SessionAddTrack = SessionAddTrack;
this.SessionCurrrentJamTrackPlayPosMs = SessionCurrrentJamTrackPlayPosMs;
this.SessionGetJamTracksPlayDurationMs = SessionGetJamTracksPlayDurationMs;
this.SessionGetControlState = SessionGetControlState;
this.SessionGetAllControlState = SessionGetAllControlState;
this.SessionSetUserName = SessionSetUserName;
@ -1140,6 +1191,19 @@
this.ClosePreviewRecording = ClosePreviewRecording;
this.OnDownloadAvailable = OnDownloadAvailable;
// Video functionality:
this.FTUESelectVideoCaptureDevice = FTUESelectVideoCaptureDevice
this.FTUESetVideoEncodeResolution = FTUESetVideoEncodeResolution;
this.FTUEGetVideoCaptureDeviceNames = FTUEGetVideoCaptureDeviceNames;
this.FTUECurrentSelectedVideoDevice = FTUECurrentSelectedVideoDevice;
this.FTUEGetAvailableEncodeVideoResolutions = FTUEGetAvailableEncodeVideoResolutions;
this.FTUEGetVideoCaptureDeviceCapabilities = FTUEGetVideoCaptureDeviceCapabilities;
this.isSessVideoShared = isSessVideoShared;
this.SessStopVideoSharing = SessStopVideoSharing;
this.SessStartVideoSharing = SessStartVideoSharing;
// Clipboard
this.SaveToClipboard = SaveToClipboard;
@ -1169,4 +1233,4 @@
this.clientID = "devtester";
};
})(window,jQuery);
})(window,jQuery);

View File

@ -6,6 +6,7 @@
"use strict";
context.JK = context.JK || {};
var rest = context.JK.Rest();
var logger = context.JK.logger;
// types
@ -130,6 +131,20 @@
jkComment : 'jkComment'
};
// JamTrack categories and actions:
var jamTrackAvailabilityTypes = {
worldwide: 'JamTrackGlobal',
usa: 'JamTrackUSA'
}
var jamTrackActions = {
isPublic: 'PublicPerformance',
isPrivate: 'PrivateUse'
}
var jamTrackSessionLabels = {
nonSession: 'NonSession',
inSession: 'InSession'
}
function translatePlatformForGA(platform) {
assertOneOf(platform, context.JK.OS);
@ -239,6 +254,36 @@
context.ga('send', 'event', categories.findSession, sessionCountRollup, numSessionsFound);
}
function trackJamTrackPlaySession(sessionId, inSession) {
rest.getSession(sessionId).done(function(session) {
if (session && session.jam_track ) {
rest.getJamTracks({id:session.jam_track.id}).done(function(response) {
if (response.jamtracks && response.jamtracks.length!=0) {
var jamtrack = response.jamtracks[0]
trackJamTrackPlay(
jamtrack.sales_region!=context.JK.AVAILABILITY_US,
session.participants.length > 1,
inSession);
}// if
})// rest.getJamTracks
}// if
})// rest.getSession
}
function trackJamTrackPlay(isGlobal, isPublic, inSession) {
assertBoolean(isGlobal)
assertBoolean(isPublic)
assertBoolean(inSession)
context.ga(
'send',
'event',
(isGlobal) ? jamTrackAvailabilityTypes.worldwide : jamTrackAvailabilityTypes.usa,
(isPublic) ? jamTrackActions.isPublic : jamTrackActions.isPrivate,
(inSession) ? jamTrackSessionLabels.inSession : jamTrackSessionLabels.nonSession
)
logger.debug("Tracked Jam Track Play")
}
// if you want to pass in no title, either omit it from the arg list when u invoke virtualPageView, or pass in undefined, NOT null
function virtualPageView(page, title) {
@ -271,7 +316,8 @@
}
// when someone plays a recording
function trackRecordingPlay(recordingAction) {
function trackRecordingPlay(recording, recordingAction) {
if (!recordingAction) {
recordingAction = _defaultPlayAction();
}
@ -279,10 +325,20 @@
var label = JK.currentUserId ? userLabels.registeredUser : userLabels.visitor;
context.ga('send', 'event', categories.recordingPlay, recordingAction, label);
if (recording.jam_track) {
rest.getJamTracks({id:recording.jam_track_id}).done(function(response) {
if (response.jamtracks && response.jamtracks.length==1) {
var jamtrack = response.jamtracks[0]
trackJamTrackPlay(jamtrack.sales_region!=context.JK.AVAILABILITY_US, recording.fan_access, false);
}
})
}
}
// when someone plays a live session broadcast
function trackSessionPlay(recordingAction) {
function trackSessionPlay(session, recordingAction) {
logger.debug("Tracking session play: ", session)
if (!recordingAction) {
recordingAction = _defaultPlayAction();
}
@ -379,7 +435,8 @@
GA.trackSessionQuality = trackSessionQuality;
GA.trackServiceInvitations = trackServiceInvitations;
GA.trackFindSessions = trackFindSessions;
GA.virtualPageView = virtualPageView;
GA.trackJamTrackPlay = trackJamTrackPlay;
GA.trackJamTrackPlaySession = trackJamTrackPlaySession;
GA.trackFriendConnect = trackFriendConnect;
GA.trackMakeRecording = trackMakeRecording;
GA.trackShareRecording = trackShareRecording;
@ -387,8 +444,8 @@
GA.trackSessionPlay = trackSessionPlay;
GA.trackBand = trackBand;
GA.trackJKSocial = trackJKSocial;
GA.virtualPageView = virtualPageView;
context.JK.GA = GA;
})(window,jQuery);

View File

@ -10,6 +10,7 @@
var rest = new context.JK.Rest();
context.JK.HelpBubbleHelper = helpBubble;
var logger = context.JK.logger;
var jamTrackGuideTimeout = null;
var defaultScoreBreakDownOptions = {positions: ['right', 'top', 'bottom', 'left'], width:'600px', closeWhenOthersOpen: true };
helpBubble.scoreBreakdown = function($element, isCurrentUser, full_score, myAudioLatency, otherAudioLatency, internetScore, options) {
@ -26,4 +27,59 @@
}
}
function bigHelpOptions(options) {
return {positions: options.positions, offsetParent: options.offsetParent,
spikeGirth: 15,
spikeLength: 20,
fill: 'white',
cornerRadius:8,
strokeWidth: 2,
cssStyles: {
fontWeight:'bold',
fontSize: '20px',
backgroundColor:'transparent',
color:'#ed3618'}}
}
function clearJamTrackGuideTimeout() {
if(jamTrackGuideTimeout) {
clearTimeout(jamTrackGuideTimeout);
jamTrackGuideTimeout = null;
}
}
function jamTrackGuide(callback) {
if(gon.isNativeClient) {
context.JK.app.user().done(function(user) {
if(user.show_jamtrack_guide) {
clearJamTrackGuideTimeout();
jamTrackGuideTimeout = setTimeout(function() {
callback()
}, 1000)
}
})
}
}
helpBubble.clearJamTrackGuide = clearJamTrackGuideTimeout;
helpBubble.jamtrackGuideTile = function ($element, $offsetParent) {
jamTrackGuide(function() {
context.JK.prodBubble($element, 'jamtrack-guide-tile', {}, bigHelpOptions({positions:['top'], offsetParent: $offsetParent}))
})
}
helpBubble.jamtrackGuidePrivate = function ($element, $offsetParent) {
jamTrackGuide(function() {
context.JK.prodBubble($element, 'jamtrack-guide-private', {}, bigHelpOptions({positions:['right'], offsetParent: $offsetParent}))
})
}
helpBubble.jamtrackGuideSession = function ($element, $offsetParent) {
jamTrackGuide(function() {
context.JK.prodBubble($element, 'jamtrack-guide-session', {}, bigHelpOptions({positions:['left'], offsetParent: $offsetParent}))
})
}
})(window, jQuery);

View File

@ -11,6 +11,14 @@
function beforeShow(data) {
}
function afterShow(data) {
context.JK.HelpBubbleHelper.jamtrackGuideTile($('.homecard.createsession'), $screen.find('.createsession'));
}
function beforeHide() {
context.JK.HelpBubbleHelper.clearJamTrackGuide();
}
function mouseenterTile() {
$(this).addClass('hover');
}
@ -84,7 +92,7 @@
}
this.initialize = function() {
var screenBindings = { 'beforeShow': beforeShow };
var screenBindings = { 'beforeShow': beforeShow, 'afterShow': afterShow, 'beforeHide' : beforeHide };
app.bindScreen('home', screenBindings);
events();
$screen = $('.screen[layout-id="home"]')

View File

@ -287,11 +287,6 @@
}
function addPlayablePlay(playableId, playableType, claimedRecordingId, userId) {
if (playableType == 'JamRuby::Recording') {
context.JK.GA.trackRecordingPlay();
} else if (playableType == 'JamRuby::MusicSession') {
context.JK.GA.trackSessionPlay();
}
return $.ajax({
url: '/api/users/' + playableId + "/plays",
type: "POST",
@ -1466,7 +1461,7 @@
});
}
function getJamtracks(options) {
function getJamTracks(options) {
return $.ajax({
type: "GET",
url: '/api/jamtracks?' + $.param(options),
@ -1475,6 +1470,15 @@
});
}
function getJamTrackArtists(options) {
return $.ajax({
type: "GET",
url: '/api/jamtracks/artists?' + $.param(options),
dataType: "json",
contentType: 'application/json'
});
}
function getJamTrackRight(options) {
var jamTrackId = options['id'];
@ -1518,7 +1522,7 @@
function getSalesHistory(options) {
return $.ajax({
type: "GET",
url: '/api/sales?' + $.param(options),
url: '/api/payment_histories?' + $.param(options),
dataType: "json",
contentType: 'application/json'
});
@ -1675,7 +1679,30 @@
});
}
function initialize() {
function createSignupHint(data) {
return $.ajax({
type: "POST",
url: '/api/signup_hints',
dataType: "json",
contentType: 'application/json',
data: JSON.stringify(data),
});
}
function createAlert(subject, data) {
var message = {subject:subject};
$.extend(message, data);
console.log("message", message)
return $.ajax({
type: "POST",
url: '/api/alerts',
dataType: "json",
contentType: 'application/json',
data: JSON.stringify(message),
});
}
function initialize() {
return self;
}
@ -1799,7 +1826,8 @@
this.updateAudioLatency = updateAudioLatency;
this.getJamTrack = getJamTrack;
this.getJamTrackWithArtistInfo = getJamTrackWithArtistInfo;
this.getJamtracks = getJamtracks;
this.getJamTracks = getJamTracks;
this.getJamTrackArtists = getJamTrackArtists;
this.getPurchasedJamTracks = getPurchasedJamTracks;
this.getPaymentHistory = getPaymentHistory;
this.getSalesHistory = getSalesHistory;
@ -1825,6 +1853,8 @@
this.getMusicianSearchFilter = getMusicianSearchFilter;
this.postMusicianSearchFilter = postMusicianSearchFilter;
this.playJamTrack = playJamTrack;
this.createSignupHint = createSignupHint;
this.createAlert = createAlert;
return this;
};

View File

@ -9,7 +9,7 @@ context.JK.JamTrackPreview = class JamTrackPreview
@EVENTS = context.JK.EVENTS
@rest = context.JK.Rest()
@logger = context.JK.logger
@options = options || {master_shows_duration: false}
@options = options || {master_shows_duration: false, color:'gray'}
@app = app
@jamTrack = jamTrack
@jamTrackTrack = jamTrackTrack
@ -23,7 +23,7 @@ context.JK.JamTrackPreview = class JamTrackPreview
template = $('#template-jam-track-preview')
throw "no jam track preview template" if not template.exists()
@root.html($(template.html()))
@root.html(context._.template(template.html(), @options, {variable:'data'}))
@playButton = @root.find('.play-button')
@stopButton = @root.find('.stop-button')
@instrumentIcon = @root.find('.instrument-icon')

View File

@ -50,7 +50,7 @@ context.JK.JamTrackScreen=class JamTrackScreen
@instrument.val(parms.instrument)
if(parms.availability?)
@availability.val(parms.availability)
window.history.replaceState({}, "", "/client#/jamtrack")
window.history.replaceState({}, "", "/client#/jamtrackBrowse")
getParams:() =>
params = {}
@ -63,16 +63,31 @@ context.JK.JamTrackScreen=class JamTrackScreen
params[key] = decodeURIComponent(val)
params
setFilterState: (state) =>
if state
@genre.easyDropDown('enable').removeAttr('disabled')
@artist.easyDropDown('enable').removeAttr('disabled')
@instrument.easyDropDown('enable').removeAttr('disabled')
@availability.easyDropDown('enable').removeAttr('disabled')
else
@genre.easyDropDown('disable').attr('disabled', 'disabled')
@artist.easyDropDown('disable').attr('disabled', 'disabled')
@instrument.easyDropDown('disable').attr('disabled', 'disabled')
@availability.easyDropDown('disable').attr('disabled', 'disabled')
refresh:() =>
this.clearResults()
@currentQuery = this.buildQuery()
that = this
rest.getJamtracks(@currentQuery).done((response) =>
that.clearResults()
this.setFilterState(false)
rest.getJamTracks(@currentQuery).done((response) =>
that.handleJamtrackResponse(response)
).fail (jqXHR) =>
).fail( (jqXHR) =>
that.clearResults()
that.noMoreJamtracks.show()
that.app.notifyServerError jqXHR, 'Jamtrack Unavailable'
that.app.notifyServerError jqXHR, 'Jamtrack Unavailable'
).always () =>
that.setFilterState(true)
search:() =>
this.refresh()
@ -117,7 +132,7 @@ context.JK.JamTrackScreen=class JamTrackScreen
# if we less results than asked for, end searching
@scroller.infinitescroll 'pause'
if @currentPage == 0 and response.jamtracks.length == 0
@content.append '<div class=\'no-jamtracks-msg\'>No JamTracks found.</div>'
@content.append '<td colspan="3" class="no-jamtracks-msg\'>No JamTracks found.</div>'
if @currentPage > 0
@noMoreJamtracks.show()
# there are bugs with infinitescroll not removing the 'loading'.
@ -162,14 +177,39 @@ context.JK.JamTrackScreen=class JamTrackScreen
licenseUSWhy:(e) =>
e.preventDefault()
@app.layout.showDialog 'jamtrack-availability-dialog'
@app.layout.showDialog 'jamtrack-availability-dialog'
registerEvents:() =>
handleExpanded:(trackElement) =>
jamTrack = trackElement.data('jamTrack')
expanded = trackElement.data('expanded')
expand = !expanded
trackElement.data('expanded', expand)
detailArrow = trackElement.find('.jamtrack-detail-btn')
if expand
trackElement.find('.extra').removeClass('hidden')
detailArrow.html('hide tracks <a class="details-arrow arrow-up-orange"></a>')
for track in jamTrack.tracks
trackElement.find("[jamtrack-track-id='#{track.id}']").removeClass('hidden')
else
trackElement.find('.extra').addClass('hidden')
detailArrow.html('preview all tracks <a class="details-arrow arrow-down-orange"></a>')
count = 0
for track in jamTrack.tracks
if count < 2
trackElement.find("[jamtrack-track-id='#{track.id}']").removeClass('hidden')
else
trackElement.find("[jamtrack-track-id='#{track.id}']").addClass('hidden')
count++
registerEvents:(parent) =>
#@screen.find('.jamtrack-detail-btn').on 'click', this.showJamtrackDescription
@screen.find('.play-button').on 'click', this.playJamtrack
@screen.find('.jamtrack-add-cart').on 'click', this.addToCartJamtrack
@screen.find('.license-us-why').on 'click', this.licenseUSWhy
@screen.find('.jamtrack-detail-btn').on 'click', this.toggleExpanded
parent.find('.play-button').on 'click', this.playJamtrack
parent.find('.jamtrack-add-cart').on 'click', this.addToCartJamtrack
parent.find('.license-us-why').on 'click', this.licenseUSWhy
parent.find('.jamtrack-detail-btn').on 'click', this.toggleExpanded
# @screen.find('.jamtrack-preview').each (index, element) =>
# new JK.JamTrackPreview(data.app, $element, jamTrack, track, {master_shows_duration: true})
@ -188,37 +228,41 @@ context.JK.JamTrackScreen=class JamTrackScreen
trackRow.track_cnt = jamtrack.tracks.length
trackRow.tracks = []
for track in jamtrack.tracks
if track.track_type != 'Master'
trackRow.tracks.push(track)
if track.track_type=='Master'
track.instrument_desc = "Master"
else
inst = '../assets/content/icon_instrument_default24.png'
if track.instrument?
if track.instrument.id in instrument_logo_map
inst = instrument_logo_map[track.instrument.id].asset
track.instrument_desc = track.instrument.description
track.instrument_url = inst
if track.part != ''
track.instrument_desc += ' (' + track.part + ')'
trackRow.tracks.push(track)
if track.track_type=='Master'
track.instrument_desc = "Master"
else
inst = '../assets/content/icon_instrument_default24.png'
if track.instrument?
if track.instrument.id in instrument_logo_map
inst = instrument_logo_map[track.instrument.id].asset
track.instrument_desc = track.instrument.description
track.instrument_url = inst
if track.part != ''
track.instrument_desc += ' (' + track.part + ')'
options =
jamtrack: trackRow
expanded: jamtrackExpanded
expanded: false
@jamtrackItem = $(context._.template($('#template-jamtrack').html(), options, variable: 'data'))
that.renderJamtrack(@jamtrackItem, jamtrack)
this.registerEvents()
that.registerEvents(@jamtrackItem)
renderJamtrack:(jamtrackElement, jamTrack) =>
jamtrackElement.data('jamTrack', jamTrack)
jamtrackElement.data('expanded', true)
@content.append jamtrackElement
if this.expanded==jamTrack.id
for track in jamTrack.tracks
trackRow = jamtrackElement.find("[jamtrack-track-id='#{track.id}']")
previewElement = trackRow.find(".jamtrack-preview")
new JK.JamTrackPreview(@app, previewElement, jamTrack, track, {master_shows_duration: false})
#if this.expanded==jamTrack.id
for track in jamTrack.tracks
trackRow = jamtrackElement.find("[jamtrack-track-id='#{track.id}']")
previewElement = trackRow.find(".jamtrack-preview")
new JK.JamTrackPreview(@app, previewElement, jamTrack, track, {master_shows_duration: true, color:'gray'})
this.handleExpanded(jamtrackElement, false)
showJamtrackDescription:(e) =>
e.preventDefault()
@ -232,17 +276,14 @@ context.JK.JamTrackScreen=class JamTrackScreen
e.preventDefault()
jamtrackRecord = $(e.target).parents('.jamtrack-record')
jamTrackId = jamtrackRecord.attr("jamtrack-id")
if this.expanded==jamTrackId
this.expanded = null
else
this.expanded = jamTrackId
this.rerenderJamtracks()
this.handleExpanded(jamtrackRecord)
initialize:() =>
screenBindings =
'beforeShow': this.beforeShow
'afterShow': this.afterShow
@app.bindScreen 'jamtrack', screenBindings
@app.bindScreen 'jamtrackBrowse', screenBindings
@screen = $('#jamtrack-find-form')
@scroller = @screen.find('.content-body-scroller')
@content = @screen.find('.jamtrack-content')

View File

@ -344,6 +344,18 @@
return userData;
}
this.refreshUser = function() {
userDeferred = rest.getUserDetail();
if (userDeferred) {
userDeferred.done(this.updateUserCache)
}
else {
userDeferred = new $.Deferred();
userDeferred.reject('not_logged_in');
}
return userDeferred;
}
this.activeElementEvent = function(evtName, data) {
return this.layout.activeElementEvent(evtName, data);
}

View File

@ -8,45 +8,70 @@ context.JK.JamTrackLanding = class JamTrackLanding
@client = context.jamClient
@logger = context.JK.logger
@screen = null
@noFreeJamTrack = null
@freeJamTrack = null
@bandList = null
@noBandsFound = null
initialize:() =>
screenBindings =
'beforeShow': @beforeShow
'afterShow': @afterShow
@app.bindScreen('jamtrackLanding', screenBindings)
@screen = $('#jamtrackLanding')
@screen = $('#jamtrackLanding')
@noFreeJamTrack = @screen.find('.no-free-jamtrack')
@freeJamTrack = @screen.find('.free-jamtrack')
@bandList = @screen.find('#band_list')
@noBandsFound = @screen.find('#no_bands_found')
beforeShow:() =>
@noFreeJamTrack.addClass('hidden')
@freeJamTrack.addClass('hidden')
afterShow:() =>
if context.JK.currentUserId
@app.user().done(@onUser)
else
@onUser({free_jamtrack: gon.global.one_free_jamtrack_per_user})
onUser:(user) =>
if user.free_jamtrack
@freeJamTrack.removeClass('hidden')
else
@noFreeJamTrack.removeClass('hidden')
# Get artist names and build links
@rest.getJamtracks({group_artist: true})
.done(this.buildArtistLinks)
.fail(this.handleFailure)
@rest.getJamTrackArtists({group_artist: true, per_page:100})
.done(this.buildArtistLinks)
.fail(this.handleFailure)
# Bind links to action that will open the jam_tracks list view filtered to given artist_name:
# artist_name
this.bindArtistLinks()
afterShow:() =>
buildArtistLinks:(response) =>
# Get artist names and build links
jamtracks = response.jamtracks
# Get artist names and build links
@logger.debug("buildArtest links response", response)
artists = response.artists
$("#band_list>li:not('#no_bands_found')").remove()
if jamtracks.length==0
$("#no_bands_found").removeClass("hidden")
if artists.length==0
@noBandsFound.removeClass("hidden")
else
$("#no_bands_found").addClass("hidden")
@noBandsFound.addClass("hidden")
# client#/jamtrack
for jamtrack in jamtracks
artistLink = "<a href='client?artist=#{encodeURIComponent(jamtrack.original_artist)}#/jamtrack' class='artist-link' artist='#{jamtrack.original_artist}'>#{jamtrack.original_artist}</a>"
$("#band_list").append("<li>#{artistLink}</li>")
for artist in artists
artistLink = "<a href='client?artist=#{encodeURIComponent(artist.original_artist)}#/jamtrackBrowse' class='artist-link' artist='#{artist.original_artist}'>#{artist.original_artist} (#{artist.song_count})</a>"
@bandList.append("<li>#{artistLink}</li>")
# We don't want to do a full page load if this is clicked on here:
bindArtistLinks:() =>
band_list=$("ul#band_list")
that=this
band_list.on "click", "a.artist-link", (event)->
context.location="client#/jamtrack"
@bandList.on "click", "a.artist-link", (event)->
context.location="client#/jamtrackBrowse"
window.history.replaceState({}, "", this.href)
event.preventDefault()

View File

@ -78,6 +78,10 @@
audioDomElement.play();
isPlaying = true;
rest.addPlayablePlay(recordingId, 'JamRuby::Recording', claimedRecordingId, context.JK.currentUserId);
rest.getRecording({id: recordingId})
.done(function(recording) {
context.JK.GA.trackRecordingPlay(recording);
})
})
}

View File

@ -112,7 +112,8 @@
waitForBufferingTimeout = setTimeout(noBuffer, WAIT_FOR_BUFFER_TIMEOUT);
logger.debug("setting buffering timeout");
rest.addPlayablePlay(musicSessionId, 'JamRuby::MusicSession', null, context.JK.currentUserId);
context.JK.GA.trackJamTrackPlaySession(musicSessionId, false)
if(needsCanPlayGuard()) {
$audio.bind('canplay', function() {
audioDomElement.play();

View File

@ -32,8 +32,8 @@
//= require jamkazam
//= require utils
//= require ui_helper
//= require ga
//= require jam_rest
//= require ga
//= require web/signup_helper
//= require web/signin_helper
//= require web/signin

View File

@ -29,6 +29,7 @@
var $playButton = $('.play-button img.playbutton', $parentElement);
var $pauseButton = $('.play-button img.pausebutton', $parentElement);
var $stopButton = $('.stop-button img.stopbutton', $parentElement);
var $currentTime = $('.recording-current', $parentElement);
var $duration = $('.duration-time', $parentElement);
var $sliderBar = $('.recording-playback', $parentElement);
@ -42,6 +43,7 @@
var playbackDurationMs = 0;
var playbackPositionMs = 0;
var durationChanged = false;
var seenActivity = false;
var endReached = false;
var dragging = false;
@ -53,16 +55,28 @@
var playbackMonitorMode = PLAYBACK_MONITOR_MODE.MEDIA_FILE;
function startPlay() {
seenActivity = false;
updateIsPlaying(true);
if(endReached) {
update(0, playbackDurationMs, playbackPlaying);
}
$self.triggerHandler('play', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode});
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
var sessionModel = context.JK.CurrentSessionModel || null;
context.JK.GA.trackJamTrackPlaySession(sessionModel.id(), true)
}
}
function stopPlay(endReached) {
updateIsPlaying(false);
$self.triggerHandler('pause', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
$self.triggerHandler('stop', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
}
function pausePlay(endReached) {
updateIsPlaying(false);
$self.triggerHandler('pause', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
}
function updateOffsetBasedOnPosition(offsetLeft) {
@ -121,6 +135,17 @@
// return false;
//}
pausePlay();
return false;
});
$stopButton.on('click', function(e) {
var sessionModel = context.JK.CurrentSessionModel || null;
//if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
// context.JK.prodBubble($pauseButton, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $pauseButton})
// return false;
//}
stopPlay();
return false;
});
@ -182,8 +207,7 @@
var positionMs = context.jamClient.SessionCurrrentJamTrackPlayPosMs();
var duration = context.jamClient.SessionGetJamTracksPlayDurationMs();
var durationMs = duration.media_len;
var start = duration.start; // needed to understand start offset, and prevent slider from moving in tapins
//console.log("JamTrack start: " + start)
var start = duration.start; // needed to understand start offset, and prevent slider from moving in tapins
}
else {
var positionMs = context.jamClient.SessionCurrrentPlayPosMs();
@ -197,18 +221,11 @@
positionMs = 0;
}
if(playbackMonitorMode = PLAYBACK_MONITOR_MODE.JAMTRACK) {
if(isPlaying) {
$jamTrackGetReady.attr('data-current-time', positionMs)
}
else {
// this is so the jamtrack 'Get Ready!' stays hidden when it's not playing
$jamTrackGetReady.attr('data-current-time', -1)
}
if(positionMs > 0) {
seenActivity = true;
}
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.METRONOME) {
updateIsPlaying(isPlaying);
}
@ -216,6 +233,17 @@
update(positionMs, durationMs, isPlaying);
}
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
if(playbackPlaying) {
$jamTrackGetReady.attr('data-current-time', positionMs)
}
else {
// this is so the jamtrack 'Get Ready!' stays hidden when it's not playing
$jamTrackGetReady.attr('data-current-time', -1)
}
}
monitorPlaybackTimeout = setTimeout(monitorRecordingPlayback, 500);
}
@ -227,9 +255,9 @@
}
// at the end of the play, the duration sets to 0, as does currentTime. but isPlaying does not reset to
logger.debug("currentTimeMs, durationTimeMs", currentTimeMs, durationTimeMs);
if(currentTimeMs == 0 && durationTimeMs == 0) {
if(isPlaying) {
//logger.debug("currentTimeMs, durationTimeMs, mode", currentTimeMs, durationTimeMs, playbackMonitorMode);
if(currentTimeMs == 0 && seenActivity) {
if(playbackPlaying) {
isPlaying = false;
durationTimeMs = playbackDurationMs;
currentTimeMs = playbackDurationMs;
@ -293,6 +321,7 @@
$pauseButton.hide();
}
logger.debug("updating is playing: " + isPlaying)
playbackPlaying = isPlaying;
}
}

View File

@ -922,6 +922,19 @@
if(!context.JK.guardAgainstBrowser(app)) {
return false;
}
if(createSessionSettings.createType == '<%= MusicSession::CREATE_TYPE_QUICK_START %>') {
// short-cut added for private sessions; just get it going
beforeMoveStep1(); // this will populate the createSessionSettings structure
startSessionClicked(); // and this will create the session
return false;
}
}
if(optionRequiresMultiplayerProfile()) {
if(context.JK.guardAgainstSinglePlayerProfile(app).canPlay == false) {
return false;
}
}
if(optionRequiresMultiplayerProfile()) {
@ -986,7 +999,11 @@
}
function afterShow() {
context.JK.HelpBubbleHelper.jamtrackGuidePrivate($screen.find('li[create-type="quick-start"] label'), $screen.find('.content-body-scroller'))
}
function beforeHide() {
context.JK.HelpBubbleHelper.clearJamTrackGuide();
}
function toggleDate(dontRebuildDropdowns) {
@ -1293,7 +1310,7 @@
instrumentSelector = instrumentSelectorInstance;
instrumentRSVP = instrumentRSVPSelectorInstance;
var screenBindings = {'beforeShow': beforeShow, 'afterShow': afterShow};
var screenBindings = {'beforeShow': beforeShow, 'afterShow': afterShow, 'beforeHide' : beforeHide};
app.bindScreen('createSession', screenBindings);
$wizardSteps = $screen.find('.create-session-wizard');

View File

@ -13,7 +13,8 @@
var modUtils = context.JK.ModUtils;
var logger = context.JK.logger;
var self = this;
var webcamViewer = new context.JK.WebcamViewer()
var defaultParticipant = {
tracks: [{
instrument_id: "unknown"
@ -133,16 +134,19 @@
var RENDER_SESSION_DELAY = 750; // When I need to render a session, I have to wait a bit for the mixers to be there.
function beforeShow(data) {
sessionId = data.id;
if(!sessionId) {
window.location = '/client#/home';
}
promptLeave = true;
$myTracksContainer.empty();
displayDoneRecording(); // assumption is that you can't join a recording session, so this should be safe
sessionId = data.id;
if(!sessionId) {
window.location = '/client#/home';
}
promptLeave = true;
$myTracksContainer.empty();
displayDoneRecording(); // assumption is that you can't join a recording session, so this should be safe
var shareDialog = new JK.ShareDialog(context.JK.app, sessionId, "session");
shareDialog.initialize(context.JK.FacebookHelperInstance);
var shareDialog = new JK.ShareDialog(context.JK.app, sessionId, "session");
shareDialog.initialize(context.JK.FacebookHelperInstance);
if(gon.global.video_available && gon.global.video_available!="none") {
webcamViewer.beforeShow()
}
}
function beforeDisconnect() {
@ -168,10 +172,13 @@
}
}
checkForCurrentUser();
context.JK.HelpBubbleHelper.jamtrackGuideSession($screen.find('li.open-a-jamtrack'), $screen)
}
function afterShow(data) {
$fluidTracks.addClass('showing');
$openBackingTrack.removeClass('disabled');
if(!context.JK.JamServer.connected) {
@ -207,7 +214,6 @@
var singlePlayerCheckOK = true;
// to know whether we are allowed to be in this session, we have to check if we are the creator when checking against single player functionality
if(musicSession.user_id != context.JK.currentUserId) {
var canPlay = context.JK.guardAgainstSinglePlayerProfile(app, function () {
promptLeave = false;
});
@ -413,7 +419,7 @@
}
else {
displayDoneRecording();
promptUserToSave(data.recordingId);
promptUserToSave(data.recordingId, timeline);
}
})
@ -501,6 +507,13 @@
function beforeHide(data) {
context.JK.HelpBubbleHelper.clearJamTrackGuide();
if(gon.global.video_available && gon.global.video_available!="none") {
webcamViewer.setVideoOff()
}
$fluidTracks.removeClass('showing');
if(screenActive) {
// this path is possible if FTUE is invoked on session page, and they cancel
sessionModel.leaveCurrentSession()
@ -532,6 +545,7 @@
var metronomeMasterMixers = getMetronomeMasterMixers();
if (metronomeMixer == null && metronomeMasterMixers.length > 0) {
logger.debug("monitoring metronome")
playbackControls.startMonitor(context.JK.PLAYBACK_MONITOR_MODE.METRONOME)
}
else if (metronomeMixer != null && metronomeMasterMixers.length == 0) {
@ -542,10 +556,14 @@
function checkJamTrackTransition(currentSession) {
// handle jam tracks
if (jamTrack == null && (currentSession && currentSession.jam_track != null)) {
// if we have a recording open, then don't go into JamTrack monitor mode even if a JamTrack is open
if (jamTrack == null && (currentSession && currentSession.jam_track != null && currentSession.claimed_recording == null)) {
logger.debug("monitoring jamtrack")
playbackControls.startMonitor(context.JK.PLAYBACK_MONITOR_MODE.JAMTRACK);
}
else if (jamTrack && (currentSession == null || currentSession.jam_track == null)) {
else if (jamTrack && (currentSession == null || (currentSession.jam_track == null && currentSession.claimed_recording == null))) {
logger.debug("stop monitoring jamtrack")
playbackControls.stopMonitor();
}
jamTrack = currentSession == null ? null : currentSession.jam_track;
@ -554,9 +572,11 @@
function checkBackingTrackTransition(currentSession) {
// handle backing tracks
if (backing_track_path == null && (currentSession && currentSession.backing_track_path != null)) {
logger.debug("monitoring backing track")
playbackControls.startMonitor();
}
else if (backing_track_path && (currentSession == null || currentSession.backing_track_path == null)) {
logger.debug("stop monitoring backing track")
playbackControls.stopMonitor();
}
backing_track_path = currentSession == null ? null : currentSession.backing_track_path;
@ -568,9 +588,11 @@
if (claimedRecording == null && (currentSession && currentSession.claimed_recording != null)) {
// this is a 'started with a claimed_recording' transition.
// we need to start a timer to watch for the state of the play session
logger.debug("monitoring recording")
playbackControls.startMonitor();
}
else if (claimedRecording && (currentSession == null || currentSession.claimed_recording == null)) {
logger.debug("stop monitoring recording")
playbackControls.stopMonitor();
}
claimedRecording = currentSession == null ? null : currentSession.claimed_recording;
@ -709,7 +731,7 @@
function _initDialogs() {
configureTrackDialog.initialize();
addNewGearDialog.initialize();
addNewGearDialog.initialize();
}
// Get the latest list of underlying audio mixer channels, and populates:
@ -1278,13 +1300,15 @@
$('.session-recording-name').text(jamTrackName);
var noCorrespondingTracks = false;
$.each(jamTrackMixers, function(index, mixer) {
$.each(jamTracks, function(index, jamTrack) {
var mixer = null;
var preMasteredClass = "";
// find the track or tracks that correspond to the mixer
var correspondingTracks = []
$.each(jamTracks, function(i, jamTrack) {
if(mixer.id == jamTrack.id) {
$.each(jamTrackMixers, function(i, matchMixer) {
if(matchMixer.id == jamTrack.id) {
correspondingTracks.push(jamTrack);
mixer = matchMixer;
}
});
@ -1692,9 +1716,11 @@
// special case; if it's me and I have no tracks, show info about this sort of use of the app
if (myTrack && participant.tracks.length == 0) {
$tracksHolder.addClass('no-local-tracks')
$liveTracksContainer.addClass('no-local-tracks')
}
else {
$tracksHolder.removeClass('no-local-tracks')
$liveTracksContainer.removeClass('no-local-tracks')
}
// loop through all tracks for each participant
@ -2003,16 +2029,13 @@
var otherAudioWidthPct = Math.floor(100 * otherAudioWidth/totalWidth);
var liveTrackWidthPct = Math.ceil(100 * liveTrackWidth/totalWidth);
logger.debug("resizeFluid: ", minimumLiveTrackWidth, otherAudioWidth, otherAudioWidthPct, liveTrackWidthPct, liveTrackWidthPct)
//logger.debug("resizeFluid: ", minimumLiveTrackWidth, otherAudioWidth, otherAudioWidthPct, liveTrackWidthPct, liveTrackWidthPct)
$audioTracks.css('width', otherAudioWidthPct + '%');
$liveTracks.css('width', liveTrackWidthPct + '%');
}
function _addRecordingTrack(trackData, mixer, oppositeMixer) {
otherAudioFilled();
$('.session-recordings .recording-controls').show();
var parentSelector = '#session-recordedtracks-container';
@ -2472,6 +2495,18 @@
return false;
}
function sessionWebCam(e) {
e.preventDefault();
if(webcamViewer.isVideoShared()) {
$('#session-webcam').removeClass("selected")
} else {
$('#session-webcam').addClass("selected")
}
webcamViewer.toggleWebcam()
return false;
}
// http://stackoverflow.com/questions/2604450/how-to-create-a-jquery-clock-timer
function updateRecordingTimer() {
@ -2511,11 +2546,12 @@
}
function displayStartedRecording() {
// the commented out code reflects dropping the counter as your recording to save space
startTimeDate = new Date;
$recordingTimer = $("<span id='recording-timer'>(0:00)</span>");
var $recordingStatus = $('<span></span>').append("<span>Stop Recording</span>").append($recordingTimer);
//$recordingTimer = $("<span id='recording-timer'>(0:00)</span>");
var $recordingStatus = $('<span></span>').append("<span>Stop Recording</span>")//.append($recordingTimer);
$('#recording-status').html( $recordingStatus );
recordingTimerInterval = setInterval(updateRecordingTimer, 1000);
//recordingTimerInterval = setInterval(updateRecordingTimer, 1000);
}
function displayStoppingRecording(data) {
@ -2542,7 +2578,7 @@
$recordingTimer = null;
$('#recording-start-stop').removeClass('currently-recording');
$('#recording-status').text("Make a Recording");
$('#recording-status').text("Make Recording");
}
function lockControlsforJamTrackRecording() {
@ -2573,9 +2609,12 @@
}
}
function promptUserToSave(recordingId) {
function promptUserToSave(recordingId, timeline) {
rest.getRecording( {id: recordingId} )
.done(function(recording) {
if(timeline) {
recording.timeline = timeline.global
}
recordingFinishedDialog.setRecording(recording);
app.layout.showDialog('recordingFinished').one(EVENTS.DIALOG_CLOSED, function(e, data) {
if(data.result && data.result.keep){
@ -2587,7 +2626,7 @@
}
function checkPendingMetronome() {
logger.debug("checkPendingMetronome", sessionModel.isMetronomeOpen(), getMetronomeMasterMixers().length)
//logger.debug("checkPendingMetronome", sessionModel.isMetronomeOpen(), getMetronomeMasterMixers().length)
if(sessionModel.isMetronomeOpen() && getMetronomeMasterMixers().length == 0) {
var pendingMetronome = $($templatePendingMetronome.html())
@ -2695,7 +2734,7 @@
text: "Unable to open your JamTrack. Please contact support@jamkazam.com"
}, null, true);
} else {
rest.playJamTrack(jamTrack.id);
playJamTrack(jamTrack.id);
}
}
})
@ -2714,8 +2753,21 @@
return false;
}
function openBackingTrackFile(e) {
function playJamTrack(jamTrackId) {
var participantCnt=sessionModel.participants().length
rest.playJamTrack(jamTrackId)
.done(function() {
app.refreshUser();
})
context.stats.write('web.jamtrack.open', {
value: 1,
session_size: participantCnt,
user_id: context.JK.currentUserId,
user_name: context.JK.currentUserName
})
}// function
function openBackingTrackFile(e) {
// just ignore the click if they are currently recording for now
if(sessionModel.recordingModel.isRecording()) {
app.notify({
@ -2726,6 +2778,12 @@
return false;
} else {
context.jamClient.openBackingTrackFile(sessionModel.backing_track)
context.stats.write('web.backingtrack.open', {
value: 1,
session_size: participantCnt,
user_id: context.JK.currentUserId,
user_name: context.JK.currentUserName
})
//context.JK.CurrentSessionModel.refreshCurrentSession(true);
}
return false;
@ -2852,6 +2910,12 @@
}
function closeBackingTrack() {
if (sessionModel.recordingModel.isRecording()) {
logger.debug("can't close backing track while recording")
return false;
}
rest.closeBackingTrack({id: sessionModel.id()})
.done(function() {
//sessionModel.refreshCurrentSession(true);
@ -2872,9 +2936,13 @@
}
function closeJamTrack() {
logger.debug("closing recording");
if (sessionModel.recordingModel.isRecording()) {
logger.debug("can't close jamtrack while recording")
return false;
}
if(downloadJamTrack) {
logger.debug("closing DownloadJamTrack widget")
downloadJamTrack.root.remove();
@ -2942,13 +3010,31 @@
}
function onPause(e, data) {
logger.debug("calling jamClient.SessionStopPlay. endReached:", data.endReached);
logger.debug("calling jamClient.SessionPausePlay. endReached:", data.endReached);
// if a JamTrack is open, and the user hits 'pause' or 'stop', we need to automatically stop the recording
if(sessionModel.jamTracks() && sessionModel.recordingModel.isRecording()) {
startStopRecording();
}
if(!data.endReached) {
context.jamClient.SessionStopPlay();
context.jamClient.SessionPausePlay();
}
}
function onStop(e, data) {
logger.debug("calling jamClient.SessionStopPlay. endReached:", data.endReached);
// if a JamTrack is open, and the user hits 'pause' or 'stop', we need to automatically stop the recording
if(sessionModel.jamTracks() && sessionModel.recordingModel.isRecording()) {
startStopRecording();
}
if(!data.endReached) {
context.jamClient.SessionStopPlay();
}
}
function onPlay(e, data) {
logger.debug("calling jamClient.SessionStartPlay");
context.jamClient.SessionStartPlay(data.playbackMode);
@ -3045,7 +3131,6 @@
var mode = data.playbackMode; // will be either 'self' or 'cricket'
logger.debug("setting metronome playback mode: ", mode)
var isCricket = mode == 'cricket';
context.jamClient.setMetronomeCricketTestState(isCricket);
}
@ -3078,6 +3163,7 @@
function events() {
$('#session-leave').on('click', sessionLeave);
$('#session-resync').on('click', sessionResync);
$('#session-webcam').on('click', sessionWebCam);
$('#session-contents').on("click", '[action="delete"]', deleteSession);
$tracksHolder.on('click', 'div[control="mute"]', toggleMute);
$('#recording-start-stop').on('click', startStopRecording);
@ -3109,6 +3195,7 @@
$closePlaybackRecording.on('click', closeOpenMedia);
$(playbackControls)
.on('pause', onPause)
.on('stop', onStop)
.on('play', onPlay)
.on('change-position', onChangePlayPosition);
$(friendInput).focus(function() { $(this).val(''); })
@ -3161,7 +3248,10 @@
$fluidTracks = $screen.find('.session-fluidtracks');
$voiceChat = $screen.find('#voice-chat');
$tracksHolder = $screen.find('#tracks')
if(gon.global.video_available && gon.global.video_available!="none") {
webcamViewer.init($(".webcam-container"))
webcamViewer.setVideoOff()
}
events();

View File

@ -215,6 +215,7 @@
// see if we already have tracks; if so, we need to run with these
var inputTracks = context.JK.TrackHelpers.getUserTracks(context.jamClient);
logger.debug("isNoInputProfile", gearUtils.isNoInputProfile())
if(inputTracks.length > 0 || gearUtils.isNoInputProfile() ) {
logger.debug("on page enter, tracks are already available")
sessionPageEnterDeferred.resolve(inputTracks);

View File

@ -11,10 +11,11 @@
var $content = null;
function beforeShow(data) {
loadShoppingCarts();
}
function afterShow(data) {
loadShoppingCarts();
}
function afterHide() {
@ -29,18 +30,22 @@
function proceedCheckout(e) {
e.preventDefault();
logger.debug("proceedCheckout")
if (!context.JK.currentUserId) {
logger.debug("proceeding to signin screen because there is no user")
window.location = '/client#/checkoutSignin';
}
else {
app.user().done(function(user) {
if(user.has_recurly_account && user.reuse_card) {
window.location = '/client#/checkoutOrder';
}
else {
window.location = '/client#/checkoutPayment';
}
})
var user = app.currentUser();
if(user.has_recurly_account && user.reuse_card) {
logger.debug("proceeding to checkout order screen because we have card info already")
window.location = '/client#/checkoutOrder';
}
else {
logger.debug("proceeding to checkout payment screen because we do not have card info")
window.location = '/client#/checkoutPayment';
}
}
}
@ -55,6 +60,7 @@
.fail(app.ajaxError);
}
function clearContent() {
$content.empty();
}
@ -69,11 +75,24 @@
function renderShoppingCarts(carts) {
var data = {};
var latest_cart = carts[carts.length - 1];
if(carts.length > 0) {
var latest_cart = carts[0];
}
var $latestCartHtml = "";
var any_in_us = false
context._.each(carts, function(cart) {
if(cart.product_info.sales_region == 'United States') {
any_in_us = true
}
})
if (latest_cart) {
latest_cart.any_in_us = any_in_us
$latestCartHtml = $(
context._.template(
$('#template-shopping-cart-header').html(),
@ -85,9 +104,9 @@
var sub_total = 0;
$.each(carts, function(index, cart) {
sub_total += parseFloat(cart.product_info.price) * parseFloat(cart.quantity);
sub_total += parseFloat(cart.product_info.real_price);
});
data.sub_total = sub_total.toFixed(2);
data.sub_total = sub_total;
data.carts = carts;
var $cartsHtml = $(

View File

@ -8,13 +8,6 @@
if(musician) {
context.JK.Downloads.listClients(true);
}
if(registrationType) {
$(function() {
// ga() object isn't ready until the page is loaded
context.JK.GA.trackRegister(musician, registrationType);
});
}
}
context.congratulations = congratulations;

View File

@ -26,7 +26,7 @@
else {
$individualizedHeader.removeClass('hidden')
$jamtrack_name.text(jam_track.name);
$jamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrack')
$jamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrackBrowse')
}
}
@ -36,7 +36,7 @@
$previews.append($element);
new context.JK.JamTrackPreview(app, $element, jam_track, track, {master_shows_duration: false})
new context.JK.JamTrackPreview(app, $element, jam_track, track, {master_shows_duration: false, color:'black'})
})
$previews.append('<br clear = "all" />')

View File

@ -21,7 +21,7 @@
logger.debug("jam_track", jam_track)
$jamTrackBandInfo.text(jam_track.band_jam_track_count + ' ' + jam_track.original_artist);
$jamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrack')
$jamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrackBrowse')
if(jam_track.band_jam_track_count == 1) {
$jamTrackNoun.text('JamTrack')
@ -33,7 +33,7 @@
$previews.append($element);
new context.JK.JamTrackPreview(app, $element, jam_track, track, {master_shows_duration: false})
new context.JK.JamTrackPreview(app, $element, jam_track, track, {master_shows_duration: false, color:'black'})
})
$previews.append('<br clear = "all" />')

View File

@ -47,8 +47,8 @@
//= require subscription_utils
//= require ui_helper
//= require custom_controls
//= require ga
//= require jam_rest
//= require ga
//= require session_utils
//= require recording_utils
//= require helpBubbleHelper

Some files were not shown because too many files have changed in this diff Show More