Recording seems to be working well enough atm. With new clients only

This commit is contained in:
Seth Call 2025-06-08 22:24:37 -05:00
parent e83805bd4a
commit 324d34ff61
14 changed files with 152 additions and 98 deletions

View File

@ -863,6 +863,10 @@ module JamRuby
self.save!(:validate => false)
end
def in_session?(user)
self.users.exists?(user.id)
end
def connected_participant_count
Connection.where(:music_session_id => self.id,
:aasm_state => Connection::CONNECT_STATE.to_s,

View File

@ -75,6 +75,10 @@ module JamRuby
has_stream_mix
end
def can_stop?(user)
# only allow the starting-user to create (ideally, perhaps, only the client that did it)
user == owner
end
# this should be a has-one relationship. until this, this is easiest way to get from recording > mix
def mix
self.mixes[0] if self.mixes.length > 0
@ -214,7 +218,7 @@ module JamRuby
def has_access?(user)
return false if user.nil?
users.exists?(user.id) || attached_with_lesson(user) || (music_session && music_session.part_of_session?(user))
users.exists?(user.id) || attached_with_lesson(user) || (music_session && music_session.in_session?(user))
end
def attached_with_lesson(user)

View File

@ -11,7 +11,7 @@ AppStore = context.AppStore
onSessionMixerChange: (sessionMixers) ->
#console.log("_DEBUG_* SessionRecordBtn onSessionMixerChange", sessionMixers.session)
@setState({isRecording: sessionMixers.session.isRecording})
@setState({isRecordingOwner: sessionMixers.session.recordingOwnerId && sessionMixers.session.recordingOwnerId == @app.currentUserId})
@setState({thisClientStartedRecording: sessionMixers.session.thisClientStartedRecording})
getInitialState: () ->
{childWindow: null, isRecording: false}
@ -25,7 +25,7 @@ AppStore = context.AppStore
return 'noclose'
handleClick: () ->
if @state.isRecording && @state.isRecordingOwner
if @state.isRecording && @state.thisClientStartedRecording
RecordingActions.stopRecording()
else
@openRecording()
@ -52,8 +52,8 @@ AppStore = context.AppStore
render: () ->
btnStyles = "session-record session-record-btn left"
if this.state.isRecording
if this.state.isRecordingOwner
if this.state.isRecording
if this.state.thisClientStartedRecording
btnStyles = btnStyles+' button-orange'
`<a className={btnStyles} data-is-recording={this.state.isRecording} onClick={this.handleClick}>
<img src="/assets/content/icon_record.png" align="texttop" height="14" width="14"/>

View File

@ -23,7 +23,7 @@ mixins.push(Reflux.listenTo(RecordingStore, "onRecordingStateChanged"))
# @setState({audioFormat: recordingState.audioRecordingFormat})
onRecordingStateChanged: `function (recordingState) {
console.log('_REC_ onRecordingStateChanged', recordingState);
//console.log('_REC_ onRecordingStateChanged', recordingState);
if (recordingState.audioRecordingFormat) {
this.setState({audioFormat: recordingState.audioRecordingFormat});
}

View File

@ -41,7 +41,11 @@ RecordingStore = @RecordingStore
events = []
for event in @state.events
events.push(`<li key={event[0]}>{event[0]} brid={event[1]}</li>`)
body = `<div className="msg">{recording} {cause} {waitingOnClientStop} {waitingOnServerStop}</div>`
initiator = ""
if this.state.thisClientStartedRecording
initiator = "Initatior"
body = `<div className="msg">{recording} {initiator} {cause} {waitingOnClientStop} {waitingOnServerStop}</div>`
`<div className={classes}>
{body}

View File

@ -2,7 +2,7 @@ context = window
@SessionHelper = class SessionHelper
constructor: (app, session, participantsEverSeen, isRecording, downloadingJamTrack, preppingVstEnable, sessionRules, subscriptionRules, recordingOwnerId) ->
constructor: (app, session, participantsEverSeen, isRecording, downloadingJamTrack, preppingVstEnable, sessionRules, subscriptionRules, thisClientStartedRecording) ->
@app = app
@session = session
@participantsEverSeen = participantsEverSeen
@ -12,7 +12,7 @@ context = window
@isLesson = @session?.lesson_session?
@sessionRules = sessionRules
@subscriptionRules = subscriptionRules
@recordingOwnerId = recordingOwnerId
@thisClientStartedRecording = thisClientStartedRecording
if @isLesson
@lessonId = @session.lesson_session.id

View File

@ -34,35 +34,42 @@ window.RecordingStore = Reflux.createStore({
onInitModel(recordingModel) {
this.recordingModel = recordingModel;
this.events = [];
this.trigger({ isRecording: this.recordingModel.isRecording(), events: this.events });
this.trigger({ isRecording: this.recordingModel.isRecording(), events: this.events, thisClientStartedRecording: false });
},
onResetRecordingState() {
console.log("onResetRecordingState");
this.events = []
this.trigger({ isRecording: this.recordingModel.isRecording(), cause: '', events: this.events });
this.trigger({ isRecording: this.recordingModel.isRecording(), cause: '', events: this.events, thisClientStartedRecording: false });
},
onStartRecording(recordSettings) {
async augmentWithBackendRecordingId(lastEvent) {
const backendRecordingId = await context.jamClient.GetCurrentRecordingId();
lastEvent.push(backendRecordingId);
return backendRecordingId;
},
async onStartRecording(recordSettings) {
this.recordingModel.startRecording(recordSettings);
this.events = [];
this.events.push(["StartRecording"]);
this.augmentWithBackendRecordingId(this.events);
this.trigger({ events: this.events });
await this.augmentWithBackendRecordingId(this.events[this.events.length - 1]);
this.trigger({ events: this.events, thisClientStartedRecording: this.recordingModel.getThisClientStartedRecording() });
},
onStopRecording() {
async onStopRecording() {
this.recordingModel.stopRecording();
this.events.push(["StopRecording"]);
this.augmentWithBackendRecordingId(this.events);
this.trigger({ events: this.events });
await this.augmentWithBackendRecordingId(this.events[this.events.length - 1]);
this.trigger({ events: this.events, thisClientStartedRecording: this.recordingModel.getThisClientStartedRecording() });
},
onStartingRecording(details) {
async onStartingRecording(details) {
details.cause = 'starting';
details.thisClientStartedRecording = this.recordingModel.getThisClientStartedRecording();
this.mixTransferred = false;
this.events.push(["OnStartingRecording"]);
this.augmentWithBackendRecordingId(this.events);
await this.augmentWithBackendRecordingId(this.events[this.events.length - 1]);
details.events = this.events;
this.trigger(details);
if (!this.recordingWindowOpened) {
@ -70,37 +77,44 @@ window.RecordingStore = Reflux.createStore({
}
},
async augmentWithBackendRecordingId(events) {
const backendRecordingId = await context.jamClient.GetCurrentRecordingId();
events[events.length - 1].push(backendRecordingId);
},
onStartedRecording(details) {
async onStartedRecording(details) {
details.cause = 'started';
details.thisClientStartedRecording = this.recordingModel.getThisClientStartedRecording();
this.mixTransferred = false;
this.events.push(["OnStartedRecording"]);
details.events = this.events;
this.augmentWithBackendRecordingId(this.events);
this.trigger(details);
const backendRecordingId = await this.augmentWithBackendRecordingId(this.events[this.events.length - 1]);
if(backendRecordingId == null) {
// if recording Id is empty here, we need to unwind that we are recording.
console.error("No backend recording ID present when startRecording signal received");
context.JK.Banner.showAlert("The JamKazam application is out-of-sync on this start of this recording. " +
"Please leave the session and re-join to fix the issue.");
this.recordingModel.stopRecording(this.recordingModel.currentOrLastRecordingId(), "start-failure", "backend recording ID not set on started signal");
}
else {
this.trigger(details);
}
if (this.recordingWindowOpened) {
this.closeRecordingWindow();
}
},
onStoppingRecording(details) {
async onStoppingRecording(details) {
details.cause = 'stopping';
details.thisClientStartedRecording = this.recordingModel.getThisClientStartedRecording();
this.events.push(["OnStoppingRecording"]);
details.events = this.events;
this.augmentWithBackendRecordingId(this.events);
await this.augmentWithBackendRecordingId(this.events[this.events.length - 1]);
this.trigger(details);
},
onStoppedRecording(details) {
async onStoppedRecording(details) {
details.cause = 'stopped';
details.thisClientStartedRecording = this.recordingModel.getThisClientStartedRecording();
this.events.push(["OnStoppedRecording"]);
details.events = this.events;
this.augmentWithBackendRecordingId(this.events);
await this.augmentWithBackendRecordingId(this.events[this.events.length - 1]);
if (this.recordingWindowOpened) {
this.closeRecordingWindow();
@ -109,11 +123,12 @@ window.RecordingStore = Reflux.createStore({
this.trigger(details);
},
onAbortedRecording(details) {
async onAbortedRecording(details) {
details.cause = 'aborted';
details.thisClientStartedRecording = this.recordingModel.getThisClientStartedRecording();
this.events.push(["OnAbortedRecording"]);
details.events = this.events;
this.augmentWithBackendRecordingId(this.events);
await this.augmentWithBackendRecordingId(this.events[this.events.length - 1]);
if (this.recordingWindowOpened) {
this.closeRecordingWindow();
@ -141,7 +156,7 @@ window.RecordingStore = Reflux.createStore({
onAudioRecordingFormatChanged(audioFormat) {
console.log("_DEBUG_ onAudioRecordingFormatChanged", JSON.stringify(this.recordingModel));
this.trigger({ audioRecordingFormat: audioFormat, isRecording: this.recordingModel.isRecording() });
this.trigger({ audioRecordingFormat: audioFormat, isRecording: this.recordingModel.isRecording(), thisClientStartedRecording: this.recordingModel.getThisClientStartedRecording() });
},
popupRecordingControls() {

View File

@ -44,6 +44,7 @@ ConfigureTracksActions = @ConfigureTracksActions
openBackingTrack: null
helper: null
downloadingJamTrack: false
startedRecording: false
init: ->
# Register with the app store to get @app
@ -57,7 +58,7 @@ ConfigureTracksActions = @ConfigureTracksActions
@sessionUtils = context.JK.SessionUtils
@recordingModel = new context.JK.RecordingModel(@app, rest, context.jamClient);
RecordingActions.initModel(@recordingModel)
@helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack, @enableVstTimeout?, @sessionRules, @subscriptionRules, @recordingOwnerId)
@helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack, @enableVstTimeout?, @sessionRules, @subscriptionRules, @thisClientStartedRecording)
# onSessionJoinedByOther: (payload) ->
# clientId = payload.client_id
@ -95,7 +96,7 @@ ConfigureTracksActions = @ConfigureTracksActions
onVideoChanged: (@videoState) ->
issueChange: () ->
@helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack, @enableVstTimeout?, @sessionRules, @subscriptionRules, @recordingOwnerId)
@helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack, @enableVstTimeout?, @sessionRules, @subscriptionRules, @thisClientStartedRecording)
this.trigger(@helper)
onWindowBackgrounded: () ->
@ -885,12 +886,14 @@ ConfigureTracksActions = @ConfigureTracksActions
onRecordingChanged: `async function(details) {
let detail, reason, timeline, title;
logger.debug("SessionStore.onRecordingChanged: " + details.cause);
//console.log("_DEBUG_* SessionStore.onRecordingChanged: " + JSON.stringify(details));
//this.isRecording = details.isRecording;
// console.log("_DEBUG_* SessionStore.onRecordingChanged: " + JSON.stringify(details));
this.isRecording = details.isRecording;
this.thisClientStartedRecording = details.thisClientStartedRecording;
this.recordingClinetId = details.clientId;
let recordingState = await this.recordingModel.getCurrentRecordingState();
this.recordingOwnerId = recordingState.recordingOwnerId;
this.isRecording = recordingState.isRecording;
//let recordingState = await this.recordingModel.getCurrentRecordingState();
//this.recordingOwnerId = recordingState.recordingOwnerId;
//this.isRecording = recordingState.isRecording;
switch (details.cause) {
case 'started':
@ -2022,7 +2025,6 @@ ConfigureTracksActions = @ConfigureTracksActions
//}
//logger.debug("session changed")
logger.debug("issue change");
return this.issueChange();
}`

View File

@ -22,6 +22,7 @@
var currentRecording = null; // the JSON response from the server for a recording
var currentOrLastRecordingId = null;
var currentRecordingId = null;
var thisClientStartedRecording = false; // set by the person who starts it
var rest = _rest;
var currentlyRecording = false;
var startingRecording = false;
@ -53,6 +54,10 @@
}
}
function getThisClientStartedRecording() {
return thisClientStartedRecording;
}
/** called every time a session is joined, to ensure clean state */
function reset(_sessionId) {
console.log("[RecordingState]: reset")
@ -66,6 +71,7 @@
currentRecording = null;
currentRecordingId = null;
stoppingRecording = false;
thisClientStartedRecording = false;
sessionId = _sessionId
}
@ -97,6 +103,7 @@
currentlyRecording = true;
stoppingRecording = false;
thisClientStartedRecording = true;
context.RecordingActions.startingRecording({isRecording: false})
@ -134,9 +141,7 @@
// return;
//}
const isRecordingOwner = recording.owner.id == context.JK.currentUserId;
console.log(`[RecordingState]: stopRecording userInitiated=${userInitiated} isRecordingOwner=${isRecordingOwner} reason=${reason} detail=${detail}`)
console.log(`[RecordingState]: stopRecording userInitiated=${userInitiated} thisClientStartedRecording=${thisClientStartedRecording} reason=${reason} detail=${detail}`)
if(stoppingRecording) {
console.log("ignoring stopRecording because we are already stopping");
@ -165,7 +170,7 @@
//await jamClient.StopRecording(recording.id, groupedTracks);
await jamClient.FrontStopRecording(recording.id, groupedTracks);
if(isRecordingOwner) {
if(thisClientStartedRecording) {
rest.stopRecording({"id": recording.id})
.done(function () {
waitingOnServerStop = false;
@ -250,7 +255,7 @@
context.SessionStore.updateSessionInfo(session, true)
getCurrentRecordingState().then(function (recordingState) {
if (recordingState.isRecording && recordingState.recordingOwnerId === app.currentUserId) {
if (recordingState.isRecording && recordingState.thisClientStartedRecording) {
// we are still recording, so don't transition to stopped
console.log("recording is still running, so don't transition to stopped");
return;
@ -306,7 +311,33 @@
}
}
function postServerRecordingFetch(recording) {
if(currentRecordingId == null) {
currentRecordingId = recording.id;
currentOrLastRecordingId = recording.id;
var details = {recordingId: currentRecordingId, isRecording: false}
$self.triggerHandler('startingRecording', details);
context.RecordingActions.startingRecording(details);
currentlyRecording = true;
details = {clientId: app.clientId, recordingId: currentRecordingId, isRecording: true}
$self.triggerHandler('startedRecording', details);
context.RecordingActions.startedRecording(details);
}
else if (currentRecordingId == recording.id) {
// noop - we fetched recording but we already know about it
}
else {
console.error("[RecordingState] we've missed the stop of previous recording", currentRecordingId, recording.id);
context.JK.Banner.showAlert("The JamKazam application is out-of-sync on this recording. " +
"Please leave the session and re-join to fix the issue.");
}
}
function handleRecordingStarted(recordingId, result, clientId) {
reset(sessionId);
context.RecordingActions.resetRecordingState()
console.log("[RecordingState] handleRecordingStarted called", {
@ -339,8 +370,7 @@
currentUserId: context.JK.currentUserId,
recordingState: recording
});
currentRecordingId = recording.id;
currentOrLastRecordingId = recording.id;
postServerRecordingFetch(recording)
})
.catch(function(error) {
console.error("[RecordingState] Failed to fetch recording data", {
@ -355,15 +385,6 @@
currentlyRecording
});
});
var details = {recordingId: recordingId, isRecording: false}
$self.triggerHandler('startingRecording', details);
context.RecordingActions.startingRecording(details)
currentlyRecording = true;
details = {clientId: clientId, recordingId: recordingId, isRecording: true}
$self.triggerHandler('startedRecording', details);
context.RecordingActions.startedRecording(details)
}
function handleRecordingStopped(recordingId, result) {
@ -374,17 +395,11 @@
context.SessionStore.updateSessionInfo(session, true)
}
// if(recordState.isRecording) {
// //we are still recording, so don't stop
// return;
// }
if(recordingId == "video") {
// comes from VideoRecordingStopped
return;
}
var success = result.success;
var reason = result.reason;
var detail = result.detail;
@ -399,40 +414,41 @@
if(recordingId == null || recordingId == "") {
// this occurs when you are told by the backend to stop; i.e., you are a non-creator of the
// recording. Stop, don't tell the server to stop; you aren't supposed to hve permission to
transitionToStopped();
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
context.RecordingActions.stoppedRecording(details)
context.RecordingActions.stoppedRecording(details);
return;
}
rest.stopRecording({
id: recordingId
})
.always(function() {
transitionToStopped();
})
.fail(function(jqXHR, textStatus, errorMessage) {
if(jqXHR.status == 422) {
console.log("recording already stopped %o", arguments);
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
$self.triggerHandler('stoppedRecording', details);
context.RecordingActions.stoppedRecording(details)
}
else if(jqXHR.status == 404) {
console.log("recording is already deleted %o", arguments);
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
$self.triggerHandler('stoppedRecording', details);
context.RecordingActions.stoppedRecording(details)
}
else {
var details = {recordingId: recordingId, reason: textStatus, detail: errorMessage, isRecording: false}
$self.triggerHandler('stoppedRecording', details);
context.RecordingActions.stoppedRecording(details)
}
})
.done(function() {
.always(function() {
transitionToStopped();
})
.fail(function(jqXHR, textStatus, errorMessage) {
if(jqXHR.status == 422) {
console.log("recording already stopped %o", arguments);
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
$self.triggerHandler('stoppedRecording', details);
context.RecordingActions.stoppedRecording(details)
})
}
else if(jqXHR.status == 404) {
console.log("recording is already deleted %o", arguments);
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
$self.triggerHandler('stoppedRecording', details);
context.RecordingActions.stoppedRecording(details)
}
else {
var details = {recordingId: recordingId, reason: textStatus, detail: errorMessage, isRecording: false}
$self.triggerHandler('stoppedRecording', details);
context.RecordingActions.stoppedRecording(details)
}
})
.done(function() {
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
$self.triggerHandler('stoppedRecording', details);
context.RecordingActions.stoppedRecording(details)
})
}
function handleRecordingAborted(recordingId, result) {
@ -516,7 +532,6 @@
async function getCurrentRecordingState() {
var recording = null;
var session = context.SessionStore.getCurrentOrLastSession();
var recordingId = await context.jamClient.GetCurrentRecordingId();
var isRecording = recordingId && recordingId != "";
@ -541,13 +556,13 @@
if (!session) {
console.debug("no session, so no recording");
return { isRecording: false, serverRecording: null, recordingOwnerId: null };
return { isRecording: false, serverRecording: null, thisClientStartedRecording: false };
}
return {
isRecording: isRecording,
isServerRecording: !!recording,
recordingOwnerId: isRecording && recording ? recording.owner.id : null,
thisClientStartedRecording: thisClientStartedRecording
}
}
@ -560,6 +575,7 @@
this.onServerStopRecording = onServerStopRecording;
this.onServerStartRecording = onServerStartRecording;
this.isRecording = isRecording;
this.getThisClientStartedRecording = getThisClientStartedRecording;
this.reset = reset;
this.stopRecordingIfNeeded = stopRecordingIfNeeded;
this.getState = getState;

View File

@ -290,19 +290,20 @@
if (app.clientId !== payload.client_id) {
var recordingId = payload.recording_id;
// this logic below is from when anyone leaving would stop a recording
//var recordingId = payload.recording_id;
//console.log("Sidebar: SESSION_DEPART isRecording", context.RecordingStore.recordingModel.isRecording(recordingId));
if (recordingId && context.RecordingStore.recordingModel.isRecording(recordingId)) {
//if (recordingId && context.RecordingStore.recordingModel.isRecording(recordingId)) {
//alert("Sidebar: SESSION_DEPART: calling onServerStopRecording");
context.RecordingStore.recordingModel.onServerStopRecording(recordingId);
}
else {
//context.RecordingStore.recordingModel.onServerStopRecording(recordingId);
//}
// else {
app.notify({
"title": "Musician Left Session",
"text": payload.msg,
"icon_url": context.JK.resolveAvatarUrl(payload.photo_url)
});
}
//}
}
//if (context.jamClient.ClientLeftSession) {
await context.jamClient.ClientLeftSession(payload["source_user_id"], payload["client_id"], payload["session_id"])

View File

@ -186,6 +186,10 @@ class ApiRecordingsController < ApiController
end
def stop
# only allow the creator to stop the recording
if @recording.can_stop?(current_user) == false
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
end
@recording.stop

View File

@ -386,6 +386,7 @@ if defined?(Bundler)
config.video_available = "full"
config.alerts_api_enabled = true
config.show_recording_debug_status = false
config.gear_check_ignore_high_latency = false
config.remove_whitespace_credit_card = false
config.estimate_taxes = true

View File

@ -112,6 +112,8 @@ SampleApp::Application.configure do
config.rating_dialog_min_num = 1
config.root_redirect_on = false
config.show_recording_debug_status = true
config.video_conferencing_host = "http://localhost:3001"
config.use_video_conferencing_server = true
config.latency_data_host = "http://localhost:4001/local"

View File

@ -31,5 +31,6 @@ Gon.global.braintree_token = Rails.application.config.braintree_token
Gon.global.paypal_admin_only = Rails.application.config.paypal_admin_only
Gon.global.use_video_conferencing_server = Rails.application.config.use_video_conferencing_server
Gon.global.manual_override_installer_ends_with = Rails.application.config.manual_override_installer_ends_with
Gon.global.show_recording_debug_status = Rails.application.config.show_recording_debug_status
Gon.global.env = Rails.env
Gon.global.version = ::JamWeb::VERSION