* VRFS-2608 - refactor and slight cleanup of UI towards new spec

This commit is contained in:
Seth Call 2015-01-06 16:51:19 -06:00
parent 1cb9f47f6c
commit af83a4f6a1
7 changed files with 257 additions and 135 deletions

View File

@ -8,6 +8,9 @@ module JamRuby
# Interact with external python tools to create the JKZ
class JamTracksManager
@@log = Logging.logger[JamTracksManager]
class << self
def save_jam_track_jkz(user, jam_track)
jam_track_right = jam_track.right_for_user(user)
@ -36,7 +39,7 @@ module JamRuby
title=jam_track.name
output_jkz=File.join(tmp_dir, "#{title.parameterize}.jkz")
py_file = File.join(py_root, "jkcreate.py")
puts "Executing python source in #{py_file}, outputting to #{tmp_dir} (#{output_jkz})"
@@log.info "Executing python source in #{py_file}, outputting to #{tmp_dir} (#{output_jkz})"
# From http://stackoverflow.com/questions/690151/getting-output-of-system-calls-in-ruby/5970819#5970819:
cli = "python #{py_file} -D -k #{sku} -p #{tmp_dir}/pkey.pem -s #{tmp_dir}/skey.pem #{jam_file_opts} -o #{output_jkz} -t '#{title}'"

View File

@ -21,10 +21,10 @@
var $fader = $(this);
var recordingDisabled = $fader.data('recording-disabled');
if(recordingDisabled) {
var recordingOpener = $fader.data('recording-opener');
window.JK.prodBubble($fader, 'recording-controls-disabled', {recordingOpener:recordingOpener}, {positions:['top'], offsetParent: $fader.closest('.screen')})
var mediaControlsDisabled = $fader.data('media-controls-disabled');
if(mediaControlsDisabled) {
var mediaTrackOpener = $fader.data('media-track-opener');
window.JK.prodBubble($fader, 'media-controls-disabled', {mediaTrackOpener:mediaTrackOpener}, {positions:['top'], offsetParent: $fader.closest('.screen')})
return false;
}
@ -127,10 +127,10 @@
$draggingFader = $draggingFaderHandle.closest('div[control="fader"]');
draggingOrientation = $draggingFader.attr('orientation');
var recordingDisabled = $draggingFaderHandle.data('recording-disabled');
var recordingOpener = $draggingFaderHandle.data('recording-opener');
var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled');
var mediaTrackOpener = $draggingFaderHandle.data('media-track-opener');
if(recordingDisabled) {
if(mediaControlsDisabled) {
return false;
}
return true;
@ -179,7 +179,7 @@
selector.html(g._.template(templateSource, options));
selector.find('div[control="fader"]').data('recording-disabled', selector.data('recording-disabled')).data('recording-opener', selector.data('recording-opener'))
selector.find('div[control="fader"]').data('media-controls-disabled', selector.data('media-controls-disabled')).data('media-track-opener', selector.data('media-track-opener'))
selector.find('div[control="fader-handle"]').draggable({
drag: onFaderDrag,
@ -187,7 +187,7 @@
stop: onFaderDragStop,
containment: "parent",
axis: options.faderType === 'horizontal' ? 'x' : 'y'
}).data('recording-disabled', selector.data('recording-disabled')).data('recording-opener', selector.data('recording-opener'))
}).data('media-controls-disabled', selector.data('media-controls-disabled')).data('media-track-opener', selector.data('media-track-opener'))
// Embed any custom styles, applied to the .fader below selector
if ("style" in options) {

View File

@ -796,119 +796,198 @@
function _renderLocalMediaTracks() {
// is this the person who opened the recording?
var isOpener = true;
var localMediaMixers = _mixersForGroupId(ChannelGroupIds.MediaTrackGroup, MIX_MODES.MASTER);
if(localMediaMixers.length == 0) {
isOpener = false; // if we have PeerMediaTracks, then we didn't open the recording
localMediaMixers = _mixersForGroupId(ChannelGroupIds.PeerMediaTrackGroup, MIX_MODES.MASTER);
}
// first gather all master mode media mixers and peer media mixers
var localMediaMixers = _mixersForGroupId(ChannelGroupIds.MediaTrackGroup, MIX_MODES.MASTER);
var peerLocalMediaMixers = _mixersForGroupId(ChannelGroupIds.PeerMediaTrackGroup, MIX_MODES.MASTER);
var recordedTracks = sessionModel.recordedTracks();
// with mixer info, we use these to decide what kind of tracks are open in the backend
if(recordedTracks && localMediaMixers.length == 0) {
// if we are the creator, then rather than raise an error, tell the server the recording is over.
// this shoudl only happen if we get temporarily disconnected by forced reload, which isn't a very normal scenario
if(sessionModel.getCurrentSession().claimed_recording_initiator_id == context.JK.userMe.id) {
closeRecording();
return;
}
}
// each mixer has a media_type field, which describes the type of media track it is.
// * JamTrack
// * BackingTrack
// * RecordingTrack
// * MetronomeTrack
// * "" - adhoc track (not supported visually)
if(recordedTracks) {
$('.session-recording-name').text(sessionModel.getCurrentSession().claimed_recording.name);
// it is supposed to be the case that there are only one type of track open at a time, however, that's a business policy/logic
// constraint; and may be buggy. **So, we should render whatever we have, so that it's obvious what's really going on.**
var noCorrespondingTracks = false;
$.each(localMediaMixers, function(index, mixer) {
var preMasteredClass = "";
// find the track or tracks that correspond to the mixer
var correspondingTracks = []
$.each(recordedTracks, function(i, recordedTrack) {
if(mixer.id.indexOf("L") == 0) {
if(mixer.id.substring(1) == recordedTrack.client_track_id) {
correspondingTracks.push(recordedTrack);
}
}
else if(mixer.id.indexOf("C") == 0) {
if(mixer.id.substring(1) == recordedTrack.client_id) {
correspondingTracks.push(recordedTrack);
preMasteredClass = "pre-mastered-track";
}
}
else {
// this should not be possible
alert("Invalid state: the recorded track had neither persisted_track_id or persisted_client_id");
}
});
// so, let's group up all mixers by type, and then ask them to be rendered
if(correspondingTracks.length == 0) {
noCorrespondingTracks = true;
app.notify({
title: "Unable to Open Recording",
text: "Could not correlate server and client tracks",
icon_url: "/assets/content/icon_alert_big.png"});
return false;
}
// prune found recorded tracks
recordedTracks = $.grep(recordedTracks, function(value) {
return $.inArray(value, correspondingTracks) < 0;
});
var oneOfTheTracks = correspondingTracks[0];
var instrumentIcon = context.JK.getInstrumentIcon45(oneOfTheTracks.instrument_id);
var photoUrl = "/assets/content/icon_recording.png";
var name = oneOfTheTracks.user.name;
if (!(name)) {
name = oneOfTheTracks.user.first_name + ' ' + oneOfTheTracks.user.last_name;
}
var recordingTrackMixers = [];
var backingTrackMixers = [];
var jamTrackMixers = [];
var metronomeTrackMixers = [];
var adhocTrackMixers = [];
// Default trackData to participant + no Mixer state.
var trackData = {
trackId: oneOfTheTracks.id,
clientId: oneOfTheTracks.client_id,
name: name,
instrumentIcon: instrumentIcon,
avatar: photoUrl,
latency: "good",
gainPercent: 0,
muteClass: 'muted',
mixerId: "",
avatarClass : 'avatar-recording',
preMasteredClass: preMasteredClass
};
function groupByType(mixers) {
context._.each(mixers, function(mixer) {
var mediaType = mixer.media_type;
var gainPercent = percentFromMixerValue(
mixer.range_low, mixer.range_high, mixer.volume_left);
var muteClass = "enabled";
if (mixer.mute) {
muteClass = "muted";
}
trackData.gainPercent = gainPercent;
trackData.muteClass = muteClass;
trackData.mixerId = mixer.id;
trackData.vuMixerId = mixer.id;
trackData.muteMixerId = mixer.id;
if(mediaType == 'RecordingTrack') {
recordingTrackMixers.push(mixer)
}
else if(mediaType == 'BackingTrack') {
backingTrackMixers.push(mixer);
}
else if(mediaType == 'MetronomeTrack') {
metronomeTrackMixers.push(mixer);
}
else if(mediaType == 'JamTrack') {
jamTrackMixers.push(mixer);
}
else {
adhocTrackMixers.push(mixer);
}
});
}
if(sessionModel.isPersonalMixMode() || !isOpener) {
trackData.recordingDisabled = true;
trackData.recordingOpener = isOpener;
}
_addMediaTrack(trackData);
});
groupByType(localMediaMixers);
groupByType(peerLocalMediaMixers);
if(!noCorrespondingTracks && recordedTracks.length > 0) {
logger.error("unable to find all recorded tracks against client tracks");
app.notify({title:"All tracks not found",
text: "Some tracks in the recording are not present in the playback",
icon_url: "/assets/content/icon_alert_big.png"})
}
}
if(recordingTrackMixers.length > 0) {
renderRecordingTracks(recordingTrackMixers)
}
if(backingTrackMixers.length > 0) {
renderBackingTracks(backingTrackMixers)
}
if(jamTrackMixers.length > 0) {
renderJamTracks(jamTrackMixers);
}
if(metronomeTrackMixers.length > 0) {
renderMetronomeTracks(jamTrackMixers);
}
if(adhocTrackMixers.length > 0) {
logger.warn("some tracks are open that we don't know how to show")
}
}
function renderBackingTracks(backingTrackMixers) {
logger.error("do not know how to draw backing tracks yet")
}
function renderJamTracks(jamTrackMixers) {
logger.error("do not know how to draw jam tracks yet")
}
function renderMetronomeTracks(metronomeTrackMixers) {
logger.error("do not know how to draw metronome tracks yet")
}
function renderRecordingTracks(recordingMixers) {
// get the server's info for the recording
var recordedTracks = sessionModel.recordedTracks();
if(recordedTracks && recordingMixers.length == 0) {
// if we are the creator, then rather than raise an error, tell the server the recording is over.
// this shoudl only happen if we get temporarily disconnected by forced reload, which isn't a very normal scenario
if(sessionModel.getCurrentSession().claimed_recording_initiator_id == context.JK.userMe.id) {
closeRecording();
return;
}
}
// pluck the 1st mixer, and assume that all other mixers in this group are of the same type (between Local vs Peer)
// if it's a locally opened track (MediaTrackGroup), then we can say this person is the opener
var isOpener = recordingMixers[0].group_id == ChannelGroupIds.MediaTrackGroup;
// using the server's info in conjuction with the client's, draw the recording tracks
if(recordedTracks) {
$('.session-recording-name').text(sessionModel.getCurrentSession().claimed_recording.name);
var noCorrespondingTracks = false;
$.each(recordingMixers, function(index, mixer) {
var preMasteredClass = "";
// find the track or tracks that correspond to the mixer
var correspondingTracks = []
$.each(recordedTracks, function(i, recordedTrack) {
if(mixer.id.indexOf("L") == 0) {
if(mixer.id.substring(1) == recordedTrack.client_track_id) {
correspondingTracks.push(recordedTrack);
}
}
else if(mixer.id.indexOf("C") == 0) {
if(mixer.id.substring(1) == recordedTrack.client_id) {
correspondingTracks.push(recordedTrack);
preMasteredClass = "pre-mastered-track";
}
}
else {
// this should not be possible
alert("Invalid state: the recorded track had neither persisted_track_id or persisted_client_id");
}
});
if(correspondingTracks.length == 0) {
noCorrespondingTracks = true;
app.notify({
title: "Unable to Open Recording",
text: "Could not correlate server and client tracks",
icon_url: "/assets/content/icon_alert_big.png"});
return false;
}
// prune found recorded tracks
recordedTracks = $.grep(recordedTracks, function(value) {
return $.inArray(value, correspondingTracks) < 0;
});
var oneOfTheTracks = correspondingTracks[0];
var instrumentIcon = context.JK.getInstrumentIcon45(oneOfTheTracks.instrument_id);
var photoUrl = "/assets/content/icon_recording.png";
var name = oneOfTheTracks.user.name;
if (!(name)) {
name = oneOfTheTracks.user.first_name + ' ' + oneOfTheTracks.user.last_name;
}
// Default trackData to participant + no Mixer state.
var trackData = {
trackId: oneOfTheTracks.id,
clientId: oneOfTheTracks.client_id,
name: name,
instrumentIcon: instrumentIcon,
avatar: photoUrl,
latency: "good",
gainPercent: 0,
muteClass: 'muted',
mixerId: "",
avatarClass : 'avatar-recording',
preMasteredClass: preMasteredClass
};
var gainPercent = percentFromMixerValue(
mixer.range_low, mixer.range_high, mixer.volume_left);
var muteClass = "enabled";
if (mixer.mute) {
muteClass = "muted";
}
trackData.gainPercent = gainPercent;
trackData.muteClass = muteClass;
trackData.mixerId = mixer.id; // the master mixer controls the volume control for recordings (no personal controls in either master or personal mode)
trackData.vuMixerId = mixer.id; // the master mixer controls the VUs for recordings (no personal controls in either master or personal mode)
trackData.muteMixerId = mixer.id; // the master mixer controls the mute for recordings (no personal controls in either master or personal mode)
if(sessionModel.isPersonalMixMode() || !isOpener) {
trackData.mediaControlsDisabled = true;
trackData.mediaTrackOpener = isOpener;
}
_addRecordingTrack(trackData);
});
if(!noCorrespondingTracks && recordedTracks.length > 0) {
logger.error("unable to find all recorded tracks against client tracks");
app.notify({title:"All tracks not found",
text: "Some tracks in the recording are not present in the playback",
icon_url: "/assets/content/icon_alert_big.png"})
}
}
}
function trackMuteSelected(e, data) {
var muteOption = data.muteOption; // muteOption is going to be either 'master' or 'personal'. We mute the correct one, based on track info
@ -1124,8 +1203,8 @@
var vuRightSelector = trackSelector + " .track-vu-right";
var faderSelector = trackSelector + " .track-gain";
var $fader = $(faderSelector).attr('mixer-id', mixerId).data('groupId', groupId)
if(track.recordingDisabled) {
$fader.data('recording-disabled', true).data('recording-opener', track.recordingOpener) // this we be applied later to the fader handle $element
if(track.mediaControlsDisabled) {
$fader.data('media-controls-disabled', true).data('media-track-opener', track.mediaTrackOpener) // this we be applied later to the fader handle $element
}
var $track = $(trackSelector);
// Set mixer-id attributes and render VU/Fader
@ -1270,12 +1349,13 @@
$closeButton.click(deleteTrack);
}
// is this used?
tracks[trackData.trackId] = new context.JK.SessionTrack(trackData.clientId);
}
function _addMediaTrack(trackData) {
function _addRecordingTrack(trackData) {
var parentSelector = '#session-recordedtracks-container';
var $destination = $(parentSelector);
$('.session-recordings .when-empty').hide();
@ -1294,13 +1374,13 @@
var gainPercent = trackData.gainPercent || 0;
var $track = connectTrackToMixer(trackSelector, trackData, trackData.mixerId, gainPercent, null);
var $trackIconMute = $track.find('.track-icon-mute')
if(trackData.recordingDisabled) {
$trackIconMute.data('recording-disabled', true).data('recording-opener', trackData.recordingOpener)
if(trackData.mediaControlsDisabled) {
$trackIconMute.data('media-controls-disabled', true).data('media-track-opener', trackData.mediaTrackOpener)
}
// is this used?
tracks[trackData.trackId] = new context.JK.SessionTrack(trackData.clientId);
}
/**
* 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.
@ -1451,10 +1531,10 @@
// track icons have a special mute behavior
if($control.is('.track-icon-mute')) {
var recordingDisabled = $control.data('recording-disabled');
if(recordingDisabled) {
var recordingOpener = $control.data('recording-opener');
context.JK.prodBubble($control, 'recording-controls-disabled', {recordingOpener:recordingOpener}, {positions:['bottom'], offsetParent: $control.closest('.screen')})
var mediaControlsDisabled = $control.data('media-controls-disabled');
if(mediaControlsDisabled) {
var mediaTrackOpener = $control.data('media-track-opener');
context.JK.prodBubble($control, 'media-controls-disabled', {mediaTrackOpener:mediaTrackOpener}, {positions:['bottom'], offsetParent: $control.closest('.screen')})
return false;
}

View File

@ -73,6 +73,39 @@
top:-7px;
left:5px;
}
.open-media-file-header {
font-size:16px;
line-height:100%;
margin:0;
float:left;
img {
position:relative;
top:3px;
}
}
.open-media-file-options {
font-size:16px;
float:left;
list-style: none !important;
margin: 7px 0 0 7px !important;
li {
margin-bottom:5px !important;
a {
text-decoration: none !important;
}
}
}
#track-settings {
img {
position:relative;
top:3px;
}
}
}
@ -239,17 +272,19 @@ table.vu td {
min-width:220px;
}
#tracks .when-empty {
#tracks .when-empty.livetracks {
margin: 0px;
padding:0px;
display:block;
padding-top: 125px;
vertical-align:middle;
text-align:center;
font-weight: bold;
}
#tracks .when-empty.recordings {
padding-top: 137px;
//padding-top: 137px;
text-align:left;
padding-top:6px;
margin:0;
}
#tracks .when-empty a {

View File

@ -3,7 +3,6 @@ require 'js_connect'
class VanillaForumsController < ApplicationController
def log
@log || Logging.logger[VanillaForumsController ]
end

View File

@ -164,8 +164,8 @@
</div>
</script>
<script type="text/template" id="template-help-recording-controls-disabled">
{% if(data.recordingOpener) { %}
<script type="text/template" id="template-help-media-controls-disabled">
{% if(data.mediaTrackOpener) { %}
Switch <b>MIX:</b> to <b>Master</b> mode to have control over volume levels.
{% } else { %}
Only the person who opened the recording can control the volume levels.

View File

@ -63,7 +63,8 @@
<div class="session-mytracks">
<h2>my tracks</h2>
<div id="track-settings" class="session-add" style="display:block;" layout-link="configure-tracks">
<%= image_tag "content/icon_settings_lg.png", {:width => 18, :height => 18} %>&nbsp;&nbsp;Settings
<%= image_tag "content/icon_settings_lg.png", {:width => 18, :height => 18} %>
<span>Settings</span>
</div>
<div class="session-tracks-scroller">
@ -86,11 +87,10 @@
</div>
<div class="session-tracks-scroller">
<div id="session-livetracks-container">
<p class="when-empty">
No Live Tracks:<br/>
<a layout-link="select-invites", href="#" id="session-invite-musicians2">Invite Other Musicians</a> to<br/>
Add Live Tracks
</p>
<div class="when-empty livetracks">
No other musicians <br/>
are in your session
</div>
</div>
<br clear="all" />
<div class="recording" id="recording-start-stop">
@ -112,9 +112,14 @@
</div>
<div class="session-tracks-scroller">
<div id="session-recordedtracks-container">
<p class="when-empty recordings">
No Recordings:<br/><a href="#" id="open-a-recording">Open a Recording</a>
</p>
<div class="when-empty recordings">
<span class="open-media-file-header"><%= image_tag "content/icon_folder.png", {width:22, height:20} %> Open:</span>
<ul class="open-media-file-options">
<li><a href="#" id="open-a-recording">Recording</a></li>
<!--<li>JamTrack</li>
<li>Audio File</li>-->
</ul>
</div>
</div>
<br clear="all" />