// 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, context.SessionStore.isRecording); 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);