Merge branch 'develop' into feature/vst

This commit is contained in:
Seth Call 2015-12-07 21:05:10 -06:00
commit 34dcc27641
20 changed files with 285 additions and 32 deletions

View File

@ -314,4 +314,5 @@ giftcard.sql
add_description_to_crash_dumps.sql add_description_to_crash_dumps.sql
acappella.sql acappella.sql
purchasable_gift_cards.sql purchasable_gift_cards.sql
versionable_jamtracks.sql versionable_jamtracks.sql
session_controller.sql

View File

@ -0,0 +1 @@
ALTER TABLE music_sessions ADD COLUMN session_controller_id VARCHAR(64) REFERENCES users(id);

View File

@ -28,13 +28,13 @@ module JamRuby
##### TODO: refactored to notification.rb but left here for backwards compatibility w/ connection_manager_spec.rb ##### TODO: refactored to notification.rb but left here for backwards compatibility w/ connection_manager_spec.rb
def gather_friends(connection, user_id) def gather_friends(connection, user_id)
friend_ids = [] friend_ids = []
connection.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results| connection.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results|
friend_results.each do |friend_result| friend_results.each do |friend_result|
friend_ids.push(friend_result['friend_id']) friend_ids.push(friend_result['friend_id'])
end
end end
return friend_ids end
return friend_ids
end end
# this simulates music_session destroy callbacks with activerecord # this simulates music_session destroy callbacks with activerecord
@ -42,7 +42,7 @@ module JamRuby
music_session = ActiveMusicSession.find_by_id(music_session_id) music_session = ActiveMusicSession.find_by_id(music_session_id)
music_session.before_destroy if music_session music_session.before_destroy if music_session
end end
# reclaim the existing connection, if ip_address is not nil then perhaps a new address as well # reclaim the existing connection, if ip_address is not nil then perhaps a new address as well
def reconnect(conn, channel_id, reconnect_music_session_id, ip_address, connection_stale_time, connection_expire_time, udp_reachable, gateway) def reconnect(conn, channel_id, reconnect_music_session_id, ip_address, connection_stale_time, connection_expire_time, udp_reachable, gateway)
music_session_id = nil music_session_id = nil
@ -65,11 +65,19 @@ module JamRuby
isp = JamIsp.lookup(addr) isp = JamIsp.lookup(addr)
#puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============") #puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============")
if isp.nil? then ispid = 0 else ispid = isp.coid end if isp.nil? then
ispid = 0
else
ispid = isp.coid
end
block = GeoIpBlocks.lookup(addr) block = GeoIpBlocks.lookup(addr)
#puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============") #puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============")
if block.nil? then locid = 0 else locid = block.locid end if block.nil? then
locid = 0
else
locid = block.locid
end
location = GeoIpLocations.find_by_locid(locid) location = GeoIpLocations.find_by_locid(locid)
if location.nil? || isp.nil? || block.nil? if location.nil? || isp.nil? || block.nil?
@ -183,11 +191,19 @@ SQL
isp = JamIsp.lookup(addr) isp = JamIsp.lookup(addr)
#puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============") #puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============")
if isp.nil? then ispid = 0 else ispid = isp.coid end if isp.nil? then
ispid = 0
else
ispid = isp.coid
end
block = GeoIpBlocks.lookup(addr) block = GeoIpBlocks.lookup(addr)
#puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============") #puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============")
if block.nil? then locid = 0 else locid = block.locid end if block.nil? then
locid = 0
else
locid = block.locid
end
location = GeoIpLocations.find_by_locid(locid) location = GeoIpLocations.find_by_locid(locid)
if location.nil? || isp.nil? || block.nil? if location.nil? || isp.nil? || block.nil?
@ -199,11 +215,11 @@ SQL
lock_connections(conn) lock_connections(conn)
conn.exec("INSERT INTO connections (user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, aasm_state, stale_time, expire_time, udp_reachable, gateway) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", conn.exec("INSERT INTO connections (user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, aasm_state, stale_time, expire_time, udp_reachable, gateway) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
[user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, Connection::CONNECT_STATE.to_s, connection_stale_time, connection_expire_time, udp_reachable, gateway]).clear [user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, Connection::CONNECT_STATE.to_s, connection_stale_time, connection_expire_time, udp_reachable, gateway]).clear
# we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends # we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends
conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result| conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result|
count = result.getvalue(0, 0) .to_i count = result.getvalue(0, 0).to_i
# we're passing all this stuff so that the user record might be updated as well... # we're passing all this stuff so that the user record might be updated as well...
blk.call(conn, count) unless blk.nil? blk.call(conn, count) unless blk.nil?
end end
@ -291,7 +307,7 @@ SQL
# destroy the music_session if it's empty # destroy the music_session if it's empty
num_participants = nil num_participants = nil
conn.exec("SELECT count(*) FROM connections WHERE music_session_id = $1", conn.exec("SELECT count(*) FROM connections WHERE music_session_id = $1",
[previous_music_session_id]) do |result| [previous_music_session_id]) do |result|
num_participants = result.getvalue(0, 0).to_i num_participants = result.getvalue(0, 0).to_i
end end
@ -324,11 +340,65 @@ SQL
conn.exec("UPDATE active_music_sessions set jam_track_id = NULL, jam_track_initiator_id = NULL where jam_track_initiator_id = $1 and id = $2", conn.exec("UPDATE active_music_sessions set jam_track_id = NULL, jam_track_initiator_id = NULL where jam_track_initiator_id = $1 and id = $2",
[user_id, previous_music_session_id]) [user_id, previous_music_session_id])
update_session_controller(previous_music_session_id)
end end
end end
end end
def update_session_controller(music_session_id)
active_music_session = ActiveMusicSession.find(music_session_id)
if active_music_session
music_session = active_music_session.music_session
if music_session.session_controller_id && !active_music_session.users.exists?(music_session.session_controller)
# find next in line, because the current 'session controller' is not part of the session
next_in_line(music_session, active_music_session)
end
end
end
# determine who should be session controller after someone leaves
def next_in_line(music_session, active_music_session)
session_users = active_music_session.users
# check friends 1st
session_friends = music_session.creator.friends && session_users
if session_friends.length > 0
music_session.session_controller = session_friends[0]
if music_session.save
active_music_session.tick_track_changes
Notification.send_tracks_changed(active_music_session)
return
end
end
# check invited 2nd
invited = music_session.invited_musicians && session_users
if invited.length > 0
music_session.session_controller = invited[0]
if music_session.save
active_music_session.tick_track_changes
Notification.send_tracks_changed(active_music_session)
return
end
end
# go by who joined earliest
earliest = active_music_sessions.connections.order(:joined_session_at).first
if earliest
music_session.session_controller = earliest
if music_session.save
active_music_session.tick_track_changes
Notification.send_tracks_changed(active_music_session)
return
end
end
music_session.creator
end
def join_music_session(user, client_id, music_session, as_musician, tracks, audio_latency, video_sources=nil) def join_music_session(user, client_id, music_session, as_musician, tracks, audio_latency, video_sources=nil)
connection = nil connection = nil
@ -349,7 +419,10 @@ SQL
if connection.errors.any? if connection.errors.any?
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
else
update_session_controller(music_session.id)
end end
end end
connection connection
@ -383,6 +456,8 @@ SQL
if result.cmd_tuples == 1 if result.cmd_tuples == 1
@log.debug("disassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}") @log.debug("disassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}")
update_session_controller(music_session.id)
JamRuby::MusicSessionUserHistory.removed_music_session(user_id, music_session_id) JamRuby::MusicSessionUserHistory.removed_music_session(user_id, music_session_id)
session_checks(conn, previous_music_session_id, user_id) session_checks(conn, previous_music_session_id, user_id)
blk.call() unless blk.nil? blk.call() unless blk.nil?

View File

@ -36,6 +36,8 @@ module JamRuby
belongs_to :active_music_session, :class_name => 'JamRuby::ActiveMusicSession', foreign_key: :music_session_id belongs_to :active_music_session, :class_name => 'JamRuby::ActiveMusicSession', foreign_key: :music_session_id
belongs_to :session_controller, :class_name => 'JamRuby::User', :foreign_key => :session_controller_id, :inverse_of => :controlled_sessions
has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id", :dependent => :delete_all has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id", :dependent => :delete_all
has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id" has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id"
has_many :session_info_comments, :class_name => "JamRuby::SessionInfoComment", :foreign_key => "music_session_id" has_many :session_info_comments, :class_name => "JamRuby::SessionInfoComment", :foreign_key => "music_session_id"
@ -116,6 +118,7 @@ module JamRuby
new_session.open_rsvps = self.open_rsvps new_session.open_rsvps = self.open_rsvps
new_session.is_unstructured_rsvp = self.is_unstructured_rsvp new_session.is_unstructured_rsvp = self.is_unstructured_rsvp
new_session.legal_terms = true new_session.legal_terms = true
new_session.session_controller = self.session_controller
# copy rsvp_slots, rsvp_requests, and rsvp_requests_rsvp_slots # copy rsvp_slots, rsvp_requests, and rsvp_requests_rsvp_slots
RsvpSlot.find_each(:conditions => "music_session_id = '#{self.id}'") do |slot| RsvpSlot.find_each(:conditions => "music_session_id = '#{self.id}'") do |slot|
@ -255,6 +258,30 @@ module JamRuby
end end
end end
def set_session_controller(current_user, user)
# only allow update of session controller by the creator or the currently marked user
should_tick = false
if current_user != creator && current_user != self.session_controller
return should_tick
end
if active_music_session
if user
if active_music_session.users.exists?(user)
self.session_controller = user
should_tick = save
end
else
self.session_controller = nil
should_tick = save
end
end
should_tick
end
def self.index(current_user, user_id, band_id = nil, genre = nil) def self.index(current_user, user_id, band_id = nil, genre = nil)
hide_private = false hide_private = false
if current_user.id != user_id if current_user.id != user_id
@ -343,6 +370,7 @@ module JamRuby
ms.legal_terms = true ms.legal_terms = true
ms.open_rsvps = options[:open_rsvps] if options[:open_rsvps] ms.open_rsvps = options[:open_rsvps] if options[:open_rsvps]
ms.creator = user ms.creator = user
ms.session_controller = user
ms.create_type = options[:create_type] ms.create_type = options[:create_type]
ms.is_unstructured_rsvp = options[:isUnstructuredRsvp] if options[:isUnstructuredRsvp] ms.is_unstructured_rsvp = options[:isUnstructuredRsvp] if options[:isUnstructuredRsvp]
ms.scheduled_start = parse_scheduled_start(options[:start], options[:timezone]) if options[:start] && options[:timezone] ms.scheduled_start = parse_scheduled_start(options[:start], options[:timezone]) if options[:start] && options[:timezone]

View File

@ -44,6 +44,8 @@ module JamRuby
belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id' belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id'
has_many :controlled_sessions, :class_name=> "JamRuby::MusicSession", inverse_of: :session_controller, foreign_key: :session_controller_id
# authorizations (for facebook, etc -- omniauth) # authorizations (for facebook, etc -- omniauth)
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization" has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"

View File

@ -66,6 +66,20 @@
$('#session-settings-fan-access').val('listen-chat-band'); $('#session-settings-fan-access').val('listen-chat-band');
} }
var $controllerSelect = $('#session-settings-master-mix-controller')
$controllerSelect.empty()
var sessionUsers = context.SessionStore.helper.users()
$controllerSelect.append('<option value="">Any one can control the Master Mix</option>')
$.each(sessionUsers, function(userId, user) {
var selected = currentSession.session_controller_id == userId ? 'selected="selected"' : ''
$controllerSelect.append('<option value="' + userId + '"' + selected + '>' + user.name +'</option>')
})
var canEditController = currentSession.session_controller_id == context.JK.currentUserId || context.JK.currentUserId == currentSession.user_id
$controllerSelect.easyDropDown(canEditController ? 'enable' : 'disable')
/** /**
// notation files in the account screen. ugh. // notation files in the account screen. ugh.
$selectedFilenames.empty(); $selectedFilenames.empty();
@ -84,10 +98,12 @@
context.JK.dropdown($('#session-settings-language')); context.JK.dropdown($('#session-settings-language'));
context.JK.dropdown($('#session-settings-musician-access')); context.JK.dropdown($('#session-settings-musician-access'));
context.JK.dropdown($('#session-settings-fan-access')); context.JK.dropdown($('#session-settings-fan-access'));
context.JK.dropdown($('#session-settings-master-mix-controller'));
var easyDropDownState = canPlayWithOthers.canPlay ? 'enable' : 'disable' var easyDropDownState = canPlayWithOthers.canPlay ? 'enable' : 'disable'
$('#session-settings-musician-access').easyDropDown(easyDropDownState) $('#session-settings-musician-access').easyDropDown(easyDropDownState)
$('#session-settings-fan-access').easyDropDown(easyDropDownState) $('#session-settings-fan-access').easyDropDown(easyDropDownState)
} }
function addNotation(notation) { function addNotation(notation) {
@ -121,6 +137,7 @@
data.name = $('#session-settings-name').val(); data.name = $('#session-settings-name').val();
data.description = $('#session-settings-description').val(); data.description = $('#session-settings-description').val();
data.language = $('#session-settings-language').val(); data.language = $('#session-settings-language').val();
data.session_controller = $('#session-settings-master-mix-controller').val()
// musician access // musician access
var musicianAccess = $('#session-settings-musician-access').val(); var musicianAccess = $('#session-settings-musician-access').val();
@ -148,7 +165,13 @@
data.fan_chat = true; data.fan_chat = true;
} }
rest.updateSession($('#session-settings-id').val(), data).done(settingsSaved); rest.updateSession($('#session-settings-id').val(), data).done(settingsSaved)
.done(function(response) {
context.SessionActions.updateSession.trigger(response);
})
.fail(function() {
app.notify({title: "Can't Update", text: "Unable to update session settings."})
})
return false; return false;
} }

View File

@ -23,7 +23,14 @@
var $fader = $(this); var $fader = $(this);
var floaterConvert = $fader.data('floaterConverter') var floaterConvert = $fader.data('floaterConverter')
var sessionModel = window.JK.CurrentSessionModel || null; var sessionModel = window.JK.CurrentSessionModel || null;
/**
if(!$fader.data('has-session-control')) {
var sessionControllerName = $fader.data('session-controller-name');
window.JK.prodBubble($fader, 'not-session-controller', {sessionControllerName:sessionControllerName}, {positions:['left', 'right'], offsetParent: $fader.closest('.top-parent'), duration:12000})
return false;
}*/
var mediaControlsDisabled = $fader.data('media-controls-disabled'); var mediaControlsDisabled = $fader.data('media-controls-disabled');
if(mediaControlsDisabled) { if(mediaControlsDisabled) {
var mediaTrackOpener = $fader.data('media-track-opener'); var mediaTrackOpener = $fader.data('media-track-opener');
@ -173,7 +180,14 @@
var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled'); var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled');
var mediaTrackOpener = $draggingFaderHandle.data('media-track-opener'); var mediaTrackOpener = $draggingFaderHandle.data('media-track-opener');
var sessionModel = window.JK.CurrentSessionModel || null; var sessionModel = window.JK.CurrentSessionModel || null;
/**
if(!$draggingFaderHandle.data('has-session-control')) {
var sessionControllerName = $draggingFaderHandle.data('session-controller-name');
window.JK.prodBubble($draggingFaderHandle, 'not-session-controller', {sessionControllerName:sessionControllerName}, {positions:['left', 'right'], offsetParent: $draggingFaderHandle.closest('.top-parent'), duration:12000})
return false;
}*/
if(mediaControlsDisabled) { if(mediaControlsDisabled) {
return false; return false;
} }
@ -267,16 +281,33 @@
throw ("renderFader: userOptions is required"); throw ("renderFader: userOptions is required");
} }
var renderDefaults = { var renderDefaults = {
faderType: "vertical" faderType: "vertical",
sessionController: null
}; };
var options = $.extend({}, renderDefaults, userOptions); var options = $.extend({}, renderDefaults, userOptions);
var sessionCanControl = true
var sessionControllerName = null
if(userOptions.sessionController) {
if(!userOptions.sessionController.can_control) {
sessionCanControl = false
sessionControllerName = userOptions.sessionController.session_controller.name
}
}
selector.find('div[data-control="fader"]') selector.find('div[data-control="fader"]')
.data('media-controls-disabled', selector.data('media-controls-disabled')) .data('media-controls-disabled', selector.data('media-controls-disabled'))
.data('media-track-opener', selector.data('media-track-opener')) .data('media-track-opener', selector.data('media-track-opener'))
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers')) .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
.data('floaterConverter', floaterConverter) .data('floaterConverter', floaterConverter)
.data('snap', userOptions.snap) .data('snap', userOptions.snap)
.data('has-session-control', sessionCanControl)
.data('session-controller-name', sessionControllerName)
if(userOptions.sessionController) {
}
selector.find('div[data-control="fader-handle"]').draggable({ selector.find('div[data-control="fader-handle"]').draggable({
drag: onFaderDrag, drag: onFaderDrag,
@ -289,6 +320,8 @@
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers')) .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
.data('floaterConverter', floaterConverter) .data('floaterConverter', floaterConverter)
.data('snap', userOptions.snap) .data('snap', userOptions.snap)
.data('has-session-control', sessionCanControl)
.data('session-controller-name', sessionControllerName)
// 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) {

View File

@ -4,7 +4,17 @@ context = window
openDialog: (e) -> openDialog: (e) ->
e.preventDefault() e.preventDefault()
context.JK.app.layout.showDialog('session-master-mix-dialog')
sessionController = context.SessionStore.helper.sessionController()
# does this user have access to control the master mixer?
if sessionController.can_control
context.JK.app.layout.showDialog('session-master-mix-dialog')
else
sessionControllerName = sessionController.session_controller?.name
$node = $(this.getDOMNode())
window.JK.prodBubble($node, 'not-session-controller', {sessionControllerName:sessionControllerName}, {positions:['bottom'], offsetParent: $node.closest('.top-parent'), duration:12000})
render: () -> render: () ->
`<a className="session-mixer button-grey left" onClick={this.openDialog}> `<a className="session-mixer button-grey left" onClick={this.openDialog}>

View File

@ -2,6 +2,7 @@ context = window
logger = context.JK.logger logger = context.JK.logger
ChannelGroupIds = context.JK.ChannelGroupIds ChannelGroupIds = context.JK.ChannelGroupIds
CategoryGroupIds = context.JK.CategoryGroupIds CategoryGroupIds = context.JK.CategoryGroupIds
MIX_MODES = context.JK.MIX_MODES
@SessionTrackGain = React.createClass({ @SessionTrackGain = React.createClass({
@ -20,6 +21,8 @@ CategoryGroupIds = context.JK.CategoryGroupIds
groupId = $target.data('groupId') groupId = $target.data('groupId')
mixers = @state.mixers.mixer mixers = @state.mixers.mixer
# if this is a media track, jam track , or media category, affect volume of both mixer and opposing mixer # if this is a media track, jam track , or media category, affect volume of both mixer and opposing mixer
if @state.mixers.mixer.group_id == ChannelGroupIds.MediaTrackGroup || @state.mixers.mixer.group_id == ChannelGroupIds.JamTrackGroup || ((@state.mixers.mixer.group_id == ChannelGroupIds.MonitorCatGroup || @state.mixers.mixer.group_id == ChannelGroupIds.MasterCatGroup) && @state.mixers.mixer.name == CategoryGroupIds.MediaTrack) if @state.mixers.mixer.group_id == ChannelGroupIds.MediaTrackGroup || @state.mixers.mixer.group_id == ChannelGroupIds.JamTrackGroup || ((@state.mixers.mixer.group_id == ChannelGroupIds.MonitorCatGroup || @state.mixers.mixer.group_id == ChannelGroupIds.MasterCatGroup) && @state.mixers.mixer.name == CategoryGroupIds.MediaTrack)
MixerActions.faderChanged(data, [@state.mixers.mixer, @state.mixers.oppositeMixer], @props.gainType) MixerActions.faderChanged(data, [@state.mixers.mixer, @state.mixers.oppositeMixer], @props.gainType)
@ -47,13 +50,18 @@ CategoryGroupIds = context.JK.CategoryGroupIds
if !$root.is('.track-gain') if !$root.is('.track-gain')
logger.error("unknown root node") logger.error("unknown root node")
context.JK.FaderHelpers.renderFader2($root, {faderType: 'vertical'});
# Initialize gain position # Initialize gain position
mixer = @state.mixers?.mixer mixer = @state.mixers?.mixer
if mixer && $.isArray(mixer) if mixer && $.isArray(mixer)
mixer = mixer[0] mixer = mixer[0]
sessionController = null
if mixer.mode == MIX_MODES.MASTER
# sessionController is only relevant for master mode
sessionController = this.props.sessionController
context.JK.FaderHelpers.renderFader2($root, {faderType: 'vertical', sessionController:sessionController});
MixerActions.initGain(mixer) MixerActions.initGain(mixer)
# watch for fader change events # watch for fader change events

View File

@ -24,10 +24,13 @@ ptrCount = 0
newMixers = mixers.refreshMixer(@state.mixers) newMixers = mixers.refreshMixer(@state.mixers)
newMixers = {} unless newMixers? newMixers = {} unless newMixers?
this.setState({mixers: newMixers}) sessionController = sessionMixers.session.sessionController()
logger.debug("session controller", sessionController)
this.setState({mixers: newMixers, sessionController: sessionController})
getInitialState: () -> getInitialState: () ->
{mixers: this.props.mixers, ptr: "STVH#{ptrCount++}" } {mixers: this.props.mixers, ptr: "STVH#{ptrCount++}", sessionController: window.SessionStore.helper.sessionController()}
handleMute: (e) -> handleMute: (e) ->
e.preventDefault() e.preventDefault()
@ -85,7 +88,7 @@ ptrCount = 0
<div>Volume</div> <div>Volume</div>
<div>{volume_left}dB</div> <div>{volume_left}dB</div>
</div> </div>
<SessionTrackGain mixers={this.state.mixers} gainType={this.props.gainType} /> <SessionTrackGain mixers={this.state.mixers} gainType={this.props.gainType} sessionController={this.state.sessionController} />
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/> <div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
<input type="checkbox" name="mute"/> <input type="checkbox" name="mute"/>

View File

@ -18,6 +18,14 @@ context = window
else else
[] []
users: () ->
found = {}
for participant in @participants()
found[participant.user.id] = participant.user
found
otherParticipants: () -> otherParticipants: () ->
others = [] others = []
for participant in @participants() for participant in @participants()
@ -26,6 +34,26 @@ context = window
others.push(participant) unless myTrack others.push(participant) unless myTrack
others others
sessionController: () ->
info = {}
# XXX testing:
info["can_control"] = false
info["session_controller"] = @participants()[0].user
if @session
if @session.session_controller_id == null
info['session_controller'] = null
info['can_control'] = true
else
for participant in @participants()
if participant.user.id == @session.session_controller_id
info['session_controller'] = participant.user
info['can_control'] = participant.user.id == context.JK.currentUserId
break
info
# if any participant has the metronome open, then we say this session has the metronome open # if any participant has the metronome open, then we say this session has the metronome open
isMetronomeOpen: () -> isMetronomeOpen: () ->
@session? && @session.metronome_active @session? && @session.metronome_active

View File

@ -60,6 +60,11 @@ body.jam, body.web, .dialog{
} }
} }
.larger-text {
margin:10px;
font-size:12px;
line-height:125%;
}

View File

@ -171,7 +171,7 @@ class ApiMusicSessionsController < ApiController
end end
def update def update
@music_session = MusicSessionManager.new.update( @music_session = MusicSessionManager.new.update(current_user,
@music_session.music_session, @music_session.music_session,
params[:name], params[:name],
params[:description], params[:description],
@ -180,14 +180,16 @@ class ApiMusicSessionsController < ApiController
params[:musician_access], params[:musician_access],
params[:approval_required], params[:approval_required],
params[:fan_chat], params[:fan_chat],
params[:fan_access]) params[:fan_access],
params[:session_controller])
if @music_session.errors.any? if @music_session.errors.any?
# we have to do this because api_session_detail_url will fail with a bad @music_session # we have to do this because api_session_detail_url will fail with a bad @music_session
response.status = :unprocessable_entity response.status = :unprocessable_entity
respond_with @music_session respond_with @music_session
else else
respond_with @music_session, responder: ApiResponder, :location => api_session_detail_url(@music_session) @music_session = @music_session.active_music_session
respond_with_model(@music_session)
end end
end end
@ -573,7 +575,6 @@ class ApiMusicSessionsController < ApiController
end end
end end
def jam_track_open def jam_track_open
unless @music_session.users.exists?(current_user) unless @music_session.users.exists?(current_user)
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR

View File

@ -0,0 +1,3 @@
object @music_session
extends "api_music_sessions/show"

View File

@ -37,12 +37,17 @@ else
end end
end end
node :share_url do |music_session| node do |music_session|
session_props = {}
unless music_session.music_session.share_token.nil? unless music_session.music_session.share_token.nil?
share_token_url(music_session.music_session.share_token.token) session_props[:share_url] = share_token_url(music_session.music_session.share_token.token)
end end
session_props[:session_controller_id] = music_session.music_session.session_controller_id
session_props
end end
child(:connections => :participants) { child(:connections => :participants) {
collection @music_sessions, :object_root => false collection @music_sessions, :object_root => false
attributes :ip_address, :client_id, :joined_session_at, :audio_latency, :id, :metronome_open attributes :ip_address, :client_id, :joined_session_at, :audio_latency, :id, :metronome_open

View File

@ -0,0 +1,3 @@
object @music_session
extends "api_music_sessions/show"

View File

@ -222,6 +222,16 @@ script type="text/template" id="template-help-media-controls-disabled"
| Only the person who opened the recording can control the volume levels. | Only the person who opened the recording can control the volume levels.
| {% } %} | {% } %}
script type="text/template" id="template-help-not-session-controller"
.not-session-controller.larger-text
| This feature controls the master mix for the session, which is used to set the mix levels for recordings and session broadcasts,
| &nbsp;so changes to the master mix affect all musicians in the session.
| &nbsp;Only {{data.sessionControllerName}} has control of the master mix for this session.
br
br
| If you want to change the personal mix i.e. the mix/levels that you personally hear you can use the volume sliders on each track on the session screen to do this, and it wont affect what other musicians in the session hear, so you can do this safely.
script type="text/template" id="template-help-jamtrack-controls-disabled" script type="text/template" id="template-help-jamtrack-controls-disabled"
| During a recording, volume and mute controls for JamTracks are disabled. So, get the session volume levels right before starting the recording. | During a recording, volume and mute controls for JamTracks are disabled. So, get the session volume levels right before starting the recording.

View File

@ -35,6 +35,11 @@
%option{:value => "#{language[:id]}"} %option{:value => "#{language[:id]}"}
= language[:label] = language[:label]
.clearall.left.w20.ib.mb10
Mix Controller:
.right.w75.ib.mb10
%select{:name => "master_mix_controller", :id => "session-settings-master-mix-controller"}
.clearall.left.w20.ib.mb10 .clearall.left.w20.ib.mb10
Musician Access: Musician Access:
.right.w75.ib.mb10 .right.w75.ib.mb10

View File

@ -227,6 +227,7 @@ SampleApp::Application.routes.draw do
match '/sessions/:id/backing_tracks/close' => 'api_music_sessions#backing_track_close', :via => :post match '/sessions/:id/backing_tracks/close' => 'api_music_sessions#backing_track_close', :via => :post
match '/sessions/:id/metronome/open' => 'api_music_sessions#metronome_open', :via => :post match '/sessions/:id/metronome/open' => 'api_music_sessions#metronome_open', :via => :post
match '/sessions/:id/metronome/close' => 'api_music_sessions#metronome_close', :via => :post match '/sessions/:id/metronome/close' => 'api_music_sessions#metronome_close', :via => :post
match '/sessions/:id/session_controller' => 'api_music_sessions#session_controller', :via => :post
# music session tracks # music session tracks
match '/sessions/:id/tracks' => 'api_music_sessions#track_create', :via => :post match '/sessions/:id/tracks' => 'api_music_sessions#track_create', :via => :post

View File

@ -67,7 +67,7 @@ class MusicSessionManager < BaseManager
end end
# Update the session. If a field is left out (meaning, it's set to nil), it's not updated. # Update the session. If a field is left out (meaning, it's set to nil), it's not updated.
def update(music_session, name, description, genre, language, musician_access, approval_required, fan_chat, fan_access) def update(current_user, music_session, name, description, genre, language, musician_access, approval_required, fan_chat, fan_access, session_controller_id)
music_session.name = name unless name.nil? music_session.name = name unless name.nil?
music_session.description = description unless description.nil? music_session.description = description unless description.nil?
@ -77,7 +77,15 @@ class MusicSessionManager < BaseManager
music_session.approval_required = approval_required unless approval_required.nil? music_session.approval_required = approval_required unless approval_required.nil?
music_session.fan_chat = fan_chat unless fan_chat.nil? music_session.fan_chat = fan_chat unless fan_chat.nil?
music_session.fan_access = fan_access unless fan_access.nil? music_session.fan_access = fan_access unless fan_access.nil?
session_controller = User.find(session_controller_id) if session_controller_id.present?
should_tick = music_session.set_session_controller(current_user, session_controller)
music_session.save music_session.save
if should_tick && music_session.active_music_session
music_session.active_music_session.tick_track_changes
Notification.send_tracks_changed(music_session.active_music_session)
end
music_session music_session
end end