wip new recording window

This commit is contained in:
Nuwan 2023-07-19 15:10:15 +05:30
parent 5727780259
commit 2dceeb86c3
7 changed files with 232 additions and 90 deletions

View File

@ -3,8 +3,6 @@ logger = context.JK.logger
MIX_MODES = context.JK.MIX_MODES
mixins = []
#MixerActions = @MixerActions
#MixerStore = context.MixerStore
RecordingActions = @RecordingActions
mixins.push(Reflux.listenTo(@SessionMyTracksStore,"onInputChanged"))
@ -15,7 +13,6 @@ mixins.push(Reflux.listenTo(@AppStore,"onAppInit"))
mixins: mixins
onAppInit: (@app) ->
@setState(app: @app)
onInputChanged: (sessionMixers) ->
inputGroupMixers = {}
@ -38,35 +35,32 @@ mixins.push(Reflux.listenTo(@AppStore,"onAppInit"))
this.setState(isRecording: recordingState.isRecording, recordedOnce: this.state.recordedOnce || recordingState.isRecording)
handleSettingSubmit: (settings) ->
volSetting = volume: @state.inputGroupMixers.mixer[0].volume_left
recordSettings = $.extend({}, settings, volSetting)
console.log('__DEBUG__ recordSettings', recordSettings)
#TODO: pass settings to backend?
success = true
if success
@startStopRecording(recordSettings)
volume = volume: @state.inputGroupMixers.mixer[0].volume_left
recordSettings = $.extend({}, settings, volume)
try
localStorage.setItem("recordSettings", JSON.stringify(recordSettings))
catch e
logger.info("error while saving recordSettings to localStorage")
logger.log(e.stack)
@startStopRecording(recordSettings)
closeDialog: () ->
@app.layout.cancelDialog('session-recording')
startStopRecording: (settings) ->
console.log('__DEBUG__ @state.isRecording', @state.isRecording)
if @state.isRecording
RecordingActions.stopRecording()
else
recordChat = settings.includeChat
recordVideo = settings.recordingInputType == 'audio-video'
if recordVideo
return unless context.JK.videoIsOngoing
logger.debug("@inputType, @udiotye", recordChat, recordVideo)
RecordingActions.startRecording(recordVideo, recordChat)
@closeDialog()
closeDialog: () ->
@state.app.layout.cancelDialog('session-recording');
render: () ->
if @state.app && Object.keys(@state.inputGroupMixers).length > 0 then `<div className="recording-container">
if Object.keys(@state.inputGroupMixers).length > 0 then `<div className="recording-container">
<div className="recording-left">
<SessionRecordingSettings app={this.state.app} handleSubmit={this.handleSettingSubmit} handleCancel={this.closeDialog} />
</div>

View File

@ -49,8 +49,8 @@ AppStore = context.AppStore
buttons: buttons})
render: () ->
`<a className="session-record button-grey left" data-is-recording={this.state.isRecording} onClick={this.handleClick}>
`<a className="session-record session-record-btn button-grey left" data-is-recording={this.state.isRecording} onClick={this.handleClick}>
<img src="/assets/content/icon_record.png" align="texttop" height="14" width="14"/>
{this.state.isRecording ? 'STOP RECORD' : 'RECORD'}
{this.state.isRecording ? 'STOP RECORDING' : 'RECORD'}
</a>`
})

View File

@ -3,34 +3,33 @@ mixins = []
ExternalVideoStore = context.ExternalVideoStore
#mixins.push(Reflux.listenTo(@AppStore,"onAppInit"))
mixins.push(Reflux.listenTo(ExternalVideoStore, "onExternalVideoStateChanged"))
@SessionRecordingSettings = React.createClass({
mixins: mixins
# beforeShow: (args) ->
# alert('beforeShow')
# afterHide: (args) ->
# onAppInit: (@app) ->
# dialogBindings = {
# 'beforeShow': @beforeShow,
# 'afterHide': @afterHide
# };
# @app.bindDialog('session-recording', dialogBindings);
getInitialState: () ->
{
recordingInputType: 'audio-only',
recordingName: '',
audioFormat: 'ogg',
videoFormat: 'mp4',
audioDelay: 0,
includeChat: false,
}
try
settings = localStorage.getItem('recordSettings')
rs = JSON.parse(settings)
{
recordingInputType: rs.recordingInputType,
audioFormat: rs.audioFormat,
videoFormat: rs.videoFormat,
audioDelay: rs.audioDelay,
includeChat: rs.includeChat,
}
catch e
console.error('error loading recording settings from local storage', e.message)
logger.log(e.stack)
{
recordingInputType: 'audio-only',
recordingName: '',
audioFormat: 'ogg',
videoFormat: 'mp4',
audioDelay: 0,
includeChat: false,
}
setRecordingNameChange: (e) ->
@setState(recordingName: e.target.value)
@ -39,20 +38,32 @@ mixins.push(Reflux.listenTo(ExternalVideoStore, "onExternalVideoStateChanged"))
@setState(recordingInputType: e.target.value)
setAudioFormatChange: (e) ->
@setState(audioFormat: e.target.value)
$root = $(@getDOMNode())
audioFormat = $root.find("#audio-format").val()
@setState(audioFormat: audioFormat)
setVideoFormatChange: (e) ->
@setState(videoFormat: e.target.value)
$root = $(@getDOMNode())
videoFormat = $root.find("#video-format").val()
@setState(videoFormat: videoFormat)
setAudioDelay: (e) ->
@setState(audioDelay: e.target.value)
setIncludeChat: (e) ->
@setState(includeChat: e.target.checked)
cancel: (e) ->
e.preventDefault()
@props.handleCancel()
startRecording: () ->
@props.handleSubmit(@state)
startRecording: (e) ->
e.preventDefault()
$root = $(@getDOMNode())
$recName = $root.find('#recording-name')
if $recName.val().length > 0
@props.handleSubmit(@state)
@setState(recordingName: '')
onExternalVideoStateChanged: (videoState) ->
$root = $(this.getDOMNode())
@ -76,26 +87,26 @@ mixins.push(Reflux.listenTo(ExternalVideoStore, "onExternalVideoStateChanged"))
<div className="form-item">
<label>Record:</label>
<div className="recording-type-container">
<div className="recording-type">
<div className="recording-type recording-type-audio">
<input type="radio" name="recording-input-type" id="recording-input-audio" value="audio-only" defaultChecked="checked" onClick={this.setRecordingInputTypeChange} />
<label htmlFor="recording-input-audio">Audio only</label>
</div>
<div className="recording-type">
<div className="recording-type recording-type-both">
<input type="radio" name="recording-input-type" id="recording-input-both" value="audio-video" onClick={this.setRecordingInputTypeChange} />
<label htmlFor="recording-input-both">Audio and video</label>
<label htmlFor="recording-input-both">Audio and Video</label>
</div>
</div>
</div>
<div className="form-item">
<label htmlFor="recording-name">Name:</label>
<div>
<input type="text" name="name" id="recording-name" onChange={this.setRecordingNameChange} />
<input type="text" name="name" id="recording-name" value={this.state.recordingName} onChange={this.setRecordingNameChange} />
</div>
</div>
<div className="form-item">
<label htmlFor="audio-format">Audio Format:</label>
<div>
<select id="audio-format" onChange={this.setAudioFormatChange}>
<select id="audio-format" value={this.state.audioFormat} onChange={this.setAudioFormatChange}>
{audioFormatOptions}
</select>
</div>
@ -103,7 +114,7 @@ mixins.push(Reflux.listenTo(ExternalVideoStore, "onExternalVideoStateChanged"))
<div className="form-item">
<label htmlFor="video-format">Video Format:</label>
<div>
<select id="video-format" onChange={this.setVideoFormatChange}>
<select id="video-format" value={this.state.videoFormat} onChange={this.setVideoFormatChange}>
{videoFormatOptions}
</select>
</div>
@ -114,9 +125,9 @@ mixins.push(Reflux.listenTo(ExternalVideoStore, "onExternalVideoStateChanged"))
<input type="number" min="0" width="3" name="name" id="audio-delay" onChange={this.setAudioDelay} value={this.state.audioDelay} />
</div>
</div>
<div className="form-item">
<div className="form-item include-chat-check">
<label>Voice Chat:</label>
<input type="checkbox" name="include-chat" id="include-chat" />
<input type="checkbox" name="include-chat" id="include-chat" checked={this.state.includeChat} />
<label className="include-chat-label" htmlFor="include-chat">Include voice chat in recorded audio</label>
</div>
<div className="form-actions">
@ -130,18 +141,31 @@ mixins.push(Reflux.listenTo(ExternalVideoStore, "onExternalVideoStateChanged"))
context.JK.dropdown($root.find('select'))
$includeChatCheckbox = $root.find('#include-chat')
context.JK.checkbox($includeChatCheckbox)
$inputAudioRadioBtn = $root.find('#recording-input-audio')
context.JK.checkbox($inputAudioRadioBtn)
$inputBothRadioBtn = $root.find('#recording-input-both')
$inputBothRadioBtn = $root.find('#recording-input-both')
#only enable audio & video radio button if a video is ongoing
$inputBothRadioBtn.iCheck().attr('disabled', !context.JK.videoIsOngoing)
context.JK.checkbox($inputBothRadioBtn)
# if @state.recordingInputType == 'audio-video'
# $inputBothRadioBtn.iCheck('check').attr('checked', true)
# else
# $inputAudioRadioBtn.iCheck('check').attr('checked', true)
$inputBothRadioBtn.prop('disabled',true).iCheck('update').iCheck('uncheck');
context.JK.checkbox($inputAudioRadioBtn)
context.JK.checkbox($inputBothRadioBtn)
$includeChatCheckbox = $root.find('#include-chat')
$includeChatCheckbox.iCheck('check').attr('checked', @state.includeChat)
$includeChatCheckbox.on 'ifToggled', (e) =>
@setState(includeChat: e.target.checked)
context.JK.checkbox($includeChatCheckbox)
componentDidUpdate: () ->
$root = $(this.getDOMNode())
context.JK.dropdown($root.find('select'))
$root.find('#audio-format').unbind('change').change(this.setAudioFormatChange)
$root.find('#video-format').unbind('change').change(this.setVideoFormatChange)
})

View File

@ -4,7 +4,7 @@ mixins = []
MixerStore = context.MixerStore
mixins.push(Reflux.listenTo(MixerStore,"onInputChanged"))
mixins.push(Reflux.listenTo(SessionMyTracksStore,"onInputChanged"))
@SessionRecordingVu = React.createClass({
mixins: mixins
@ -17,16 +17,15 @@ mixins.push(Reflux.listenTo(MixerStore,"onInputChanged"))
categoryMixers = mixers.simulatedMusicCategoryMixers[MIX_MODES.PERSONAL]
if categoryMixers
inputGroupMixers = categoryMixers
@setState({mixers: inputGroupMixers})
getInitialState: () ->
{ mixers: @props.mixers }
render: () ->
monitorMuteMixer = @props.mixers.muteMixer[0]
renderVu: () ->
monitorMuteMixer = @state.mixers.muteMixer[0]
monitorMuteMixerId = mixers?.id
monitorVolumeLeft = @props.mixers.mixer[0].volume_left
monitorVolumeLeft = @state.mixers.mixer[0].volume_left
monitorMuteClasses = classNames({
'track-icon-mute': true
'enabled' : !monitorMuteMixer?.mute
@ -38,7 +37,6 @@ mixins.push(Reflux.listenTo(MixerStore,"onInputChanged"))
Volume:
</div>
<div className="vol-gauge">
<div className="session-track track">
<div className="track-vu-left">
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="best" mixers={this.state.mixers} />
@ -63,17 +61,19 @@ mixins.push(Reflux.listenTo(MixerStore,"onInputChanged"))
<p>To change the volume of individual tracks in the mix, use the personal mix controls in the session window.</p>
</div>
</div>
</div>
`
</div>`
render: () ->
if Object.keys(@state.mixers).length > 0 then @renderVu() else `<span>Loading...</span>`
handleAudioInputMute: (e) ->
e.preventDefault()
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute(@props.mixers.muteMixer, muting)
MixerActions.mute(@state.mixers.muteMixer, muting)
handleAudioInputMuteCheckbox: (e) ->
muting = $(e.target).is(':checked')
MixerActions.mute(@props.mixers.muteMixer, muting)
MixerActions.mute(@state.mixers.muteMixer, muting)
componentDidMount: () ->
$root = jQuery(this.getDOMNode())
@ -83,7 +83,7 @@ mixins.push(Reflux.listenTo(MixerStore,"onInputChanged"))
context.JK.checkbox($audioInputMuteCheckbox)
$audioInputMuteCheckbox.on('ifChanged', @handleAudioInputMuteCheckbox)
if @props.mixers.muteMixer[0].mute
if @state.mixers.muteMixer[0].mute
$audioInputMuteCheckbox.iCheck('check').attr('checked', true)
else
$audioInputMuteCheckbox.iCheck('uncheck').attr('checked', false)

View File

@ -14,15 +14,17 @@ BackendToFrontendFPS = {
@RecordingStore = Reflux.createStore(
{
listenables: @RecordingActions
recordingWindow: null
#recordingWindow: null
recordingWindowOpened: false
init: ->
# Register with the app store to get @app
this.listenTo(context.AppStore, this.onAppInit)
onSessionEnded: () ->
if @recordingWindow?
@recordingWindow.close()
if @recordingWindowOpened
#@recordingWindow.close()
@closeRecordingWindow()
onAppInit: (app) ->
@app = app
@ -46,7 +48,6 @@ BackendToFrontendFPS = {
# @recordingModel.startRecording(recordVideo, recordChat, frameRate)
onStartRecording: `async function(recordVideo, recordChat) {
console.log("onStartRecording...", recordVideo, recordChat)
let frameRate = 0;
if (recordVideo) {
//if (await context.jamClient.GetCurrentVideoFrameRate() != null) {
@ -58,7 +59,6 @@ BackendToFrontendFPS = {
const WebCamRecordActive = 1;
const ScreenRecordActive = 2;
logger.debug('onStartRecording: recordVideo: '+recordVideo+' , recordChat:' +recordChat+' frameRate: '+frameRate);
//logger.debug('this.recordingModel', this.recordingModel)
this.recordingModel.startRecording(recordVideo, recordChat, frameRate);
}`
@ -69,15 +69,15 @@ BackendToFrontendFPS = {
details.cause = 'starting'
@mixTransferred = false
this.trigger(details)
@popupRecordingControls() unless @recordingWindow?
@popupRecordingControls() unless @recordingWindowOpened
onStartedRecording: (details) ->
details.cause = 'started'
@mixTransferred = false
this.trigger(details)
@popupRecordingControls() unless @recordingWindow?
#@popupRecordingControls() unless @recordingWindowOpened
@closeRecordingWindow() if @recordingWindowOpened
onStoppingRecording: (details) ->
details.cause = 'stopping'
@ -86,31 +86,33 @@ BackendToFrontendFPS = {
onStoppedRecording: (details) ->
details.cause = 'stopped'
if @recordingWindow?
@recordingWindow.close()
if @recordingWindowOpened
#@recordingWindow.close()
@closeRecordingWindow()
this.trigger(details)
onAbortedRecording: (details) ->
details.cause = 'aborted'
if @recordingWindow?
@recordingWindow.close()
if @recordingWindowOpened
#@recordingWindow.close()
@closeRecordingWindow()
this.trigger(details)
onOpenRecordingControls: () ->
logger.debug("recording controls opening")
if @recordingWindow?
@recordingWindow.close()
if @recordingWindowOpened
#@recordingWindow.close()
@closeRecordingWindow()
@popupRecordingControls()
onRecordingControlsClosed: () ->
logger.debug("recording controls closed")
@recordingWindow = null
#@recordingWindow = null
@recordingWindowOpened = false
onMixTransferred: () ->
@mixTransferred = true
@ -121,6 +123,10 @@ BackendToFrontendFPS = {
#@recordingWindow.ParentRecordingStore = context.RecordingStore
#@recordingWindow.ParentIsRecording = @recordingModel.isRecording()
@app.layout.showDialog('session-recording', {})
@recordingWindowOpened = true
closeRecordingWindow: () ->
@app.layout.cancelDialog('session-recording');
@recordingWindowOpened = false
}
)

View File

@ -0,0 +1,46 @@
require 'spec_helper'
describe "Session Recording", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
before(:all) do
#Capybara.javascript_driver = :poltergeist
#Capybara.current_driver = Capybara.javascript_driver
#Capybara.default_max_wait_time = 30 # these tests are SLOOOOOW
end
let(:creator) { FactoryGirl.create(:user) }
before(:each) do
ActiveMusicSession.delete_all
end
it "video checkbox is diabled if video session is not started" do
creator.subscription_plan_code = SubscriptionDefinitions::JAM_PLATINUM
creator.subscription_trial_ends_at = 1.day.ago
creator.admin_override_plan_code = SubscriptionDefinitions::JAM_PLATINUM
creator.admin_override_ends_at = 1.day.from_now
creator.save!
create_new_session(creator: creator, include_video: true) do
expect(page.find("input#recording-input-both")).not_to be_checked
# page.find('.btnCancel').click
# video_window = page.window_opened_by do
# page.find('.in-session-controls a.session-video-btn').click
# end
# sleep 1
# within_window(video_window) do
# click_link "JOIN ROOM"
# end
# sleep 1
end
end
it "start/stop recording", focus: true do
create_new_session(creator: creator) do
page.find('.recording-left .form-actions a', text: 'START RECORDING').click
page.find('.in-session-controls a.session-record-btn', text: 'STOP RECORDING').click
page.find('.in-session-controls a.session-record-btn', text: 'RECORD')
end
end
end

View File

@ -344,8 +344,8 @@ end
# will select the value from a easydropdown'ed select element
def jk_select(text, select)
# the approach here is to find the hidden select element, and work way back up to the elements that need to be interacted with
find(select, :visible => false).find(:xpath, 'ancestor::div[contains(@class, "dropdown easydropdown") and not(contains(@class, "disabled"))]').trigger(:click)
find(select, :visible => false).find(:xpath, 'ancestor::div[contains(@class, "dropdown-wrapper") and contains(@class, "easydropdown-wrapper") and contains(@class, "open") ]').find('li', text: text).trigger(:click)
find(select, :visible => false).find(:xpath, 'ancestor::div[contains(@class, "dropdown easydropdown") and not(contains(@class, "disabled"))]').click
find(select, :visible => false).find(:xpath, 'ancestor::div[contains(@class, "dropdown-wrapper") and contains(@class, "easydropdown-wrapper") and contains(@class, "open") ]').find('li', text: text).click
# works, but is 'cheating' because of visible = false
#select(genre, :from => 'genres', :visible => false)
@ -418,6 +418,78 @@ def create_session(options={})
end
#===START test helpers for session creation and recording windows===
def create_new_session(options={}, &block)
creator = options[:creator] || FactoryGirl.create(:user)
unique_session_name = options[:name] || "create_join_session #{SecureRandom.urlsafe_base64}"
audio_format = options[:audio_format] || '.mp3'
video_format = options[:video_format] || '.mp4'
audio_delay = options[:audio_delay] || 0
include_voice_chat = options[:include_voice_chat] || false
include_video = options[:include_video] || false
in_client(creator) do
#page.driver.resize(1500, 800) # makes sure all the elements are visible
emulate_client
fast_signin(creator, "/client#/createSession")
expect(page).to have_selector('h1', text: 'sessions')
find('a.quick-start-solo').click()
find('.in-session-controls a.session-record-btn').click()
within('#session-recording') do
expect(page).to have_selector('h1', text: 'recording')
expect(page).to have_selector('a', text: 'START RECORDING')
if include_video
find('.recording-type-both ins.iCheck-helper').click
end
fill_in('Name', :with => unique_session_name)
jk_select(audio_format, 'select#audio-format')
jk_select(video_format, 'select#video-format')
fill_in('Audio Delay (ms)', :with => audio_delay)
if include_voice_chat
find('.include-chat-check ins.iCheck-helper').click
end
end
yield
end
return creator, unique_session_name
end
def create_session_and_join(creator, joiners=[], options={})
options[:creator] = creator
creator, unique_session_name = create_new_session(options)
# find session in second client
[*joiners].each do |joiner|
join_session(joiner, name: unique_session_name)
end
return creator, unique_session_name
end
def join_with_session(joiner, options)
name = options[:name]
in_client(joiner) do
#page.driver.resize(1500, 800) # makes sure all the elements are visible
emulate_client
fast_signin(joiner, "/client#/findSession")
# verify the session name is seen by second client
expect(page).to have_text(name)
find('.join-link').click
unless options[:no_verify]
find('#btn-accept-terms').click
expect(page).to have_selector('h2', text: 'my live tracks')
find('#session-screen .session-my-tracks .session-track.my-track')
end
end
end
#===END test helpers for session creation and recording windows===
def schedule_session(options = {})
creator = options[:creator] || FactoryGirl.create(:user)
unique_session_name = options[:name] || "schedule_session #{SecureRandom.urlsafe_base64}"