merge develop
This commit is contained in:
commit
e211871168
|
|
@ -116,8 +116,8 @@ ActiveAdmin.register_page 'Feed' do
|
||||||
mixes.each do |mix|
|
mixes.each do |mix|
|
||||||
li do
|
li do
|
||||||
text_node "Created At: #{mix.created_at.strftime('%b %d %Y, %H:%M')}, "
|
text_node "Created At: #{mix.created_at.strftime('%b %d %Y, %H:%M')}, "
|
||||||
text_node "Started At: #{mix.started_at.strftime('%b %d %Y, %H:%M')}, "
|
text_node "Started At: #{mix.started_at ? mix.started_at.strftime('%b %d %Y, %H:%M') : ''}, "
|
||||||
text_node "Completed At: #{mix.completed_at.strftime('%b %d %Y, %H:%M')}, "
|
text_node "Completed At: #{mix.completed_at ? mix.completed_at.strftime('%b %d %Y, %H:%M') : ''}, "
|
||||||
text_node "Error Count: #{mix.error_count}, "
|
text_node "Error Count: #{mix.error_count}, "
|
||||||
text_node "Error Reason: #{mix.error_reason}, "
|
text_node "Error Reason: #{mix.error_reason}, "
|
||||||
text_node "Error Detail: #{mix.error_detail}, "
|
text_node "Error Detail: #{mix.error_detail}, "
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
||||||
filter :email
|
filter :email
|
||||||
filter :first_name
|
filter :first_name
|
||||||
filter :last_name
|
filter :last_name
|
||||||
filter :internet_service_provider
|
|
||||||
filter :created_at
|
filter :created_at
|
||||||
filter :updated_at
|
filter :updated_at
|
||||||
|
|
||||||
|
|
@ -28,7 +27,6 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
||||||
ff.input :musician
|
ff.input :musician
|
||||||
ff.input :can_invite
|
ff.input :can_invite
|
||||||
ff.input :photo_url
|
ff.input :photo_url
|
||||||
ff.input :internet_service_provider
|
|
||||||
ff.input :session_settings
|
ff.input :session_settings
|
||||||
end
|
end
|
||||||
ff.inputs "Signup" do
|
ff.inputs "Signup" do
|
||||||
|
|
@ -53,7 +51,6 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
||||||
row :last_name
|
row :last_name
|
||||||
row :birth_date
|
row :birth_date
|
||||||
row :gender
|
row :gender
|
||||||
row :internet_service_provider
|
|
||||||
row :email_confirmed
|
row :email_confirmed
|
||||||
row :image do user.photo_url ? image_tag(user.photo_url) : '' end
|
row :image do user.photo_url ? image_tag(user.photo_url) : '' end
|
||||||
row :session_settings
|
row :session_settings
|
||||||
|
|
@ -81,7 +78,6 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
||||||
column :last_name
|
column :last_name
|
||||||
column :birth_date
|
column :birth_date
|
||||||
column :gender
|
column :gender
|
||||||
column :internet_service_provider
|
|
||||||
column :email_confirmed
|
column :email_confirmed
|
||||||
column :photo_url
|
column :photo_url
|
||||||
column :session_settings
|
column :session_settings
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
CONFIRM_URL = "http://www.jamkazam.com/confirm" # we can't get request.host_with_port, so hard-code confirm url (with user override)
|
CONFIRM_URL = "http://www.jamkazam.com/confirm" # we can't get request.host_with_port, so hard-code confirm url (with user override)
|
||||||
|
|
||||||
attr_accessible :admin, :raw_password, :musician, :can_invite, :photo_url, :internet_service_provider, :session_settings, :confirm_url, :email_template # :invite_email
|
attr_accessible :admin, :raw_password, :musician, :can_invite, :photo_url, :session_settings, :confirm_url, :email_template # :invite_email
|
||||||
|
|
||||||
def raw_password
|
def raw_password
|
||||||
''
|
''
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,11 @@ description "jam-admin"
|
||||||
start on startup
|
start on startup
|
||||||
start on runlevel [2345]
|
start on runlevel [2345]
|
||||||
stop on runlevel [016]
|
stop on runlevel [016]
|
||||||
|
limit nofile 20000 20000
|
||||||
|
limit core unlimited unlimited
|
||||||
|
|
||||||
|
respawn
|
||||||
|
respawn limit 10 5
|
||||||
|
|
||||||
pre-start script
|
pre-start script
|
||||||
set -e
|
set -e
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ FactoryGirl.define do
|
||||||
addr 0
|
addr 0
|
||||||
locidispid 0
|
locidispid 0
|
||||||
client_type 'client'
|
client_type 'client'
|
||||||
|
sequence(:channel_id) { |n| "Channel#{n}"}
|
||||||
association :user, factory: :user
|
association :user, factory: :user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,4 +179,5 @@ sms_index.sql
|
||||||
music_sessions_description_search.sql
|
music_sessions_description_search.sql
|
||||||
rsvp_slots_prof_level.sql
|
rsvp_slots_prof_level.sql
|
||||||
add_file_name_music_notation.sql
|
add_file_name_music_notation.sql
|
||||||
change_scheduled_start_music_session.sql
|
change_scheduled_start_music_session.sql
|
||||||
|
connection_channel_id.sql
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE connections ADD COLUMN channel_id VARCHAR(256) NOT NULL;
|
||||||
|
|
@ -86,6 +86,7 @@ message ClientMessage {
|
||||||
SERVER_REJECTION_ERROR = 1005;
|
SERVER_REJECTION_ERROR = 1005;
|
||||||
SERVER_PERMISSION_ERROR = 1010;
|
SERVER_PERMISSION_ERROR = 1010;
|
||||||
SERVER_BAD_STATE_ERROR = 1015;
|
SERVER_BAD_STATE_ERROR = 1015;
|
||||||
|
SERVER_DUPLICATE_CLIENT_ERROR = 1016;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identifies which inner message is filled in
|
// Identifies which inner message is filled in
|
||||||
|
|
@ -179,6 +180,7 @@ message ClientMessage {
|
||||||
optional ServerRejectionError server_rejection_error = 1005;
|
optional ServerRejectionError server_rejection_error = 1005;
|
||||||
optional ServerPermissionError server_permission_error = 1010;
|
optional ServerPermissionError server_permission_error = 1010;
|
||||||
optional ServerBadStateError server_bad_state_error = 1015;
|
optional ServerBadStateError server_bad_state_error = 1015;
|
||||||
|
optional ServerDuplicateClientError server_duplicate_client_error = 1016;
|
||||||
}
|
}
|
||||||
|
|
||||||
// route_to: server
|
// route_to: server
|
||||||
|
|
@ -636,4 +638,9 @@ message ServerBadStateError {
|
||||||
optional string error_msg = 1;
|
optional string error_msg = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// route_to: client
|
||||||
|
// this indicates that we detected another client with the same client ID
|
||||||
|
message ServerDuplicateClientError {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
# reclaim the existing connection, if ip_address is not nil then perhaps a new address as well
|
# reclaim the existing connection, if ip_address is not nil then perhaps a new address as well
|
||||||
def reconnect(conn, reconnect_music_session_id, ip_address, connection_stale_time, connection_expire_time)
|
def reconnect(conn, channel_id, reconnect_music_session_id, ip_address, connection_stale_time, connection_expire_time)
|
||||||
music_session_id = nil
|
music_session_id = nil
|
||||||
reconnected = false
|
reconnected = false
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
sql =<<SQL
|
sql =<<SQL
|
||||||
UPDATE connections SET (aasm_state, updated_at, music_session_id, joined_session_at, stale_time, expire_time) = ('#{Connection::CONNECT_STATE.to_s}', NOW(), #{music_session_id_expression}, #{joined_session_at_expression}, #{connection_stale_time}, #{connection_expire_time})
|
UPDATE connections SET (channel_id, aasm_state, updated_at, music_session_id, joined_session_at, stale_time, expire_time) = ('#{channel_id}', '#{Connection::CONNECT_STATE.to_s}', NOW(), #{music_session_id_expression}, #{joined_session_at_expression}, #{connection_stale_time}, #{connection_expire_time})
|
||||||
WHERE
|
WHERE
|
||||||
client_id = '#{conn.client_id}'
|
client_id = '#{conn.client_id}'
|
||||||
RETURNING music_session_id
|
RETURNING music_session_id
|
||||||
|
|
@ -184,7 +184,7 @@ SQL
|
||||||
# this number is used by notification logic elsewhere to know
|
# this number is used by notification logic elsewhere to know
|
||||||
# 'oh the user joined for the 1st time, so send a friend update', or
|
# 'oh the user joined for the 1st time, so send a friend update', or
|
||||||
# 'don't bother because the user has connected somewhere else already'
|
# 'don't bother because the user has connected somewhere else already'
|
||||||
def create_connection(user_id, client_id, ip_address, client_type, connection_stale_time, connection_expire_time, &blk)
|
def create_connection(user_id, client_id, channel_id, ip_address, client_type, connection_stale_time, connection_expire_time, &blk)
|
||||||
|
|
||||||
# validate client_type
|
# validate client_type
|
||||||
raise "invalid client_type: #{client_type}" if client_type != 'client' && client_type != 'browser'
|
raise "invalid client_type: #{client_type}" if client_type != 'client' && client_type != 'browser'
|
||||||
|
|
@ -216,8 +216,8 @@ SQL
|
||||||
|
|
||||||
lock_connections(conn)
|
lock_connections(conn)
|
||||||
|
|
||||||
conn.exec("INSERT INTO connections (user_id, client_id, ip_address, client_type, addr, locidispid, aasm_state, stale_time, expire_time) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
conn.exec("INSERT INTO connections (user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, aasm_state, stale_time, expire_time) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||||
[user_id, client_id, ip_address, client_type, addr, locidispid, Connection::CONNECT_STATE.to_s, connection_stale_time, connection_expire_time]).clear
|
[user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, Connection::CONNECT_STATE.to_s, connection_stale_time, connection_expire_time]).clear
|
||||||
|
|
||||||
# we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends
|
# we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends
|
||||||
conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result|
|
conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result|
|
||||||
|
|
|
||||||
|
|
@ -222,6 +222,17 @@ module JamRuby
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# create a server bad state error
|
||||||
|
def server_duplicate_client_error
|
||||||
|
error = Jampb::ServerDuplicateClientError.new()
|
||||||
|
|
||||||
|
Jampb::ClientMessage.new(
|
||||||
|
:type => ClientMessage::Type::SERVER_DUPLICATE_CLIENT_ERROR,
|
||||||
|
:route_to => CLIENT_TARGET,
|
||||||
|
:server_duplicate_client_error => error
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
###################################### NOTIFICATIONS ######################################
|
###################################### NOTIFICATIONS ######################################
|
||||||
|
|
||||||
# create a friend update message
|
# create a friend update message
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ module JamRuby
|
||||||
# or bootstrap a new latency_tester
|
# or bootstrap a new latency_tester
|
||||||
def self.connect(options)
|
def self.connect(options)
|
||||||
client_id = options[:client_id]
|
client_id = options[:client_id]
|
||||||
|
channel_id = options[:channel_id]
|
||||||
ip_address = options[:ip_address]
|
ip_address = options[:ip_address]
|
||||||
connection_stale_time = options[:connection_stale_time]
|
connection_stale_time = options[:connection_stale_time]
|
||||||
connection_expire_time = options[:connection_expire_time]
|
connection_expire_time = options[:connection_expire_time]
|
||||||
|
|
@ -69,6 +70,7 @@ module JamRuby
|
||||||
connection.stale_time = connection_stale_time
|
connection.stale_time = connection_stale_time
|
||||||
connection.expire_time = connection_expire_time
|
connection.expire_time = connection_expire_time
|
||||||
connection.as_musician = false
|
connection.as_musician = false
|
||||||
|
connection.channel_id = channel_id
|
||||||
unless connection.save
|
unless connection.save
|
||||||
return connection
|
return connection
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,8 @@ FactoryGirl.define do
|
||||||
addr 0
|
addr 0
|
||||||
locidispid 0
|
locidispid 0
|
||||||
client_type 'client'
|
client_type 'client'
|
||||||
last_jam_audio_latency { user.last_jam_audio_latency if user }
|
last_jam_audio_latency { user.last_jam_audio_latency if user }
|
||||||
|
# sequence(:channel_id) { |n| "Channel#{n}"}
|
||||||
association :user, factory: :user
|
association :user, factory: :user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ describe ConnectionManager do
|
||||||
STALE_BUT_NOT_EXPIRED = 50
|
STALE_BUT_NOT_EXPIRED = 50
|
||||||
DEFINITELY_EXPIRED = 70
|
DEFINITELY_EXPIRED = 70
|
||||||
|
|
||||||
|
let(:channel_id) {'1'}
|
||||||
|
|
||||||
before do
|
before do
|
||||||
@conn = PG::Connection.new(:dbname => SpecDb::TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost")
|
@conn = PG::Connection.new(:dbname => SpecDb::TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost")
|
||||||
@connman = ConnectionManager.new(:conn => @conn)
|
@connman = ConnectionManager.new(:conn => @conn)
|
||||||
|
|
@ -46,8 +48,8 @@ describe ConnectionManager do
|
||||||
user.save!
|
user.save!
|
||||||
user = nil
|
user = nil
|
||||||
|
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
expect { @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) }.to raise_error(PG::Error)
|
expect { @connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) }.to raise_error(PG::Error)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "create connection then delete it" do
|
it "create connection then delete it" do
|
||||||
|
|
@ -56,7 +58,7 @@ describe ConnectionManager do
|
||||||
#user_id = create_user("test", "user2", "user2@jamkazam.com")
|
#user_id = create_user("test", "user2", "user2@jamkazam.com")
|
||||||
user = FactoryGirl.create(:user)
|
user = FactoryGirl.create(:user)
|
||||||
|
|
||||||
count = @connman.create_connection(user.id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
count = @connman.create_connection(user.id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
|
|
||||||
count.should == 1
|
count.should == 1
|
||||||
|
|
||||||
|
|
@ -86,7 +88,7 @@ describe ConnectionManager do
|
||||||
#user_id = create_user("test", "user2", "user2@jamkazam.com")
|
#user_id = create_user("test", "user2", "user2@jamkazam.com")
|
||||||
user = FactoryGirl.create(:user)
|
user = FactoryGirl.create(:user)
|
||||||
|
|
||||||
count = @connman.create_connection(user.id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
count = @connman.create_connection(user.id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
|
|
||||||
count.should == 1
|
count.should == 1
|
||||||
|
|
||||||
|
|
@ -102,7 +104,7 @@ describe ConnectionManager do
|
||||||
cc.addr.should == 0x01010101
|
cc.addr.should == 0x01010101
|
||||||
cc.locidispid.should == 17192000002
|
cc.locidispid.should == 17192000002
|
||||||
|
|
||||||
@connman.reconnect(cc, nil, "33.1.2.3", STALE_TIME, EXPIRE_TIME)
|
@connman.reconnect(cc, channel_id, nil, "33.1.2.3", STALE_TIME, EXPIRE_TIME)
|
||||||
|
|
||||||
cc = Connection.find_by_client_id!(client_id)
|
cc = Connection.find_by_client_id!(client_id)
|
||||||
cc.connected?.should be_true
|
cc.connected?.should be_true
|
||||||
|
|
@ -215,7 +217,7 @@ describe ConnectionManager do
|
||||||
it "flag stale connection" do
|
it "flag stale connection" do
|
||||||
client_id = "client_id8"
|
client_id = "client_id8"
|
||||||
user_id = create_user("test", "user8", "user8@jamkazam.com")
|
user_id = create_user("test", "user8", "user8@jamkazam.com")
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
|
|
||||||
num = JamRuby::Connection.count(:conditions => ['aasm_state = ?','connected'])
|
num = JamRuby::Connection.count(:conditions => ['aasm_state = ?','connected'])
|
||||||
num.should == 1
|
num.should == 1
|
||||||
|
|
@ -256,7 +258,7 @@ describe ConnectionManager do
|
||||||
it "expires stale connection" do
|
it "expires stale connection" do
|
||||||
client_id = "client_id8"
|
client_id = "client_id8"
|
||||||
user_id = create_user("test", "user8", "user8@jamkazam.com")
|
user_id = create_user("test", "user8", "user8@jamkazam.com")
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
|
|
||||||
conn = Connection.find_by_client_id(client_id)
|
conn = Connection.find_by_client_id(client_id)
|
||||||
set_updated_at(conn, Time.now - STALE_BUT_NOT_EXPIRED)
|
set_updated_at(conn, Time.now - STALE_BUT_NOT_EXPIRED)
|
||||||
|
|
@ -282,7 +284,7 @@ describe ConnectionManager do
|
||||||
music_session_id = music_session.id
|
music_session_id = music_session.id
|
||||||
user = User.find(user_id)
|
user = User.find(user_id)
|
||||||
|
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
||||||
|
|
||||||
connection.errors.any?.should be_false
|
connection.errors.any?.should be_false
|
||||||
|
|
@ -318,8 +320,8 @@ describe ConnectionManager do
|
||||||
client_id2 = "client_id10.12"
|
client_id2 = "client_id10.12"
|
||||||
user_id = create_user("test", "user10.11", "user10.11@jamkazam.com", :musician => true)
|
user_id = create_user("test", "user10.11", "user10.11@jamkazam.com", :musician => true)
|
||||||
user_id2 = create_user("test", "user10.12", "user10.12@jamkazam.com", :musician => false)
|
user_id2 = create_user("test", "user10.12", "user10.12@jamkazam.com", :musician => false)
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
@connman.create_connection(user_id2, client_id2, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id2, client_id2, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
|
|
||||||
music_session = FactoryGirl.create(:active_music_session, user_id: user_id)
|
music_session = FactoryGirl.create(:active_music_session, user_id: user_id)
|
||||||
music_session_id = music_session.id
|
music_session_id = music_session.id
|
||||||
|
|
@ -338,7 +340,7 @@ describe ConnectionManager do
|
||||||
client_id = "client_id10.2"
|
client_id = "client_id10.2"
|
||||||
|
|
||||||
user_id = create_user("test", "user10.2", "user10.2@jamkazam.com")
|
user_id = create_user("test", "user10.2", "user10.2@jamkazam.com")
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
|
|
||||||
music_session = FactoryGirl.create(:active_music_session, user_id: user_id)
|
music_session = FactoryGirl.create(:active_music_session, user_id: user_id)
|
||||||
user = User.find(user_id)
|
user = User.find(user_id)
|
||||||
|
|
@ -354,8 +356,8 @@ describe ConnectionManager do
|
||||||
fan_client_id = "client_id10.4"
|
fan_client_id = "client_id10.4"
|
||||||
musician_id = create_user("test", "user10.3", "user10.3@jamkazam.com")
|
musician_id = create_user("test", "user10.3", "user10.3@jamkazam.com")
|
||||||
fan_id = create_user("test", "user10.4", "user10.4@jamkazam.com", :musician => false)
|
fan_id = create_user("test", "user10.4", "user10.4@jamkazam.com", :musician => false)
|
||||||
@connman.create_connection(musician_id, musician_client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(musician_id, musician_client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
@connman.create_connection(fan_id, fan_client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(fan_id, fan_client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
|
|
||||||
music_session = FactoryGirl.create(:active_music_session, :fan_access => false, user_id: musician_id)
|
music_session = FactoryGirl.create(:active_music_session, :fan_access => false, user_id: musician_id)
|
||||||
music_session_id = music_session.id
|
music_session_id = music_session.id
|
||||||
|
|
@ -379,7 +381,7 @@ describe ConnectionManager do
|
||||||
music_session_id = music_session.id
|
music_session_id = music_session.id
|
||||||
user = User.find(user_id2)
|
user = User.find(user_id2)
|
||||||
|
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
# specify real user id, but not associated with this session
|
# specify real user id, but not associated with this session
|
||||||
expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10) } .to raise_error(ActiveRecord::RecordNotFound)
|
expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10) } .to raise_error(ActiveRecord::RecordNotFound)
|
||||||
end
|
end
|
||||||
|
|
@ -391,7 +393,7 @@ describe ConnectionManager do
|
||||||
user = User.find(user_id)
|
user = User.find(user_id)
|
||||||
music_session = ActiveMusicSession.new
|
music_session = ActiveMusicSession.new
|
||||||
|
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
||||||
connection.errors.size.should == 1
|
connection.errors.size.should == 1
|
||||||
connection.errors.get(:music_session).should == [ValidationMessages::MUSIC_SESSION_MUST_BE_SPECIFIED]
|
connection.errors.get(:music_session).should == [ValidationMessages::MUSIC_SESSION_MUST_BE_SPECIFIED]
|
||||||
|
|
@ -405,7 +407,7 @@ describe ConnectionManager do
|
||||||
music_session_id = music_session.id
|
music_session_id = music_session.id
|
||||||
user = User.find(user_id2)
|
user = User.find(user_id2)
|
||||||
|
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
# specify real user id, but not associated with this session
|
# specify real user id, but not associated with this session
|
||||||
expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10) } .to raise_error(ActiveRecord::RecordNotFound)
|
expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10) } .to raise_error(ActiveRecord::RecordNotFound)
|
||||||
end
|
end
|
||||||
|
|
@ -419,7 +421,7 @@ describe ConnectionManager do
|
||||||
user = User.find(user_id)
|
user = User.find(user_id)
|
||||||
dummy_music_session = ActiveMusicSession.new
|
dummy_music_session = ActiveMusicSession.new
|
||||||
|
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
|
|
||||||
expect { @connman.leave_music_session(user, Connection.find_by_client_id(client_id), dummy_music_session) }.to raise_error(JamRuby::StateError)
|
expect { @connman.leave_music_session(user, Connection.find_by_client_id(client_id), dummy_music_session) }.to raise_error(JamRuby::StateError)
|
||||||
end
|
end
|
||||||
|
|
@ -434,7 +436,7 @@ describe ConnectionManager do
|
||||||
|
|
||||||
dummy_music_session = ActiveMusicSession.new
|
dummy_music_session = ActiveMusicSession.new
|
||||||
|
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
@connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
@connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
||||||
expect { @connman.leave_music_session(user, Connection.find_by_client_id(client_id), dummy_music_session) }.to raise_error(JamRuby::StateError)
|
expect { @connman.leave_music_session(user, Connection.find_by_client_id(client_id), dummy_music_session) }.to raise_error(JamRuby::StateError)
|
||||||
end
|
end
|
||||||
|
|
@ -447,7 +449,7 @@ describe ConnectionManager do
|
||||||
music_session_id = music_session.id
|
music_session_id = music_session.id
|
||||||
user = User.find(user_id)
|
user = User.find(user_id)
|
||||||
|
|
||||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
@connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
@connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
||||||
|
|
||||||
assert_session_exists(music_session_id, true)
|
assert_session_exists(music_session_id, true)
|
||||||
|
|
@ -490,7 +492,7 @@ describe ConnectionManager do
|
||||||
user = User.find(user_id)
|
user = User.find(user_id)
|
||||||
|
|
||||||
client_id1 = Faker::Number.number(20)
|
client_id1 = Faker::Number.number(20)
|
||||||
@connman.create_connection(user_id, client_id1, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
@connman.create_connection(user_id, client_id1, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||||
music_session1 = FactoryGirl.create(:active_music_session, :user_id => user_id)
|
music_session1 = FactoryGirl.create(:active_music_session, :user_id => user_id)
|
||||||
connection1 = @connman.join_music_session(user, client_id1, music_session1, true, TRACKS, 10)
|
connection1 = @connman.join_music_session(user, client_id1, music_session1, true, TRACKS, 10)
|
||||||
connection1.errors.size.should == 0
|
connection1.errors.size.should == 0
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ require 'spec_helper'
|
||||||
|
|
||||||
describe LatencyTester do
|
describe LatencyTester do
|
||||||
|
|
||||||
let(:params) {{client_id: 'abc', ip_address: '10.1.1.1', connection_stale_time:40, connection_expire_time:60} }
|
let(:params) {{client_id: 'abc', ip_address: '10.1.1.1', connection_stale_time:40, connection_expire_time:60, channel_id: '1'} }
|
||||||
|
|
||||||
it "success" do
|
it "success" do
|
||||||
latency_tester = FactoryGirl.create(:latency_tester)
|
latency_tester = FactoryGirl.create(:latency_tester)
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 206 B |
|
|
@ -75,7 +75,8 @@
|
||||||
SERVER_BAD_STATE_RECOVERED: "SERVER_BAD_STATE_RECOVERED",
|
SERVER_BAD_STATE_RECOVERED: "SERVER_BAD_STATE_RECOVERED",
|
||||||
SERVER_GENERIC_ERROR : "SERVER_GENERIC_ERROR",
|
SERVER_GENERIC_ERROR : "SERVER_GENERIC_ERROR",
|
||||||
SERVER_REJECTION_ERROR : "SERVER_REJECTION_ERROR",
|
SERVER_REJECTION_ERROR : "SERVER_REJECTION_ERROR",
|
||||||
SERVER_BAD_STATE_ERROR : "SERVER_BAD_STATE_ERROR"
|
SERVER_BAD_STATE_ERROR : "SERVER_BAD_STATE_ERROR",
|
||||||
|
SERVER_DUPLICATE_CLIENT_ERROR : "SERVER_DUPLICATE_CLIENT_ERROR"
|
||||||
};
|
};
|
||||||
|
|
||||||
var route_to = context.JK.RouteToPrefix = {
|
var route_to = context.JK.RouteToPrefix = {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
var notificationLastSeenAt = undefined;
|
var notificationLastSeenAt = undefined;
|
||||||
var notificationLastSeen = undefined;
|
var notificationLastSeen = undefined;
|
||||||
var clientClosedConnection = false;
|
var clientClosedConnection = false;
|
||||||
|
var initialConnectAttempt = true;
|
||||||
|
|
||||||
// reconnection logic
|
// reconnection logic
|
||||||
var connectDeferred = null;
|
var connectDeferred = null;
|
||||||
|
|
@ -85,6 +86,7 @@
|
||||||
|
|
||||||
// handles logic if the websocket connection closes, and if it was in error then also prompt for reconnect
|
// handles logic if the websocket connection closes, and if it was in error then also prompt for reconnect
|
||||||
function closedCleanup(in_error) {
|
function closedCleanup(in_error) {
|
||||||
|
server.connected = false;
|
||||||
|
|
||||||
// stop future heartbeats
|
// stop future heartbeats
|
||||||
if (heartbeatInterval != null) {
|
if (heartbeatInterval != null) {
|
||||||
|
|
@ -98,15 +100,16 @@
|
||||||
heartbeatAckCheckInterval = null;
|
heartbeatAckCheckInterval = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server.connected) {
|
clearConnectTimeout();
|
||||||
server.connected = false;
|
|
||||||
if (app.clientUpdating) {
|
|
||||||
// we don't want to do a 'cover the whole screen' dialog
|
|
||||||
// because the client update is already showing.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
server.reconnecting = true;
|
// noReconnect is a global to suppress reconnect behavior, so check it first
|
||||||
|
|
||||||
|
// we don't show any reconnect dialog on the initial connect; so we have this one-time flag
|
||||||
|
// to cause reconnects in the case that the websocket is down on the initially connect
|
||||||
|
if (!server.noReconnect &&
|
||||||
|
(initialConnectAttempt || !server.connecting)) {
|
||||||
|
server.connecting = true;
|
||||||
|
initialConnectAttempt = false;
|
||||||
|
|
||||||
var result = activeElementEvent('beforeDisconnect');
|
var result = activeElementEvent('beforeDisconnect');
|
||||||
|
|
||||||
|
|
@ -174,12 +177,16 @@
|
||||||
return mode == "client";
|
return mode == "client";
|
||||||
}
|
}
|
||||||
|
|
||||||
function loggedIn(header, payload) {
|
function clearConnectTimeout() {
|
||||||
|
if (connectTimeout) {
|
||||||
if (!connectTimeout) {
|
|
||||||
clearTimeout(connectTimeout);
|
clearTimeout(connectTimeout);
|
||||||
connectTimeout = null;
|
connectTimeout = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loggedIn(header, payload) {
|
||||||
|
|
||||||
|
clearConnectTimeout();
|
||||||
|
|
||||||
heartbeatStateReset();
|
heartbeatStateReset();
|
||||||
|
|
||||||
|
|
@ -191,6 +198,13 @@
|
||||||
$.cookie('client_id', payload.client_id);
|
$.cookie('client_id', payload.client_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this has to be after context.jamclient.OnLoggedIn, because it hangs in scenarios
|
||||||
|
// where there is no device on startup for the current profile.
|
||||||
|
// So, in that case, it's possible that a reconnect loop will attempt, but we *do not want*
|
||||||
|
// it to go through unless we've passed through .OnLoggedIn
|
||||||
|
server.connecting = false;
|
||||||
|
initialConnectAttempt = false;
|
||||||
|
|
||||||
heartbeatMS = payload.heartbeat_interval * 1000;
|
heartbeatMS = payload.heartbeat_interval * 1000;
|
||||||
connection_expire_time = payload.connection_expire_time * 1000;
|
connection_expire_time = payload.connection_expire_time * 1000;
|
||||||
logger.info("jamkazam.js.loggedIn(): clientId=" + app.clientId + ", heartbeat=" + payload.heartbeat_interval + "s, expire_time=" + payload.connection_expire_time + 's');
|
logger.info("jamkazam.js.loggedIn(): clientId=" + app.clientId + ", heartbeat=" + payload.heartbeat_interval + "s, expire_time=" + payload.connection_expire_time + 's');
|
||||||
|
|
@ -240,7 +254,7 @@
|
||||||
var start = new Date().getTime();
|
var start = new Date().getTime();
|
||||||
server.connect()
|
server.connect()
|
||||||
.done(function () {
|
.done(function () {
|
||||||
guardAgainstRapidTransition(start, performReconnect);
|
guardAgainstRapidTransition(start, finishReconnect);
|
||||||
})
|
})
|
||||||
.fail(function () {
|
.fail(function () {
|
||||||
guardAgainstRapidTransition(start, closedOnReconnectAttempt);
|
guardAgainstRapidTransition(start, closedOnReconnectAttempt);
|
||||||
|
|
@ -252,7 +266,7 @@
|
||||||
failedReconnect();
|
failedReconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
function performReconnect() {
|
function finishReconnect() {
|
||||||
|
|
||||||
if(!clientClosedConnection) {
|
if(!clientClosedConnection) {
|
||||||
lastDisconnectedReason = 'WEBSOCKET_CLOSED_REMOTELY'
|
lastDisconnectedReason = 'WEBSOCKET_CLOSED_REMOTELY'
|
||||||
|
|
@ -277,7 +291,6 @@
|
||||||
else {
|
else {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
server.reconnecting = false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -459,16 +472,29 @@
|
||||||
connectDeferred = new $.Deferred();
|
connectDeferred = new $.Deferred();
|
||||||
channelId = context.JK.generateUUID(); // create a new channel ID for every websocket connection
|
channelId = context.JK.generateUUID(); // create a new channel ID for every websocket connection
|
||||||
|
|
||||||
var uri = context.gon.websocket_gateway_uri + '?channel_id=' + channelId; // Set in index.html.erb.
|
// we will log in one of 3 ways:
|
||||||
|
// browser: use session cookie, and auth with token
|
||||||
|
// native: use session cookie, and use the token
|
||||||
|
// latency_tester: ask for client ID from backend; no token (trusted)
|
||||||
|
var params = {
|
||||||
|
channel_id: channelId,
|
||||||
|
token: $.cookie("remember_token"),
|
||||||
|
client_type: isClientMode() ? context.JK.clientType : 'latency_tester',
|
||||||
|
client_id: isClientMode() ? (gon.global.env == "development" ? $.cookie('client_id') : null): context.jamClient.clientID
|
||||||
|
}
|
||||||
|
|
||||||
|
var uri = context.gon.websocket_gateway_uri + '?' + $.param(params); // Set in index.html.erb.
|
||||||
|
|
||||||
logger.debug("connecting websocket: " + uri);
|
logger.debug("connecting websocket: " + uri);
|
||||||
|
|
||||||
|
server.connecting = true;
|
||||||
server.socket = new context.WebSocket(uri);
|
server.socket = new context.WebSocket(uri);
|
||||||
server.socket.onopen = server.onOpen;
|
server.socket.onopen = server.onOpen;
|
||||||
server.socket.onmessage = server.onMessage;
|
server.socket.onmessage = server.onMessage;
|
||||||
server.socket.onclose = server.onClose;
|
server.socket.onclose = server.onClose;
|
||||||
|
|
||||||
connectTimeout = setTimeout(function () {
|
connectTimeout = setTimeout(function () {
|
||||||
|
logger.debug("connection timeout fired")
|
||||||
connectTimeout = null;
|
connectTimeout = null;
|
||||||
|
|
||||||
if(connectDeferred.state() === 'pending') {
|
if(connectDeferred.state() === 'pending') {
|
||||||
|
|
@ -505,12 +531,7 @@
|
||||||
server.onOpen = function () {
|
server.onOpen = function () {
|
||||||
logger.debug("server.onOpen");
|
logger.debug("server.onOpen");
|
||||||
|
|
||||||
if(isClientMode()) {
|
// we should receive LOGIN_ACK very soon. we already set a timer elsewhere to give 4 seconds to receive it
|
||||||
server.rememberLogin();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
server.latencyTesterLogin();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
server.onMessage = function (e) {
|
server.onMessage = function (e) {
|
||||||
|
|
@ -675,7 +696,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initialize = initialize;
|
this.initialize = initialize;
|
||||||
this.initiateReconnect = initiateReconnect;
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,11 @@
|
||||||
//= require jquery.pulse
|
//= require jquery.pulse
|
||||||
//= require jquery.browser
|
//= require jquery.browser
|
||||||
//= require jquery.custom-protocol
|
//= require jquery.custom-protocol
|
||||||
|
//= require AAC_underscore
|
||||||
//= require AAA_Log
|
//= require AAA_Log
|
||||||
//= require globals
|
//= require globals
|
||||||
//= require AAB_message_factory
|
//= require AAB_message_factory
|
||||||
//= require jam_rest
|
//= require jam_rest
|
||||||
//= require AAC_underscore
|
|
||||||
//= require utils
|
//= require utils
|
||||||
//= require custom_controls
|
//= require custom_controls
|
||||||
//= require_directory .
|
//= require_directory .
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
(function(context,$) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
context.JK = context.JK || {};
|
||||||
|
|
||||||
|
// Class to intercept and delegate out all backend alerts as necessary
|
||||||
|
// better if modules that needed certain events would just register for them.
|
||||||
|
context.JK.BackendAlerts = function(app) {
|
||||||
|
|
||||||
|
var $document = $(document);
|
||||||
|
|
||||||
|
var ALERT_TYPES = context.JK.ALERT_TYPES;
|
||||||
|
|
||||||
|
function onNoValidAudioConfig(type, text) {
|
||||||
|
app.notify({
|
||||||
|
"title": ALERT_TYPES[type].title,
|
||||||
|
"text": text,
|
||||||
|
"icon_url": "/assets/content/icon_alert_big.png"
|
||||||
|
});
|
||||||
|
context.location = "/client#"; // leaveSession will be called in beforeHide below
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStunEvent() {
|
||||||
|
var testResults = context.jamClient.NetworkTestResult();
|
||||||
|
|
||||||
|
$.each(testResults, function (index, val) {
|
||||||
|
if (val.bStunFailed) {
|
||||||
|
// if true we could not reach a stun server
|
||||||
|
}
|
||||||
|
else if (val.bRemoteUdpBocked) {
|
||||||
|
// if true the user cannot communicate with peer via UDP, although they could do LAN based session
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onGenericEvent(type, text) {
|
||||||
|
context.setTimeout(function() {
|
||||||
|
var alert = ALERT_TYPES[type];
|
||||||
|
|
||||||
|
if(alert && alert.title) {
|
||||||
|
app.notify({
|
||||||
|
"title": ALERT_TYPES[type].title,
|
||||||
|
"text": text,
|
||||||
|
"icon_url": "/assets/content/icon_alert_big.png"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug("Unknown Backend Event type %o, data %o", type, text)
|
||||||
|
}
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function alertCallback(type, text) {
|
||||||
|
|
||||||
|
function timeCallback() {
|
||||||
|
var start = new Date();
|
||||||
|
setTimeout(function() {
|
||||||
|
var timed = new Date().getTime() - start.getTime();
|
||||||
|
if(timed > 250) {
|
||||||
|
logger.warn("SLOW AlERT_CALLBACK. type: %o text: %o time: %o", type, text, timed);
|
||||||
|
}
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
timeCallback();
|
||||||
|
|
||||||
|
logger.debug("alert callback", type, text);
|
||||||
|
|
||||||
|
var alertData = ALERT_TYPES[type];
|
||||||
|
|
||||||
|
if(alertData) {
|
||||||
|
$document.triggerHandler(alertData.name, alertData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 2) { // BACKEND_MIXER_CHANGE
|
||||||
|
context.JK.CurrentSessionModel.onBackendMixerChanged(type, text)
|
||||||
|
}
|
||||||
|
else if (type === 19) { // NO_VALID_AUDIO_CONFIG
|
||||||
|
onNoValidAudioConfig(type, text);
|
||||||
|
}
|
||||||
|
else if (type === 24) {
|
||||||
|
onStunEvent();
|
||||||
|
}
|
||||||
|
else if (type === 26) { // DEAD_USER_REMOVE_EVENT
|
||||||
|
context.JK.CurrentSessionModel.onDeadUserRemove(type, text);
|
||||||
|
}
|
||||||
|
else if (type === 27) { // WINDOW_CLOSE_BACKGROUND_MODE
|
||||||
|
context.JK.CurrentSessionModel.onWindowBackgrounded(type, text);
|
||||||
|
}
|
||||||
|
else if(type != 30 && type != 31 && type != 21){ // these are handled elsewhere
|
||||||
|
onGenericEvent(type, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
context.jamClient.SessionSetAlertCallback("JK.AlertCallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initialize = initialize;
|
||||||
|
|
||||||
|
|
||||||
|
context.JK.AlertCallback = alertCallback;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
})(window, jQuery);
|
||||||
|
|
@ -10,9 +10,17 @@
|
||||||
var logger = context.JK.logger;
|
var logger = context.JK.logger;
|
||||||
var $banner = null;
|
var $banner = null;
|
||||||
|
|
||||||
|
// you can also do
|
||||||
|
// * showAlert('title', 'text')
|
||||||
|
// * showAlert('text')
|
||||||
function showAlert(options) {
|
function showAlert(options) {
|
||||||
if (typeof options == 'string' || options instanceof String) {
|
if (typeof options == 'string' || options instanceof String) {
|
||||||
options = {html:options};
|
if(arguments.length == 2) {
|
||||||
|
options = {title: options, html:arguments[1]}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options = {html:options};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
options.type = 'alert'
|
options.type = 'alert'
|
||||||
return show(options);
|
return show(options);
|
||||||
|
|
@ -24,6 +32,13 @@
|
||||||
var text = options.text;
|
var text = options.text;
|
||||||
var html = options.html;
|
var html = options.html;
|
||||||
|
|
||||||
|
if(!options.title) {
|
||||||
|
options.title = 'alert'
|
||||||
|
}
|
||||||
|
|
||||||
|
var $h1 = $banner.find('h1');
|
||||||
|
$h1.html(options.title);
|
||||||
|
|
||||||
var newContent = null;
|
var newContent = null;
|
||||||
if (html) {
|
if (html) {
|
||||||
newContent = $('#banner .dialog-inner').html(html);
|
newContent = $('#banner .dialog-inner').html(html);
|
||||||
|
|
|
||||||
|
|
@ -274,7 +274,6 @@
|
||||||
if(response.next == null) {
|
if(response.next == null) {
|
||||||
// if we less results than asked for, end searching
|
// if we less results than asked for, end searching
|
||||||
$chatMessagesScroller.infinitescroll('pause');
|
$chatMessagesScroller.infinitescroll('pause');
|
||||||
logger.debug("end of chatss");
|
|
||||||
|
|
||||||
if(currentPage > 0) {
|
if(currentPage > 0) {
|
||||||
// there are bugs with infinitescroll not removing the 'loading'.
|
// there are bugs with infinitescroll not removing the 'loading'.
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateVoiceChatSettings() {
|
function validateVoiceChatSettings() {
|
||||||
return voiceChatHelper.trySave();
|
//return voiceChatHelper.trySave(); // not necessary since we use saveImmediate now
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMusicAudioPanel() {
|
function showMusicAudioPanel() {
|
||||||
|
|
@ -117,7 +118,9 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
$btnCancel.click(function() {
|
$btnCancel.click(function() {
|
||||||
app.layout.closeDialog('configure-tracks')
|
if(voiceChatHelper.cancel()) {
|
||||||
|
app.layout.closeDialog('configure-tracks')
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -185,10 +188,12 @@
|
||||||
|
|
||||||
configureTracksHelper.reset();
|
configureTracksHelper.reset();
|
||||||
voiceChatHelper.reset();
|
voiceChatHelper.reset();
|
||||||
|
voiceChatHelper.beforeShow();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterHide() {
|
function afterHide() {
|
||||||
|
voiceChatHelper.beforeHide();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,11 +217,11 @@
|
||||||
$btnAddNewGear = $dialog.find('.btn-add-new-audio-gear');
|
$btnAddNewGear = $dialog.find('.btn-add-new-audio-gear');
|
||||||
$btnUpdateTrackSettings = $dialog.find('.btn-update-settings');
|
$btnUpdateTrackSettings = $dialog.find('.btn-update-settings');
|
||||||
|
|
||||||
configureTracksHelper = new JK.ConfigureTracksHelper(app);
|
configureTracksHelper = new context.JK.ConfigureTracksHelper(app);
|
||||||
configureTracksHelper.initialize($dialog);
|
configureTracksHelper.initialize($dialog);
|
||||||
|
|
||||||
voiceChatHelper = new JK.VoiceChatHelper(app);
|
voiceChatHelper = new context.JK.VoiceChatHelper(app);
|
||||||
voiceChatHelper.initialize($dialog, false);
|
voiceChatHelper.initialize($dialog, 'configure_track_dialog', true, {vuType: "vertical", lightCount: 10, lightWidth: 3, lightHeight: 17}, 191);
|
||||||
|
|
||||||
events();
|
events();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,25 +22,30 @@
|
||||||
var $instrumentsHolder = null;
|
var $instrumentsHolder = null;
|
||||||
var isDragging = false;
|
var isDragging = false;
|
||||||
|
|
||||||
|
function removeHoverer($hoverChannel) {
|
||||||
|
var $channel = $hoverChannel.data('original')
|
||||||
|
$channel.data('cloned', null);
|
||||||
|
$hoverChannel.remove();
|
||||||
|
}
|
||||||
|
|
||||||
function hoverIn($channel) {
|
function hoverIn($channel) {
|
||||||
if(isDragging) return;
|
if(isDragging) return;
|
||||||
|
|
||||||
$channel.css('color', 'white')
|
|
||||||
|
|
||||||
var $container = $channel.closest('.target');
|
var $container = $channel.closest('.target');
|
||||||
var inTarget = $container.length > 0;
|
var inTarget = $container.length > 0;
|
||||||
if(!inTarget) {
|
if(!inTarget) {
|
||||||
$container = $channel.closest('.channels-holder')
|
$container = $channel.closest('.channels-holder')
|
||||||
}
|
}
|
||||||
|
|
||||||
$channel.data('container', $container)
|
|
||||||
$channel.addClass('hovering');
|
|
||||||
var $inputs = $container.find('.ftue-input');
|
var $inputs = $container.find('.ftue-input');
|
||||||
|
|
||||||
var index = $inputs.index($channel);
|
var index = $inputs.index($channel);
|
||||||
$channel.css('background-color', '#333');
|
|
||||||
// $channel.css('padding', '0 5px');
|
// $channel.css('padding', '0 5px');
|
||||||
if(inTarget) {
|
if(inTarget) {
|
||||||
|
$channel.data('container', $container)
|
||||||
|
$channel.addClass('hovering');
|
||||||
|
$channel.css('color', 'white')
|
||||||
|
$channel.css('background-color', '#333');
|
||||||
$channel.css('border', '#333');
|
$channel.css('border', '#333');
|
||||||
$channel.css('border-radius', '2px');
|
$channel.css('border-radius', '2px');
|
||||||
$channel.css('min-width', '49%');
|
$channel.css('min-width', '49%');
|
||||||
|
|
@ -49,10 +54,44 @@
|
||||||
$container.css('overflow', 'visible');
|
$container.css('overflow', 'visible');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// TODO: make the unassigned work
|
var $offsetParent = $channel.offsetParent();
|
||||||
// $channel.css('min-width', $channel.css('width'));
|
var parentOffset = $offsetParent.offset();
|
||||||
// $channel.css('position', 'absolute');
|
|
||||||
// $container.addClass('compensate');
|
var hoverChannel = $(context._.template($templateAssignablePort.html(), {id: 'bogus', name: $channel.text()}, { variable: 'data' }));
|
||||||
|
hoverChannel
|
||||||
|
.css('position', 'absolute')
|
||||||
|
.css('color', 'white')
|
||||||
|
.css('left', $channel.position().left)
|
||||||
|
.css('top', $channel.position().top)
|
||||||
|
.css('background-color', '#333')
|
||||||
|
.css('min-width', $channel.width())
|
||||||
|
.css('min-height', $channel.height())
|
||||||
|
.css('z-index', 10000)
|
||||||
|
.css('background-position', '4px 6px')
|
||||||
|
.css('padding-left', '14px')
|
||||||
|
.data('original', $channel);
|
||||||
|
|
||||||
|
$channel.data('cloned', hoverChannel);
|
||||||
|
hoverChannel
|
||||||
|
.hover(function(e) {
|
||||||
|
var hoverCheckTimeout = hoverChannel.data('hoverCheckTimeout');
|
||||||
|
if(hoverCheckTimeout) {
|
||||||
|
clearTimeout(hoverCheckTimeout);
|
||||||
|
hoverChannel.data('hoverCheckTimeout', null);
|
||||||
|
}
|
||||||
|
}, function() { removeHoverer($(this)); })
|
||||||
|
.mousedown(function(e) {
|
||||||
|
// because we have obscured the element the user wants to drag,
|
||||||
|
// we proxy a mousedown on the hover-element to the covered .ftue-input ($channel).
|
||||||
|
// this causes jquery.drag to get going even though the user clicked a different element
|
||||||
|
$channel.trigger(e)
|
||||||
|
})
|
||||||
|
hoverChannel.data('hoverCheckTimeout', setTimeout(function() {
|
||||||
|
// check if element has already been left
|
||||||
|
hoverChannel.data('hoverCheckTimeout', null);
|
||||||
|
removeHoverer(hoverChannel);
|
||||||
|
}, 500));
|
||||||
|
hoverChannel.prependTo($offsetParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
$channel.css('z-index', 10000)
|
$channel.css('z-index', 10000)
|
||||||
|
|
@ -69,6 +108,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function hoverOut($channel) {
|
function hoverOut($channel) {
|
||||||
|
|
||||||
|
var $cloned = $channel.data('cloned');
|
||||||
|
if($cloned) {
|
||||||
|
return; // let the cloned handle the rest of hover out logic when it's hovered-out
|
||||||
|
}
|
||||||
|
|
||||||
$channel
|
$channel
|
||||||
.removeClass('hovering')
|
.removeClass('hovering')
|
||||||
.css('color', '')
|
.css('color', '')
|
||||||
|
|
@ -77,6 +122,8 @@
|
||||||
.css('width', '')
|
.css('width', '')
|
||||||
.css('background-color', '')
|
.css('background-color', '')
|
||||||
.css('padding', '')
|
.css('padding', '')
|
||||||
|
.css('padding-left', '')
|
||||||
|
.css('background-position', '')
|
||||||
.css('border', '')
|
.css('border', '')
|
||||||
.css('border-radius', '')
|
.css('border-radius', '')
|
||||||
.css('right', '')
|
.css('right', '')
|
||||||
|
|
@ -88,9 +135,9 @@
|
||||||
|
|
||||||
//var $container = $channel.closest('.target');
|
//var $container = $channel.closest('.target');
|
||||||
var $container = $channel.data('container');
|
var $container = $channel.data('container');
|
||||||
$container.css('overflow', '')
|
if($container) {
|
||||||
$container.removeClass('compensate');
|
$container.css('overflow', '')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fixClone($clone) {
|
function fixClone($clone) {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
var $draggingFader = null;
|
var $draggingFader = null;
|
||||||
var draggingOrientation = null;
|
var draggingOrientation = null;
|
||||||
|
|
||||||
var subscribers = {};
|
|
||||||
var logger = g.JK.logger;
|
var logger = g.JK.logger;
|
||||||
|
|
||||||
function faderClick(e) {
|
function faderClick(e) {
|
||||||
|
|
@ -21,7 +20,6 @@
|
||||||
|
|
||||||
var $fader = $(this);
|
var $fader = $(this);
|
||||||
draggingOrientation = $fader.attr('orientation');
|
draggingOrientation = $fader.attr('orientation');
|
||||||
var faderId = $fader.attr("fader-id");
|
|
||||||
var offset = $fader.offset();
|
var offset = $fader.offset();
|
||||||
var position = { top: e.pageY - offset.top, left: e.pageX - offset.left}
|
var position = { top: e.pageY - offset.top, left: e.pageX - offset.left}
|
||||||
|
|
||||||
|
|
@ -31,12 +29,7 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify subscribers of value change
|
$fader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false})
|
||||||
g._.each(subscribers, function (changeFunc, index, list) {
|
|
||||||
if (faderId === index) {
|
|
||||||
changeFunc(faderId, faderPct, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setHandlePosition($fader, faderPct);
|
setHandlePosition($fader, faderPct);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -110,7 +103,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFaderDrag(e, ui) {
|
function onFaderDrag(e, ui) {
|
||||||
var faderId = $draggingFader.attr("fader-id");
|
|
||||||
var faderPct = faderValue($draggingFader, e, ui.position);
|
var faderPct = faderValue($draggingFader, e, ui.position);
|
||||||
|
|
||||||
// protect against attempts to drag outside of the slider, which jquery.draggable sometimes allows
|
// protect against attempts to drag outside of the slider, which jquery.draggable sometimes allows
|
||||||
|
|
@ -118,12 +110,7 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify subscribers of value change
|
$draggingFader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: true})
|
||||||
g._.each(subscribers, function (changeFunc, index, list) {
|
|
||||||
if (faderId === index) {
|
|
||||||
changeFunc(faderId, faderPct, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFaderDragStart(e, ui) {
|
function onFaderDragStart(e, ui) {
|
||||||
|
|
@ -133,7 +120,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFaderDragStop(e, ui) {
|
function onFaderDragStop(e, ui) {
|
||||||
var faderId = $draggingFader.attr("fader-id");
|
|
||||||
var faderPct = faderValue($draggingFader, e, ui.position);
|
var faderPct = faderValue($draggingFader, e, ui.position);
|
||||||
|
|
||||||
// protect against attempts to drag outside of the slider, which jquery.draggable sometimes allows
|
// protect against attempts to drag outside of the slider, which jquery.draggable sometimes allows
|
||||||
|
|
@ -142,12 +128,8 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify subscribers of value change
|
$draggingFader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false});
|
||||||
g._.each(subscribers, function (changeFunc, index, list) {
|
|
||||||
if (faderId === index) {
|
|
||||||
changeFunc(faderId, faderPct, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$draggingFaderHandle = null;
|
$draggingFaderHandle = null;
|
||||||
$draggingFader = null;
|
$draggingFader = null;
|
||||||
draggingOrientation = null;
|
draggingOrientation = null;
|
||||||
|
|
@ -159,26 +141,15 @@
|
||||||
|
|
||||||
g.JK.FaderHelpers = {
|
g.JK.FaderHelpers = {
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribe to fader change events. Provide a subscriber id
|
|
||||||
* and a function in the form: change(faderId, newValue) which
|
|
||||||
* will be called anytime a fader changes value.
|
|
||||||
*/
|
|
||||||
subscribe: function (subscriberId, changeFunction) {
|
|
||||||
subscribers[subscriberId] = changeFunction;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a fader into the element identifed by the provided
|
* Render a fader into the element identifed by the provided
|
||||||
* selector, with the provided options.
|
* selector, with the provided options.
|
||||||
*/
|
*/
|
||||||
renderFader: function (selector, userOptions) {
|
renderFader: function (selector, userOptions) {
|
||||||
|
selector = $(selector);
|
||||||
if (userOptions === undefined) {
|
if (userOptions === undefined) {
|
||||||
throw ("renderFader: userOptions is required");
|
throw ("renderFader: userOptions is required");
|
||||||
}
|
}
|
||||||
if (!(userOptions.hasOwnProperty("faderId"))) {
|
|
||||||
throw ("renderFader: userOptions.faderId is required");
|
|
||||||
}
|
|
||||||
var renderDefaults = {
|
var renderDefaults = {
|
||||||
faderType: "vertical",
|
faderType: "vertical",
|
||||||
height: 83, // only used for vertical
|
height: 83, // only used for vertical
|
||||||
|
|
@ -189,9 +160,9 @@
|
||||||
"#template-fader-h" : '#template-fader-v';
|
"#template-fader-h" : '#template-fader-v';
|
||||||
var templateSource = $(templateSelector).html();
|
var templateSource = $(templateSelector).html();
|
||||||
|
|
||||||
$(selector).html(g._.template(templateSource, options));
|
selector.html(g._.template(templateSource, options));
|
||||||
|
|
||||||
$('div[control="fader-handle"]', $(selector)).draggable({
|
selector.find('div[control="fader-handle"]').draggable({
|
||||||
drag: onFaderDrag,
|
drag: onFaderDrag,
|
||||||
start: onFaderDragStart,
|
start: onFaderDragStart,
|
||||||
stop: onFaderDragStop,
|
stop: onFaderDragStop,
|
||||||
|
|
@ -202,7 +173,7 @@
|
||||||
// Embed any custom styles, applied to the .fader below selector
|
// Embed any custom styles, applied to the .fader below selector
|
||||||
if ("style" in options) {
|
if ("style" in options) {
|
||||||
for (var key in options.style) {
|
for (var key in options.style) {
|
||||||
$(selector + ' .fader').css(key, options.style[key]);
|
selector.find(' .fader').css(key, options.style[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -110,11 +110,8 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function faderChange(faderId, newValue, dragging) {
|
function faderChange(e, data) {
|
||||||
var setFunction = faderMap[faderId];
|
|
||||||
// TODO - using hardcoded range of -80 to 20 for output levels.
|
|
||||||
var mixerLevel = newValue - 80; // Convert our [0-100] to [-80 - +20] range
|
|
||||||
setFunction(mixerLevel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSaveButtonState($save, enabled) {
|
function setSaveButtonState($save, enabled) {
|
||||||
|
|
@ -1043,7 +1040,7 @@
|
||||||
context.JK.ftueVUCallback = function (dbValue, selector) {
|
context.JK.ftueVUCallback = function (dbValue, selector) {
|
||||||
// Convert DB into a value from 0.0 - 1.0
|
// Convert DB into a value from 0.0 - 1.0
|
||||||
var floatValue = (dbValue + 80) / 100;
|
var floatValue = (dbValue + 80) / 100;
|
||||||
context.JK.VuHelpers.updateVU(selector, floatValue);
|
context.JK.VuHelpers.updateVU($(selector), floatValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
context.JK.ftueAudioInputVUCallback = function (dbValue) {
|
context.JK.ftueAudioInputVUCallback = function (dbValue) {
|
||||||
|
|
|
||||||
|
|
@ -85,10 +85,33 @@
|
||||||
google: 'google',
|
google: 'google',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var audioTestFailReasons = {
|
||||||
|
latency : 'Latency',
|
||||||
|
ioVariance : 'ioVariance',
|
||||||
|
ioTarget : 'ioTarget'
|
||||||
|
}
|
||||||
|
|
||||||
|
var audioTestDataReasons = {
|
||||||
|
pass : 'Pass',
|
||||||
|
latencyFail : 'LatencyFail',
|
||||||
|
ioVarianceFail : 'ioVarianceFail',
|
||||||
|
ioTargetFail : 'ioTargetFail'
|
||||||
|
}
|
||||||
|
|
||||||
|
var networkTestFailReasons = {
|
||||||
|
stun : 'STUN',
|
||||||
|
bandwidth : 'Bandwidth',
|
||||||
|
packetRate : 'PacketRate',
|
||||||
|
jitter : 'Jitter',
|
||||||
|
jamerror : 'ServiceError',
|
||||||
|
noNetwork : 'NoNetwork'
|
||||||
|
}
|
||||||
|
|
||||||
var categories = {
|
var categories = {
|
||||||
register : "Register",
|
register : "Register",
|
||||||
download : "DownloadClient",
|
download : "DownloadClient",
|
||||||
audioTest : "AudioTest",
|
audioTest : "AudioTest",
|
||||||
|
audioTestData : 'AudioTestData',
|
||||||
trackConfig : "AudioTrackConfig",
|
trackConfig : "AudioTrackConfig",
|
||||||
networkTest : "NetworkTest",
|
networkTest : "NetworkTest",
|
||||||
sessionCount : "SessionCount",
|
sessionCount : "SessionCount",
|
||||||
|
|
@ -105,7 +128,6 @@
|
||||||
jkFollow : 'jkFollow',
|
jkFollow : 'jkFollow',
|
||||||
jkFavorite : 'jkFavorite',
|
jkFavorite : 'jkFavorite',
|
||||||
jkComment : 'jkComment'
|
jkComment : 'jkComment'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function translatePlatformForGA(platform) {
|
function translatePlatformForGA(platform) {
|
||||||
|
|
@ -289,18 +311,48 @@
|
||||||
context.ga('send', 'event', categories.networkTest, 'Passed', normalizedPlatform, numUsers);
|
context.ga('send', 'event', categories.networkTest, 'Passed', normalizedPlatform, numUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trackNetworkTestFailure(reason, data) {
|
||||||
|
assertOneOf(reason, networkTestFailReasons);
|
||||||
|
|
||||||
|
context.ga('send', 'event', categories.networkTest, 'Failed', reason, data);
|
||||||
|
}
|
||||||
|
|
||||||
function trackAudioTestCompletion(platform) {
|
function trackAudioTestCompletion(platform) {
|
||||||
var normalizedPlatform = translatePlatformForGA(platform);
|
var normalizedPlatform = translatePlatformForGA(platform);
|
||||||
|
|
||||||
context.ga('send', 'event', categories.audioTest, 'Passed', normalizedPlatform);
|
context.ga('send', 'event', categories.audioTest, 'Passed', normalizedPlatform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trackAudioTestFailure(platform, reason, data) {
|
||||||
|
assertOneOf(reason, audioTestFailReasons);
|
||||||
|
|
||||||
|
var normalizedPlatform = translatePlatformForGA(platform);
|
||||||
|
|
||||||
|
if(normalizedPlatform == "Windows") {
|
||||||
|
var action = "FailedWin";
|
||||||
|
}
|
||||||
|
else if(normalizedPlatform == "Mac") {
|
||||||
|
var action = "FailedMac";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var action = "FailedLinux";
|
||||||
|
}
|
||||||
|
|
||||||
|
context.ga('send', 'event', categories.audioTest, action, reason, data);
|
||||||
|
}
|
||||||
|
|
||||||
function trackConfigureTracksCompletion(platform) {
|
function trackConfigureTracksCompletion(platform) {
|
||||||
var normalizedPlatform = translatePlatformForGA(platform);
|
var normalizedPlatform = translatePlatformForGA(platform);
|
||||||
|
|
||||||
context.ga('send', 'event', categories.trackConfig, 'Passed', normalizedPlatform);
|
context.ga('send', 'event', categories.trackConfig, 'Passed', normalizedPlatform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trackAudioTestData(uniqueDeviceName, reason, data) {
|
||||||
|
assertOneOf(reason, audioTestDataReasons);
|
||||||
|
|
||||||
|
context.ga('send', 'event', categories.audioTestData, uniqueDeviceName, reason, data);
|
||||||
|
}
|
||||||
|
|
||||||
var GA = {};
|
var GA = {};
|
||||||
GA.Categories = categories;
|
GA.Categories = categories;
|
||||||
GA.SessionCreationTypes = sessionCreationTypes;
|
GA.SessionCreationTypes = sessionCreationTypes;
|
||||||
|
|
@ -310,11 +362,17 @@
|
||||||
GA.RecordingActions = recordingActions;
|
GA.RecordingActions = recordingActions;
|
||||||
GA.BandActions = bandActions;
|
GA.BandActions = bandActions;
|
||||||
GA.JKSocialTargets = jkSocialTargets;
|
GA.JKSocialTargets = jkSocialTargets;
|
||||||
|
GA.AudioTestFailReasons = audioTestFailReasons;
|
||||||
|
GA.AudioTestDataReasons = audioTestDataReasons;
|
||||||
|
GA.NetworkTestFailReasons = networkTestFailReasons;
|
||||||
GA.trackRegister = trackRegister;
|
GA.trackRegister = trackRegister;
|
||||||
GA.trackDownload = trackDownload;
|
GA.trackDownload = trackDownload;
|
||||||
GA.trackFTUECompletion = trackFTUECompletion;
|
GA.trackFTUECompletion = trackFTUECompletion;
|
||||||
GA.trackNetworkTest = trackNetworkTest;
|
GA.trackNetworkTest = trackNetworkTest;
|
||||||
|
GA.trackNetworkTestFailure = trackNetworkTestFailure;
|
||||||
GA.trackAudioTestCompletion = trackAudioTestCompletion;
|
GA.trackAudioTestCompletion = trackAudioTestCompletion;
|
||||||
|
GA.trackAudioTestFailure = trackAudioTestFailure;
|
||||||
|
GA.trackAudioTestData = trackAudioTestData;
|
||||||
GA.trackConfigureTracksCompletion = trackConfigureTracksCompletion;
|
GA.trackConfigureTracksCompletion = trackConfigureTracksCompletion;
|
||||||
GA.trackSessionCount = trackSessionCount;
|
GA.trackSessionCount = trackSessionCount;
|
||||||
GA.trackSessionMusicians = trackSessionMusicians;
|
GA.trackSessionMusicians = trackSessionMusicians;
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,118 @@
|
||||||
DIALOG_CLOSED : 'dialog_closed'
|
DIALOG_CLOSED : 'dialog_closed'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.JK.ALERT_NAMES = {
|
||||||
|
NO_EVENT : 0,
|
||||||
|
BACKEND_ERROR : 1, //generic error - eg P2P message error
|
||||||
|
BACKEND_MIXER_CHANGE : 2, //event that controls have been regenerated
|
||||||
|
|
||||||
|
//network related
|
||||||
|
PACKET_JTR : 3,
|
||||||
|
PACKET_LOSS : 4,
|
||||||
|
PACKET_LATE : 5,
|
||||||
|
|
||||||
|
JTR_QUEUE_DEPTH : 6,
|
||||||
|
|
||||||
|
NETWORK_JTR : 7,
|
||||||
|
NETWORK_PING : 8,
|
||||||
|
|
||||||
|
BITRATE_THROTTLE_WARN : 9,
|
||||||
|
BANDWIDTH_LOW : 10,
|
||||||
|
|
||||||
|
//IO related events
|
||||||
|
INPUT_IO_RATE : 11,
|
||||||
|
INPUT_IO_JTR : 12,
|
||||||
|
OUTPUT_IO_RATE : 13,
|
||||||
|
OUTPUT_IO_JTR : 14,
|
||||||
|
|
||||||
|
// CPU load related
|
||||||
|
CPU_LOAD : 15,
|
||||||
|
DECODE_VIOLATIONS : 16,
|
||||||
|
|
||||||
|
LAST_THRESHOLD : 17,
|
||||||
|
|
||||||
|
WIFI_NETWORK_ALERT : 18, //user or peer is using wifi
|
||||||
|
NO_VALID_AUDIO_CONFIG : 19, // alert the user to popup a config
|
||||||
|
AUDIO_DEVICE_NOT_PRESENT : 20, // the audio device is not connected
|
||||||
|
RECORD_PLAYBACK_STATE : 21, // record/playback events have occurred
|
||||||
|
RUN_UPDATE_CHECK_BACKGROUND : 22, //this is auto check - do
|
||||||
|
RUN_UPDATE_CHECK_INTERACTIVE : 23, //this is initiated by user
|
||||||
|
STUN_EVENT : 24, // system completed stun test... come get the result
|
||||||
|
DEAD_USER_WARN_EVENT : 25, //the backend is not receiving audio from this peer
|
||||||
|
DEAD_USER_REMOVE_EVENT : 26, //the backend is removing the user from session as no audio is coming from this peer
|
||||||
|
WINDOW_CLOSE_BACKGROUND_MODE : 27, //the user has closed the window and the client is now in background mode
|
||||||
|
WINDOW_OPEN_FOREGROUND_MODE : 28, //the user has opened the window and the client is now in forground mode/
|
||||||
|
SESSION_LIVEBROADCAST_FAIL : 29, //error of some sort - so can't broadcast
|
||||||
|
SESSION_LIVEBROADCAST_ACTIVE : 30, //active
|
||||||
|
SESSION_LIVEBROADCAST_STOPPED : 31, //stopped by server/user
|
||||||
|
SESSION_LIVEBROADCAST_PINNED : 32, //node pinned by user
|
||||||
|
SESSION_LIVEBROADCAST_UNPINNED : 33, //node unpinned by user
|
||||||
|
BACKEND_STATUS_MSG : 34, //status/informational message
|
||||||
|
LOCAL_NETWORK_VARIANCE_HIGH : 35,//the ping time via a hairpin for the user network is unnaturally high or variable.
|
||||||
|
//indicates problem with user computer stack or network itself (wifi, antivirus etc)
|
||||||
|
LOCAL_NETWORK_LATENCY_HIGH : 36,
|
||||||
|
RECORDING_CLOSE : 37, //update and remove tracks from front-end
|
||||||
|
PEER_REPORTS_NO_AUDIO_RECV : 38, //letting front-end know audio is not being received from a user in session
|
||||||
|
LAST_ALERT : 39
|
||||||
|
}
|
||||||
|
// recreate eThresholdType enum from MixerDialog.h
|
||||||
|
context.JK.ALERT_TYPES = {
|
||||||
|
0: {"title": "", "message": ""}, // NO_EVENT,
|
||||||
|
1: {"title": "", "message": ""}, // BACKEND_ERROR: generic error - eg P2P message error
|
||||||
|
2: {"title": "", "message": ""}, // BACKEND_MIXER_CHANGE, - event that controls have been regenerated
|
||||||
|
3: {"title": "High Packet Jitter", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // PACKET_JTR,
|
||||||
|
4: {"title": "High Packet Loss", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // PACKET_LOSS
|
||||||
|
5: {"title": "High Packet Late", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // PACKET_LATE,
|
||||||
|
6: {"title": "Large Jitter Queue", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // JTR_QUEUE_DEPTH,
|
||||||
|
7: {"title": "High Network Jitter", "message": "Your network connection is currently experiencing network jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // NETWORK_JTR,
|
||||||
|
8: {"title": "High Session Latency", "message": "The latency of your audio device combined with your Internet connection has become high enough to impact your session quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // NETWORK_PING,
|
||||||
|
9: {"title": "Bandwidth Throttled", "message": "The available bandwidth on your network has diminished, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // BITRATE_THROTTLE_WARN,
|
||||||
|
10:{"title": "Low Bandwidth", "message": "The available bandwidth on your network has become too low, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // BANDWIDTH_LOW
|
||||||
|
|
||||||
|
// IO related events
|
||||||
|
11:{"title": "Variable Input Rate", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // INPUT_IO_RATE
|
||||||
|
12:{"title": "High Input Jitter", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // INPUT_IO_JTR,
|
||||||
|
13:{"title": "Variable Output Rate", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // OUTPUT_IO_RATE
|
||||||
|
14:{"title": "High Output Jitter", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // OUTPUT_IO_JTR,
|
||||||
|
|
||||||
|
// CPU load related
|
||||||
|
15: { "title": "CPU Utilization High", "message": "The CPU of your computer is unable to keep up with the current processing load, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // CPU_LOAD
|
||||||
|
16: {"title": "Decode Violations", "message": ""}, // DECODE_VIOLATIONS,
|
||||||
|
17: {"title": "", "message": ""}, // LAST_THRESHOLD
|
||||||
|
18: {"title": "Wifi Alert", "message": ""}, // WIFI_NETWORK_ALERT, //user or peer is using wifi
|
||||||
|
19: {"title": "No Audio Configuration", "message": "You cannot join the session because you do not have a valid audio configuration."}, // NO_VALID_AUDIO_CONFIG,
|
||||||
|
20: {"title": "Audio Device Not Present", "message": ""}, // AUDIO_DEVICE_NOT_PRESENT, // the audio device is not connected
|
||||||
|
21: {"title": "", "message": ""}, // RECORD_PLAYBACK_STATE, // record/playback events have occurred
|
||||||
|
22: {"title": "", "message": ""}, // RUN_UPDATE_CHECK_BACKGROUND, //this is auto check - do
|
||||||
|
23: {"title": "", "message": ""}, // RUN_UPDATE_CHECK_INTERACTIVE, //this is initiated by user
|
||||||
|
24: {"title": "", "message": ""}, // STUN_EVENT, // system completed stun test... come get the result
|
||||||
|
25: {"title": "No Audio", "message": "Your system is no longer transmitting audio. Other session members are unable to hear you."}, // DEAD_USER_WARN_EVENT, //the backend is not receiving audio from this peer
|
||||||
|
26: {"title": "No Audio", "message": "Your system is no longer transmitting audio. Other session members are unable to hear you."}, // DEAD_USER_REMOVE_EVENT, //the backend is removing the user from session as no audio is coming from this peer
|
||||||
|
27: {"title": "", "message": ""}, // WINDOW_CLOSE_BACKGROUND_MODE, //the user has closed the window and the client is now in background mode
|
||||||
|
28: {"title": "", "message": ""}, // WINDOW_OPEN_FOREGROUND_MODE, //the user has opened the window and the client is now in forground mode/
|
||||||
|
|
||||||
|
29: {"title": "Failed to Broadcast", "message": ""}, // SESSION_LIVEBROADCAST_FAIL, //error of some sort - so can't broadcast
|
||||||
|
30: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_ACTIVE, //active
|
||||||
|
31: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_STOPPED, //stopped by server/user
|
||||||
|
32: {"title": "Client Pinned", "message": "This client will be the source of a broadcast."}, // SESSION_LIVEBROADCAST_PINNED, //node pinned by user
|
||||||
|
33: {"title": "Client No Longer Pinned", "message": "This client is no longer designated as the source of the broadcast."}, // SESSION_LIVEBROADCAST_UNPINNED, //node unpinned by user
|
||||||
|
|
||||||
|
34: {"title": "", "message": ""}, // BACKEND_STATUS_MSG, //status/informational message
|
||||||
|
35: {"title": "LAN Unpredictable", "message": "Your local network is adding considerable variance to transmit times. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // LOCAL_NETWORK_VARIANCE_HIGH,//the ping time via a hairpin for the user network is unnaturally high or variable.
|
||||||
|
|
||||||
|
//indicates problem with user computer stack or network itself (wifi, antivirus etc)
|
||||||
|
36: {"title": "LAN High Latency", "message": "Your local network is adding considerable latency. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // LOCAL_NETWORK_LATENCY_HIGH,
|
||||||
|
37: {"title": "", "message": ""}, // RECORDING_CLOSE, //update and remove tracks from front-end
|
||||||
|
38: {"title": "No Audio Sent", "message": ""}, // PEER_REPORTS_NO_AUDIO_RECV, //update and remove tracks from front-end
|
||||||
|
39: {"title": "", "message": ""} // LAST_ALERT
|
||||||
|
};
|
||||||
|
|
||||||
|
// add the alert's name to the ALERT_TYPES structure
|
||||||
|
context._.each(context.JK.ALERT_NAMES, function(alert_code, alert_name) {
|
||||||
|
var alert_data = context.JK.ALERT_TYPES[alert_code];
|
||||||
|
alert_data.name = alert_name;
|
||||||
|
})
|
||||||
|
|
||||||
context.JK.MAX_TRACKS = 6;
|
context.JK.MAX_TRACKS = 6;
|
||||||
context.JK.MAX_OUTPUTS = 2;
|
context.JK.MAX_OUTPUTS = 2;
|
||||||
|
|
||||||
|
|
@ -113,36 +225,42 @@
|
||||||
context.JK.AUDIO_DEVICE_BEHAVIOR = {
|
context.JK.AUDIO_DEVICE_BEHAVIOR = {
|
||||||
MacOSX_builtin: {
|
MacOSX_builtin: {
|
||||||
display: 'MacOSX Built-In',
|
display: 'MacOSX Built-In',
|
||||||
videoURL: undefined,
|
shortName: 'Built-In',
|
||||||
|
videoURL: "https://www.youtube.com/watch?v=7-9PW50ygHk",
|
||||||
showKnobs: false,
|
showKnobs: false,
|
||||||
showASIO: false
|
showASIO: false
|
||||||
},
|
},
|
||||||
MacOSX_interface: {
|
MacOSX_interface: {
|
||||||
display: 'MacOSX external interface',
|
display: 'MacOSX external interface',
|
||||||
videoURL: undefined,
|
shortName: 'External',
|
||||||
|
videoURL: "https://www.youtube.com/watch?v=7BLld6ogm14",
|
||||||
showKnobs: false,
|
showKnobs: false,
|
||||||
showASIO: false
|
showASIO: false
|
||||||
},
|
},
|
||||||
Win32_wdm: {
|
Win32_wdm: {
|
||||||
display: 'Windows WDM',
|
display: 'Windows WDM',
|
||||||
videoURL: undefined,
|
shortName : 'WDM',
|
||||||
|
videoURL: "https://www.youtube.com/watch?v=L36UBkAV14c",
|
||||||
showKnobs: true,
|
showKnobs: true,
|
||||||
showASIO: false
|
showASIO: false
|
||||||
},
|
},
|
||||||
Win32_asio: {
|
Win32_asio: {
|
||||||
display: 'Windows ASIO',
|
display: 'Windows ASIO',
|
||||||
videoURL: undefined,
|
shortName : 'ASIO',
|
||||||
|
videoURL: "https://www.youtube.com/watch?v=PGUmISTVVMY",
|
||||||
showKnobs: false,
|
showKnobs: false,
|
||||||
showASIO: true
|
showASIO: true
|
||||||
},
|
},
|
||||||
Win32_asio4all: {
|
Win32_asio4all: {
|
||||||
display: 'Windows ASIO4ALL',
|
display: 'Windows ASIO4ALL',
|
||||||
videoURL: undefined,
|
shortName : 'ASIO4ALL',
|
||||||
|
videoURL: "https://www.youtube.com/watch?v=PGUmISTVVMY",
|
||||||
showKnobs: false,
|
showKnobs: false,
|
||||||
showASIO: true
|
showASIO: true
|
||||||
},
|
},
|
||||||
Linux: {
|
Linux: {
|
||||||
display: 'Linux',
|
display: 'Linux',
|
||||||
|
shortName : 'linux',
|
||||||
videoURL: undefined,
|
videoURL: undefined,
|
||||||
showKnobs: true,
|
showKnobs: true,
|
||||||
showASIO: false
|
showASIO: false
|
||||||
|
|
|
||||||
|
|
@ -718,10 +718,9 @@
|
||||||
|
|
||||||
/** check if the server is alive */
|
/** check if the server is alive */
|
||||||
function serverHealthCheck(options) {
|
function serverHealthCheck(options) {
|
||||||
logger.debug("serverHealthCheck")
|
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/versioncheck"
|
url: "/api/healthcheck"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,22 +94,30 @@
|
||||||
context.jamClient.OnDownloadAvailable();
|
context.jamClient.OnDownloadAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This most likely occurs when multiple tabs in the same browser are open, until we make a fix for this...
|
||||||
|
*/
|
||||||
|
function duplicateClientError() {
|
||||||
|
context.JK.Banner.showAlert("Duplicate Window (Development Mode Only)", "You have logged in another window in this browser. This window will continue to work but with degraded functionality. This limitation will soon be fixed.")
|
||||||
|
context.JK.JamServer.noReconnect = true;
|
||||||
|
}
|
||||||
|
|
||||||
function registerBadStateError() {
|
function registerBadStateError() {
|
||||||
logger.debug("register for server_bad_state_error");
|
|
||||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SERVER_BAD_STATE_ERROR, serverBadStateError);
|
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SERVER_BAD_STATE_ERROR, serverBadStateError);
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerBadStateRecovered() {
|
function registerBadStateRecovered() {
|
||||||
logger.debug("register for server_bad_state_recovered");
|
|
||||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SERVER_BAD_STATE_RECOVERED, serverBadStateRecovered);
|
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SERVER_BAD_STATE_RECOVERED, serverBadStateRecovered);
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerDownloadAvailable() {
|
function registerDownloadAvailable() {
|
||||||
logger.debug("register for download_available");
|
|
||||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.DOWNLOAD_AVAILABLE, downloadAvailable);
|
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.DOWNLOAD_AVAILABLE, downloadAvailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function registerDuplicateClientError() {
|
||||||
|
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SERVER_DUPLICATE_CLIENT_ERROR, duplicateClientError);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic error handler for Ajax calls.
|
* Generic error handler for Ajax calls.
|
||||||
*/
|
*/
|
||||||
|
|
@ -330,6 +338,7 @@
|
||||||
registerBadStateRecovered();
|
registerBadStateRecovered();
|
||||||
registerBadStateError();
|
registerBadStateError();
|
||||||
registerDownloadAvailable();
|
registerDownloadAvailable();
|
||||||
|
registerDuplicateClientError();
|
||||||
context.JK.FaderHelpers.initialize();
|
context.JK.FaderHelpers.initialize();
|
||||||
context.window.onunload = this.unloadFunction;
|
context.window.onunload = this.unloadFunction;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
var $currentScore = null;
|
var $currentScore = null;
|
||||||
var $scoredClients = null;
|
var $scoredClients = null;
|
||||||
var $subscore = null;
|
var $subscore = null;
|
||||||
|
var $watchVideo = null;
|
||||||
var backendGuardTimeout = null;
|
var backendGuardTimeout = null;
|
||||||
|
|
||||||
var serverClientId = '';
|
var serverClientId = '';
|
||||||
|
|
@ -43,6 +44,12 @@
|
||||||
var $self = $(this);
|
var $self = $(this);
|
||||||
var scoringZoneWidth = 100;//px
|
var scoringZoneWidth = 100;//px
|
||||||
var inGearWizard = false;
|
var inGearWizard = false;
|
||||||
|
var operatingSystem = null;
|
||||||
|
|
||||||
|
// these try to make it such that we only pass a NetworkTest Pass/Failed one time in a new user flow
|
||||||
|
var trackedPass = false;
|
||||||
|
var lastNetworkFailure = null;
|
||||||
|
var bandwidthSamples = [];
|
||||||
|
|
||||||
var NETWORK_TEST_START = 'network_test.start';
|
var NETWORK_TEST_START = 'network_test.start';
|
||||||
var NETWORK_TEST_DONE = 'network_test.done';
|
var NETWORK_TEST_DONE = 'network_test.done';
|
||||||
|
|
@ -66,7 +73,27 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this averages bandwidthSamples; this method is meant just for GA data
|
||||||
|
function avgBandwidth(num_others) {
|
||||||
|
if(bandwidthSamples.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var total = 0;
|
||||||
|
context._.each(bandwidthSamples, function(sample) {
|
||||||
|
total += (sample * num_others * 400 * (100 + 28)); // sample is a percentage of 400. So sample * 400 gives us how many packets/sec. 100 is payload; 28 is UDP+ETHERNET overhead, to give us bandwidth
|
||||||
|
})
|
||||||
|
return total / bandwidthSamples.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
|
trackedPass = false;
|
||||||
|
lastNetworkFailure = null;
|
||||||
|
resetTestState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTestState() {
|
||||||
serverClientId = '';
|
serverClientId = '';
|
||||||
scoring = false;
|
scoring = false;
|
||||||
numClientsToTest = STARTING_NUM_CLIENTS;
|
numClientsToTest = STARTING_NUM_CLIENTS;
|
||||||
|
|
@ -77,6 +104,8 @@
|
||||||
$testText.empty();
|
$testText.empty();
|
||||||
$subscore.empty();
|
$subscore.empty();
|
||||||
$currentScore.width(0);
|
$currentScore.width(0);
|
||||||
|
bandwidthSamples = [];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderStartTest() {
|
function renderStartTest() {
|
||||||
|
|
@ -107,6 +136,17 @@
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLastNetworkFailure() {
|
||||||
|
return lastNetworkFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeLastNetworkFailure(reason, data) {
|
||||||
|
if(!trackedPass) {
|
||||||
|
lastNetworkFailure = {reason:reason, data:data};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function testFinished() {
|
function testFinished() {
|
||||||
var attempt = getCurrentAttempt();
|
var attempt = getCurrentAttempt();
|
||||||
|
|
||||||
|
|
@ -123,7 +163,14 @@
|
||||||
if(!testSummary.final.num_clients) {
|
if(!testSummary.final.num_clients) {
|
||||||
testSummary.final.num_clients = attempt.num_clients;
|
testSummary.final.num_clients = attempt.num_clients;
|
||||||
}
|
}
|
||||||
context.JK.GA.trackNetworkTest(context.JK.detectOS(), testSummary.final.num_clients);
|
|
||||||
|
// context.jamClient.GetNetworkTestScore() == 0 is a rough approximation if the user has passed the FTUE before
|
||||||
|
if(inGearWizard || context.jamClient.GetNetworkTestScore() == 0) {
|
||||||
|
trackedPass = true;
|
||||||
|
lastNetworkFailure = null;
|
||||||
|
context.JK.GA.trackNetworkTest(context.JK.detectOS(), testSummary.final.num_clients);
|
||||||
|
}
|
||||||
|
|
||||||
context.jamClient.SetNetworkTestScore(attempt.num_clients);
|
context.jamClient.SetNetworkTestScore(attempt.num_clients);
|
||||||
if(testSummary.final.num_clients == 2) {
|
if(testSummary.final.num_clients == 2) {
|
||||||
$testResults.addClass('acceptable');
|
$testResults.addClass('acceptable');
|
||||||
|
|
@ -136,49 +183,65 @@
|
||||||
else if(reason == "minimum_client_threshold") {
|
else if(reason == "minimum_client_threshold") {
|
||||||
context.jamClient.SetNetworkTestScore(0);
|
context.jamClient.SetNetworkTestScore(0);
|
||||||
renderStopTest('', "We're sorry, but your router and Internet service will not effectively support JamKazam sessions. Please click the HELP button for more information.")
|
renderStopTest('', "We're sorry, but your router and Internet service will not effectively support JamKazam sessions. Please click the HELP button for more information.")
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.bandwidth, avgBandwidth(attempt.num_clients - 1));
|
||||||
}
|
}
|
||||||
else if(reason == "unreachable") {
|
else if(reason == "unreachable" || reason == "no-transmit") {
|
||||||
context.jamClient.SetNetworkTestScore(0);
|
context.jamClient.SetNetworkTestScore(0);
|
||||||
renderStopTest('', "We're sorry, but your router will not support JamKazam in its current configuration. Please click the HELP button for more information.");
|
renderStopTest('', "We're sorry, but your router will not support JamKazam in its current configuration. Please click the HELP button for more information.");
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.stun, attempt.num_clients);
|
||||||
}
|
}
|
||||||
else if(reason == "internal_error") {
|
else if(reason == "internal_error") {
|
||||||
context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection.");
|
context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection.");
|
||||||
renderStopTest('', '');
|
renderStopTest('', '');
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
|
||||||
}
|
}
|
||||||
else if(reason == "remote_peer_cant_test") {
|
else if(reason == "remote_peer_cant_test") {
|
||||||
context.JK.alertSupportedNeeded("The JamKazam service is experiencing technical difficulties.");
|
context.JK.alertSupportedNeeded("The JamKazam service is experiencing technical difficulties.");
|
||||||
renderStopTest('', '');
|
renderStopTest('', '');
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
|
||||||
|
}
|
||||||
|
else if(reason == "server_comm_timeout") {
|
||||||
|
context.JK.alertSupportedNeeded("Communication with the JamKazam network service has timed out." + appendContextualStatement());
|
||||||
|
renderStopTest('', '');
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
|
||||||
}
|
}
|
||||||
else if(reason == 'backend_gone') {
|
else if(reason == 'backend_gone') {
|
||||||
context.JK.alertSupportedNeeded("The JamKazam client is experiencing technical difficulties.");
|
context.JK.alertSupportedNeeded("The JamKazam client is experiencing technical difficulties.");
|
||||||
renderStopTest('', '');
|
renderStopTest('', '');
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
|
||||||
}
|
}
|
||||||
else if(reason == "invalid_response") {
|
else if(reason == "invalid_response") {
|
||||||
context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection. Reason=" + attempt.backend_data.reason + '.');
|
context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection.<br/><br/>Reason: " + attempt.backend_data.reason + '.');
|
||||||
renderStopTest('', '');
|
renderStopTest('', '');
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
|
||||||
}
|
}
|
||||||
else if(reason == 'no_servers') {
|
else if(reason == 'no_servers') {
|
||||||
context.JK.alertSupportedNeeded("No network test servers are available." + appendContextualStatement());
|
context.JK.alertSupportedNeeded("No network test servers are available." + appendContextualStatement());
|
||||||
renderStopTest('', '');
|
renderStopTest('', '');
|
||||||
testedSuccessfully = true;
|
testedSuccessfully = true;
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
|
||||||
}
|
}
|
||||||
else if(reason == 'no_network') {
|
else if(reason == 'no_network') {
|
||||||
context.JK.Banner.showAlert("Please try again later. Your network appears down.");
|
context.JK.Banner.showAlert("Please try again later. Your network appears down.");
|
||||||
renderStopTest('', '');
|
renderStopTest('', '');
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.noNetwork);
|
||||||
}
|
}
|
||||||
else if(reason == "rest_api_error") {
|
else if(reason == "rest_api_error") {
|
||||||
context.JK.alertSupportedNeeded("Unable to acquire a network test server." + appendContextualStatement());
|
context.JK.alertSupportedNeeded("Unable to acquire a network test server." + appendContextualStatement());
|
||||||
testedSuccessfully = true;
|
testedSuccessfully = true;
|
||||||
renderStopTest('', '');
|
renderStopTest('', '');
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
|
||||||
}
|
}
|
||||||
else if(reason == "timeout") {
|
else if(reason == "timeout") {
|
||||||
context.JK.alertSupportedNeeded("Communication with a network test servers timed out." + appendContextualStatement());
|
context.JK.alertSupportedNeeded("Communication with the JamKazam network service timed out." + appendContextualStatement());
|
||||||
testedSuccessfully = true;
|
testedSuccessfully = true;
|
||||||
renderStopTest('', '');
|
renderStopTest('', '');
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
context.JK.alertSupportedNeeded("The JamKazam client software had a logic error while scoring your Internet connection.");
|
context.JK.alertSupportedNeeded("The JamKazam client software had a logic error while scoring your Internet connection.");
|
||||||
renderStopTest('', '');
|
renderStopTest('', '');
|
||||||
|
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
|
||||||
}
|
}
|
||||||
|
|
||||||
numClientsToTest = STARTING_NUM_CLIENTS;
|
numClientsToTest = STARTING_NUM_CLIENTS;
|
||||||
|
|
@ -204,8 +267,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
clearBackendGuard();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearBackendGuard() {
|
function clearBackendGuard() {
|
||||||
if(backendGuardTimeout) {
|
if(backendGuardTimeout) {
|
||||||
clearTimeout(backendGuardTimeout);
|
clearTimeout(backendGuardTimeout);
|
||||||
|
|
@ -213,6 +277,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setBackendGuard() {
|
||||||
|
clearBackendGuard();
|
||||||
|
backendGuardTimeout = setTimeout(function(){backendTimedOut()}, (gon.ftue_network_test_duration + 1) * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
function attemptTestPass() {
|
function attemptTestPass() {
|
||||||
|
|
||||||
var attempt = {};
|
var attempt = {};
|
||||||
|
|
@ -230,7 +299,7 @@
|
||||||
|
|
||||||
updateProgress(0, false);
|
updateProgress(0, false);
|
||||||
|
|
||||||
backendGuardTimeout = setTimeout(function(){backendTimedOut()}, (gon.ftue_network_test_duration + 1) * 1000);
|
setBackendGuard();
|
||||||
|
|
||||||
context.jamClient.TestNetworkPktBwRate(serverClientId, createSuccessCallbackName(), createTimeoutCallbackName(),
|
context.jamClient.TestNetworkPktBwRate(serverClientId, createSuccessCallbackName(), createTimeoutCallbackName(),
|
||||||
NETWORK_TEST_TYPES.PktTest400LowLatency,
|
NETWORK_TEST_TYPES.PktTest400LowLatency,
|
||||||
|
|
@ -262,7 +331,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
resetTestState();
|
||||||
scoring = true;
|
scoring = true;
|
||||||
$self.triggerHandler(NETWORK_TEST_START);
|
$self.triggerHandler(NETWORK_TEST_START);
|
||||||
renderStartTest();
|
renderStartTest();
|
||||||
|
|
@ -300,6 +369,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateProgress(throughput, showSubscore) {
|
function updateProgress(throughput, showSubscore) {
|
||||||
|
|
||||||
var width = throughput * 100;
|
var width = throughput * 100;
|
||||||
|
|
||||||
$currentScore.stop().data('showSubscore', showSubscore);
|
$currentScore.stop().data('showSubscore', showSubscore);
|
||||||
|
|
@ -322,9 +392,7 @@
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
function networkTestSuccess(data) {
|
function networkTestComplete(data) {
|
||||||
clearBackendGuard();
|
|
||||||
|
|
||||||
var attempt = getCurrentAttempt();
|
var attempt = getCurrentAttempt();
|
||||||
|
|
||||||
function refineTest(up) {
|
function refineTest(up) {
|
||||||
|
|
@ -364,6 +432,8 @@
|
||||||
|
|
||||||
if(data.progress === true) {
|
if(data.progress === true) {
|
||||||
|
|
||||||
|
setBackendGuard();
|
||||||
|
|
||||||
var animate = true;
|
var animate = true;
|
||||||
if(data.downthroughput && data.upthroughput) {
|
if(data.downthroughput && data.upthroughput) {
|
||||||
|
|
||||||
|
|
@ -376,12 +446,14 @@
|
||||||
// take the lower
|
// take the lower
|
||||||
var throughput= data.downthroughput < data.upthroughput ? data.downthroughput : data.upthroughput;
|
var throughput= data.downthroughput < data.upthroughput ? data.downthroughput : data.upthroughput;
|
||||||
|
|
||||||
|
bandwidthSamples.push(data.upthroughput);
|
||||||
|
|
||||||
updateProgress(throughput, true);
|
updateProgress(throughput, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
clearBackendGuard();
|
||||||
logger.debug("network test pass success. data: ", data);
|
logger.debug("network test pass success. data: ", data);
|
||||||
|
|
||||||
if(data.reason == "unreachable") {
|
if(data.reason == "unreachable") {
|
||||||
|
|
@ -390,6 +462,11 @@
|
||||||
attempt.reason = data.reason;
|
attempt.reason = data.reason;
|
||||||
testFinished();
|
testFinished();
|
||||||
}
|
}
|
||||||
|
else if(data.reason == "no-transmit") {
|
||||||
|
logger.debug("network test: no-transmit (STUN issue or similar)");
|
||||||
|
attempt.reason = data.reason;
|
||||||
|
testFinished();
|
||||||
|
}
|
||||||
else if(data.reason == "internal_error") {
|
else if(data.reason == "internal_error") {
|
||||||
// oops
|
// oops
|
||||||
logger.debug("network test: internal_error (client had a unexpected problem)");
|
logger.debug("network test: internal_error (client had a unexpected problem)");
|
||||||
|
|
@ -402,6 +479,11 @@
|
||||||
attempt.reason = data.reason;
|
attempt.reason = data.reason;
|
||||||
testFinished();
|
testFinished();
|
||||||
}
|
}
|
||||||
|
else if(data.reason == "server_comm_timeout") {
|
||||||
|
logger.debug("network test: server_comm_timeout (communication with server problem)")
|
||||||
|
attempt.reason = data.reason;
|
||||||
|
testFinished();
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if(!data.downthroughput || !data.upthroughput) {
|
if(!data.downthroughput || !data.upthroughput) {
|
||||||
// we have to assume this is bad. just not a reason we know about in code
|
// we have to assume this is bad. just not a reason we know about in code
|
||||||
|
|
@ -467,9 +549,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function beforeHide() {
|
function beforeHide() {
|
||||||
clearBackendGuard();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initializeVideoWatchButton() {
|
||||||
|
if(operatingSystem == "Win32") {
|
||||||
|
$watchVideo.attr('href', 'https://www.youtube.com/watch?v=rhAdCVuwhBc');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$watchVideo.attr('href', 'https://www.youtube.com/watch?v=0r1py0AYJ4Y');
|
||||||
|
}
|
||||||
|
}
|
||||||
function initialize(_$step, _inGearWizard) {
|
function initialize(_$step, _inGearWizard) {
|
||||||
$step = _$step;
|
$step = _$step;
|
||||||
inGearWizard = _inGearWizard;
|
inGearWizard = _inGearWizard;
|
||||||
|
|
@ -487,15 +577,19 @@
|
||||||
$currentScore = $step.find('.current-score');
|
$currentScore = $step.find('.current-score');
|
||||||
$scoredClients= $step.find('.scored-clients');
|
$scoredClients= $step.find('.scored-clients');
|
||||||
$subscore = $step.find('.subscore');
|
$subscore = $step.find('.subscore');
|
||||||
|
$watchVideo = $step.find('.watch-video');
|
||||||
$startNetworkTestBtn.on('click', startNetworkTest);
|
$startNetworkTestBtn.on('click', startNetworkTest);
|
||||||
|
operatingSystem = context.JK.GetOSAsString();
|
||||||
|
|
||||||
|
initializeVideoWatchButton();
|
||||||
|
|
||||||
// if this network test is instantiated anywhere else than the gearWizard, or a dialog, then this will have to be expanded
|
// if this network test is instantiated anywhere else than the gearWizard, or a dialog, then this will have to be expanded
|
||||||
if(inGearWizard) {
|
if(inGearWizard) {
|
||||||
context.JK.HandleNetworkTestSuccessForGearWizard = function(data) { networkTestSuccess(data)}; // pin to global for bridge callback
|
context.JK.HandleNetworkTestSuccessForGearWizard = function(data) { networkTestComplete(data)}; // pin to global for bridge callback
|
||||||
context.JK.HandleNetworkTestTimeoutForGearWizard = function(data) { networkTestTimeout(data)}; // pin to global for bridge callback
|
context.JK.HandleNetworkTestTimeoutForGearWizard = function(data) { networkTestTimeout(data)}; // pin to global for bridge callback
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
context.JK.HandleNetworkTestSuccessForDialog = function(data) { networkTestSuccess(data)}; // pin to global for bridge callback
|
context.JK.HandleNetworkTestSuccessForDialog = function(data) { networkTestComplete(data)}; // pin to global for bridge callback
|
||||||
context.JK.HandleNetworkTestTimeoutForDialog = function(data) { networkTestTimeout(data)}; // pin to global for bridge callback
|
context.JK.HandleNetworkTestTimeoutForDialog = function(data) { networkTestTimeout(data)}; // pin to global for bridge callback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -505,6 +599,7 @@
|
||||||
this.initialize = initialize;
|
this.initialize = initialize;
|
||||||
this.reset = reset;
|
this.reset = reset;
|
||||||
this.cancel = cancel;
|
this.cancel = cancel;
|
||||||
|
this.getLastNetworkFailure = getLastNetworkFailure;
|
||||||
|
|
||||||
this.NETWORK_TEST_START = NETWORK_TEST_START;
|
this.NETWORK_TEST_START = NETWORK_TEST_START;
|
||||||
this.NETWORK_TEST_DONE = NETWORK_TEST_DONE;
|
this.NETWORK_TEST_DONE = NETWORK_TEST_DONE;
|
||||||
|
|
@ -30,7 +30,6 @@
|
||||||
var claimedRecording = null;
|
var claimedRecording = null;
|
||||||
var playbackControls = null;
|
var playbackControls = null;
|
||||||
var promptLeave = false;
|
var promptLeave = false;
|
||||||
var backendMixerAlertThrottleTimer = null;
|
|
||||||
var rateSessionDialog = null;
|
var rateSessionDialog = null;
|
||||||
|
|
||||||
var rest = context.JK.Rest();
|
var rest = context.JK.Rest();
|
||||||
|
|
@ -76,60 +75,6 @@
|
||||||
"PeerMediaTrackGroup": 10
|
"PeerMediaTrackGroup": 10
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// recreate eThresholdType enum from MixerDialog.h
|
|
||||||
var alert_type = {
|
|
||||||
0: {"title": "", "message": ""}, // NO_EVENT,
|
|
||||||
1: {"title": "", "message": ""}, // BACKEND_ERROR: generic error - eg P2P message error
|
|
||||||
2: {"title": "", "message": ""}, // BACKEND_MIXER_CHANGE, - event that controls have been regenerated
|
|
||||||
3: {"title": "High Packet Jitter", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // PACKET_JTR,
|
|
||||||
4: {"title": "High Packet Loss", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // PACKET_LOSS
|
|
||||||
5: {"title": "High Packet Late", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // PACKET_LATE,
|
|
||||||
6: {"title": "Large Jitter Queue", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // JTR_QUEUE_DEPTH,
|
|
||||||
7: {"title": "High Network Jitter", "message": "Your network connection is currently experiencing network jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // NETWORK_JTR,
|
|
||||||
8: {"title": "High Session Latency", "message": "The latency of your audio device combined with your Internet connection has become high enough to impact your session quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // NETWORK_PING,
|
|
||||||
9: {"title": "Bandwidth Throttled", "message": "The available bandwidth on your network has diminished, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // BITRATE_THROTTLE_WARN,
|
|
||||||
10:{"title": "Low Bandwidth", "message": "The available bandwidth on your network has become too low, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // BANDWIDTH_LOW
|
|
||||||
|
|
||||||
// IO related events
|
|
||||||
11:{"title": "Variable Input Rate", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // INPUT_IO_RATE
|
|
||||||
12:{"title": "High Input Jitter", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // INPUT_IO_JTR,
|
|
||||||
13:{"title": "Variable Output Rate", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // OUTPUT_IO_RATE
|
|
||||||
14:{"title": "High Output Jitter", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // OUTPUT_IO_JTR,
|
|
||||||
|
|
||||||
// CPU load related
|
|
||||||
15: { "title": "CPU Utilization High", "message": "The CPU of your computer is unable to keep up with the current processing load, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // CPU_LOAD
|
|
||||||
16: {"title": "Decode Violations", "message": ""}, // DECODE_VIOLATIONS,
|
|
||||||
17: {"title": "", "message": ""}, // LAST_THRESHOLD
|
|
||||||
18: {"title": "Wifi Alert", "message": ""}, // WIFI_NETWORK_ALERT, //user or peer is using wifi
|
|
||||||
19: {"title": "No Audio Configuration", "message": "You cannot join the session because you do not have a valid audio configuration."}, // NO_VALID_AUDIO_CONFIG,
|
|
||||||
20: {"title": "Audio Device Not Present", "message": ""}, // AUDIO_DEVICE_NOT_PRESENT, // the audio device is not connected
|
|
||||||
21: {"title": "", "message": ""}, // RECORD_PLAYBACK_STATE, // record/playback events have occurred
|
|
||||||
22: {"title": "", "message": ""}, // RUN_UPDATE_CHECK_BACKGROUND, //this is auto check - do
|
|
||||||
23: {"title": "", "message": ""}, // RUN_UPDATE_CHECK_INTERACTIVE, //this is initiated by user
|
|
||||||
24: {"title": "", "message": ""}, // STUN_EVENT, // system completed stun test... come get the result
|
|
||||||
25: {"title": "No Audio", "message": "Your system is no longer transmitting audio. Other session members are unable to hear you."}, // DEAD_USER_WARN_EVENT, //the backend is not receiving audio from this peer
|
|
||||||
26: {"title": "No Audio", "message": "Your system is no longer transmitting audio. Other session members are unable to hear you."}, // DEAD_USER_REMOVE_EVENT, //the backend is removing the user from session as no audio is coming from this peer
|
|
||||||
27: {"title": "", "message": ""}, // WINDOW_CLOSE_BACKGROUND_MODE, //the user has closed the window and the client is now in background mode
|
|
||||||
28: {"title": "", "message": ""}, // WINDOW_OPEN_FOREGROUND_MODE, //the user has opened the window and the client is now in forground mode/
|
|
||||||
|
|
||||||
29: {"title": "Failed to Broadcast", "message": ""}, // SESSION_LIVEBROADCAST_FAIL, //error of some sort - so can't broadcast
|
|
||||||
30: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_ACTIVE, //active
|
|
||||||
31: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_STOPPED, //stopped by server/user
|
|
||||||
32: {"title": "Client Pinned", "message": "This client will be the source of a broadcast."}, // SESSION_LIVEBROADCAST_PINNED, //node pinned by user
|
|
||||||
33: {"title": "Client No Longer Pinned", "message": "This client is no longer designated as the source of the broadcast."}, // SESSION_LIVEBROADCAST_UNPINNED, //node unpinned by user
|
|
||||||
|
|
||||||
34: {"title": "", "message": ""}, // BACKEND_STATUS_MSG, //status/informational message
|
|
||||||
35: {"title": "LAN Unpredictable", "message": "Your local network is adding considerable variance to transmit times. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // LOCAL_NETWORK_VARIANCE_HIGH,//the ping time via a hairpin for the user network is unnaturally high or variable.
|
|
||||||
|
|
||||||
//indicates problem with user computer stack or network itself (wifi, antivirus etc)
|
|
||||||
36: {"title": "LAN High Latency", "message": "Your local network is adding considerable latency. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // LOCAL_NETWORK_LATENCY_HIGH,
|
|
||||||
37: {"title": "", "message": ""}, // RECORDING_CLOSE, //update and remove tracks from front-end
|
|
||||||
38: {"title": "No Audio Sent", "message": ""}, // PEER_REPORTS_NO_AUDIO_RECV, //update and remove tracks from front-end
|
|
||||||
39: {"title": "", "message": ""} // LAST_ALERT
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function beforeShow(data) {
|
function beforeShow(data) {
|
||||||
sessionId = data.id;
|
sessionId = data.id;
|
||||||
promptLeave = true;
|
promptLeave = true;
|
||||||
|
|
@ -144,114 +89,6 @@
|
||||||
return { freezeInteraction: true };
|
return { freezeInteraction: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
function alertCallback(type, text) {
|
|
||||||
|
|
||||||
function timeCallback() {
|
|
||||||
var start = new Date();
|
|
||||||
setTimeout(function() {
|
|
||||||
var timed = new Date().getTime() - start.getTime();
|
|
||||||
if(timed > 250) {
|
|
||||||
logger.warn("SLOW AlERT_CALLBACK. type: %o text: %o time: %o", type, text, timed);
|
|
||||||
}
|
|
||||||
}, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
timeCallback();
|
|
||||||
|
|
||||||
logger.debug("alert callback", type, text);
|
|
||||||
|
|
||||||
if (type === 2) { // BACKEND_MIXER_CHANGE
|
|
||||||
logger.debug("BACKEND_MIXER_CHANGE alert. reason:" + text);
|
|
||||||
|
|
||||||
if(sessionModel.id() && text == "RebuildAudioIoControl") {
|
|
||||||
|
|
||||||
// the backend will send these events rapid-fire back to back.
|
|
||||||
// the server can still perform correctly, but it is nicer to wait 100 ms to let them all fall through
|
|
||||||
if(backendMixerAlertThrottleTimer) {clearTimeout(backendMixerAlertThrottleTimer);}
|
|
||||||
|
|
||||||
backendMixerAlertThrottleTimer = setTimeout(function() {
|
|
||||||
// this is a local change to our tracks. we need to tell the server about our updated track information
|
|
||||||
var inputTracks = context.JK.TrackHelpers.getUserTracks(context.jamClient);
|
|
||||||
|
|
||||||
// create a trackSync request based on backend data
|
|
||||||
var syncTrackRequest = {};
|
|
||||||
syncTrackRequest.client_id = app.clientId;
|
|
||||||
syncTrackRequest.tracks = inputTracks;
|
|
||||||
syncTrackRequest.id = sessionModel.id();
|
|
||||||
|
|
||||||
rest.putTrackSyncChange(syncTrackRequest)
|
|
||||||
.done(function() {
|
|
||||||
})
|
|
||||||
.fail(function() {
|
|
||||||
app.notify({
|
|
||||||
"title": "Can't Sync Local Tracks",
|
|
||||||
"text": "The client is unable to sync local track information with the server. You should rejoin the session to ensure a good experience.",
|
|
||||||
"icon_url": "/assets/content/icon_alert_big.png"
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
else if(sessionModel.id() && (text == 'RebuildMediaControl' || text == 'RebuildRemoteUserControl')) {
|
|
||||||
sessionModel.refreshCurrentSession(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type === 19) { // NO_VALID_AUDIO_CONFIG
|
|
||||||
app.notify({
|
|
||||||
"title": alert_type[type].title,
|
|
||||||
"text": text,
|
|
||||||
"icon_url": "/assets/content/icon_alert_big.png"
|
|
||||||
});
|
|
||||||
context.location = "/client#"; // leaveSession will be called in beforeHide below
|
|
||||||
}
|
|
||||||
else if (type === 24) { // STUN_EVENT
|
|
||||||
var testResults = context.jamClient.NetworkTestResult();
|
|
||||||
|
|
||||||
$.each(testResults, function(index, val) {
|
|
||||||
if (val.bStunFailed) {
|
|
||||||
// if true we could not reach a stun server
|
|
||||||
}
|
|
||||||
else if (val.bRemoteUdpBocked) {
|
|
||||||
// if true the user cannot communicate with peer via UDP, although they could do LAN based session
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (type === 26) {
|
|
||||||
var clientId = text;
|
|
||||||
var participant = sessionModel.getParticipant(clientId);
|
|
||||||
if(participant) {
|
|
||||||
app.notify({
|
|
||||||
"title": alert_type[type].title,
|
|
||||||
"text": participant.user.name + " is no longer sending audio.",
|
|
||||||
"icon_url": context.JK.resolveAvatarUrl(participant.user.photo_url)
|
|
||||||
});
|
|
||||||
var $track = $('div.track[client-id="' + clientId + '"]');
|
|
||||||
$('.disabled-track-overlay', $track).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type === 27) { // WINDOW_CLOSE_BACKGROUND_MODE
|
|
||||||
// the window was closed; just attempt to nav to home, which will cause all the right REST calls to happen
|
|
||||||
promptLeave = false;
|
|
||||||
context.location = '/client#/home'
|
|
||||||
}
|
|
||||||
else if(type != 30 && type != 31 && type != 21){ // these are handled elsewhere
|
|
||||||
context.setTimeout(function() {
|
|
||||||
var alert = alert_type[type];
|
|
||||||
|
|
||||||
if(alert && alert.title) {
|
|
||||||
app.notify({
|
|
||||||
"title": alert_type[type].title,
|
|
||||||
"text": text,
|
|
||||||
"icon_url": "/assets/content/icon_alert_big.png"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger.debug("Unknown Backend Event type %o, data %o", type, text)
|
|
||||||
}
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeSession() {
|
function initializeSession() {
|
||||||
// indicate that the screen is active, so that
|
// indicate that the screen is active, so that
|
||||||
// body-scoped drag handlers can go active
|
// body-scoped drag handlers can go active
|
||||||
|
|
@ -327,7 +164,8 @@
|
||||||
context.JK.CurrentSessionModel = sessionModel = new context.JK.SessionModel(
|
context.JK.CurrentSessionModel = sessionModel = new context.JK.SessionModel(
|
||||||
context.JK.app,
|
context.JK.app,
|
||||||
context.JK.JamServer,
|
context.JK.JamServer,
|
||||||
context.jamClient
|
context.jamClient,
|
||||||
|
self
|
||||||
);
|
);
|
||||||
|
|
||||||
$(sessionModel.recordingModel)
|
$(sessionModel.recordingModel)
|
||||||
|
|
@ -653,7 +491,8 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var faderId = mixerIds.join(',');
|
var faderId = mixerIds.join(',');
|
||||||
$('#volume').attr('mixer-id', faderId);
|
var $volume = $('#volume');
|
||||||
|
$volume.attr('mixer-id', faderId);
|
||||||
var faderOpts = {
|
var faderOpts = {
|
||||||
faderId: faderId,
|
faderId: faderId,
|
||||||
faderType: "horizontal",
|
faderType: "horizontal",
|
||||||
|
|
@ -664,8 +503,9 @@
|
||||||
"height": "24px"
|
"height": "24px"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
context.JK.FaderHelpers.renderFader("#volume", faderOpts);
|
context.JK.FaderHelpers.renderFader($volume, faderOpts);
|
||||||
context.JK.FaderHelpers.subscribe(faderId, faderChanged);
|
|
||||||
|
$volume.on('fader_change', faderChanged);
|
||||||
// Visually update fader to underlying mixer start value.
|
// Visually update fader to underlying mixer start value.
|
||||||
// Always do this, even if gainPercent is zero.
|
// Always do this, even if gainPercent is zero.
|
||||||
|
|
||||||
|
|
@ -699,8 +539,8 @@
|
||||||
"height": "24px"
|
"height": "24px"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
context.JK.FaderHelpers.renderFader(faderId, faderOpts);
|
context.JK.FaderHelpers.renderFader($mixSlider, faderOpts);
|
||||||
context.JK.FaderHelpers.subscribe(faderId, l2mChanged);
|
$mixSlider.on('fader_change', l2mChanged);
|
||||||
|
|
||||||
var value = context.jamClient.SessionGetMasterLocalMix();
|
var value = context.jamClient.SessionGetMasterLocalMix();
|
||||||
context.JK.FaderHelpers.setFaderValue(faderId, percentFromMixerValue(-80, 20, value));
|
context.JK.FaderHelpers.setFaderValue(faderId, percentFromMixerValue(-80, 20, value));
|
||||||
|
|
@ -709,9 +549,9 @@
|
||||||
/**
|
/**
|
||||||
* This has a specialized jamClient call, so custom handler.
|
* This has a specialized jamClient call, so custom handler.
|
||||||
*/
|
*/
|
||||||
function l2mChanged(faderId, newValue, dragging) {
|
function l2mChanged(e, data) {
|
||||||
//var dbValue = context.JK.FaderHelpers.convertLinearToDb(newValue);
|
//var dbValue = context.JK.FaderHelpers.convertLinearToDb(newValue);
|
||||||
context.jamClient.SessionSetMasterLocalMix(newValue - 80);
|
context.jamClient.SessionSetMasterLocalMix(data.percentage - 80);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _addVoiceChat() {
|
function _addVoiceChat() {
|
||||||
|
|
@ -723,7 +563,8 @@
|
||||||
var $voiceChat = $('#voice-chat');
|
var $voiceChat = $('#voice-chat');
|
||||||
$voiceChat.show();
|
$voiceChat.show();
|
||||||
$voiceChat.attr('mixer-id', mixer.id);
|
$voiceChat.attr('mixer-id', mixer.id);
|
||||||
$('#voice-chat .voicechat-mute').attr('mixer-id', mixer.id);
|
var $voiceChatGain = $voiceChat.find('.voicechat-gain');
|
||||||
|
var $voiceChatMute = $voiceChat.find('.voicechat-mute').attr('mixer-id', mixer.id);
|
||||||
var gainPercent = percentFromMixerValue(
|
var gainPercent = percentFromMixerValue(
|
||||||
mixer.range_low, mixer.range_high, mixer.volume_left);
|
mixer.range_low, mixer.range_high, mixer.volume_left);
|
||||||
var faderOpts = {
|
var faderOpts = {
|
||||||
|
|
@ -731,12 +572,11 @@
|
||||||
faderType: "horizontal",
|
faderType: "horizontal",
|
||||||
width: 50
|
width: 50
|
||||||
};
|
};
|
||||||
context.JK.FaderHelpers.renderFader("#voice-chat .voicechat-gain", faderOpts);
|
context.JK.FaderHelpers.renderFader($voiceChatGain, faderOpts);
|
||||||
context.JK.FaderHelpers.subscribe(mixer.id, faderChanged);
|
$voiceChatGain.on('fader_change', faderChanged);
|
||||||
context.JK.FaderHelpers.setFaderValue(mixer.id, gainPercent);
|
context.JK.FaderHelpers.setFaderValue(mixer.id, gainPercent);
|
||||||
if (mixer.mute) {
|
if (mixer.mute) {
|
||||||
var $mute = $voiceChat.find('.voicechat-mute');
|
_toggleVisualMuteControl($voiceChatMute, true);
|
||||||
_toggleVisualMuteControl($mute, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -945,16 +785,17 @@
|
||||||
var vuLeftSelector = trackSelector + " .track-vu-left";
|
var vuLeftSelector = trackSelector + " .track-vu-left";
|
||||||
var vuRightSelector = trackSelector + " .track-vu-right";
|
var vuRightSelector = trackSelector + " .track-vu-right";
|
||||||
var faderSelector = trackSelector + " .track-gain";
|
var faderSelector = trackSelector + " .track-gain";
|
||||||
|
var $fader = $(faderSelector).attr('mixer-id', mixerId);
|
||||||
var $track = $(trackSelector);
|
var $track = $(trackSelector);
|
||||||
// Set mixer-id attributes and render VU/Fader
|
// Set mixer-id attributes and render VU/Fader
|
||||||
context.JK.VuHelpers.renderVU(vuLeftSelector, vuOpts);
|
context.JK.VuHelpers.renderVU(vuLeftSelector, vuOpts);
|
||||||
$track.find('.track-vu-left').attr('mixer-id', mixerId + '_vul');
|
$track.find('.track-vu-left').attr('mixer-id', mixerId + '_vul');
|
||||||
context.JK.VuHelpers.renderVU(vuRightSelector, vuOpts);
|
context.JK.VuHelpers.renderVU(vuRightSelector, vuOpts);
|
||||||
$track.find('.track-vu-right').attr('mixer-id', mixerId + '_vur');
|
$track.find('.track-vu-right').attr('mixer-id', mixerId + '_vur');
|
||||||
context.JK.FaderHelpers.renderFader(faderSelector, faderOpts);
|
context.JK.FaderHelpers.renderFader($fader, faderOpts);
|
||||||
// Set gain position
|
// Set gain position
|
||||||
context.JK.FaderHelpers.setFaderValue(mixerId, gainPercent);
|
context.JK.FaderHelpers.setFaderValue(mixerId, gainPercent);
|
||||||
context.JK.FaderHelpers.subscribe(mixerId, faderChanged);
|
$fader.on('fader_change', faderChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function called on an interval when participants change. Mixers seem to
|
// Function called on an interval when participants change. Mixers seem to
|
||||||
|
|
@ -976,6 +817,8 @@
|
||||||
],
|
],
|
||||||
usedMixers);
|
usedMixers);
|
||||||
if (mixer) {
|
if (mixer) {
|
||||||
|
var participant = (sessionModel.getParticipant(clientId) || {name:'unknown'}).name;
|
||||||
|
logger.debug("found mixer=" + mixer.id + ", participant=" + participant)
|
||||||
usedMixers[mixer.id] = true;
|
usedMixers[mixer.id] = true;
|
||||||
keysToDelete.push(key);
|
keysToDelete.push(key);
|
||||||
var gainPercent = percentFromMixerValue(
|
var gainPercent = percentFromMixerValue(
|
||||||
|
|
@ -998,6 +841,8 @@
|
||||||
$('.disabled-track-overlay', $track).show();
|
$('.disabled-track-overlay', $track).show();
|
||||||
$('.track-connection', $track).removeClass('red yellow green').addClass('red');
|
$('.track-connection', $track).removeClass('red yellow green').addClass('red');
|
||||||
}
|
}
|
||||||
|
var participant = (sessionModel.getParticipant(clientId) || {name:'unknown'}).name;
|
||||||
|
logger.debug("still looking for mixer for participant=" + participant + ", clientId=" + clientId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1029,14 +874,14 @@
|
||||||
if (!(mixer.stereo)) { // mono track
|
if (!(mixer.stereo)) { // mono track
|
||||||
if (mixerId.substr(-4) === "_vul") {
|
if (mixerId.substr(-4) === "_vul") {
|
||||||
// Do the left
|
// Do the left
|
||||||
selector = '#tracks [mixer-id="' + pureMixerId + '_vul"]';
|
selector = $('#tracks [mixer-id="' + pureMixerId + '_vul"]');
|
||||||
context.JK.VuHelpers.updateVU(selector, value);
|
context.JK.VuHelpers.updateVU(selector, value);
|
||||||
// Do the right
|
// Do the right
|
||||||
selector = '#tracks [mixer-id="' + pureMixerId + '_vur"]';
|
selector = $('#tracks [mixer-id="' + pureMixerId + '_vur"]');
|
||||||
context.JK.VuHelpers.updateVU(selector, value);
|
context.JK.VuHelpers.updateVU(selector, value);
|
||||||
} // otherwise, it's a mono track, _vur event - ignore.
|
} // otherwise, it's a mono track, _vur event - ignore.
|
||||||
} else { // stereo track
|
} else { // stereo track
|
||||||
selector = '#tracks [mixer-id="' + mixerId + '"]';
|
selector = $('#tracks [mixer-id="' + mixerId + '"]');
|
||||||
context.JK.VuHelpers.updateVU(selector, value);
|
context.JK.VuHelpers.updateVU(selector, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1102,12 +947,14 @@
|
||||||
* Will be called when fader changes. The fader id (provided at subscribe time),
|
* Will be called when fader changes. The fader id (provided at subscribe time),
|
||||||
* the new value (0-100) and whether the fader is still being dragged are passed.
|
* the new value (0-100) and whether the fader is still being dragged are passed.
|
||||||
*/
|
*/
|
||||||
function faderChanged(faderId, newValue, dragging) {
|
function faderChanged(e, data) {
|
||||||
|
var $target = $(this);
|
||||||
|
var faderId = $target.attr('mixer-id');
|
||||||
var mixerIds = faderId.split(',');
|
var mixerIds = faderId.split(',');
|
||||||
$.each(mixerIds, function(i,v) {
|
$.each(mixerIds, function(i,v) {
|
||||||
var broadcast = !(dragging); // If fader is still dragging, don't broadcast
|
var broadcast = !(data.dragging); // If fader is still dragging, don't broadcast
|
||||||
fillTrackVolumeObject(v, broadcast);
|
fillTrackVolumeObject(v, broadcast);
|
||||||
setMixerVolume(v, newValue);
|
setMixerVolume(v, data.percentage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1568,7 +1415,6 @@
|
||||||
|
|
||||||
context.JK.HandleVolumeChangeCallback = handleVolumeChangeCallback;
|
context.JK.HandleVolumeChangeCallback = handleVolumeChangeCallback;
|
||||||
context.JK.HandleBridgeCallback = handleBridgeCallback;
|
context.JK.HandleBridgeCallback = handleBridgeCallback;
|
||||||
context.JK.AlertCallback = alertCallback;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
})(window,jQuery);
|
})(window,jQuery);
|
||||||
|
|
@ -7,7 +7,10 @@
|
||||||
context.JK = context.JK || {};
|
context.JK = context.JK || {};
|
||||||
var logger = context.JK.logger;
|
var logger = context.JK.logger;
|
||||||
|
|
||||||
context.JK.SessionModel = function(app, server, client) {
|
// screen can be null
|
||||||
|
context.JK.SessionModel = function(app, server, client, sessionScreen) {
|
||||||
|
var ALERT_TYPES = context.JK.ALERT_TYPES;
|
||||||
|
|
||||||
var clientId = client.clientID;
|
var clientId = client.clientID;
|
||||||
var currentSessionId = null; // Set on join, prior to setting currentSession.
|
var currentSessionId = null; // Set on join, prior to setting currentSession.
|
||||||
var currentSession = null;
|
var currentSession = null;
|
||||||
|
|
@ -19,6 +22,7 @@
|
||||||
var pendingSessionRefresh = false;
|
var pendingSessionRefresh = false;
|
||||||
var recordingModel = new context.JK.RecordingModel(app, this, rest, context.jamClient);
|
var recordingModel = new context.JK.RecordingModel(app, this, rest, context.jamClient);
|
||||||
var currentTrackChanges = 0;
|
var currentTrackChanges = 0;
|
||||||
|
var backendMixerAlertThrottleTimer = null;
|
||||||
// we track all the clientIDs of all the participants ever seen by this session, so that we can reliably convert a clientId from the backend into a username/avatar
|
// we track all the clientIDs of all the participants ever seen by this session, so that we can reliably convert a clientId from the backend into a username/avatar
|
||||||
var participantsEverSeen = {};
|
var participantsEverSeen = {};
|
||||||
var $self = $(this);
|
var $self = $(this);
|
||||||
|
|
@ -29,6 +33,10 @@
|
||||||
return currentSession ? currentSession.id : null;
|
return currentSession ? currentSession.id : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function inSession() {
|
||||||
|
return !!currentSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
function participants() {
|
function participants() {
|
||||||
if (currentSession) {
|
if (currentSession) {
|
||||||
return currentSession.participants;
|
return currentSession.participants;
|
||||||
|
|
@ -131,7 +139,7 @@
|
||||||
|
|
||||||
// 'unregister' for callbacks
|
// 'unregister' for callbacks
|
||||||
context.jamClient.SessionRegisterCallback("");
|
context.jamClient.SessionRegisterCallback("");
|
||||||
context.jamClient.SessionSetAlertCallback("");
|
//context.jamClient.SessionSetAlertCallback("");
|
||||||
context.jamClient.SessionSetConnectionStatusRefreshRate(0);
|
context.jamClient.SessionSetConnectionStatusRefreshRate(0);
|
||||||
updateCurrentSession(null);
|
updateCurrentSession(null);
|
||||||
$(document).trigger('jamkazam.session_stopped', {session: {id: currentSessionId}});
|
$(document).trigger('jamkazam.session_stopped', {session: {id: currentSessionId}});
|
||||||
|
|
@ -214,6 +222,12 @@
|
||||||
* the provided callback when complete.
|
* the provided callback when complete.
|
||||||
*/
|
*/
|
||||||
function refreshCurrentSessionRest(callback, force) {
|
function refreshCurrentSessionRest(callback, force) {
|
||||||
|
|
||||||
|
if(!inSession()) {
|
||||||
|
logger.debug("refreshCurrentSession skipped: ")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var url = "/api/sessions/" + currentSessionId;
|
var url = "/api/sessions/" + currentSessionId;
|
||||||
if(requestingSessionRefresh) {
|
if(requestingSessionRefresh) {
|
||||||
// if someone asks for a refresh while one is going on, we ask for another to queue up
|
// if someone asks for a refresh while one is going on, we ask for another to queue up
|
||||||
|
|
@ -239,7 +253,14 @@
|
||||||
logger.info("ignoring refresh because we already have current: " + currentTrackChanges + ", seen: " + response.track_changes_counter);
|
logger.info("ignoring refresh because we already have current: " + currentTrackChanges + ", seen: " + response.track_changes_counter);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(jqXHR) { app.notifyServerError(jqXHR, "Unable to refresh session data") },
|
error: function(jqXHR) {
|
||||||
|
if(jqXHR.status != 404) {
|
||||||
|
app.notifyServerError(jqXHR, "Unable to refresh session data")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug("refreshCurrentSessionRest: could not refresh data for session because it's gone")
|
||||||
|
}
|
||||||
|
},
|
||||||
complete: function() {
|
complete: function() {
|
||||||
requestingSessionRefresh = false;
|
requestingSessionRefresh = false;
|
||||||
if(pendingSessionRefresh) {
|
if(pendingSessionRefresh) {
|
||||||
|
|
@ -411,6 +432,73 @@
|
||||||
return $.Deferred().reject().promise();
|
return $.Deferred().reject().promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onDeadUserRemove(type, text) {
|
||||||
|
if(!inSession()) return;
|
||||||
|
var clientId = text;
|
||||||
|
var participant = participantsEverSeen[clientId];
|
||||||
|
if(participant) {
|
||||||
|
app.notify({
|
||||||
|
"title": ALERT_TYPES[type].title,
|
||||||
|
"text": participant.user.name + " is no longer sending audio.",
|
||||||
|
"icon_url": context.JK.resolveAvatarUrl(participant.user.photo_url)
|
||||||
|
});
|
||||||
|
var $track = $('div.track[client-id="' + clientId + '"]');
|
||||||
|
$('.disabled-track-overlay', $track).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWindowBackgrounded(type, text) {
|
||||||
|
if(!inSession()) return;
|
||||||
|
|
||||||
|
// the window was closed; just attempt to nav to home, which will cause all the right REST calls to happen
|
||||||
|
if(sessionScreen) {
|
||||||
|
sessionScreen.setPromptLeave(false);
|
||||||
|
context.location = '/client#/home'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBackendMixerChanged(type, text) {
|
||||||
|
logger.debug("BACKEND_MIXER_CHANGE alert. reason:" + text);
|
||||||
|
|
||||||
|
if(inSession() && text == "RebuildAudioIoControl") {
|
||||||
|
|
||||||
|
// the backend will send these events rapid-fire back to back.
|
||||||
|
// the server can still perform correctly, but it is nicer to wait 100 ms to let them all fall through
|
||||||
|
if(backendMixerAlertThrottleTimer) {clearTimeout(backendMixerAlertThrottleTimer);}
|
||||||
|
|
||||||
|
backendMixerAlertThrottleTimer = setTimeout(function() {
|
||||||
|
// this is a local change to our tracks. we need to tell the server about our updated track information
|
||||||
|
var inputTracks = context.JK.TrackHelpers.getUserTracks(context.jamClient);
|
||||||
|
|
||||||
|
// create a trackSync request based on backend data
|
||||||
|
var syncTrackRequest = {};
|
||||||
|
syncTrackRequest.client_id = app.clientId;
|
||||||
|
syncTrackRequest.tracks = inputTracks;
|
||||||
|
syncTrackRequest.id = id();
|
||||||
|
|
||||||
|
rest.putTrackSyncChange(syncTrackRequest)
|
||||||
|
.done(function() {
|
||||||
|
})
|
||||||
|
.fail(function(jqXHR) {
|
||||||
|
if(jqXHR.status != 404) {
|
||||||
|
app.notify({
|
||||||
|
"title": "Can't Sync Local Tracks",
|
||||||
|
"text": "The client is unable to sync local track information with the server. You should rejoin the session to ensure a good experience.",
|
||||||
|
"icon_url": "/assets/content/icon_alert_big.png"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug("Unable to sync local tracks because session is gone.")
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
else if(inSession() && (text == 'RebuildMediaControl' || text == 'RebuildRemoteUserControl')) {
|
||||||
|
refreshCurrentSession(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Public interface
|
// Public interface
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.recordedTracks = recordedTracks;
|
this.recordedTracks = recordedTracks;
|
||||||
|
|
@ -425,6 +513,12 @@
|
||||||
this.onWebsocketDisconnected = onWebsocketDisconnected;
|
this.onWebsocketDisconnected = onWebsocketDisconnected;
|
||||||
this.recordingModel = recordingModel;
|
this.recordingModel = recordingModel;
|
||||||
this.findUserBy = findUserBy;
|
this.findUserBy = findUserBy;
|
||||||
|
|
||||||
|
// ALERT HANDLERS
|
||||||
|
this.onBackendMixerChanged = onBackendMixerChanged;
|
||||||
|
this.onDeadUserRemove = onDeadUserRemove;
|
||||||
|
this.onWindowBackgrounded = onWindowBackgrounded;
|
||||||
|
|
||||||
this.getCurrentSession = function() {
|
this.getCurrentSession = function() {
|
||||||
return currentSession;
|
return currentSession;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@
|
||||||
var logger = context.JK.logger;
|
var logger = context.JK.logger;
|
||||||
var AUDIO_DEVICE_BEHAVIOR = context.JK.AUDIO_DEVICE_BEHAVIOR;
|
var AUDIO_DEVICE_BEHAVIOR = context.JK.AUDIO_DEVICE_BEHAVIOR;
|
||||||
|
|
||||||
|
var ALERT_NAMES = context.JK.ALERT_NAMES;
|
||||||
|
var ALERT_TYPES = context.JK.ALERT_TYPES;
|
||||||
|
|
||||||
var days = new Array("Sun", "Mon", "Tue",
|
var days = new Array("Sun", "Mon", "Tue",
|
||||||
"Wed", "Thu", "Fri", "Sat");
|
"Wed", "Thu", "Fri", "Sat");
|
||||||
|
|
||||||
|
|
@ -448,6 +451,20 @@
|
||||||
$('.dialog-overlay').hide();
|
$('.dialog-overlay').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// usage: context.JK.onBackendEvent(ALERT_NAMES.SOME_EVENT)
|
||||||
|
context.JK.onBackendEvent = function(type, namespace, callback) {
|
||||||
|
var alertData = ALERT_TYPES[type];
|
||||||
|
if(!alertData) {throw "onBackendEvent: unknown alert type " + type};
|
||||||
|
logger.debug("onBackendEvent: " + alertData.name + '.' + namespace)
|
||||||
|
$(document).on(alertData.name + '.' + namespace, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.JK.offBackendEvent = function(type, namespace, callback) {
|
||||||
|
var alertData = ALERT_TYPES[type];
|
||||||
|
if(!alertData) {throw "offBackendEvent: unknown alert type " + type};
|
||||||
|
logger.debug("offBackendEvent: " + alertData.name + '.' + namespace, alertData)
|
||||||
|
$(document).off(alertData.name + '.' + namespace);
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Loads a listbox or dropdown with the values in input_array, setting the option value
|
* Loads a listbox or dropdown with the values in input_array, setting the option value
|
||||||
* to the id_field and the option text to text_field. It will preselect the option with
|
* to the id_field and the option text to text_field. It will preselect the option with
|
||||||
|
|
@ -529,8 +546,8 @@
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.JK.alertSupportedNeeded = function(additionalContext) {
|
context.JK.alertSupportedNeeded = function(additionalContext) {
|
||||||
var $item = context.JK.Banner.showAlert(additionalContext + ' Please <a href="http://jamkazam.desk.com" rel="external">contact support</a>');
|
var $item = context.JK.Banner.showAlert(additionalContext + '<br/><br/>Please <a href="http://jamkazam.desk.com" rel="external">contact support</a>.');
|
||||||
context.JK.popExternalLinks($item);
|
context.JK.popExternalLinks($item);
|
||||||
return $item;
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
context.JK = context.JK || {};
|
context.JK = context.JK || {};
|
||||||
context.JK.VoiceChatHelper = function (app) {
|
context.JK.VoiceChatHelper = function (app) {
|
||||||
var logger = context.JK.logger;
|
var logger = context.JK.logger;
|
||||||
|
var ALERT_NAMES = context.JK.ALERT_NAMES;
|
||||||
var ASSIGNMENT = context.JK.ASSIGNMENT;
|
var ASSIGNMENT = context.JK.ASSIGNMENT;
|
||||||
var VOICE_CHAT = context.JK.VOICE_CHAT;
|
var VOICE_CHAT = context.JK.VOICE_CHAT;
|
||||||
var MAX_TRACKS = context.JK.MAX_TRACKS;
|
var MAX_TRACKS = context.JK.MAX_TRACKS;
|
||||||
|
|
@ -18,10 +19,19 @@
|
||||||
var $chatInputs = null;
|
var $chatInputs = null;
|
||||||
var $templateChatInput = null;
|
var $templateChatInput = null;
|
||||||
var $selectedChatInput = null;// should only be used if isChatEnabled = true
|
var $selectedChatInput = null;// should only be used if isChatEnabled = true
|
||||||
var saveImmediate = null; // if true, then every action by the user results in a save to the backend immediately, false means you have to call trySave to persist
|
var $voiceChatVuLeft = null;
|
||||||
|
var $voiceChatVuRight = null;
|
||||||
|
var $voiceChatFader = null;
|
||||||
|
|
||||||
|
var saveImmediate = null; // if true, then every action by the user results in a save to the backend immediately, false means you have to call trySave to persist
|
||||||
|
var uniqueCallbackName = null;
|
||||||
// needed because iCheck fires iChecked event even when you programmatically change it, unlike when using .val(x)
|
// needed because iCheck fires iChecked event even when you programmatically change it, unlike when using .val(x)
|
||||||
var ignoreICheckEvent = false;
|
var ignoreICheckEvent = false;
|
||||||
|
var lastSavedTime = new Date();
|
||||||
|
var vuOptions = null;
|
||||||
|
var faderHeight = null;
|
||||||
|
var startingState = null;
|
||||||
|
var resettedOnInvalidDevice = false; // this is set to true when we clear chat, and set to false when the user interacts in anyway
|
||||||
|
|
||||||
function defaultReuse() {
|
function defaultReuse() {
|
||||||
suppressChange(function(){
|
suppressChange(function(){
|
||||||
|
|
@ -34,6 +44,48 @@
|
||||||
return $useChatInputRadio.is(':checked');
|
return $useChatInputRadio.is(':checked');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onInvalidAudioDevice(e, data) {
|
||||||
|
logger.debug("voice_chat_helper: onInvalidAudioDevice")
|
||||||
|
|
||||||
|
if(resettedOnInvalidDevice) {
|
||||||
|
// we've already tried to clear the audio device, and the user hasn't interacted, but still we are getting this event
|
||||||
|
// we can't keep taking action, so stop
|
||||||
|
logger.error("voice_chat_helper: onInvalidAudioDevice: ignoring event because we have already tried to handle it");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resettedOnInvalidDevice = true;
|
||||||
|
|
||||||
|
$selectedChatInput = null;
|
||||||
|
// you can't do this in the event callback; it hangs the app indefinitely, and somehow 'sticks' the mic input into bad state until reboot
|
||||||
|
setTimeout(function() {
|
||||||
|
context.jamClient.FTUEClearChatInput();
|
||||||
|
context.jamClient.TrackSetChatEnable(true);
|
||||||
|
var result = context.jamClient.TrackSaveAssignments();
|
||||||
|
|
||||||
|
if(!result || result.length == 0) {
|
||||||
|
context.JK.Banner.showAlert('It appears the selected chat input is not functioning. Please try another chat input.');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.JK.alertSupportedNeeded("Unable to unwind invalid chat input selection.")
|
||||||
|
}
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function beforeShow() {
|
||||||
|
userInteracted();
|
||||||
|
renderNoVolume();
|
||||||
|
context.JK.onBackendEvent(ALERT_NAMES.AUDIO_DEVICE_NOT_PRESENT, 'voice_chat_helper', onInvalidAudioDevice);
|
||||||
|
registerVuCallbacks();
|
||||||
|
}
|
||||||
|
function beforeHide() {
|
||||||
|
context.JK.offBackendEvent(ALERT_NAMES.AUDIO_DEVICE_NOT_PRESENT, 'voice_chat_helper', onInvalidAudioDevice);
|
||||||
|
jamClient.FTUERegisterVUCallbacks('', '', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function userInteracted() {
|
||||||
|
resettedOnInvalidDevice = false;
|
||||||
|
}
|
||||||
|
|
||||||
function reset(forceDisabledChat) {
|
function reset(forceDisabledChat) {
|
||||||
|
|
||||||
$selectedChatInput = null;
|
$selectedChatInput = null;
|
||||||
|
|
@ -62,24 +114,36 @@
|
||||||
$selectedChatInput = $chatInput;
|
$selectedChatInput = $chatInput;
|
||||||
$selectedChatInput.attr('checked', 'checked');
|
$selectedChatInput.attr('checked', 'checked');
|
||||||
}
|
}
|
||||||
$chat.hide(); // we'll show it once it's styled with iCheck
|
//$chat.hide(); // we'll show it once it's styled with iCheck
|
||||||
$chatInputs.append($chat);
|
$chatInputs.append($chat);
|
||||||
});
|
});
|
||||||
|
|
||||||
var $radioButtons = $chatInputs.find('input[name="chat-device"]');
|
var $radioButtons = $chatInputs.find('input[name="chat-device"]');
|
||||||
context.JK.checkbox($radioButtons).on('ifChecked', function(e) {
|
context.JK.checkbox($radioButtons).on('ifChecked', function(e) {
|
||||||
|
userInteracted();
|
||||||
var $input = $(e.currentTarget);
|
var $input = $(e.currentTarget);
|
||||||
$selectedChatInput = $input; // for use in handleNext
|
$selectedChatInput = $input; // for use in handleNext
|
||||||
if(saveImmediate) {
|
if(saveImmediate) {
|
||||||
var channelId = $input.attr('data-channel-id');
|
var channelId = $input.attr('data-channel-id');
|
||||||
context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.CHAT);
|
lastSavedTime = new Date();
|
||||||
|
context.jamClient.TrackSetChatInput(channelId);
|
||||||
|
lastSavedTime = new Date();
|
||||||
|
//context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.CHAT);
|
||||||
var result = context.jamClient.TrackSaveAssignments();
|
var result = context.jamClient.TrackSaveAssignments();
|
||||||
|
|
||||||
if(!result || result.length == 0) {
|
if(!result || result.length == 0) {
|
||||||
// success
|
// success
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
context.JK.Banner.showAlert('Unable to save assignments. ' + result);
|
context.jamClient.FTUEClearChatInput();
|
||||||
|
context.jamClient.TrackSetChatEnable(true);
|
||||||
|
var result = context.jamClient.TrackSaveAssignments();
|
||||||
|
if(!result || result.length == 0) {
|
||||||
|
context.JK.Banner.showAlert('Unable to save chat selection. ' + result);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.JK.alertSupportedNeeded("Unable to unwind invalid chat selection.")
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,18 +153,17 @@
|
||||||
disableChatButtonsUI();
|
disableChatButtonsUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
$chatInputs.find('.chat-input').show().on('click', function() {
|
renderVolumes();
|
||||||
// obnoxious; remove soon XXX
|
|
||||||
// if(!isChatEnabled()) {
|
startingState = getCurrentState();
|
||||||
// context.JK.prodBubble($parent.find('.use-chat-input h3'), 'chat-not-enabled', {}, { positions:['left']});
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableChatButtonsUI() {
|
function disableChatButtonsUI() {
|
||||||
var $radioButtons = $chatInputs.find('input[name="chat-device"]');
|
var $radioButtons = $chatInputs.find('input[name="chat-device"]');
|
||||||
$radioButtons.iCheck('disable')
|
$radioButtons.iCheck('disable')
|
||||||
$chatInputs.addClass('disabled');
|
$chatInputs.addClass('disabled');
|
||||||
|
$radioButtons.iCheck('uncheck');
|
||||||
|
$selectedChatInput = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableChatButtonsUI() {
|
function enableChatButtonsUI() {
|
||||||
|
|
@ -133,6 +196,7 @@
|
||||||
var result = context.jamClient.TrackSaveAssignments();
|
var result = context.jamClient.TrackSaveAssignments();
|
||||||
|
|
||||||
if(!result || result.length == 0) {
|
if(!result || result.length == 0) {
|
||||||
|
renderNoVolume();
|
||||||
// success
|
// success
|
||||||
suppressChange(function() {
|
suppressChange(function() {
|
||||||
$reuseAudioInputRadio.iCheck('check').attr('checked', 'checked');
|
$reuseAudioInputRadio.iCheck('check').attr('checked', 'checked');
|
||||||
|
|
@ -151,7 +215,7 @@
|
||||||
$useChatInputRadio.removeAttr('checked');
|
$useChatInputRadio.removeAttr('checked');
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
disableChatButtonsUI()
|
disableChatButtonsUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableChat(applyToBackend) {
|
function enableChat(applyToBackend) {
|
||||||
|
|
@ -181,6 +245,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
enableChatButtonsUI();
|
enableChatButtonsUI();
|
||||||
|
renderVolumes();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChatEnabledToggle() {
|
function handleChatEnabledToggle() {
|
||||||
|
|
@ -191,8 +256,18 @@
|
||||||
$reuseAudioInputRadio.closest('.iradio_minimal').css('position', 'absolute');
|
$reuseAudioInputRadio.closest('.iradio_minimal').css('position', 'absolute');
|
||||||
$useChatInputRadio.closest('.iradio_minimal').css('position', 'absolute');
|
$useChatInputRadio.closest('.iradio_minimal').css('position', 'absolute');
|
||||||
|
|
||||||
$reuseAudioInputRadio.on('ifChecked', function() { if(!ignoreICheckEvent) disableChat(true) });
|
$reuseAudioInputRadio.on('ifChecked', function() {
|
||||||
$useChatInputRadio.on('ifChecked', function() { if(!ignoreICheckEvent) enableChat(true) });
|
if(!ignoreICheckEvent) {
|
||||||
|
userInteracted();
|
||||||
|
disableChat(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$useChatInputRadio.on('ifChecked', function() {
|
||||||
|
if(!ignoreICheckEvent) {
|
||||||
|
userInteracted();
|
||||||
|
enableChat(true)
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets the state of the UI
|
// gets the state of the UI
|
||||||
|
|
@ -208,9 +283,17 @@
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
function trySave() {
|
function cancel() {
|
||||||
|
logger.debug("canceling voice chat state");
|
||||||
|
return trySave(startingState);
|
||||||
|
}
|
||||||
|
|
||||||
var state = getCurrentState();
|
|
||||||
|
function trySave(state) {
|
||||||
|
|
||||||
|
if(!state) {
|
||||||
|
state = getCurrentState();
|
||||||
|
}
|
||||||
|
|
||||||
if(state.enabled && state.chat_channel) {
|
if(state.enabled && state.chat_channel) {
|
||||||
logger.debug("enabling chat. chat_channel=" + state.chat_channel);
|
logger.debug("enabling chat. chat_channel=" + state.chat_channel);
|
||||||
|
|
@ -231,6 +314,9 @@
|
||||||
|
|
||||||
if(!result || result.length == 0) {
|
if(!result || result.length == 0) {
|
||||||
// success
|
// success
|
||||||
|
if(!state.enabled) {
|
||||||
|
renderNoVolume();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -239,21 +325,80 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize(_$step, _saveImmediate) {
|
function initializeVUMeters() {
|
||||||
|
context.JK.VuHelpers.renderVU($voiceChatVuLeft, vuOptions);
|
||||||
|
context.JK.VuHelpers.renderVU($voiceChatVuRight, vuOptions);
|
||||||
|
|
||||||
|
context.JK.FaderHelpers.renderFader($voiceChatFader, {faderId: '', faderType: "vertical", height: faderHeight});
|
||||||
|
$voiceChatFader.on('fader_change', faderChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
// renders volumes based on what the backend says
|
||||||
|
function renderVolumes() {
|
||||||
|
var $fader = $voiceChatFader.find('[control="fader"]');
|
||||||
|
var db = context.jamClient.FTUEGetChatInputVolume();
|
||||||
|
var faderPct = db + 80;
|
||||||
|
context.JK.FaderHelpers.setHandlePosition($fader, faderPct);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNoVolume() {
|
||||||
|
var $fader = $voiceChatFader.find('[control="fader"]');
|
||||||
|
context.JK.FaderHelpers.setHandlePosition($fader, 50);
|
||||||
|
context.JK.VuHelpers.updateVU($voiceChatVuLeft, 0);
|
||||||
|
context.JK.VuHelpers.updateVU($voiceChatVuRight, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function faderChange(e, data) {
|
||||||
|
// TODO - using hardcoded range of -80 to 20 for output levels.
|
||||||
|
var mixerLevel = data.percentage - 80; // Convert our [0-100] to [-80 - +20] range
|
||||||
|
context.jamClient.FTUESetChatInputVolume(mixerLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerVuCallbacks() {
|
||||||
|
logger.debug("voice-chat-helper: registering vu callbacks");
|
||||||
|
jamClient.FTUERegisterVUCallbacks(
|
||||||
|
"JK.voiceChatHelperAudioOutputVUCallback",
|
||||||
|
"JK.voiceChatHelperAudioInputVUCallback",
|
||||||
|
"JK." + uniqueCallbackName
|
||||||
|
);
|
||||||
|
jamClient.SetVURefreshRate(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize(_$step, caller, _saveImmediate, _vuOptions, _faderHeight) {
|
||||||
$parent = _$step;
|
$parent = _$step;
|
||||||
saveImmediate = _saveImmediate;
|
saveImmediate = _saveImmediate;
|
||||||
|
vuOptions = _vuOptions;
|
||||||
|
faderHeight = _faderHeight;
|
||||||
$reuseAudioInputRadio = $parent.find('.reuse-audio-input input');
|
$reuseAudioInputRadio = $parent.find('.reuse-audio-input input');
|
||||||
$useChatInputRadio = $parent.find('.use-chat-input input');
|
$useChatInputRadio = $parent.find('.use-chat-input input');
|
||||||
$chatInputs = $parent.find('.chat-inputs');
|
$chatInputs = $parent.find('.chat-inputs');
|
||||||
$templateChatInput = $('#template-chat-input');
|
$templateChatInput = $('#template-chat-input');
|
||||||
|
$voiceChatVuLeft = $parent.find('.voice-chat-vu-left');
|
||||||
|
$voiceChatVuRight = $parent.find('.voice-chat-vu-right');
|
||||||
|
$voiceChatFader = $parent.find('.chat-fader')
|
||||||
|
|
||||||
handleChatEnabledToggle();
|
handleChatEnabledToggle();
|
||||||
|
initializeVUMeters();
|
||||||
|
renderVolumes();
|
||||||
|
|
||||||
|
uniqueCallbackName = 'voiceChatHelperChatInputVUCallback' + caller;
|
||||||
|
context.JK[uniqueCallbackName] = function(dbValue) {
|
||||||
|
context.JK.ftueVUCallback(dbValue, $voiceChatVuLeft);
|
||||||
|
context.JK.ftueVUCallback(dbValue, $voiceChatVuRight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.JK.voiceChatHelperAudioInputVUCallback = function (dbValue) {};
|
||||||
|
context.JK.voiceChatHelperAudioOutputVUCallback = function (dbValue) {};
|
||||||
|
|
||||||
|
|
||||||
this.reset = reset;
|
this.reset = reset;
|
||||||
this.trySave = trySave;
|
this.trySave = trySave;
|
||||||
|
this.cancel = cancel;
|
||||||
this.initialize = initialize;
|
this.initialize = initialize;
|
||||||
|
this.beforeShow = beforeShow;
|
||||||
|
this.beforeHide = beforeHide;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
* vuType can be either "horizontal" or "vertical"
|
* vuType can be either "horizontal" or "vertical"
|
||||||
*/
|
*/
|
||||||
renderVU: function(selector, userOptions) {
|
renderVU: function(selector, userOptions) {
|
||||||
|
selector = $(selector);
|
||||||
/**
|
/**
|
||||||
* The default options for rendering a VU
|
* The default options for rendering a VU
|
||||||
*/
|
*/
|
||||||
|
|
@ -35,25 +36,25 @@
|
||||||
templateSelector = "#template-vu-h";
|
templateSelector = "#template-vu-h";
|
||||||
}
|
}
|
||||||
var templateSource = $(templateSelector).html();
|
var templateSource = $(templateSelector).html();
|
||||||
$(selector).empty();
|
selector.empty();
|
||||||
|
|
||||||
$(selector).html(context._.template(templateSource, options, {variable: 'data'}));
|
selector.html(context._.template(templateSource, options, {variable: 'data'}));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a selector representing a container for a VU meter and
|
* Given a selector representing a container for a VU meter and
|
||||||
* a value between 0.0 and 1.0, light the appropriate lights.
|
* a value between 0.0 and 1.0, light the appropriate lights.
|
||||||
*/
|
*/
|
||||||
updateVU: function (selector, value) {
|
updateVU: function ($selector, value) {
|
||||||
// There are 13 VU lights. Figure out how many to
|
// There are 13 VU lights. Figure out how many to
|
||||||
// light based on the incoming value.
|
// light based on the incoming value.
|
||||||
var countSelector = 'tr';
|
var countSelector = 'tr';
|
||||||
var horizontal = ($('table.horizontal', selector).length);
|
var horizontal = ($selector.find('table.horizontal').length);
|
||||||
if (horizontal) {
|
if (horizontal) {
|
||||||
countSelector = 'td';
|
countSelector = 'td';
|
||||||
}
|
}
|
||||||
|
|
||||||
var lightCount = $(countSelector, selector).length;
|
var lightCount = $selector.find(countSelector).length;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var state = 'on';
|
var state = 'on';
|
||||||
var lights = Math.round(value * lightCount);
|
var lights = Math.round(value * lightCount);
|
||||||
|
|
@ -61,15 +62,15 @@
|
||||||
|
|
||||||
var $light = null;
|
var $light = null;
|
||||||
var colorClass = 'vu-green-';
|
var colorClass = 'vu-green-';
|
||||||
var lightSelectorPrefix = selector + ' td.vu';
|
var lightSelectorPrefix = $selector.find('td.vu');
|
||||||
var thisLightSelector = null;
|
var thisLightSelector = null;
|
||||||
|
|
||||||
// Remove all light classes from all lights
|
// Remove all light classes from all lights
|
||||||
var allLightsSelector = selector + ' td.vulight';
|
var allLightsSelector = $selector.find('td.vulight');
|
||||||
$(allLightsSelector).removeClass('vu-green-off vu-green-on vu-red-off vu-red-on');
|
$(allLightsSelector).removeClass('vu-green-off vu-green-on vu-red-off vu-red-on');
|
||||||
|
|
||||||
// Set the lights
|
// Set the lights
|
||||||
for (i=0; i<lightCount; i++) {
|
for (i = 0; i < lightCount; i++) {
|
||||||
colorClass = 'vu-green-';
|
colorClass = 'vu-green-';
|
||||||
state = 'on';
|
state = 'on';
|
||||||
if (i >= redSwitch) {
|
if (i >= redSwitch) {
|
||||||
|
|
@ -78,9 +79,9 @@
|
||||||
if (i >= lights) {
|
if (i >= lights) {
|
||||||
state = 'off';
|
state = 'off';
|
||||||
}
|
}
|
||||||
thisLightSelector = lightSelectorPrefix + i;
|
|
||||||
$light = $(thisLightSelector);
|
var lightIndex = horizontal ? i : lightCount - i - 1;
|
||||||
$light.addClass(colorClass + state);
|
allLightsSelector.eq(lightIndex).addClass(colorClass + state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,30 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function reportFailureAnalytics() {
|
||||||
|
// on cancel, see if the user is leaving without having passed the audio test; if so, report it
|
||||||
|
var lastAnalytics = stepSelectGear.getLastAudioTestFailAnalytics();
|
||||||
|
if(lastAnalytics) {
|
||||||
|
logger.debug("reporting audio test failed")
|
||||||
|
context.JK.GA.trackAudioTestFailure(lastAnalytics.platform, lastAnalytics.reason, lastAnalytics.data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug("audiotest failure: nothing to report");
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastAnalytics = stepNetworkTest.getLastNetworkFailAnalytics();
|
||||||
|
if(lastAnalytics) {
|
||||||
|
logger.debug("reporting network test failed");
|
||||||
|
context.JK.GA.trackNetworkTestFailure(lastAnalytics.reason, lastAnalytics.data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug("network test failure: nothing to report");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onCanceled() {
|
function onCanceled() {
|
||||||
|
reportFailureAnalytics();
|
||||||
|
|
||||||
if (app.cancelFtue) {
|
if (app.cancelFtue) {
|
||||||
app.cancelFtue();
|
app.cancelFtue();
|
||||||
app.afterFtue = null;
|
app.afterFtue = null;
|
||||||
|
|
@ -116,6 +139,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClosed() {
|
function onClosed() {
|
||||||
|
reportFailureAnalytics();
|
||||||
|
|
||||||
if (app.afterFtue) {
|
if (app.afterFtue) {
|
||||||
// If there's a function to invoke, invoke it.
|
// If there's a function to invoke, invoke it.
|
||||||
app.afterFtue();
|
app.afterFtue();
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,14 @@
|
||||||
var forceDisabledChat = firstTimeShown;
|
var forceDisabledChat = firstTimeShown;
|
||||||
|
|
||||||
voiceChatHelper.reset(forceDisabledChat);
|
voiceChatHelper.reset(forceDisabledChat);
|
||||||
|
voiceChatHelper.beforeShow();
|
||||||
firstTimeShown = false;
|
firstTimeShown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function beforeHide() {
|
||||||
|
voiceChatHelper.beforeHide();
|
||||||
|
}
|
||||||
|
|
||||||
function handleNext() {
|
function handleNext() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -39,13 +43,14 @@
|
||||||
function initialize(_$step) {
|
function initialize(_$step) {
|
||||||
$step = _$step;
|
$step = _$step;
|
||||||
|
|
||||||
voiceChatHelper.initialize($step, true);
|
voiceChatHelper.initialize($step, 'configure_voice_gear_wizard', true, {vuType: "vertical", lightCount: 8, lightWidth: 3, lightHeight: 10}, 101);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleNext = handleNext;
|
this.handleNext = handleNext;
|
||||||
this.newSession = newSession;
|
this.newSession = newSession;
|
||||||
this.beforeShow = beforeShow;
|
this.beforeShow = beforeShow;
|
||||||
|
this.beforeHide = beforeHide;
|
||||||
this.initialize = initialize;
|
this.initialize = initialize;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
context.JK = context.JK || {};
|
context.JK = context.JK || {};
|
||||||
context.JK.StepDirectMonitoring = function (app) {
|
context.JK.StepDirectMonitoring = function (app) {
|
||||||
|
|
||||||
|
var logger = context.JK.logger;
|
||||||
var $step = null;
|
var $step = null;
|
||||||
var $directMonitoringBtn = null;
|
var $directMonitoringBtn = null;
|
||||||
var isPlaying = false;
|
var isPlaying = false;
|
||||||
|
|
@ -58,6 +59,7 @@
|
||||||
|
|
||||||
function beforeShow() {
|
function beforeShow() {
|
||||||
context.jamClient.SessionRemoveAllPlayTracks();
|
context.jamClient.SessionRemoveAllPlayTracks();
|
||||||
|
logger.debug("adding test sound");
|
||||||
if(!context.jamClient.SessionAddPlayTrack("skin:jktest-audio.wav")) {
|
if(!context.jamClient.SessionAddPlayTrack("skin:jktest-audio.wav")) {
|
||||||
context.JK.alertSupportedNeeded('Unable to open test sound');
|
context.JK.alertSupportedNeeded('Unable to open test sound');
|
||||||
}
|
}
|
||||||
|
|
@ -68,6 +70,7 @@
|
||||||
stopPlay();
|
stopPlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug("removing test sound")
|
||||||
context.jamClient.SessionRemoveAllPlayTracks();
|
context.jamClient.SessionRemoveAllPlayTracks();
|
||||||
|
|
||||||
if(playCheckInterval) {
|
if(playCheckInterval) {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@
|
||||||
var $step = null;
|
var $step = null;
|
||||||
|
|
||||||
|
|
||||||
|
function getLastNetworkFailAnalytics() {
|
||||||
|
return networkTest.getLastNetworkFailure();
|
||||||
|
}
|
||||||
|
|
||||||
function networkTestDone() {
|
function networkTestDone() {
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
@ -62,6 +66,7 @@
|
||||||
this.beforeHide = beforeHide;
|
this.beforeHide = beforeHide;
|
||||||
this.beforeShow = beforeShow;
|
this.beforeShow = beforeShow;
|
||||||
this.initialize = initialize;
|
this.initialize = initialize;
|
||||||
|
this.getLastNetworkFailAnalytics = getLastNetworkFailAnalytics;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
context.JK = context.JK || {};
|
context.JK = context.JK || {};
|
||||||
context.JK.StepSelectGear = function (app, dialog) {
|
context.JK.StepSelectGear = function (app, dialog) {
|
||||||
|
|
||||||
|
var ALERT_NAMES = context.JK.ALERT_NAMES;
|
||||||
var EVENTS = context.JK.EVENTS;
|
var EVENTS = context.JK.EVENTS;
|
||||||
var ASSIGNMENT = context.JK.ASSIGNMENT;
|
var ASSIGNMENT = context.JK.ASSIGNMENT;
|
||||||
var VOICE_CHAT = context.JK.VOICE_CHAT;
|
var VOICE_CHAT = context.JK.VOICE_CHAT;
|
||||||
|
|
@ -19,6 +20,10 @@
|
||||||
var gearTest = new context.JK.GearTest(app);
|
var gearTest = new context.JK.GearTest(app);
|
||||||
var loopbackShowing = false;
|
var loopbackShowing = false;
|
||||||
|
|
||||||
|
// the goal of lastFailureAnalytics and trackedPass are to send only a single AudioTest 'Pass' or 'Failed' event, per FTUE wizard open/close
|
||||||
|
var lastFailureAnalytics = {};
|
||||||
|
var trackedPass = false;
|
||||||
|
|
||||||
var $watchVideoInput = null;
|
var $watchVideoInput = null;
|
||||||
var $watchVideoOutput = null;
|
var $watchVideoOutput = null;
|
||||||
var $audioInput = null;
|
var $audioInput = null;
|
||||||
|
|
@ -135,17 +140,11 @@
|
||||||
if(!audioInputDeviceId || audioInputDeviceId == '') {
|
if(!audioInputDeviceId || audioInputDeviceId == '') {
|
||||||
context.JK.prodBubble($audioInput.closest('.easydropdown-wrapper'), 'select-input', {}, {positions:['right', 'top']});
|
context.JK.prodBubble($audioInput.closest('.easydropdown-wrapper'), 'select-input', {}, {positions:['right', 'top']});
|
||||||
}
|
}
|
||||||
//context.JK.Banner.showAlert('To be a valid input audio device, the device must have at least 1 input channel.');
|
else {
|
||||||
return false;
|
// this path should be impossible because we filter output devices with 0 inputs from the input device dropdown
|
||||||
}
|
// but we might flip that, so it's nice to leave this in to catch us later
|
||||||
|
context.JK.Banner.showAlert('To be a valid input audio device, the device must have at least 1 input port.');
|
||||||
var $allOutputs = $outputChannels.find('input[type="checkbox"]');
|
|
||||||
if ($allOutputs.length < 2) {
|
|
||||||
if(!audioOutputDeviceId || audioOutputDeviceId == '') {
|
|
||||||
context.JK.prodBubble($audioOutput.closest('.easydropdown-wrapper'), 'select-output', {}, {positions:['right', 'top'], duration:7000});
|
|
||||||
}
|
}
|
||||||
// ERROR: not enough channels
|
|
||||||
//context.JK.Banner.showAlert('To be a valid output audio device, the device must have at least 2 output channels.');
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,6 +165,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var $allOutputs = $outputChannels.find('input[type="checkbox"]');
|
||||||
|
if ($allOutputs.length < 2) {
|
||||||
|
if(!audioOutputDeviceId || audioOutputDeviceId == '') {
|
||||||
|
context.JK.prodBubble($audioOutput.closest('.easydropdown-wrapper'), 'select-output', {}, {positions:['right', 'top'], duration:7000});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// this path indicates that the user has deliberately chosen a device, so we need to tell them that this device does not work with JamKazam
|
||||||
|
context.JK.prodBubble($audioOutput.closest('.easydropdown-wrapper'), 'select-output', {}, {positions:['right', 'top'], duration:7000});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ensure 2 outputs are selected
|
// ensure 2 outputs are selected
|
||||||
var $assignedOutputs = $outputChannels.find('input[type="checkbox"]:checked');
|
var $assignedOutputs = $outputChannels.find('input[type="checkbox"]:checked');
|
||||||
var $unassignedOutputs = $outputChannels.find('input[type="checkbox"]:not(:checked)');
|
var $unassignedOutputs = $outputChannels.find('input[type="checkbox"]:not(:checked)');
|
||||||
|
|
@ -435,10 +448,19 @@
|
||||||
|
|
||||||
function initializeASIOButtons() {
|
function initializeASIOButtons() {
|
||||||
$asioInputControlBtn.unbind('click').click(function () {
|
$asioInputControlBtn.unbind('click').click(function () {
|
||||||
|
if(gearTest.isScoring()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
context.jamClient.FTUEOpenControlPanel(selectedAudioInput());
|
context.jamClient.FTUEOpenControlPanel(selectedAudioInput());
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
$asioOutputControlBtn.unbind('click').click(function () {
|
$asioOutputControlBtn.unbind('click').click(function () {
|
||||||
|
if(gearTest.isScoring()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
context.jamClient.FTUEOpenControlPanel(selectedAudioOutput());
|
context.jamClient.FTUEOpenControlPanel(selectedAudioOutput());
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -715,7 +737,7 @@
|
||||||
// * reuse IO score if it was good/acceptable
|
// * reuse IO score if it was good/acceptable
|
||||||
// * rescore IO if it was bad or skipped from previous try
|
// * rescore IO if it was bad or skipped from previous try
|
||||||
function attemptScore(refocused) {
|
function attemptScore(refocused) {
|
||||||
gearTest.attemptScore(refocused);
|
gearTest.attemptScore(selectedDeviceInfo, refocused);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeAudioInputChanged() {
|
function initializeAudioInputChanged() {
|
||||||
|
|
@ -735,9 +757,42 @@
|
||||||
gearUtils.postDiagnostic(operatingSystem, deviceInformation, selectedDeviceInfo, gearTest, frameBuffers, true);
|
gearUtils.postDiagnostic(operatingSystem, deviceInformation, selectedDeviceInfo, gearTest, frameBuffers, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLastAudioTestFailAnalytics() {
|
||||||
|
return lastFailureAnalytics;
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeLastFailureForAnalytics(platform, reason, data) {
|
||||||
|
if(!trackedPass) {
|
||||||
|
lastFailureAnalytics = {
|
||||||
|
platform: platform,
|
||||||
|
reason: reason,
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onGearTestFail(e, data) {
|
function onGearTestFail(e, data) {
|
||||||
renderScoringStopped();
|
renderScoringStopped();
|
||||||
gearUtils.postDiagnostic(operatingSystem, deviceInformation, selectedDeviceInfo, gearTest, frameBuffers, true);
|
gearUtils.postDiagnostic(operatingSystem, deviceInformation, selectedDeviceInfo, gearTest, frameBuffers, true);
|
||||||
|
|
||||||
|
if(data.reason == "latency") {
|
||||||
|
storeLastFailureForAnalytics(context.JK.detectOS(), context.JK.GA.AudioTestFailReasons.latency, data.latencyScore);
|
||||||
|
}
|
||||||
|
else if(data.reason = "io") {
|
||||||
|
if(data.ioTarget == 'bad') {
|
||||||
|
storeLastFailureForAnalytics(context.JK.detectOS(), context.JK.GA.AudioTestFailReasons.ioTarget, data.ioTargetScore);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
storeLastFailureForAnalytics(context.JK.detectOS(), context.JK.GA.AudioTestFailReasons.ioVariance, data.ioVarianceScore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.error("unknown reason in onGearTestFail: " + data.reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onGearTestInvalidated(e, data) {
|
||||||
|
initializeNextButtonState();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNext() {
|
function handleNext() {
|
||||||
|
|
@ -775,8 +830,10 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
savedProfile = true;
|
|
||||||
context.JK.GA.trackAudioTestCompletion(context.JK.detectOS());
|
context.JK.GA.trackAudioTestCompletion(context.JK.detectOS());
|
||||||
|
trackedPass = true;
|
||||||
|
lastFailureAnalytics = null;
|
||||||
|
savedProfile = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -800,14 +857,21 @@
|
||||||
initializeResync();
|
initializeResync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function beforeWizardShow() {
|
||||||
|
lastFailureAnalytics = null;
|
||||||
|
trackedPass = false;
|
||||||
|
}
|
||||||
|
|
||||||
function beforeShow() {
|
function beforeShow() {
|
||||||
$(window).on('focus', onFocus);
|
$(window).on('focus', onFocus);
|
||||||
initializeNextButtonState();
|
initializeNextButtonState();
|
||||||
|
context.JK.onBackendEvent(ALERT_NAMES.AUDIO_DEVICE_NOT_PRESENT, 'gear_test', gearTest.onInvalidAudioDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
function beforeHide() {
|
function beforeHide() {
|
||||||
logger.debug("unregistering focus watch")
|
logger.debug("unregistering focus watch")
|
||||||
$(window).off('focus', onFocus);
|
$(window).off('focus', onFocus);
|
||||||
|
context.JK.offBackendEvent(ALERT_NAMES.AUDIO_DEVICE_NOT_PRESENT, 'gear_test', gearTest.onInvalidAudioDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetState() {
|
function resetState() {
|
||||||
|
|
@ -849,12 +913,15 @@
|
||||||
.on(gearTest.GEAR_TEST_START, onGearTestStarted)
|
.on(gearTest.GEAR_TEST_START, onGearTestStarted)
|
||||||
.on(gearTest.GEAR_TEST_DONE, onGearTestDone)
|
.on(gearTest.GEAR_TEST_DONE, onGearTestDone)
|
||||||
.on(gearTest.GEAR_TEST_FAIL, onGearTestFail)
|
.on(gearTest.GEAR_TEST_FAIL, onGearTestFail)
|
||||||
|
.on(gearTest.GEAR_TEST_INVALIDATED_ASYNC, onGearTestInvalidated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.getLastAudioTestFailAnalytics = getLastAudioTestFailAnalytics;
|
||||||
this.handleNext = handleNext;
|
this.handleNext = handleNext;
|
||||||
this.newSession = newSession;
|
this.newSession = newSession;
|
||||||
this.beforeShow = beforeShow;
|
this.beforeShow = beforeShow;
|
||||||
this.beforeHide = beforeHide;
|
this.beforeHide = beforeHide;
|
||||||
|
this.beforeWizardShow = beforeWizardShow;
|
||||||
this.initialize = initialize;
|
this.initialize = initialize;
|
||||||
|
|
||||||
self = this;
|
self = this;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,11 @@
|
||||||
var validIOScore = false;
|
var validIOScore = false;
|
||||||
var latencyScore = null;
|
var latencyScore = null;
|
||||||
var ioScore = null;
|
var ioScore = null;
|
||||||
|
var lastSavedTime = new Date();
|
||||||
|
// this should be marked TRUE when the backend sends an invalid_audio_device alert
|
||||||
|
var asynchronousInvalidDevice = false;
|
||||||
|
var selectedDeviceInfo = null;
|
||||||
|
|
||||||
|
|
||||||
var $scoreReport = null;
|
var $scoreReport = null;
|
||||||
var $ioHeader = null;
|
var $ioHeader = null;
|
||||||
|
|
@ -31,6 +36,7 @@
|
||||||
var $ioScoreSection = null;
|
var $ioScoreSection = null;
|
||||||
var $latencyScoreSection = null;
|
var $latencyScoreSection = null;
|
||||||
|
|
||||||
|
|
||||||
var $self = $(this);
|
var $self = $(this);
|
||||||
|
|
||||||
var GEAR_TEST_START = "gear_test.start";
|
var GEAR_TEST_START = "gear_test.start";
|
||||||
|
|
@ -41,9 +47,10 @@
|
||||||
var GEAR_TEST_DONE = "gear_test.done";
|
var GEAR_TEST_DONE = "gear_test.done";
|
||||||
var GEAR_TEST_FAIL = "gear_test.fail";
|
var GEAR_TEST_FAIL = "gear_test.fail";
|
||||||
var GEAR_TEST_IO_PROGRESS = "gear_test.io_progress";
|
var GEAR_TEST_IO_PROGRESS = "gear_test.io_progress";
|
||||||
|
var GEAR_TEST_INVALIDATED_ASYNC = "gear_test.async_invalidated"; // happens when backend alerts us device is invalid
|
||||||
|
|
||||||
function isGoodFtue() {
|
function isGoodFtue() {
|
||||||
return validLatencyScore && validIOScore;
|
return validLatencyScore && validIOScore && !asynchronousInvalidDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
function processIOScore(io) {
|
function processIOScore(io) {
|
||||||
|
|
@ -93,13 +100,15 @@
|
||||||
$self.triggerHandler(GEAR_TEST_DONE)
|
$self.triggerHandler(GEAR_TEST_DONE)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$self.triggerHandler(GEAR_TEST_FAIL, {reason:'io'});
|
$self.triggerHandler(GEAR_TEST_FAIL, {reason:'io', ioTarget: medianIOClass, ioTargetScore: median, ioVariance: stdIOClass, ioVarianceScore: std});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function automaticScore() {
|
function automaticScore() {
|
||||||
logger.debug("automaticScore: calling FTUESave(false)");
|
logger.debug("automaticScore: calling FTUESave(false)");
|
||||||
|
lastSavedTime = new Date(); // save before and after FTUESave, because the event happens in a multithreaded way
|
||||||
var result = jamClient.FTUESave(false);
|
var result = jamClient.FTUESave(false);
|
||||||
|
lastSavedTime = new Date();
|
||||||
if(result && result != "") {
|
if(result && result != "") {
|
||||||
logger.debug("unable to FTUESave(false). reason=" + result);
|
logger.debug("unable to FTUESave(false). reason=" + result);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -125,12 +134,14 @@
|
||||||
// on refocus=true:
|
// on refocus=true:
|
||||||
// * reuse IO score if it was good/acceptable
|
// * reuse IO score if it was good/acceptable
|
||||||
// * rescore IO if it was bad or skipped from previous try
|
// * rescore IO if it was bad or skipped from previous try
|
||||||
function attemptScore(refocused) {
|
function attemptScore(_selectedDeviceInfo, refocused) {
|
||||||
if(scoring) {
|
if(scoring) {
|
||||||
logger.debug("gear-test: already scoring");
|
logger.debug("gear-test: already scoring");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
selectedDeviceInfo = _selectedDeviceInfo;
|
||||||
scoring = true;
|
scoring = true;
|
||||||
|
asynchronousInvalidDevice = false;
|
||||||
$self.triggerHandler(GEAR_TEST_START);
|
$self.triggerHandler(GEAR_TEST_START);
|
||||||
$self.triggerHandler(GEAR_TEST_LATENCY_START);
|
$self.triggerHandler(GEAR_TEST_LATENCY_START);
|
||||||
validLatencyScore = false;
|
validLatencyScore = false;
|
||||||
|
|
@ -197,7 +208,7 @@
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
scoring = false;
|
scoring = false;
|
||||||
$self.triggerHandler(GEAR_TEST_FAIL, {reason:'latency'})
|
$self.triggerHandler(GEAR_TEST_FAIL, {reason:'latency', latencyScore: latencyScore.latency})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, 250);
|
}, 250);
|
||||||
|
|
@ -276,6 +287,18 @@
|
||||||
return ioScore;
|
return ioScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLastSavedTime() {
|
||||||
|
return lastSavedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onInvalidAudioDevice() {
|
||||||
|
logger.debug("gear_test: onInvalidAudioDevice")
|
||||||
|
asynchronousInvalidDevice = true;
|
||||||
|
$self.triggerHandler(GEAR_TEST_INVALIDATED_ASYNC);
|
||||||
|
context.JK.Banner.showAlert('Invalid Audio Device', 'It appears this audio device is not currently connected. Attach the device to your computer and restart the application, or select a different device.')
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function showLoopbackDone() {
|
function showLoopbackDone() {
|
||||||
$loopbackCompleted.show();
|
$loopbackCompleted.show();
|
||||||
}
|
}
|
||||||
|
|
@ -301,6 +324,7 @@
|
||||||
function invalidateScore() {
|
function invalidateScore() {
|
||||||
validLatencyScore = false;
|
validLatencyScore = false;
|
||||||
validIOScore = false;
|
validIOScore = false;
|
||||||
|
asynchronousInvalidDevice = false;
|
||||||
resetScoreReport();
|
resetScoreReport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,6 +361,18 @@
|
||||||
$ioCountdownSecs.text(secondsLeft);
|
$ioCountdownSecs.text(secondsLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function uniqueDeviceName() {
|
||||||
|
try {
|
||||||
|
return selectedDeviceInfo.input.info.displayName + '(' + selectedDeviceInfo.input.behavior.shortName + ')' + '-' +
|
||||||
|
selectedDeviceInfo.output.info.displayName + '(' + selectedDeviceInfo.output.behavior.shortName + ')' + '-' +
|
||||||
|
context.JK.GetOSAsString();
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
logger.error("unable to devise unique device name for stats: " + e.toString());
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleUI($testResults) {
|
function handleUI($testResults) {
|
||||||
|
|
||||||
if(!$testResults.is('.ftue-box.results')) {
|
if(!$testResults.is('.ftue-box.results')) {
|
||||||
|
|
@ -394,6 +430,9 @@
|
||||||
|
|
||||||
function onGearTestDone(e, data) {
|
function onGearTestDone(e, data) {
|
||||||
$resultsText.attr('scored', 'complete');
|
$resultsText.attr('scored', 'complete');
|
||||||
|
|
||||||
|
context.JK.GA.trackAudioTestData(uniqueDeviceName(), context.JK.GA.AudioTestDataReasons.pass, latencyScore);
|
||||||
|
|
||||||
rest.userCertifiedGear({success: true, client_id: app.clientId, audio_latency: getLatencyScore()});
|
rest.userCertifiedGear({success: true, client_id: app.clientId, audio_latency: getLatencyScore()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -405,6 +444,21 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
rest.userCertifiedGear({success: false});
|
rest.userCertifiedGear({success: false});
|
||||||
|
|
||||||
|
if(data.reason == "latency") {
|
||||||
|
context.JK.GA.trackAudioTestData(uniqueDeviceName(), context.JK.GA.AudioTestDataReasons.latencyFail, data.latencyScore);
|
||||||
|
}
|
||||||
|
else if(data.reason = "io") {
|
||||||
|
if(data.ioTarget == 'bad') {
|
||||||
|
context.JK.GA.trackAudioTestData(uniqueDeviceName(), context.JK.GA.AudioTestDataReasons.ioTargetFail, data.ioTargetScore);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.JK.GA.trackAudioTestData(uniqueDeviceName(), context.JK.GA.AudioTestDataReasons.ioVarianceFail, data.ioVarianceScore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.error("unknown reason in onGearTestFail: " + data.reason)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$self
|
$self
|
||||||
|
|
@ -445,6 +499,7 @@
|
||||||
this.GEAR_TEST_DONE = GEAR_TEST_DONE;
|
this.GEAR_TEST_DONE = GEAR_TEST_DONE;
|
||||||
this.GEAR_TEST_FAIL = GEAR_TEST_FAIL;
|
this.GEAR_TEST_FAIL = GEAR_TEST_FAIL;
|
||||||
this.GEAR_TEST_IO_PROGRESS = GEAR_TEST_IO_PROGRESS;
|
this.GEAR_TEST_IO_PROGRESS = GEAR_TEST_IO_PROGRESS;
|
||||||
|
this.GEAR_TEST_INVALIDATED_ASYNC = GEAR_TEST_INVALIDATED_ASYNC;
|
||||||
|
|
||||||
this.initialize = initialize;
|
this.initialize = initialize;
|
||||||
this.isScoring = isScoring;
|
this.isScoring = isScoring;
|
||||||
|
|
@ -457,6 +512,8 @@
|
||||||
this.isGoodFtue = isGoodFtue;
|
this.isGoodFtue = isGoodFtue;
|
||||||
this.getLatencyScore = getLatencyScore;
|
this.getLatencyScore = getLatencyScore;
|
||||||
this.getIOScore = getIOScore;
|
this.getIOScore = getIOScore;
|
||||||
|
this.getLastSavedTime = getLastSavedTime;
|
||||||
|
this.onInvalidAudioDevice = onInvalidAudioDevice;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,6 @@
|
||||||
rest.createDiagnostic({
|
rest.createDiagnostic({
|
||||||
type: 'GEAR_SELECTION',
|
type: 'GEAR_SELECTION',
|
||||||
data: {
|
data: {
|
||||||
logs: logger.logCache,
|
|
||||||
client_type: context.JK.clientType(),
|
client_type: context.JK.clientType(),
|
||||||
client_id:
|
client_id:
|
||||||
context.JK.JamServer.clientID,
|
context.JK.JamServer.clientID,
|
||||||
|
|
@ -249,7 +248,6 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.debug("chatInputs:", chatInputs)
|
|
||||||
return chatInputs;
|
return chatInputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,12 @@
|
||||||
var $outputChannels = null;
|
var $outputChannels = null;
|
||||||
var $templateAudioPort = null;
|
var $templateAudioPort = null;
|
||||||
var $scoreReport = null;
|
var $scoreReport = null;
|
||||||
|
var $audioInputVuLeft = null;
|
||||||
|
var $audioInputVuRight = null;
|
||||||
|
var $audioInputFader = null;
|
||||||
|
var $audioOutputVuLeft = null;
|
||||||
|
var $audioOutputVuRight = null;
|
||||||
|
var $audioOutputFader = null;
|
||||||
|
|
||||||
var faderMap = {
|
var faderMap = {
|
||||||
'loopback-audio-input-fader': jamClient.FTUESetInputVolume,
|
'loopback-audio-input-fader': jamClient.FTUESetInputVolume,
|
||||||
|
|
@ -42,7 +48,7 @@
|
||||||
|
|
||||||
|
|
||||||
function attemptScore() {
|
function attemptScore() {
|
||||||
gearTest.attemptScore();
|
gearTest.attemptScore(selectedDeviceInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -220,43 +226,45 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeVUMeters() {
|
function initializeVUMeters() {
|
||||||
var vuMeters = [
|
var vuOptions = {vuType: "horizontal", lightCount: 12, lightWidth: 15, lightHeight: 3};
|
||||||
'#loopback-audio-input-vu-left',
|
|
||||||
'#loopback-audio-input-vu-right',
|
|
||||||
'#loopback-audio-output-vu-left',
|
|
||||||
'#loopback-audio-output-vu-right'
|
|
||||||
];
|
|
||||||
$.each(vuMeters, function () {
|
|
||||||
context.JK.VuHelpers.renderVU(this,
|
|
||||||
{vuType: "horizontal", lightCount: 12, lightWidth: 15, lightHeight: 3});
|
|
||||||
});
|
|
||||||
|
|
||||||
var faders = context._.keys(faderMap);
|
context.JK.VuHelpers.renderVU($audioInputVuLeft, vuOptions);
|
||||||
$.each(faders, function () {
|
context.JK.VuHelpers.renderVU($audioInputVuRight, vuOptions);
|
||||||
var fid = this;
|
|
||||||
context.JK.FaderHelpers.renderFader('#' + fid,
|
context.JK.VuHelpers.renderVU($audioOutputVuLeft, vuOptions);
|
||||||
{faderId: fid, faderType: "horizontal", width: 163});
|
context.JK.VuHelpers.renderVU($audioOutputVuRight, vuOptions);
|
||||||
context.JK.FaderHelpers.subscribe(fid, faderChange);
|
|
||||||
});
|
var faderOptions = {faderId: '', faderType: "horizontal", width: 163};
|
||||||
|
context.JK.FaderHelpers.renderFader($audioInputFader, faderOptions);
|
||||||
|
context.JK.FaderHelpers.renderFader($audioOutputFader, faderOptions);
|
||||||
|
$audioInputFader.on('fader_change', inputFaderChange);
|
||||||
|
$audioOutputFader.on('fader_change', outputFaderChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
// renders volumes based on what the backend says
|
// renders volumes based on what the backend says
|
||||||
function renderVolumes() {
|
function renderVolumes() {
|
||||||
$.each(context._.keys(faderReadMap), function (index, faderId) {
|
|
||||||
// faderChange takes a value from 0-100
|
|
||||||
var $fader = $('[fader-id="' + faderId + '"]');
|
|
||||||
|
|
||||||
var db = faderReadMap[faderId]();
|
// input
|
||||||
var faderPct = db + 80;
|
var $inputFader = $audioInputFader.find('[control="fader"]');
|
||||||
context.JK.FaderHelpers.setHandlePosition($fader, faderPct);
|
var db = context.jamClient.FTUEGetInputVolume();
|
||||||
});
|
var faderPct = db + 80;
|
||||||
|
context.JK.FaderHelpers.setHandlePosition($inputFader, faderPct);
|
||||||
|
|
||||||
|
// output
|
||||||
|
var $outputFader = $audioOutputFader.find('[control="fader"]');
|
||||||
|
var db = context.jamClient.FTUEGetOutputVolume();
|
||||||
|
var faderPct = db + 80;
|
||||||
|
context.JK.FaderHelpers.setHandlePosition($outputFader, faderPct);
|
||||||
}
|
}
|
||||||
|
|
||||||
function faderChange(faderId, newValue, dragging) {
|
function inputFaderChange(e, data) {
|
||||||
var setFunction = faderMap[faderId];
|
var mixerLevel = data.percentage - 80; // Convert our [0-100] to [-80 - +20] range
|
||||||
// TODO - using hardcoded range of -80 to 20 for output levels.
|
context.jamClient.FTUESetInputVolume(mixerLevel);
|
||||||
var mixerLevel = newValue - 80; // Convert our [0-100] to [-80 - +20] range
|
}
|
||||||
setFunction(mixerLevel);
|
|
||||||
|
function outputFaderChange(e, data) {
|
||||||
|
var mixerLevel = data.percentage - 80;
|
||||||
|
context.jamClient.FTUESetOutputVolume(mixerLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerVuCallbacks() {
|
function registerVuCallbacks() {
|
||||||
|
|
@ -329,6 +337,13 @@
|
||||||
$outputChannels = $step.find('.output-ports')
|
$outputChannels = $step.find('.output-ports')
|
||||||
$templateAudioPort = $('#template-audio-port');
|
$templateAudioPort = $('#template-audio-port');
|
||||||
$scoreReport = $step.find('.results');
|
$scoreReport = $step.find('.results');
|
||||||
|
$audioInputVuLeft = $step.find('.audio-input-vu-left');
|
||||||
|
$audioInputVuRight = $step.find('.audio-input-vu-right');
|
||||||
|
$audioInputFader= $step.find('.audio-input-fader');
|
||||||
|
$audioOutputVuLeft = $step.find('.audio-output-vu-left');
|
||||||
|
$audioOutputVuRight = $step.find('.audio-output-vu-right');
|
||||||
|
$audioOutputFader= $step.find('.audio-output-fader');
|
||||||
|
|
||||||
operatingSystem = context.JK.GetOSAsString();
|
operatingSystem = context.JK.GetOSAsString();
|
||||||
|
|
||||||
frameBuffers.initialize($step.find('.frame-and-buffers'));
|
frameBuffers.initialize($step.find('.frame-and-buffers'));
|
||||||
|
|
@ -348,18 +363,19 @@
|
||||||
|
|
||||||
initializeASIOButtons();
|
initializeASIOButtons();
|
||||||
initializeResync();
|
initializeResync();
|
||||||
}
|
|
||||||
|
|
||||||
context.JK.loopbackAudioInputVUCallback = function (dbValue) {
|
|
||||||
context.JK.ftueVUCallback(dbValue, '#loopback-audio-input-vu-left');
|
context.JK.loopbackAudioInputVUCallback = function (dbValue) {
|
||||||
context.JK.ftueVUCallback(dbValue, '#loopback-audio-input-vu-right');
|
context.JK.ftueVUCallback(dbValue, $audioInputVuLeft);
|
||||||
};
|
context.JK.ftueVUCallback(dbValue, $audioInputVuRight);
|
||||||
context.JK.loopbackAudioOutputVUCallback = function (dbValue) {
|
};
|
||||||
context.JK.ftueVUCallback(dbValue, '#loopback-audio-output-vu-left');
|
context.JK.loopbackAudioOutputVUCallback = function (dbValue) {
|
||||||
context.JK.ftueVUCallback(dbValue, '#loopback-audio-output-vu-right');
|
context.JK.ftueVUCallback(dbValue, $audioOutputVuLeft);
|
||||||
};
|
context.JK.ftueVUCallback(dbValue, $audioOutputVuRight);
|
||||||
context.JK.loopbackChatInputVUCallback = function (dbValue) {
|
};
|
||||||
};
|
context.JK.loopbackChatInputVUCallback = function (dbValue) {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
this.getGearTest = getGearTest;
|
this.getGearTest = getGearTest;
|
||||||
this.handleNext = handleNext;
|
this.handleNext = handleNext;
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,13 @@
|
||||||
|
|
||||||
function onBeforeShow(args) {
|
function onBeforeShow(args) {
|
||||||
|
|
||||||
|
context._.each(STEPS, function(step) {
|
||||||
|
// let every step know the wizard is being shown
|
||||||
|
if(step.beforeWizardShow) {
|
||||||
|
step.beforeWizardShow(args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$currentWizardStep = null;
|
$currentWizardStep = null;
|
||||||
previousStep = null;
|
previousStep = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
float:left;
|
float:left;
|
||||||
vertical-align:top;
|
vertical-align:top;
|
||||||
@include border_box_sizing;
|
@include border_box_sizing;
|
||||||
padding: 20px 20px 0 0;
|
padding: 0 20px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub-column {
|
.sub-column {
|
||||||
|
|
@ -48,6 +48,25 @@
|
||||||
@include border_box_sizing;
|
@include border_box_sizing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ftue-box.chat-inputs {
|
||||||
|
height:224px;
|
||||||
|
width:90%;
|
||||||
|
@include border_box_sizing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vu-meter {
|
||||||
|
width:10%;
|
||||||
|
height:224px;
|
||||||
|
padding: 0 3px;
|
||||||
|
|
||||||
|
.ftue-controls {
|
||||||
|
height: 224px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
padding-top:20px;
|
||||||
|
}
|
||||||
.tab[tab-id="music-audio"] {
|
.tab[tab-id="music-audio"] {
|
||||||
.column {
|
.column {
|
||||||
&:nth-of-type(1) {
|
&:nth-of-type(1) {
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.channels-holder {
|
||||||
|
.ftue-input {
|
||||||
|
background-position:4px 6px;
|
||||||
|
padding-left:14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
.unassigned-input-channels {
|
.unassigned-input-channels {
|
||||||
min-height: 22px;
|
min-height: 240px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height:23%;
|
max-height:93%;
|
||||||
|
|
||||||
//padding-right:18px; // to keep draggables off of scrollbar. maybe necessary
|
//padding-right:18px; // to keep draggables off of scrollbar. maybe necessary
|
||||||
|
|
||||||
|
|
@ -39,6 +45,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ftue-input {
|
.ftue-input {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.drag-in-progress {
|
&.drag-in-progress {
|
||||||
|
|
@ -84,6 +91,7 @@
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
cursor: move;
|
cursor: move;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
padding-left:10px;
|
||||||
border: solid 1px #999;
|
border: solid 1px #999;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
@ -91,6 +99,9 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
line-height:20px;
|
line-height:20px;
|
||||||
|
background-image:url('/assets/content/icon_drag_handle.png');
|
||||||
|
background-position:0 3px;
|
||||||
|
background-repeat:no-repeat;
|
||||||
//direction: rtl;
|
//direction: rtl;
|
||||||
&.ui-draggable-dragging {
|
&.ui-draggable-dragging {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
@ -126,10 +137,13 @@
|
||||||
|
|
||||||
.ftue-input {
|
.ftue-input {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
padding-left:10px;
|
||||||
border: 0;
|
border: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
&.ui-draggable-dragging {
|
&.ui-draggable-dragging {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
padding-left:14px;
|
||||||
|
background-position:4px 6px;
|
||||||
border: solid 1px #999;
|
border: solid 1px #999;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +177,7 @@
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
&:nth-of-type(2) {
|
&:nth-of-type(2) {
|
||||||
padding-left:2px;
|
padding-left:10px;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,14 @@
|
||||||
display:inline;
|
display:inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recording-controls {
|
.recording-position {
|
||||||
position:relative;
|
margin: 5px 0 0 -15px;
|
||||||
|
width: 95%;
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recording-current {
|
||||||
|
position:absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icheckbuttons {
|
.icheckbuttons {
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,31 @@
|
||||||
top:85px;
|
top:85px;
|
||||||
left:12px;
|
left:12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recording-position {
|
||||||
|
display:inline-block;
|
||||||
|
width:80%;
|
||||||
|
|
||||||
|
font-family:Arial, Helvetica, sans-serif;
|
||||||
|
font-size:11px;
|
||||||
|
height:18px;
|
||||||
|
vertical-align:top;
|
||||||
|
margin-left:15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recording-controls {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
height: 25px;
|
||||||
|
|
||||||
|
.play-button {
|
||||||
|
top:2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.playback-mode-buttons {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -509,18 +534,6 @@ table.vu td {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.recording-controls {
|
|
||||||
display:none;
|
|
||||||
|
|
||||||
.play-button {
|
|
||||||
outline:none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.play-button img.pausebutton {
|
|
||||||
display:none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#recording-finished-dialog .recording-controls {
|
#recording-finished-dialog .recording-controls {
|
||||||
display:block;
|
display:block;
|
||||||
}
|
}
|
||||||
|
|
@ -624,16 +637,6 @@ table.vu td {
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recording-position {
|
|
||||||
display:inline-block;
|
|
||||||
width:80%;
|
|
||||||
|
|
||||||
font-family:Arial, Helvetica, sans-serif;
|
|
||||||
font-size:11px;
|
|
||||||
height:18px;
|
|
||||||
vertical-align:top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recording-time {
|
.recording-time {
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
height:16px;
|
height:16px;
|
||||||
|
|
@ -672,6 +675,22 @@ table.vu td {
|
||||||
font-size:18px;
|
font-size:18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recording-controls {
|
||||||
|
display:none;
|
||||||
|
|
||||||
|
.play-button {
|
||||||
|
outline:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-button img.pausebutton {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.playback-mode-buttons {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
.currently-recording {
|
.currently-recording {
|
||||||
background-color: $ColorRecordingBackground;
|
background-color: $ColorRecordingBackground;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,47 @@
|
||||||
|
@import "client/common.css.scss";
|
||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
.dialog.configure-tracks, .dialog.gear-wizard {
|
.dialog.configure-tracks, .dialog.gear-wizard {
|
||||||
|
|
||||||
|
table.vu {
|
||||||
|
position:absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ftue-controls {
|
||||||
|
position:relative;
|
||||||
|
width: 55px;
|
||||||
|
background-color: #222;
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
.ftue-vu-left {
|
||||||
|
position: relative;
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
.ftue-vu-right {
|
||||||
|
position: relative;
|
||||||
|
left: 46px;
|
||||||
|
}
|
||||||
|
.ftue-fader {
|
||||||
|
//margin:5px 6px;
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gain-label {
|
||||||
|
color: $ColorScreenPrimary;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -1px;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.voicechat-option {
|
.voicechat-option {
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
float:left;
|
||||||
|
width:50%;
|
||||||
|
height:109px;
|
||||||
|
|
||||||
div {
|
div {
|
||||||
|
|
||||||
|
|
@ -33,14 +72,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ftue-box {
|
.vu-meter {
|
||||||
|
@include border_box_sizing;
|
||||||
|
float:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ftue-box {
|
||||||
|
@include border_box_sizing;
|
||||||
background-color: #222222;
|
background-color: #222222;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
&.chat-inputs {
|
&.chat-inputs {
|
||||||
height: 230px !important;
|
//height: 230px !important;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
color:white;
|
color:white;
|
||||||
|
float:left;
|
||||||
|
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
color:gray;
|
color:gray;
|
||||||
|
|
@ -50,6 +98,7 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
line-height:20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input {
|
.chat-input {
|
||||||
|
|
@ -60,5 +109,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,6 +99,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.results-text {
|
ul.results-text {
|
||||||
|
margin-left:-5px;
|
||||||
padding: 10px 8px;
|
padding: 10px 8px;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.instructions {
|
&.instructions {
|
||||||
height: 268px !important;
|
height: 248px !important;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
@include border_box_sizing;
|
@include border_box_sizing;
|
||||||
|
|
||||||
|
|
@ -203,14 +203,40 @@
|
||||||
width: 25%;
|
width: 25%;
|
||||||
|
|
||||||
&:nth-of-type(2) {
|
&:nth-of-type(2) {
|
||||||
width: 50%;
|
width: 75%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.instructions {
|
||||||
|
height: 268px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.watch-video {
|
.watch-video {
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ftue-box.chat-inputs {
|
||||||
|
height:124px;
|
||||||
|
width:85%;
|
||||||
|
@include border_box_sizing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vu-meter {
|
||||||
|
width:15%;
|
||||||
|
padding: 0 3px;
|
||||||
|
|
||||||
|
.ftue-controls {
|
||||||
|
height: 124px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-fader {
|
||||||
|
top:4px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.voice-chat-header {
|
||||||
|
margin-top:10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-step[layout-wizard-step="4"] {
|
.wizard-step[layout-wizard-step="4"] {
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.ftue-controls {
|
.ftue-controls {
|
||||||
margin-top: 16px;
|
margin-top: 10px;
|
||||||
position:relative;
|
position:relative;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
width: 220px;
|
width: 220px;
|
||||||
|
|
@ -101,11 +101,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.ports-header {
|
.ports-header {
|
||||||
margin-top:20px;
|
margin-top:10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ports {
|
.ports {
|
||||||
height:100px;
|
height:100px;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asio-settings-input-btn, .asio-settings-output-btn {
|
.asio-settings-input-btn, .asio-settings-output-btn {
|
||||||
|
|
@ -114,7 +115,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.resync-btn {
|
.resync-btn {
|
||||||
margin-top:35px;
|
margin-top:29px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.run-test-btn {
|
.run-test-btn {
|
||||||
|
|
@ -124,7 +125,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.frame-and-buffers {
|
.frame-and-buffers {
|
||||||
margin-top:10px;
|
margin-top:4px;
|
||||||
}
|
}
|
||||||
.test-results-header {
|
.test-results-header {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ class ArtifactsController < ApiController
|
||||||
render :json => result, :status => :ok
|
render :json => result, :status => :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def healthcheck
|
||||||
|
render :json => {}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def versioncheck
|
def versioncheck
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<!-- dialog header -->
|
<!-- dialog header -->
|
||||||
<div class="content-head">
|
<div class="content-head">
|
||||||
<%= image_tag("content/icon_alert.png", :height => '24', :width => '24', :class => "content-icon") %><h1>alert</h1>
|
<%= image_tag("content/icon_alert.png", :height => '24', :width => '24', :class => "content-icon") %><h1></h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,21 +48,26 @@
|
||||||
|
|
||||||
.tab{'tab-id' => 'voice-chat'}
|
.tab{'tab-id' => 'voice-chat'}
|
||||||
|
|
||||||
.column
|
%form.select-voice-chat-option.section.voice
|
||||||
%form.select-voice-chat-option.section
|
.sub-header Select Voice Chat Option
|
||||||
.sub-header Select Voice Chat Option
|
.voicechat-option.reuse-audio-input
|
||||||
.voicechat-option.reuse-audio-input
|
%input{type:"radio", name: "voicechat", checked:"checked"}
|
||||||
%input{type:"radio", name: "voicechat", checked:"checked"}
|
%h3 Use Music Microphone
|
||||||
%h3 Use Music Microphone
|
%p I am already using a microphone to capture my vocal or instrumental music, so I can talk with other musicians using that microphone
|
||||||
%p I am already using a microphone to capture my vocal or instrumental music, so I can talk with other musicians using that microphone
|
.voicechat-option.use-chat-input
|
||||||
.voicechat-option.use-chat-input
|
%input{type:"radio", name: "voicechat"}
|
||||||
%input{type:"radio", name: "voicechat"}
|
%h3 Use Chat Microphone
|
||||||
%h3 Use Chat Microphone
|
%p I am not using a microphone for acoustic instruments or vocals, so use the input selected to the right for voice chat during my sessions
|
||||||
%p I am not using a microphone for acoustic instruments or vocals, so use the input selected to the right for voice chat during my sessions
|
.clearall
|
||||||
.column
|
.select-voice-chat
|
||||||
.select-voice-chat
|
.sub-header Voice Chat Input
|
||||||
.sub-header Voice Chat Input
|
.ftue-box.chat-inputs
|
||||||
.ftue-box.chat-inputs
|
.vu-meter
|
||||||
|
.ftue-controls
|
||||||
|
.ftue-vu-left.voice-chat-vu-left
|
||||||
|
.ftue-fader.chat-fader
|
||||||
|
.gain-label GAIN
|
||||||
|
.ftue-vu-right.voice-chat-vu-right
|
||||||
.clearall
|
.clearall
|
||||||
|
|
||||||
.buttons
|
.buttons
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,8 @@
|
||||||
|
|
||||||
<script type="text/template" id="template-help-move-on-loopback-success">
|
<script type="text/template" id="template-help-move-on-loopback-success">
|
||||||
You can move to the next step now.
|
You can move to the next step now.
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/template" id="template-help-minimum-output-channels">
|
||||||
|
To be a valid output audio device, it must have at least 2 output ports.
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -277,10 +277,6 @@
|
||||||
var testBridgeScreen = new JK.TestBridgeScreen(JK.app);
|
var testBridgeScreen = new JK.TestBridgeScreen(JK.app);
|
||||||
testBridgeScreen.initialize();
|
testBridgeScreen.initialize();
|
||||||
|
|
||||||
if(!connected) {
|
|
||||||
jamServer.initiateReconnect(null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
JK.app.initialRouting();
|
JK.app.initialRouting();
|
||||||
JK.hideCurtain(300);
|
JK.hideCurtain(300);
|
||||||
}
|
}
|
||||||
|
|
@ -317,8 +313,12 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
// this ensures that there is always a CurrentSessionModel, even if it's for a non-active session
|
// this ensures that there is always a CurrentSessionModel, even if it's for a non-active session
|
||||||
JK.CurrentSessionModel = new JK.SessionModel(JK.app, JK.JamServer, window.jamClient);
|
JK.CurrentSessionModel = new JK.SessionModel(JK.app, JK.JamServer, window.jamClient, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make surethe CurrentSessionModel exists before initializing backend alerts
|
||||||
|
var backendAlerts = new JK.BackendAlerts(JK.app);
|
||||||
|
backendAlerts.initialize();
|
||||||
|
|
||||||
JK.bindHoverEvents();
|
JK.bindHoverEvents();
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,6 @@
|
||||||
function _initAfterConnect(connected) {
|
function _initAfterConnect(connected) {
|
||||||
if (this.didInitAfterConnect) return;
|
if (this.didInitAfterConnect) return;
|
||||||
this.didInitAfterConnect = true
|
this.didInitAfterConnect = true
|
||||||
|
|
||||||
if(!connected) {
|
|
||||||
jamServer.initiateReconnect(null, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JK.app = JK.JamKazam();
|
JK.app = JK.JamKazam();
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,13 @@
|
||||||
.clearall
|
.clearall
|
||||||
%ul.results-text
|
%ul.results-text
|
||||||
%li.latency-good Your latency is good.
|
%li.latency-good Your latency is good.
|
||||||
%li.latency-acceptable Your latency is acceptable.
|
%li.latency-acceptable Your latency is fair.
|
||||||
%li.latency-bad Your latency is poor.
|
%li.latency-bad Your latency is poor.
|
||||||
%li.io-rate-good Your I/O rate is good.
|
%li.io-rate-good Your I/O rate is good.
|
||||||
%li.io-rate-acceptable Your I/O rate is acceptable.
|
%li.io-rate-acceptable Your I/O rate is fair.
|
||||||
%li.io-rate-bad Your I/O rate is poor.
|
%li.io-rate-bad Your I/O rate is poor.
|
||||||
%li.io-var-good Your I/O variance is good.
|
%li.io-var-good Your I/O variance is good.
|
||||||
%li.io-var-acceptable Your I/O variance is acceptable.
|
%li.io-var-acceptable Your I/O variance is fair.
|
||||||
%li.io-var-bad Your I/O variance is poor.
|
%li.io-var-bad Your I/O variance is poor.
|
||||||
%li.success You may proceed to the next step.
|
%li.success You may proceed to the next step.
|
||||||
%li.failure
|
%li.failure
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@
|
||||||
%li Drag and drop the input port(s) from your audio interface to each track.
|
%li Drag and drop the input port(s) from your audio interface to each track.
|
||||||
%li Select the instrument for each track.
|
%li Select the instrument for each track.
|
||||||
.center
|
.center
|
||||||
%a.button-orange.watch-video{href:'#'} WATCH VIDEO
|
%a.button-orange.watch-video{href:'https://www.youtube.com/watch?v=SjMeMZpKNR4', rel:'external'} WATCH VIDEO
|
||||||
.wizard-step-column
|
.wizard-step-column
|
||||||
%h2 Unassigned Ports
|
%h2 Unassigned Ports
|
||||||
.unassigned-input-channels.channels-holder
|
.unassigned-input-channels.channels-holder
|
||||||
|
|
@ -97,9 +97,9 @@
|
||||||
%p Determine if you need to set up a voice chat input.
|
%p Determine if you need to set up a voice chat input.
|
||||||
%p If you do, then assign the audio input to use to capture voice chat.
|
%p If you do, then assign the audio input to use to capture voice chat.
|
||||||
.center
|
.center
|
||||||
%a.button-orange.watch-video{href:'#'} WATCH VIDEO
|
%a.button-orange.watch-video{href:'https://www.youtube.com/watch?v=f7niycdWm7Y', rel:'external'} WATCH VIDEO
|
||||||
.wizard-step-column
|
.wizard-step-column
|
||||||
%h2 Select Voice Chat Option
|
%h2.sub-header Select Voice Chat Option
|
||||||
%form.voice
|
%form.voice
|
||||||
.voicechat-option.reuse-audio-input
|
.voicechat-option.reuse-audio-input
|
||||||
%input{type:"radio", name: "voicechat", checked:"checked"}
|
%input{type:"radio", name: "voicechat", checked:"checked"}
|
||||||
|
|
@ -109,9 +109,16 @@
|
||||||
%input{type:"radio", name: "voicechat"}
|
%input{type:"radio", name: "voicechat"}
|
||||||
%h3 Use Chat Microphone
|
%h3 Use Chat Microphone
|
||||||
%p I am not using a microphone for acoustic instruments or vocals, so use the input selected to the right for voice chat during my sessions
|
%p I am not using a microphone for acoustic instruments or vocals, so use the input selected to the right for voice chat during my sessions
|
||||||
.wizard-step-column
|
.clearall
|
||||||
%h2 Voice Chat Input
|
// .wizard-step-column
|
||||||
|
%h2.sub-header.voice-chat-header Voice Chat Input
|
||||||
.ftue-box.chat-inputs
|
.ftue-box.chat-inputs
|
||||||
|
.vu-meter
|
||||||
|
.ftue-controls
|
||||||
|
.ftue-vu-left.voice-chat-vu-left
|
||||||
|
.ftue-fader.chat-fader
|
||||||
|
.gain-label GAIN
|
||||||
|
.ftue-vu-right.voice-chat-vu-right
|
||||||
|
|
||||||
|
|
||||||
.wizard-step{ 'layout-wizard-step' => "4", 'dialog-title' => "Turn Off Direct Monitoring", 'dialog-purpose' => "DirectMonitoring" }
|
.wizard-step{ 'layout-wizard-step' => "4", 'dialog-title' => "Turn Off Direct Monitoring", 'dialog-purpose' => "DirectMonitoring" }
|
||||||
|
|
@ -127,7 +134,7 @@
|
||||||
%li If a button, push it into its off position.
|
%li If a button, push it into its off position.
|
||||||
%li If a knob, turn it so that 100% of audio is from your computer, and 0% is from the direct monitor.
|
%li If a knob, turn it so that 100% of audio is from your computer, and 0% is from the direct monitor.
|
||||||
.center
|
.center
|
||||||
%a.button-orange.watch-video{href:'#'} WATCH VIDEO
|
%a.button-orange.watch-video{href:'https://www.youtube.com/watch?v=-nC-D3JBHnk', rel:'external'} WATCH VIDEO
|
||||||
.wizard-step-column
|
.wizard-step-column
|
||||||
.help-content
|
.help-content
|
||||||
When you have fully turned off the direct monitoring control (if any) on your audio interface,
|
When you have fully turned off the direct monitoring control (if any) on your audio interface,
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,10 @@
|
||||||
%h2.ports-header Audio Input Ports
|
%h2.ports-header Audio Input Ports
|
||||||
.ftue-box.input-ports.ports
|
.ftue-box.input-ports.ports
|
||||||
.ftue-controls
|
.ftue-controls
|
||||||
.ftue-vu-left#loopback-audio-input-vu-left
|
.ftue-vu-left.audio-input-vu-left
|
||||||
.ftue-fader#loopback-audio-input-fader
|
.ftue-fader.audio-input-fader
|
||||||
.gain-label GAIN
|
.gain-label GAIN
|
||||||
.ftue-vu-right#loopback-audio-input-vu-right
|
.ftue-vu-right.audio-input-vu-right
|
||||||
= render :partial => "/clients/wizard/framebuffers"
|
= render :partial => "/clients/wizard/framebuffers"
|
||||||
.wizard-step-column
|
.wizard-step-column
|
||||||
%h2
|
%h2
|
||||||
|
|
@ -59,10 +59,10 @@
|
||||||
%h2.ports-header Audio Output Ports
|
%h2.ports-header Audio Output Ports
|
||||||
.ftue-box.output-ports.ports
|
.ftue-box.output-ports.ports
|
||||||
.ftue-controls
|
.ftue-controls
|
||||||
.ftue-vu-left#loopback-audio-output-vu-left
|
.ftue-vu-left.audio-output-vu-left
|
||||||
.ftue-fader#loopback-audio-output-fader
|
.ftue-fader.audio-output-fader
|
||||||
.gain-label GAIN
|
.gain-label GAIN
|
||||||
.ftue-vu-right#loopback-audio-output-vu-right
|
.ftue-vu-right.audio-output-vu-right
|
||||||
%a.button-orange.resync-btn RESYNC
|
%a.button-orange.resync-btn RESYNC
|
||||||
.wizard-step-column
|
.wizard-step-column
|
||||||
%h2.test-results-header
|
%h2.test-results-header
|
||||||
|
|
|
||||||
|
|
@ -408,6 +408,9 @@ SampleApp::Application.routes.draw do
|
||||||
# version check for JamClient
|
# version check for JamClient
|
||||||
match '/versioncheck' => 'artifacts#versioncheck'
|
match '/versioncheck' => 'artifacts#versioncheck'
|
||||||
|
|
||||||
|
# no-op method to see if server is running
|
||||||
|
match '/healthcheck' => 'artifacts#healthcheck'
|
||||||
|
|
||||||
# list all uris for available clients on mac, windows, linux, if available
|
# list all uris for available clients on mac, windows, linux, if available
|
||||||
match '/artifacts/clients' => 'artifacts#client_downloads'
|
match '/artifacts/clients' => 'artifacts#client_downloads'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,11 @@ description "jam-web"
|
||||||
start on startup
|
start on startup
|
||||||
start on runlevel [2345]
|
start on runlevel [2345]
|
||||||
stop on runlevel [016]
|
stop on runlevel [016]
|
||||||
|
limit nofile 20000 20000
|
||||||
|
limit core unlimited unlimited
|
||||||
|
|
||||||
|
respawn
|
||||||
|
respawn limit 10 5
|
||||||
|
|
||||||
pre-start script
|
pre-start script
|
||||||
set -e
|
set -e
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,8 @@ FactoryGirl.define do
|
||||||
addr {JamIsp.ip_to_num(ip_address)}
|
addr {JamIsp.ip_to_num(ip_address)}
|
||||||
locidispid 0
|
locidispid 0
|
||||||
client_type 'client'
|
client_type 'client'
|
||||||
last_jam_audio_latency { user.last_jam_audio_latency if user }
|
last_jam_audio_latency { user.last_jam_audio_latency if user }
|
||||||
|
# sequence(:channel_id) { |n| "Channel#{n}"}
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :friendship, :class => JamRuby::Friendship do
|
factory :friendship, :class => JamRuby::Friendship do
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ describe "Gear Wizard", :js => true, :type => :feature, :capybara_feature => tru
|
||||||
# step 2 - select gear
|
# step 2 - select gear
|
||||||
find('.ftue-step-title', text: 'Select & Test Audio Gear')
|
find('.ftue-step-title', text: 'Select & Test Audio Gear')
|
||||||
jk_select('Built-in', 'div[layout-wizard-step="1"] select.select-audio-input-device')
|
jk_select('Built-in', 'div[layout-wizard-step="1"] select.select-audio-input-device')
|
||||||
find('.btn-next.button-orange').trigger(:click)
|
find('.btn-next.button-orange:not(.disabled)').trigger(:click)
|
||||||
|
|
||||||
# step 3 - configure tracks
|
# step 3 - configure tracks
|
||||||
find('.ftue-step-title', text: 'Configure Tracks')
|
find('.ftue-step-title', text: 'Configure Tracks')
|
||||||
|
|
@ -31,22 +31,22 @@ describe "Gear Wizard", :js => true, :type => :feature, :capybara_feature => tru
|
||||||
track_slot = first('.track-target')
|
track_slot = first('.track-target')
|
||||||
input.drag_to(track_slot)
|
input.drag_to(track_slot)
|
||||||
|
|
||||||
find('.btn-next.button-orange').trigger(:click)
|
find('.btn-next.button-orange:not(.disabled)').trigger(:click)
|
||||||
|
|
||||||
# step 4 - configure voice chat
|
# step 4 - configure voice chat
|
||||||
find('.ftue-step-title', text: 'Configure Voice Chat')
|
find('.ftue-step-title', text: 'Configure Voice Chat')
|
||||||
find('.btn-next.button-orange').trigger(:click)
|
find('.btn-next.button-orange:not(.disabled)').trigger(:click)
|
||||||
|
|
||||||
# step 5 - configure direct monitoring
|
# step 5 - configure direct monitoring
|
||||||
find('.ftue-step-title', text: 'Turn Off Direct Monitoring')
|
find('.ftue-step-title', text: 'Turn Off Direct Monitoring')
|
||||||
find('.btn-next.button-orange').trigger(:click)
|
find('.btn-next.button-orange:not(.disabled)').trigger(:click)
|
||||||
|
|
||||||
# step 6 - Test Router & Network
|
# step 6 - Test Router & Network
|
||||||
find('.ftue-step-title', text: 'Test Router & Network')
|
find('.ftue-step-title', text: 'Test Router & Network')
|
||||||
find('.button-orange.start-network-test').trigger(:click)
|
find('.button-orange.start-network-test').trigger(:click)
|
||||||
find('.user-btn', text: 'RUN NETWORK TEST ANYWAY').trigger(:click)
|
find('.user-btn', text: 'RUN NETWORK TEST ANYWAY').trigger(:click)
|
||||||
find('.button-orange.start-network-test')
|
find('.button-orange.start-network-test')
|
||||||
find('.btn-next.button-orange').trigger(:click)
|
find('.btn-next.button-orange:not(.disabled)').trigger(:click)
|
||||||
|
|
||||||
# step 7 - Success
|
# step 7 - Success
|
||||||
find('.ftue-step-title', text: 'Success!')
|
find('.ftue-step-title', text: 'Success!')
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,17 @@ describe "User Progression", :js => true, :type => :feature, :capybara_feature
|
||||||
describe "certified gear" do
|
describe "certified gear" do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
sign_in_poltergeist user
|
sign_in_poltergeist user
|
||||||
|
FactoryGirl.create(:latency_tester)
|
||||||
visit '/client#/account/audio'
|
visit '/client#/account/audio'
|
||||||
|
# step 1 - intro
|
||||||
find("div.account-audio a[data-purpose='add-profile']").trigger(:click)
|
find("div.account-audio a[data-purpose='add-profile']").trigger(:click)
|
||||||
find('.btn-next').trigger(:click)
|
find('.btn-next').trigger(:click)
|
||||||
jk_select('Built-in', 'div[layout-wizard-step="1"] select.select-audio-input-device')
|
|
||||||
|
|
||||||
find('.btn-next.button-orange').trigger(:click)
|
# step 2 - select gear
|
||||||
|
find('.ftue-step-title', text: 'Select & Test Audio Gear')
|
||||||
|
jk_select('Built-in', 'div[layout-wizard-step="1"] select.select-audio-input-device')
|
||||||
|
find('.btn-next.button-orange:not(.disabled)').trigger(:click)
|
||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,22 +11,6 @@ module EventMachine
|
||||||
module WebSocket
|
module WebSocket
|
||||||
class Connection < EventMachine::Connection
|
class Connection < EventMachine::Connection
|
||||||
attr_accessor :encode_json, :channel_id, :client_id, :user_id, :context, :trusted # client_id is uuid we give to each client to track them as we like
|
attr_accessor :encode_json, :channel_id, :client_id, :user_id, :context, :trusted # client_id is uuid we give to each client to track them as we like
|
||||||
|
|
||||||
# http://stackoverflow.com/questions/11150147/how-to-check-if-eventmachineconnection-is-open
|
|
||||||
attr_accessor :connected
|
|
||||||
def connection_completed
|
|
||||||
connected = true
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
def connected?
|
|
||||||
!!connected
|
|
||||||
end
|
|
||||||
|
|
||||||
def unbind
|
|
||||||
connected = false
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -226,6 +210,40 @@ module JamWebsockets
|
||||||
MQRouter.client_exchange = @clients_exchange
|
MQRouter.client_exchange = @clients_exchange
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# this method allows you to translate exceptions into websocket channel messages and behavior safely.
|
||||||
|
# pass in your block, throw an error in your logic, and have the right things happen on the websocket channel
|
||||||
|
def websocket_comm(client, original_message_id, &blk)
|
||||||
|
begin
|
||||||
|
blk.call
|
||||||
|
rescue SessionError => e
|
||||||
|
@log.info "ending client session deliberately due to malformed client behavior. reason=#{e}"
|
||||||
|
begin
|
||||||
|
# wrap the message up and send it down
|
||||||
|
error_msg = @message_factory.server_rejection_error(e.to_s)
|
||||||
|
send_to_client(client, error_msg)
|
||||||
|
ensure
|
||||||
|
cleanup_client(client)
|
||||||
|
end
|
||||||
|
rescue PermissionError => e
|
||||||
|
@log.info "permission error. reason=#{e.to_s}"
|
||||||
|
@log.info e
|
||||||
|
|
||||||
|
# wrap the message up and send it down
|
||||||
|
error_msg = @message_factory.server_permission_error(original_message_id, e.to_s)
|
||||||
|
send_to_client(client, error_msg)
|
||||||
|
rescue => e
|
||||||
|
@log.error "ending client session due to server programming or runtime error. reason=#{e.to_s}"
|
||||||
|
@log.error e
|
||||||
|
|
||||||
|
begin
|
||||||
|
# wrap the message up and send it down
|
||||||
|
error_msg = @message_factory.server_generic_error(e.to_s)
|
||||||
|
send_to_client(client, error_msg)
|
||||||
|
ensure
|
||||||
|
cleanup_client(client)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def new_client(client, is_trusted)
|
def new_client(client, is_trusted)
|
||||||
# default to using json instead of pb
|
# default to using json instead of pb
|
||||||
|
|
@ -246,6 +264,9 @@ module JamWebsockets
|
||||||
client.encode_json = false
|
client.encode_json = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
websocket_comm(client, nil) do
|
||||||
|
handle_login(client, handshake.query)
|
||||||
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
client.onclose {
|
client.onclose {
|
||||||
|
|
@ -261,50 +282,26 @@ module JamWebsockets
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
client.onmessage { |msg|
|
client.onmessage { |data|
|
||||||
|
|
||||||
# TODO: set a max message size before we put it through PB?
|
# TODO: set a max message size before we put it through PB?
|
||||||
# TODO: rate limit?
|
# TODO: rate limit?
|
||||||
|
|
||||||
pb_msg = nil
|
msg = nil
|
||||||
|
|
||||||
begin
|
# extract the message safely
|
||||||
|
websocket_comm(client, nil) do
|
||||||
if client.encode_json
|
if client.encode_json
|
||||||
#example: {"type":"LOGIN", "target":"server", "login" : {"username":"hi"}}
|
json = JSON.parse(data)
|
||||||
parse = JSON.parse(msg)
|
msg = Jampb::ClientMessage.json_create(json)
|
||||||
pb_msg = Jampb::ClientMessage.json_create(parse)
|
|
||||||
self.route(pb_msg, client)
|
|
||||||
else
|
else
|
||||||
pb_msg = Jampb::ClientMessage.parse(msg.to_s)
|
msg = Jampb::ClientMessage.parse(data.to_s)
|
||||||
self.route(pb_msg, client)
|
|
||||||
end
|
end
|
||||||
rescue SessionError => e
|
end
|
||||||
@log.info "ending client session deliberately due to malformed client behavior. reason=#{e}"
|
|
||||||
begin
|
|
||||||
# wrap the message up and send it down
|
|
||||||
error_msg = @message_factory.server_rejection_error(e.to_s)
|
|
||||||
send_to_client(client, error_msg)
|
|
||||||
ensure
|
|
||||||
cleanup_client(client)
|
|
||||||
end
|
|
||||||
rescue PermissionError => e
|
|
||||||
@log.info "permission error. reason=#{e.to_s}"
|
|
||||||
@log.info e
|
|
||||||
|
|
||||||
# wrap the message up and send it down
|
# then route it internally
|
||||||
error_msg = @message_factory.server_permission_error(pb_msg.message_id, e.to_s)
|
websocket_comm(client, msg.message_id) do
|
||||||
send_to_client(client, error_msg)
|
self.route(msg, client)
|
||||||
rescue => e
|
|
||||||
@log.error "ending client session due to server programming or runtime error. reason=#{e.to_s}"
|
|
||||||
@log.error e
|
|
||||||
|
|
||||||
begin
|
|
||||||
# wrap the message up and send it down
|
|
||||||
error_msg = @message_factory.server_generic_error(e.to_s)
|
|
||||||
send_to_client(client, error_msg)
|
|
||||||
ensure
|
|
||||||
cleanup_client(client)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
@ -391,9 +388,9 @@ module JamWebsockets
|
||||||
|
|
||||||
# removes all resources associated with a client
|
# removes all resources associated with a client
|
||||||
def cleanup_client(client)
|
def cleanup_client(client)
|
||||||
@semaphore.synchronize do
|
client.close
|
||||||
client.close if client.connected?
|
|
||||||
|
|
||||||
|
@semaphore.synchronize do
|
||||||
pending = client.context.nil? # presence of context implies this connection has been logged into
|
pending = client.context.nil? # presence of context implies this connection has been logged into
|
||||||
|
|
||||||
if pending
|
if pending
|
||||||
|
|
@ -514,6 +511,7 @@ module JamWebsockets
|
||||||
heartbeat_interval, connection_stale_time, connection_expire_time = determine_connection_times(nil, client_type)
|
heartbeat_interval, connection_stale_time, connection_expire_time = determine_connection_times(nil, client_type)
|
||||||
latency_tester = LatencyTester.connect({
|
latency_tester = LatencyTester.connect({
|
||||||
client_id: client_id,
|
client_id: client_id,
|
||||||
|
channel_id: client.channel_id,
|
||||||
ip_address: remote_ip,
|
ip_address: remote_ip,
|
||||||
connection_stale_time: connection_stale_time,
|
connection_stale_time: connection_stale_time,
|
||||||
connection_expire_time: connection_expire_time})
|
connection_expire_time: connection_expire_time})
|
||||||
|
|
@ -543,15 +541,15 @@ module JamWebsockets
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_login(login, client)
|
def handle_login(client, options)
|
||||||
username = login.username if login.value_for_tag(1)
|
username = options["username"]
|
||||||
password = login.password if login.value_for_tag(2)
|
password = options["password"]
|
||||||
token = login.token if login.value_for_tag(3)
|
token = options["token"]
|
||||||
client_id = login.client_id if login.value_for_tag(4)
|
client_id = options["client_id"]
|
||||||
reconnect_music_session_id = login.reconnect_music_session_id if login.value_for_tag(5)
|
reconnect_music_session_id = options["music_session_id"]
|
||||||
client_type = login.client_type if login.value_for_tag(6)
|
client_type = options["client_type"]
|
||||||
|
|
||||||
@log.info("*** handle_login: token=#{token}; client_id=#{client_id}, client_type=#{client_type}")
|
@log.info("handle_login: client_type=#{client_type} token=#{token} client_id=#{client_id} channel_id=#{client.channel_id}")
|
||||||
|
|
||||||
if client_type == Connection::TYPE_LATENCY_TESTER
|
if client_type == Connection::TYPE_LATENCY_TESTER
|
||||||
handle_latency_tester_login(client_id, client_type, client)
|
handle_latency_tester_login(client_id, client_type, client)
|
||||||
|
|
@ -568,17 +566,21 @@ module JamWebsockets
|
||||||
|
|
||||||
user = valid_login(username, password, token, client_id)
|
user = valid_login(username, password, token, client_id)
|
||||||
|
|
||||||
|
# XXX This logic needs to instead be handled by a broadcast out to all websockets indicating dup
|
||||||
# kill any websocket connections that have this same client_id, which can happen in race conditions
|
# kill any websocket connections that have this same client_id, which can happen in race conditions
|
||||||
# this code must happen here, before we go any further, so that there is only one websocket connection per client_id
|
# this code must happen here, before we go any further, so that there is only one websocket connection per client_id
|
||||||
existing_context = @client_lookup[client_id]
|
existing_context = @client_lookup[client_id]
|
||||||
if existing_context
|
if existing_context
|
||||||
# in some reconnect scenarios, we may have in memory a websocket client still.
|
# in some reconnect scenarios, we may have in memory a websocket client still.
|
||||||
|
# let's whack it, and tell the other client, if still connected, that this is a duplicate login attempt
|
||||||
@log.info "duplicate client: #{existing_context}"
|
@log.info "duplicate client: #{existing_context}"
|
||||||
Diagnostic.duplicate_client(existing_context.user, existing_context) if existing_context.client.connected
|
Diagnostic.duplicate_client(existing_context.user, existing_context)
|
||||||
|
error_msg = @message_factory.server_duplicate_client_error
|
||||||
|
send_to_client(existing_context.client, error_msg)
|
||||||
cleanup_client(existing_context.client)
|
cleanup_client(existing_context.client)
|
||||||
end
|
end
|
||||||
|
|
||||||
connection = JamRuby::Connection.find_by_client_id(client_id)
|
connection = Connection.find_by_client_id(client_id)
|
||||||
# if this connection is reused by a different user (possible in logout/login scenarios), then whack the connection
|
# if this connection is reused by a different user (possible in logout/login scenarios), then whack the connection
|
||||||
# because it will recreate a new connection lower down
|
# because it will recreate a new connection lower down
|
||||||
if connection && user && connection.user != user
|
if connection && user && connection.user != user
|
||||||
|
|
@ -608,7 +610,7 @@ module JamWebsockets
|
||||||
recording_id = nil
|
recording_id = nil
|
||||||
|
|
||||||
ConnectionManager.active_record_transaction do |connection_manager|
|
ConnectionManager.active_record_transaction do |connection_manager|
|
||||||
music_session_id, reconnected = connection_manager.reconnect(connection, reconnect_music_session_id, remote_ip, connection_stale_time, connection_expire_time)
|
music_session_id, reconnected = connection_manager.reconnect(connection, client.channel_id, reconnect_music_session_id, remote_ip, connection_stale_time, connection_expire_time)
|
||||||
|
|
||||||
if music_session_id.nil?
|
if music_session_id.nil?
|
||||||
# if this is a reclaim of a connection, but music_session_id comes back null, then we need to check if this connection was IN a music session before.
|
# if this is a reclaim of a connection, but music_session_id comes back null, then we need to check if this connection was IN a music session before.
|
||||||
|
|
@ -644,7 +646,7 @@ module JamWebsockets
|
||||||
unless connection
|
unless connection
|
||||||
# log this connection in the database
|
# log this connection in the database
|
||||||
ConnectionManager.active_record_transaction do |connection_manager|
|
ConnectionManager.active_record_transaction do |connection_manager|
|
||||||
connection_manager.create_connection(user.id, client.client_id, remote_ip, client_type, connection_stale_time, connection_expire_time) do |conn, count|
|
connection_manager.create_connection(user.id, client.client_id, client.channel_id, remote_ip, client_type, connection_stale_time, connection_expire_time) do |conn, count|
|
||||||
if count == 1
|
if count == 1
|
||||||
Notification.send_friend_update(user.id, true, conn)
|
Notification.send_friend_update(user.id, true, conn)
|
||||||
end
|
end
|
||||||
|
|
@ -755,7 +757,7 @@ module JamWebsockets
|
||||||
if !token.nil? && token != ''
|
if !token.nil? && token != ''
|
||||||
@log.debug "logging in via token"
|
@log.debug "logging in via token"
|
||||||
# attempt login with token
|
# attempt login with token
|
||||||
user = JamRuby::User.find_by_remember_token(token)
|
user = User.find_by_remember_token(token)
|
||||||
|
|
||||||
if user.nil?
|
if user.nil?
|
||||||
@log.debug "no user found with token #{token}"
|
@log.debug "no user found with token #{token}"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,11 @@ description "websocket-gateway"
|
||||||
start on startup
|
start on startup
|
||||||
start on runlevel [2345]
|
start on runlevel [2345]
|
||||||
stop on runlevel [016]
|
stop on runlevel [016]
|
||||||
|
limit nofile 20000 20000
|
||||||
|
limit core unlimited unlimited
|
||||||
|
|
||||||
|
respawn
|
||||||
|
respawn limit 10 5
|
||||||
|
|
||||||
pre-start script
|
pre-start script
|
||||||
set -e
|
set -e
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ FactoryGirl.define do
|
||||||
ip_address '1.1.1.1'
|
ip_address '1.1.1.1'
|
||||||
as_musician true
|
as_musician true
|
||||||
client_type 'client'
|
client_type 'client'
|
||||||
|
sequence(:channel_id) { |n| "Channel#{n}"}
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :instrument, :class => JamRuby::Instrument do
|
factory :instrument, :class => JamRuby::Instrument do
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
# does a login and returns client
|
# does a login and returns client
|
||||||
def login(router, user, password, client_id)
|
def login(router, user, password, client_id, token, client_type)
|
||||||
|
|
||||||
message_factory = MessageFactory.new
|
message_factory = MessageFactory.new
|
||||||
client = LoginClient.new
|
client = LoginClient.new
|
||||||
|
|
@ -60,15 +60,9 @@ def login(router, user, password, client_id)
|
||||||
|
|
||||||
@router.new_client(client, false)
|
@router.new_client(client, false)
|
||||||
handshake = double("handshake")
|
handshake = double("handshake")
|
||||||
handshake.should_receive(:query).twice.and_return({ "pb" => "true", "channel_id" => SecureRandom.uuid })
|
handshake.should_receive(:query).exactly(3).times.and_return({ "pb" => "true", "channel_id" => SecureRandom.uuid, "client_id" => client_id, "token" => token, "client_type" => client_type })
|
||||||
client.onopenblock.call handshake
|
client.onopenblock.call handshake
|
||||||
|
|
||||||
# create a login message, and pass it into the router via onmsgblock.call
|
|
||||||
login = message_factory.login_with_user_pass(user.email, password, :client_id => client_id, :client_type => 'client')
|
|
||||||
|
|
||||||
# first log in
|
|
||||||
client.onmsgblock.call login.to_s
|
|
||||||
|
|
||||||
client
|
client
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -98,15 +92,9 @@ def login_latency_tester(router, latency_tester, client_id)
|
||||||
|
|
||||||
@router.new_client(client, true)
|
@router.new_client(client, true)
|
||||||
handshake = double("handshake")
|
handshake = double("handshake")
|
||||||
handshake.should_receive(:query).twice.and_return({ "pb" => "true", "channel_id" => SecureRandom.uuid })
|
handshake.should_receive(:query).exactly(3).times.and_return({ "pb" => "true", "channel_id" => SecureRandom.uuid, "client_type" => "latency_tester", "client_id" => client_id })
|
||||||
client.onopenblock.call handshake
|
client.onopenblock.call handshake
|
||||||
|
|
||||||
# create a login message, and pass it into the router via onmsgblock.call
|
|
||||||
login = message_factory.login_with_client_id(client_id)
|
|
||||||
|
|
||||||
# first log in
|
|
||||||
client.onmsgblock.call login.to_s
|
|
||||||
|
|
||||||
client
|
client
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -239,7 +227,7 @@ describe Router do
|
||||||
it "should allow login of valid user", :mq => true do
|
it "should allow login of valid user", :mq => true do
|
||||||
@user = FactoryGirl.create(:user,
|
@user = FactoryGirl.create(:user,
|
||||||
:password => "foobar", :password_confirmation => "foobar")
|
:password => "foobar", :password_confirmation => "foobar")
|
||||||
client1 = login(@router, @user, "foobar", "1")
|
client1 = login(@router, @user, "foobar", "1", @user.remember_token, "client")
|
||||||
done
|
done
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -271,7 +259,7 @@ describe Router do
|
||||||
# make a music_session and define two members
|
# make a music_session and define two members
|
||||||
|
|
||||||
# create client 1, log him in, and log him in to music session
|
# create client 1, log him in, and log him in to music session
|
||||||
client1 = login(@router, user1, "foobar", "1")
|
client1 = login(@router, user1, "foobar", "1", user1.remember_token, "client")
|
||||||
done
|
done
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -284,9 +272,9 @@ describe Router do
|
||||||
|
|
||||||
|
|
||||||
# create client 1, log him in, and log him in to music session
|
# create client 1, log him in, and log him in to music session
|
||||||
client1 = login(@router, user1, "foobar", "1")
|
client1 = login(@router, user1, "foobar", "1", user1.remember_token, "client")
|
||||||
|
|
||||||
client2 = login(@router, user2, "foobar", "2")
|
client2 = login(@router, user2, "foobar", "2", user2.remember_token, "client")
|
||||||
|
|
||||||
# make a music_session and define two members
|
# make a music_session and define two members
|
||||||
|
|
||||||
|
|
@ -305,10 +293,10 @@ describe Router do
|
||||||
music_session = FactoryGirl.create(:active_music_session, :creator => user1)
|
music_session = FactoryGirl.create(:active_music_session, :creator => user1)
|
||||||
|
|
||||||
# create client 1, log him in, and log him in to music session
|
# create client 1, log him in, and log him in to music session
|
||||||
client1 = login(@router, user1, "foobar", "1")
|
client1 = login(@router, user1, "foobar", "1", user1.remember_token, "client")
|
||||||
#login_music_session(@router, client1, music_session)
|
#login_music_session(@router, client1, music_session)
|
||||||
|
|
||||||
client2 = login(@router, user2, "foobar", "2")
|
client2 = login(@router, user2, "foobar", "2", user2.remember_token, "client")
|
||||||
#login_music_session(@router, client2, music_session)
|
#login_music_session(@router, client2, music_session)
|
||||||
|
|
||||||
# by creating
|
# by creating
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue