This commit is contained in:
Seth Call 2015-01-08 20:35:39 -06:00
parent b5c446dd04
commit cc3bf2ce1f
22 changed files with 628 additions and 216 deletions

View File

@ -316,6 +316,9 @@ SQL
#ensure that there is no active claimed recording if the owner of that recording left the session
conn.exec("UPDATE active_music_sessions set claimed_recording_id = NULL, claimed_recording_initiator_id = NULL where claimed_recording_initiator_id = $1 and id = $2",
[user_id, previous_music_session_id])
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])
end
end

View File

@ -64,8 +64,7 @@ module JamRuby
start = start.to_i || 0
query = JamTrack.joins(:jam_track_tracks)
.offset(start)
.limit(limit)
.paginate(page: 1 + start/limit, per_page: limit)
if options[:show_purchased_only]
query = query.joins(:jam_track_rights)

View File

@ -0,0 +1,130 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.OpenJamTrackDialog = function(app) {
var logger = context.JK.logger;
var rest = context.JK.Rest();
var showing = false;
var perPage = 10;
var $dialog = null;
var $tbody = null;
var $paginatorHolder = null;
var $templateOpenJamTrackRow = null;
var $downloadedTrackHelp = null;
var $whatAreJamTracks = null;
function emptyList() {
$tbody.empty();
}
function resetPagination() {
$dialog.find('.paginator').remove();
}
function beforeShow() {
emptyList();
resetPagination();
showing = true;
getPurchasedJamTracks(0)
.done(function(data, textStatus, jqXHR) {
// initialize pagination
var $paginator = context.JK.Paginator.create(parseInt(jqXHR.getResponseHeader('total-entries')), perPage, 0, onPageSelected)
$paginatorHolder.append($paginator);
});
}
function afterHide() {
showing = false;
}
function onPageSelected(targetPage) {
return getPurchasedJamTracks(targetPage);
}
function getPurchasedJamTracks(page) {
return rest.getPurchasedJamTracks({page:page + 1, per_page:10})
.done(function(purchasedJamTracks) {
emptyList();
$.each(purchasedJamTracks.jamtracks, function(index, jamTrack) {
var options = {}
options.jamTrackState = null;
options.jamTrackId = jamTrack.id;
options.name = jamTrack.name;
options.artist = jamTrack.original_artist;
options.downloaded = 'Yes'
var $tr = $(context._.template($templateOpenJamTrackRow.html(), options, { variable: 'data' }));
$tr.data('server-model', jamTrack);
$tbody.append($tr);
});
})
.fail(function(jqXHR, textStatus, errorMessage) {
app.ajaxError(jqXHR, textStatus, errorMessage);
});
}
function registerStaticEvents() {
$tbody.on('click', 'tr', function(e) {
var jamTrack = $(this).data('server-model');
// tell the server we are about to start a recording
rest.openJamTrack({id: context.JK.CurrentSessionModel.id(), jam_track_id: jamTrack.id})
.done(function(response) {
var result = context.jamClient.JamTrackPlay('t');
logger.debug("JamTrackPlay response: %o", result);
if(result) {
app.layout.closeDialog('open-jam-track-dialog');
}
else {
logger.error("unable to open jam track")
}
})
.fail(function(jqXHR) {
app.notifyServerError(jqXHR, "Unable to Open JamTrack For Playback");
})
return false;
})
context.JK.helpBubble($downloadedTrackHelp, 'downloaded-jamtrack', {}, {width:'400px'})
$downloadedTrackHelp.on('click', false)
context.JK.helpBubble($whatAreJamTracks, 'no help yet for this topic', {}, {positions:['bottom'], offsetParent: $dialog})
$whatAreJamTracks.on('click', false) // no help yet
}
function initialize(){
var dialogBindings = {
'beforeShow' : beforeShow,
'afterHide': afterHide
};
app.bindDialog('open-jam-track-dialog', dialogBindings);
$dialog = $('#open-jam-track-dialog');
$tbody = $dialog.find('table.open-jam-tracks tbody');
$paginatorHolder = $dialog.find('.paginator-holder');
$templateOpenJamTrackRow = $('#template-jam-track-row')
$downloadedTrackHelp = $dialog.find('.downloaded-jamtrack-help')
$whatAreJamTracks = $dialog.find('.what-are-jamtracks')
registerStaticEvents();
};
this.initialize = initialize;
this.isShowing = function isShowing() { return showing; }
}
return this;
})(window,jQuery);

View File

@ -1157,6 +1157,34 @@
})
}
function openJamTrack(options) {
var musicSessionId = options["id"];
var jamTrackId = options["jam_track_id"];
delete options["id"];
delete options["jam_track_id"];
return $.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: "/api/sessions/" + musicSessionId + "/jam_tracks/" + jamTrackId + "/open",
data: JSON.stringify(options)
})
}
function closeJamTrack(options) {
var musicSessionId = options["id"];
delete options["id"];
return $.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: "/api/sessions/" + musicSessionId + "/jam_tracks/close",
data: JSON.stringify(options)
})
}
function discardRecording(options) {
var recordingId = options["id"];
@ -1319,6 +1347,15 @@
});
}
function getPurchasedJamTracks(options) {
return $.ajax({
type: "GET",
url: '/api/jamtracks/purchased?' + $.param(options),
dataType: "json",
contentType: 'application/json'
});
}
function addJamtrackToShoppingCart(options) {
return $.ajax({
type: "POST",
@ -1515,6 +1552,8 @@
this.claimRecording = claimRecording;
this.startPlayClaimedRecording = startPlayClaimedRecording;
this.stopPlayClaimedRecording = stopPlayClaimedRecording;
this.openJamTrack = openJamTrack;
this.closeJamTrack = closeJamTrack;
this.discardRecording = discardRecording;
this.putTrackSyncChange = putTrackSyncChange;
this.createBand = createBand;
@ -1543,6 +1582,7 @@
this.getLatencyTester = getLatencyTester;
this.updateAudioLatency = updateAudioLatency;
this.getJamtracks = getJamtracks;
this.getPurchasedJamTracks = getPurchasedJamTracks;
this.addJamtrackToShoppingCart = addJamtrackToShoppingCart;
this.getShoppingCarts = getShoppingCarts;
this.removeShoppingCart = removeShoppingCart;

View File

@ -1658,7 +1658,7 @@
}
function bailOut() {
promptLeave = false;
promptLeave = false;
context.window.location = '/client#/home';
}
@ -1796,6 +1796,22 @@
.fail(app.ajaxError);
}
function openJamTrack(e) {
// just ignore the click if they are currently recording for now
if(sessionModel.recordingModel.isRecording()) {
app.notify({
"title": "Currently Recording",
"text": "You can't open a jam track while creating a recording.",
"icon_url": "/assets/content/icon_alert_big.png"
});
return false;
}
app.layout.showDialog('open-jam-track-dialog');
return false;
}
function openRecording(e) {
// just ignore the click if they are currently recording for now
if(sessionModel.recordingModel.isRecording()) {
@ -1814,6 +1830,36 @@
return false;
}
function closeOpenMedia() {
if(sessionModel.recordedTracks()) {
closeRecording();
}
else if(sessionModel.jamTrack()) {
closeJamTrack();
}
else {
logger.error("don't know how to close open media (backing track?)");
}
}
function closeJamTrack() {
rest.closeJamTrack({id: sessionModel.id()})
.done(function() {
sessionModel.refreshCurrentSession();
})
.fail(function(jqXHR) {
app.notify({
"title": "Couldn't Close JamTrack",
"text": "Couldn't inform the server to close JamTrack. msg=" + jqXHR.responseText,
"icon_url": "/assets/content/icon_alert_big.png"
});
});
context.jamClient.JamTrackStopPlay();
return false;
}
function closeRecording() {
rest.stopPlayClaimedRecording({id: sessionModel.id(), claimed_recording_id: sessionModel.getCurrentSession().claimed_recording.id})
.done(function() {
@ -1857,7 +1903,7 @@
}
function inviteMusicians() {
friendInput = inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians',
friendInput = inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians',
sessionId);
inviteMusiciansUtil.loadFriends();
$(friendInput).show();
@ -1892,6 +1938,7 @@
$('#tracks').on('click', 'div[control="mute"]', toggleMute);
$('#recording-start-stop').on('click', startStopRecording);
$('#open-a-recording').on('click', openRecording);
$('#open-a-jamtrack').on('click', openJamTrack);
$('#session-invite-musicians').on('click', inviteMusicians);
$('#session-invite-musicians2').on('click', inviteMusicians);
$('#track-settings').click(function() {
@ -1900,7 +1947,7 @@
configureTrackDialog.showMusicAudioPanel(true);
});
$('#close-playback-recording').on('click', closeRecording);
$('#close-playback-recording').on('click', closeOpenMedia);
$(playbackControls)
.on('pause', onPause)
.on('play', onPlay)

View File

@ -105,7 +105,7 @@
/**
* Associates a help bubble on hover (by default) with the specified $element, using jquery.bt.js (BeautyTips)
* @param $element The element that should show the help when hovered
* @param templateName the name of the help template (without the '#template-help' prefix). Add to _help.html.erb
* @param templateName the name of the help template (without the '#template-help' prefix). Add to _help.html.slim
* @param data (optional) data for your template, if applicable
* @param options (optional) You can override the default BeautyTips options: https://github.com/dillon-sellars/BeautyTips
*/
@ -146,7 +146,7 @@
/**
* Meant to show a little bubble to confirm something happened, in a way less obtrusive than a app.notify
* @param $element The element that should show the help when hovered
* @param templateName the name of the help template (without the '#template-help' prefix). Add to _help.html.erb
* @param templateName the name of the help template (without the '#template-help' prefix). Add to _help.html.slim
* @param data (optional) data for your template, if applicable
* @param options (optional) You can override the default BeautyTips options: https://github.com/dillon-sellars/BeautyTips
*/
@ -167,7 +167,7 @@
* This will open a bubble immediately and show it for 4 seconds,
* if you call it again before the 4 second timer is up, it will renew the 4 second timer.
* @param $element The element that should show the help when hovered
* @param templateName the name of the help template (without the '#template-help' prefix). Add to _help.html.erb
* @param templateName the name of the help template (without the '#template-help' prefix). Add to _help.html.slim
* @param data (optional) data for your template, if applicable
* @param options (optional) You can override the default BeautyTips options: https://github.com/dillon-sellars/BeautyTips
*/

View File

@ -106,6 +106,33 @@
top:3px;
}
}
.session-recording-name-wrapper{
position:relative;
white-space:nowrap;
display:none;
.session-add {
margin-top:9px;
}
.session-add a {
vertical-align:top;
outline:none;
img {
margin-top:-3px;
}
}
}
.session-recording-name {
width:60%;
overflow:hidden;
margin-top:9px;
margin-bottom:8px;
font-size:16px;
}
}
@ -312,33 +339,6 @@ table.vu td {
vertical-align:bottom;
}
.session-recording-name-wrapper {
position:relative;
white-space:nowrap;
display:none;
.session-add {
margin-top:9px;
}
.session-add a {
vertical-align:top;
outline:none;
img {
margin-top:-3px;
}
}
}
.session-recording-name {
width:60%;
overflow:hidden;
margin-top:9px;
margin-bottom:8px;
font-size:16px;
}
.session-tracks-scroller {
position:relative;

View File

@ -1,7 +1,7 @@
@import "client/common";
table.findsession-table, table.local-recordings, #account-session-detail {
table.findsession-table, table.local-recordings, table.open-jam-tracks, #account-session-detail {
.latency-unacceptable {
width: 50px;
@ -64,7 +64,7 @@ table.findsession-table, table.local-recordings, #account-session-detail {
text-align:center;
}
}
table.findsession-table, table.local-recordings {
table.findsession-table, table.local-recordings, table.open-jam-tracks {
width:98%;
height:10%;
font-size:11px;
@ -77,6 +77,9 @@ table.findsession-table, table.local-recordings {
background-color:#4d4d4d;
padding:6px;
border-right:solid 1px #333;
a {
color: #fc0;
}
}
td {

View File

@ -1,16 +1,26 @@
@import "client/common";
#local-recordings-dialog {
table.local-recordings {
tbody {
tr:hover {
background-color: #400606;
background-color: #777;
cursor:pointer;
}
tr[data-local-state=MISSING], tr[data-local-state=PARTIALLY_MISSING] {
background-color:#777;
background-color:#400606;
color:#aaa;
}
}
}
.right {
margin-right:10px;
}
.paginator-holder {
padding-top:3px;
}
}

View File

@ -0,0 +1,44 @@
@import "client/common";
#open-jam-track-dialog {
table.open-jam-tracks {
tbody {
tr:hover {
background-color: #777;
cursor:pointer;
}
tr[data-local-state=MISSING], tr[data-local-state=PARTIALLY_MISSING] {
background-color:#777;
color:#aaa;
}
}
}
.downloaded-jamtrack-help {
margin-left:15px;
}
.right {
margin-right:10px;
}
.help-links {
text-align: center;
position: absolute;
margin: 0 auto;
width: 70%;
left: 15%;
font-size: 12px;
padding-top:5px;
a {
margin:0 10px;
}
}
.paginator-holder {
padding-top:3px;
}
}

View File

@ -13,6 +13,16 @@ class ApiJamTracksController < ApiController
render "api_jam_tracks/index", :layout => nil
end
def purchased
params[:show_purchased_only] = true
data = JamTrack.index(params, current_user)
@jam_tracks, @next = data[0], data[1]
response.headers['total-entries'] = @jam_tracks.total_entries.to_s
render "api_jam_tracks/purchased", :layout => nil
end
def downloads
begin
render :json => JamTrack.list_downloads(current_user, params[:limit], params[:since]), :status => 200

View File

@ -0,0 +1,7 @@
node :next do |page|
@next
end
node :jamtracks do |page|
partial "api_jam_tracks/show_for_client", object: @jam_tracks
end

View File

@ -0,0 +1,11 @@
object @jam_track
attributes :id, :name, :description, :initial_play_silence, :original_artist
child(:jam_track_tracks => :tracks) {
attributes :id, :part, :instrument
}
child(:jam_track_tap_ins => :tap_ins) {
attributes :offset_time, :bpm, :tap_in_count
}

View File

@ -1,173 +0,0 @@
<script type="text/template" id="template-help-pre-processed-track">
This track has not yet been processed into master form and may include multiple streams from the source musician.
</script>
<script type="text/template" id="template-help-refocus-rescore">
{% if(data.validIOScore) { %}
We have re-scored latency based on your changes.
{% } else { %}
We have re-scored latency and IO based on your changes.
{% } %}
</script>
<script type="text/template" id="template-help-ftue-watch-video">
Be sure to watch the help video.
</script>
<script type="text/template" id="template-help-chat-not-enabled">
You must first chose this option in order to activate a chat input.
</script>
<script type="text/template" id="template-help-select-input">
You still need to select an input device.
</script>
<script type="text/template" id="template-help-select-output">
Select an output device, because the current input device has no candidate output ports.
</script>
<script type="text/template" id="template-help-push-resync-when-done">
Push 'Resync' when done modifying Framesize, Buffer In, or Buffer Out.
</script>
<script type="text/template" id="template-help-can-move-on">
<div class="help-can-move-on">
You can move to the next step now.
</div>
</script>
<script type="text/template" id="template-help-tweak-asio-settings">
Click here to try faster ASIO settings.
</script>
<script type="text/template" id="template-help-session-plus-musicians">
Plus any interested JamKazam musicians that I approve.
</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 type="text/template" id="template-help-follower-count">
The number of followers that this {{data.entity_type}} has.
</script>
<script type="text/template" id="template-help-friend-count">
The number of friends that this {{data.entity_type}} has.
</script>
<script type="text/template" id="template-help-recording-count">
The number of recordings that this {{data.entity_type}} has made.
</script>
<script type="text/template" id="template-help-session-count">
The number of sessions that this {{data.entity_type}} has played in.
</script>
<script type="text/template" id="template-help-musician-score-count">
<div class="help-musician-score-count">
<p>The score shown is the one-way latency (or delay) in milliseconds from you to this user. This score is calculated using the following three values that JamKazam gathers:</p>
<ul>
<li><span class="definition">Your Audio Gear Latency:</span> <span class="measurement my-gear-latency partial"><span class="measurement-value">{{data.my_gear_latency ? data.my_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.my_gear_latency ? '' : "(you have not qualified any gear, so we picked an average gear latency)"}}</span></span></li>
<li><span class="definition">Their Audio Gear Latency:</span> <span class="measurement their-gear-latency partial"><span class="measurement-value">{{data.their_gear_latency ? data.their_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.their_gear_latency ? '' : "(they have not qualified any gear, so we picked an average gear latency)"}}</span></span></li>
<li><span class="definition">Round-trip Internet Latency:</span> <span class="measurement internet-latency partial"><span class="measurement-value">{{data.internet_latency ? data.internet_latency + ' ms': '?'}}</span> <span class="measurement-absent">{{data.internet_latency ? '' : "(we have not scored you with this user yet)"}}</span></span></li>
</ul>
<p> <span class="definition">Total One-Way Latency:</span> <span class="measurement my-gear-latency"><span class="measurement-value">( {{data.my_gear_latency ? data.my_gear_latency: '13'}} + {{data.their_gear_latency ? data.their_gear_latency: '13'}} + {{data.internet_latency ? data.internet_latency: '?'}} ) / 2 = {{data.full_score ? data.full_score + ' ms' : "?"}}</span> <span class="measurement-absent">{{data.full_score ? '' : "(when we don't know internet latency, we don't try to guess your one-way latency)"}}</span></span>
<p>We categorize this score as good, fair, poor, unacceptable, or unknown. Those categories are defined as follows:
<ul>
<li><span class="definition">Good:</span> 20ms or less <img src="/assets/content/icon_green_score.png" /></li>
<li><span class="definition">Fair:</span> 20ms to 35ms <img src="/assets/content/icon_yellow_score.png" /></li>
<li><span class="definition">Poor:</span> 35ms to 50ms <img src="/assets/content/icon_red_score.png" /></li>
<li><span class="definition">Unacceptable:</span> Above 50ms <img src="/assets/content/icon_blue_score.png" /></li>
<li><span class="definition">Unknown:</span> No internet score is available between you and them. <img src="/assets/content/icon_purple_score.png" /></li>
</ul>
</div>
</script>
<script type="text/template" id="template-help-musician-score-self">
<div class="help-musician-score-self">
<p>You are looking at your own account.</p>
<p>Try hovering over other user's latency score to find out your one-way latency to them.</p>
<p><span class="definition">Your Audio Gear Latency:</span> <span class="measurement my-gear-latency"><span class="measurement-value">{{data.my_gear_latency ? data.my_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.my_gear_latency ? '' : "(you have not qualified any gear, so we picked an average gear latency)"}}</span></span></p>
</div>
</script>
<script type="text/template" id="template-help-gear-wizard-inputs-changed">
<div class='help-inputs-changed'>
{% if(data.missingInputDevice) { %}
<p> {{ data.missingInputDevice }} is no longer connected or malfunctioning.
{% } %}
{% if(data.newInputDevice) { %}
<p> {{ data.newInputDevice }} is now available.
{% } %}
</div>
</script>
<script type="text/template" id="template-help-gear-wizard-outputs-changed">
<div class='help-outputs-changed'>
{% if(data.missingOutputDevice) { %}
<p> {{ data.missingOutputDevice }} is no longer connected or malfunctioning.
{% } %}
{% if(data.newOutputDevice) { %}
<p> {{ data.newOutputDevice }} is now available.
{% } %}
</div>
</script>
<script type="text/template" id="template-help-no-audio-profiles">
<div class="help-no-audio-profiles">
Click here to configure new audio gear.
</div>
</script>
<script type="text/template" id="template-help-file-manager-poke">
<div class="help-file-manager-poke">
<p>After the session is over, your recording will synchronize with the server to make a final mix.</p>
<p>You can find out more by clicking <em>File Manager</em> at any time.</p>
</div>
</script>
<script type="text/template" id="template-help-sync-viewer-paused">
<div class="help-sync-viewer-paused">
JamKazam prevents file uploads, downloads, and recording management while in a session.
</div>
</script>
<script type="text/template" id="template-help-sync-viewer-retry">
<div class="help-sync-viewer-retry">
Your request will be attempted as soon as possible.
</div>
</script>
<script type="text/template" id="template-help-recording-discarded-soon">
<div class="help-recording-discarded-soon">
This recording will be discarded {% $.timeago(data.discardTime) %}. If you want to keep this recording, click the (edit) link.
</div>
</script>
<script type="text/template" id="template-help-command-enqueued">
<div class="help-recording-command-enqueued">
Your request will be executed as soon as possible.
</div>
</script>
<script type="text/template" id="template-help-file-sync-delayed-deletion">
<div class="file-sync-delayed-deletion">
The files associated with this recording will be deleted as soon as your client has uploaded your tracks and stream mix from this recording.
</div>
</script>
<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.
{% } %}
</script>

View File

@ -0,0 +1,206 @@
script type="text/template" id="template-help-pre-processed-track"
| This track has not yet been processed into master form and may include multiple streams from the source musician.
script type="text/template" id="template-help-refocus-rescore"
| {% if(data.validIOScore) { %}
| We have re-scored latency based on your changes.
| {% } else { %}
| We have re-scored latency and IO based on your changes.
| {% } %}
script type="text/template" id="template-help-ftue-watch-video"
| Be sure to watch the help video.
script type="text/template" id="template-help-chat-not-enabled"
| You must first chose this option in order to activate a chat input.
script type="text/template" id="template-help-select-input"
| You still need to select an input device.
script type="text/template" id="template-help-select-output"
| Select an output device, because the current input device has no candidate output ports.
script type="text/template" id="template-help-push-resync-when-done"
| Push 'Resync' when done modifying Framesize, Buffer In, or Buffer Out.
script type="text/template" id="template-help-can-move-on"
.help-can-move-on
| You can move to the next step now.
script type="text/template" id="template-help-tweak-asio-settings"
| Click here to try faster ASIO settings.
script type="text/template" id="template-help-session-plus-musicians"
| Plus any interested JamKazam musicians that I approve.
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 type="text/template" id="template-help-follower-count"
| The number of followers that this {{data.entity_type}} has.
script type="text/template" id="template-help-friend-count"
| The number of friends that this {{data.entity_type}} has.
script type="text/template" id="template-help-recording-count"
| The number of recordings that this {{data.entity_type}} has made.
script type="text/template" id="template-help-session-count"
| The number of sessions that this {{data.entity_type}} has played in.
script type="text/template" id="template-help-musician-score-count"
.help-musician-score-count
p
| The score shown is the one-way latency (or delay) in milliseconds from you to this user. This score is calculated using the following three values that JamKazam gathers:
ul
li
span.definition
| Your Audio Gear Latency:&nbsp;
span.measurement.my-gear-latency.partial
span.measurement-value
| {{data.my_gear_latency ? data.my_gear_latency + ' ms': '13 ms*'}}
span.measurement-absent
| {{data.my_gear_latency ? '' : "(you have not qualified any gear, so we picked an average gear latency)"}}
li
span.definition
| Their Audio Gear Latency:&nbsp;
span.measurement.their-gear-latency.partial
span.measurement-value
| {{data.their_gear_latency ? data.their_gear_latency + ' ms': '13 ms*'}}
span.measurement-absent
| {{data.their_gear_latency ? '' : "(they have not qualified any gear, so we picked an average gear latency)"}}
li
span.definition
| Round-trip Internet Latency:&nbsp;
span.measurement.internet-latency.partial
span.measurement-value
| {{data.internet_latency ? data.internet_latency + ' ms': '?'}}
span.measurement-absent
| {{data.internet_latency ? '' : "(we have not scored you with this user yet)"}}
p
span.definition
| Total One-Way Latency:&nbsp;
span.measurement.my-gear-latency
span.measurement-value
| ( {{data.my_gear_latency ? data.my_gear_latency: '13'}} + {{data.their_gear_latency ? data.their_gear_latency: '13'}} + {{data.internet_latency ? data.internet_latency: '?'}} ) / 2 = {{data.full_score ? data.full_score + ' ms' : "?"}}
span.measurement-absent
| {{data.full_score ? '' : "(when we don't know internet latency, we don't try to guess your one-way latency)"}}
p
| We categorize this score as good, fair, poor, unacceptable, or unknown. Those categories are defined as follows:
ul
li
span.definition
| Good:&nbsp;
| 20ms or less&nbsp;
img src="/assets/content/icon_green_score.png"
li
span.definition
| Fair:&nbsp;
| 20ms to 35ms
img src="/assets/content/icon_yellow_score.png"
li
span.definition
| Poor:&nbsp;
| 35ms to 50ms&nbsp;
img src="/assets/content/icon_red_score.png"
li
span.definition
| Unacceptable:&nbsp;
| Above 50ms&nbsp;
img src="/assets/content/icon_blue_score.png"
li
span.definition
| Unknown:&nbsp;
| No internet score is available between you and them.
img src="/assets/content/icon_purple_score.png"
script type="text/template" id="template-help-musician-score-self"
.help-musician-score-self
p
| You are looking at your own account.
p
| Try hovering over other user's latency score to find out your one-way latency to them.
p
span.definition
| Your Audio Gear Latency:&nbsp;
span.measurement.my-gear-latency
span.measurement-value
| {{data.my_gear_latency ? data.my_gear_latency + ' ms': '13 ms*'}}
span.measurement-absent
| {{data.my_gear_latency ? '' : "(you have not qualified any gear, so we picked an average gear latency)"}}
script type="text/template" id="template-help-gear-wizard-inputs-changed"
.help-inputs-changed
| {% if(data.missingInputDevice) { %}
p
| {{ data.missingInputDevice }} is no longer connected or malfunctioning.
| {% } %}
| {% if(data.newInputDevice) { %}
p
| {{ data.newInputDevice }} is now available.
| {% } %}
script type="text/template" id="template-help-gear-wizard-outputs-changed"
.help-outputs-changed
| {% if(data.missingOutputDevice) { %}
p
| {{ data.missingOutputDevice }} is no longer connected or malfunctioning.
| {% } %}
| {% if(data.newOutputDevice) { %}
p
| {{ data.newOutputDevice }} is now available.
| {% } %}
script type="text/template" id="template-help-no-audio-profiles"
.help-no-audio-profiles
| Click here to configure new audio gear.
script type="text/template" id="template-help-file-manager-poke"
.help-file-manager-poke
p
| After the session is over, your recording will synchronize with the server to make a final mix.
p
| You can find out more by clicking
em &nbsp;File Manager&nbsp;
| at any time.
script type="text/template" id="template-help-sync-viewer-paused"
.help-sync-viewer-paused
| JamKazam prevents file uploads, downloads, and recording management while in a session.
script type="text/template" id="template-help-sync-viewer-retry"
.help-sync-viewer-retry
| Your request will be attempted as soon as possible.
script type="text/template" id="template-help-recording-discarded-soon"
.help-recording-discarded-soon
| This recording will be discarded {% $.timeago(data.discardTime) %}. If you want to keep this recording, click the (edit) link.
script type="text/template" id="template-help-command-enqueued"
.help-recording-command-enqueued
| Your request will be executed as soon as possible.
script type="text/template" id="template-help-file-sync-delayed-deletion"
.file-sync-delayed-deletion
| The files associated with this recording will be deleted as soon as your client has uploaded your tracks and stream mix from this recording.
script type="text/template" id="template-help-media-controls-disabled"
| {% if(data.mediaTrackOpener) { %}
| Switch
b &nbsp;MIX:&nbsp;
| to
b &nbsp;Master&nbsp;
| mode to have control over volume levels.
| {% } else { %}
| Only the person who opened the recording can control the volume levels.
| {% } %}
script type="text/template" id="template-help-downloaded-jamtrack"
.downloaded-jamtrack
p When a JamTrack is first purchased, a user-specific version of it is created on the server. Once it's ready, it's then downloaded to the client.
p However, in some cases, you may need to download the JamTrack again (if you change machines, for instance).
p If you do not currently have it and try to open it now, we will try to download it immediately.

View File

@ -116,8 +116,10 @@
<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>-->
<% if Rails.application.config.jam_tracks_available %>
<li><a href="#" id="open-a-jamtrack">JamTrack</a></li>
<% end %>
<!--<li>Audio File</li>-->
</ul>
</div>
</div>

View File

@ -152,6 +152,9 @@
var localRecordingsDialog = new JK.LocalRecordingsDialog(JK.app);
localRecordingsDialog.initialize();
var openJamTrackDialog = new JK.OpenJamTrackDialog(JK.app);
openJamTrackDialog.initialize();
var configureTracksDialog = new JK.ConfigureTracksDialog(JK.app);
configureTracksDialog.initialize();

View File

@ -31,3 +31,4 @@
= render 'dialogs/changeSearchLocationDialog'
= render 'dialogs/allSyncsDialog'
= render 'dialogs/adjustGearSpeedDialog'
= render 'dialogs/openJamTrackDialog'

View File

@ -1,4 +1,3 @@
<!-- Invitation Dialog -->
<div class="dialog localRecordings-overlay ftue-overlay tall" layout="dialog" layout-id="localRecordings" id="local-recordings-dialog">
<div class="content-head">

View File

@ -0,0 +1,50 @@
.dialog.openJamTrackDialog-overlay.ftue-overlay.tall#open-jam-track-dialog layout="dialog" layout-id="open-jam-track-dialog"
.content-head
= image_tag "content/icon_add.png", {:width => 19, :height => 19, :class => 'content-icon' }
h1
| open a jamtrack
.dialog-inner
.recording-wrapper
table.open-jam-tracks cellspacing="0" cellpadding="0" border="0"
thead
tr
th align="left"
| NAME
th align="left"
| ORIGINAL ARTIST
th align="left"
| DOWNLOADED
a.downloaded-jamtrack-help href="#"
| ?
tbody
br
.left.paginator-holder
.help-links
a.what-are-jamtracks href='#'
| What are JamTracks?
a href='/client#/jamtrack' rel="external"
| Shop for JamTracks
.right
a href="#" class="button-grey" layout-action="close"
| CANCEL
br clear="all"
script#template-jam-track-row type="text/template"
tr data-recording-id="{{data.jamTrackId}}" data-local-state="{{data.jamTrackState}}"
td
| {{data.name}}
td
| {{data.artist}}
td
| {{data.downloaded}}

View File

@ -193,6 +193,7 @@ SampleApp::Application.routes.draw do
# Jamtracks
match '/jamtracks' => 'api_jam_tracks#index', :via => :get, :as => 'api_jam_tracks_list'
match '/jamtracks/purchased' => 'api_jam_tracks#purchased', :via => :get, :as => 'api_jam_tracks_purchased'
match '/jamtracks/downloads' => 'api_jam_tracks#downloads', :via => :get, :as => 'api_jam_tracks_downloads'
match '/jamtracks/download/:id' => 'api_jam_tracks#download', :via => :get, :as => 'api_jam_tracks_download'
match '/jamtracks/keys' => 'api_jam_tracks#keys', :via => :post, :as => 'api_jam_tracks_keys'

View File

@ -82,8 +82,27 @@ describe ApiJamTracksController do
response.status.should == 202
response.body.should =~ /not available.*/
end
end
describe "purchased" do
it "can return empty" do
get :purchased
response.should be_success
json = JSON.parse(response.body)
json['jamtracks'].length.should eq(0)
json['next'].should eq(nil)
end
it "can return single item" do
# purchase the item for the user
right = FactoryGirl.create(:jam_track_right, jam_track: @jam_track, user: @user)
get :purchased
response.should be_success
json = JSON.parse(response.body)
json['jamtracks'].length.should eq(1)
json['next'].should be_nil
end
end
describe "with a JamTrack" do