jam-cloud/web/app/assets/javascripts/fakeJamClientRecordings.js

257 lines
12 KiB
JavaScript

// this code simulates what the actual backend recording feature will do
(function(context, $) {
"use strict";
context.JK = context.JK || {};
context.JK.FakeJamClientRecordings = function(app, fakeJamClient, p2pMessageFactory) {
var logger = context.JK.logger;
var startRecordingResultCallbackName = null;
var stopRecordingResultCallbackName = null;
var startedRecordingResultCallbackName = null;
var stoppedRecordingEventCallbackName = null;
var abortedRecordingEventCallbackName = null;
var startingSessionState = null;
var stoppingSessionState = null;
var currentRecordingId = null;
var currentRecordingCreatorClientId = null;
var currentRecordingClientIds = null;
function timeoutStartRecordingTimer() {
eval(startRecordingResultCallbackName).call(this, startingSessionState.recordingId, {success:false, reason:'client-no-response', detail:startingSessionState.groupedClientTracks[0]});
startingSessionState = null;
}
function timeoutStopRecordingTimer() {
eval(stopRecordingResultCallbackName).call(this, stoppingSessionState.recordingId, {success:false, reason:'client-no-response', detail:stoppingSessionState.groupedClientTracks[0]});
}
function StartRecording(recordingId, clients) {
startingSessionState = {};
// we expect all clients to respond within 1 seconds to mimic the reliable UDP layer
startingSessionState.aggegratingStartResultsTimer = setTimeout(timeoutStartRecordingTimer, 1000);
startingSessionState.recordingId = recordingId;
startingSessionState.groupedClientTracks = copyClientIds(clients, app.clientId); // we will manipulate this new one
// store the current recording's data
currentRecordingId = recordingId;
currentRecordingCreatorClientId = app.clientId;
currentRecordingClientIds = copyClientIds(clients, app.clientId);
if(startingSessionState.groupedClientTracks.length == 0) {
// if there are no clients but 'self', then you can declare a successful recording immediately
finishSuccessfulStart(recordingId);
}
else {
// signal all other connected clients that the recording has started
for(var i = 0; i < startingSessionState.groupedClientTracks.length; i++) {
var clientId = startingSessionState.groupedClientTracks[i];
context.JK.JamServer.sendP2PMessage(clientId, JSON.stringify(p2pMessageFactory.startRecording(recordingId)));
}
}
}
function StopRecording(recordingId, clients, result) {
if(startingSessionState) {
// we are currently starting a session.
// TODO
}
if(!result) {
result = {success:true}
}
stoppingSessionState = {};
// we expect all clients to respond within 1 seconds to mimic the reliable UDP layer
stoppingSessionState.aggegratingStopResultsTimer = setTimeout(timeoutStopRecordingTimer, 1000);
stoppingSessionState.recordingId = recordingId;
stoppingSessionState.groupedClientTracks = copyClientIds(clients, app.clientId);
if(stoppingSessionState.groupedClientTracks.length == 0) {
finishSuccessfulStop(recordingId);
}
else {
// signal all other connected clients that the recording has stopped
for(var i = 0; i < stoppingSessionState.groupedClientTracks.length; i++) {
var clientId = stoppingSessionState.groupedClientTracks[i];
context.JK.JamServer.sendP2PMessage(clientId, JSON.stringify(p2pMessageFactory.stopRecording(recordingId, result.success, result.reason, result.detail)));
}
}
}
function AbortRecording(recordingId, errorReason, errorDetail) {
// todo check recordingId
context.JK.JamServer.sendP2PMessage(currentRecordingCreatorClientId, JSON.stringify(p2pMessageFactory.abortRecording(recordingId, errorReason, errorDetail)));
}
function onStartRecording(from, payload) {
logger.debug("received start recording request from " + from);
if(context.SessionStore.isRecording()) {
// reject the request to start the recording
context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.startRecordingAck(payload.recordingId, false, "already-recording", null)));
}
else {
// accept the request, and then tell the frontend we are now recording
// a better client implementation would verify that the tracks specified match that what we have configured currently
// store the current recording's data
currentRecordingId = payload.recordingId;
currentRecordingCreatorClientId = from;
context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.startRecordingAck(payload.recordingId, true, null, null)));
eval(startedRecordingResultCallbackName).call(this, payload.recordingId, {success:true}, from);
}
}
function onStartRecordingAck(from, payload) {
logger.debug("received start recording ack from " + from);
// we should check transactionId; this could be an ACK for a different recording
if(startingSessionState) {
if(payload.success) {
var index = startingSessionState.groupedClientTracks.indexOf(from);
startingSessionState.groupedClientTracks.splice(index, 1);
if(startingSessionState.groupedClientTracks.length == 0) {
finishSuccessfulStart(payload.recordingId);
}
}
else {
// TOOD: a client responded with error; we need to tell all other clients to abandon recording
logger.warn("received an unsuccessful start_record_ack from: " + from);
}
}
else {
logger.warn("received a start_record_ack when there is no recording starting from: " + from);
// TODO: this is an error case; we should signal back to the sender that we gave up
}
}
function onStopRecording(from, payload) {
logger.debug("received stop recording request from " + from);
// TODO check recordingId, and if currently recording
// we should return success if we are currently recording, or if we were already asked to stop for this recordingId
// this means we should keep a list of the last N recordings that we've seen, rather than just keeping the current
context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.stopRecordingAck(payload.recordingId, true)));
if(stopRecordingResultCallbackName) {
eval(stopRecordingResultCallbackName).call(this, payload.recordingId, {success:payload.success, reason:payload.reason, detail:from});
}
}
function onStopRecordingAck(from, payload) {
logger.debug("received stop recording ack from " + from);
// we should check transactionId; this could be an ACK for a different recording
if(stoppingSessionState) {
if(payload.success) {
var index = stoppingSessionState.groupedClientTracks.indexOf(from);
stoppingSessionState.groupedClientTracks.splice(index, 1);
if(stoppingSessionState.groupedClientTracks.length == 0) {
finishSuccessfulStop(payload.recordingId);
}
}
else {
// TOOD: a client responded with error; what now?
logger.error("client responded with error: ", payload);
}
}
else {
// TODO: this is an error case; we should tell the caller we have no recording at the moment
}
}
function onAbortRecording(from, payload) {
logger.debug("received abort recording from " + from);
// TODO check if currently recording and if matches payload.recordingId
// if creator, tell everyone else to stop
if(app.clientId == currentRecordingCreatorClientId) {
// ask the front end to stop the recording because it has the full track listing
for(var i = 0; i < currentRecordingClientIds.length; i++) {
var clientId = currentRecordingClientIds[i];
context.JK.JamServer.sendP2PMessage(clientId, JSON.stringify(p2pMessageFactory.abortRecording(currentRecordingId, payload.reason, from)));
}
}
else {
logger.debug("only the creator currently deals with the abort request. abort request sent from:" + from + " with a reason of: " + payload.errorReason);
}
eval(abortedRecordingEventCallbackName).call(this, payload.recordingId, {success:payload.success, reason:payload.reason, detail:from});
}
function RegisterRecordingCallbacks(startRecordingCallbackName,
stopRecordingCallbackName,
startedRecordingCallbackName,
stoppedRecordingCallbackName,
abortedRecordingCallbackName) {
startRecordingResultCallbackName = startRecordingCallbackName;
stopRecordingResultCallbackName = stopRecordingCallbackName;
startedRecordingResultCallbackName = startedRecordingCallbackName;
stoppedRecordingEventCallbackName = stoppedRecordingCallbackName;
abortedRecordingEventCallbackName = abortedRecordingCallbackName;
}
// copies all clientIds, but removes current client ID because we don't want to message that user
function copyClientIds(clientIds, myClientId) {
var newClientIds = [];
for(var i = 0; i < clientIds.length; i++) {
var clientId = clientIds[i]
if(clientId != myClientId) {
newClientIds.push(clientId);
}
}
return newClientIds;
}
function finishSuccessfulStart(recordingId) {
// all clients have responded.
clearTimeout(startingSessionState.aggegratingStartResultsTimer);
startingSessionState = null;
eval(startRecordingResultCallbackName).call(this, recordingId, {success:true});
}
function finishSuccessfulStop(recordingId, errorReason) {
// all clients have responded.
clearTimeout(stoppingSessionState.aggegratingStopResultsTimer);
stoppingSessionState = null;
var result = { success: true }
if(errorReason)
{
result.success = false;
result.reason = errorReason
result.detail = ""
}
eval(stopRecordingResultCallbackName).call(this, recordingId, result);
}
// register for p2p callbacks
var callbacks = {};
callbacks[p2pMessageFactory.Types.START_RECORDING] = onStartRecording;
callbacks[p2pMessageFactory.Types.START_RECORDING_ACK] = onStartRecordingAck;
callbacks[p2pMessageFactory.Types.STOP_RECORDING] = onStopRecording;
callbacks[p2pMessageFactory.Types.STOP_RECORDING_ACK] = onStopRecordingAck;
callbacks[p2pMessageFactory.Types.ABORT_RECORDING] = onAbortRecording;
fakeJamClient.RegisterP2PMessageCallbacks(callbacks);
this.StartRecording = StartRecording;
this.StopRecording = StopRecording;
this.AbortRecording = AbortRecording;
this.RegisterRecordingCallbacks = RegisterRecordingCallbacks;
}
})(window, jQuery);