From d64769919a4df2d0835f7640719b493ae44ac790 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Fri, 28 Nov 2025 19:37:57 +0530 Subject: [PATCH] Implement recording modal for jam-ui session screen - Add JKSessionRecordingModal component with form for recording controls - Integrate modal into JKSessionScreen with Record button - Use component state instead of Redux for modal management - Include audio/video options, video source selection, and chat inclusion - Connect to existing useRecordingHelpers hook for recording logic - Match functionality from web project's PopupRecordingStartStop --- .../client/JKSessionRecordingModal.js | 265 ++++++++++++++++++ .../src/components/client/JKSessionScreen.js | 11 +- 2 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 jam-ui/src/components/client/JKSessionRecordingModal.js diff --git a/jam-ui/src/components/client/JKSessionRecordingModal.js b/jam-ui/src/components/client/JKSessionRecordingModal.js new file mode 100644 index 000000000..832a5aa45 --- /dev/null +++ b/jam-ui/src/components/client/JKSessionRecordingModal.js @@ -0,0 +1,265 @@ +import React, { useState, useEffect } from 'react'; +import { + Modal, + ModalHeader, + ModalBody, + ModalFooter, + Button, + Form, + FormGroup, + Label, + Input, + Alert +} from 'reactstrap'; +import { useJamServerContext } from '../../context/JamServerContext'; +import useRecordingHelpers from '../../hooks/useRecordingHelpers'; + +const JKSessionRecordingModal = ({ isOpen, toggle }) => { + const { jamClient } = useJamServerContext(); + const recordingHelpers = useRecordingHelpers(jamClient); + + // Form state + const [recordingType, setRecordingType] = useState('audio-only'); + const [videoSource, setVideoSource] = useState('webcam-only'); + const [includeChat, setIncludeChat] = useState(false); + const [availableVideoSources, setAvailableVideoSources] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + // Recording state from helpers + const isRecording = recordingHelpers.currentlyRecording; + const isStarting = recordingHelpers.startingRecording; + const isStopping = recordingHelpers.stoppingRecording; + + // Video source options (matching web project) + const videoSourceOptions = [ + { key: 'webcam-only', text: 'Record my webcam only' }, + { key: 'webcam-only-2', text: 'Record my 2nd webcam only' }, + { key: 'desktop-only', text: 'Record my computer desktop' }, + { key: 'video-window', text: 'Record session video window' } + ]; + + // Load available video sources when modal opens + useEffect(() => { + if (isOpen && jamClient) { + loadAvailableVideoSources(); + } + }, [isOpen, jamClient]); + + const loadAvailableVideoSources = async () => { + try { + if (jamClient && jamClient.getOpenVideoSources) { + const openSources = await jamClient.getOpenVideoSources(); + console.log('Available video sources:', openSources); + + // Map backend sources to frontend options + const sources = []; + if (openSources?.webcam1) sources.push(1); // WebCamRecordActive + if (openSources?.webcam2) sources.push(3); // WebCam2RecordActive + if (openSources?.screen_capture) sources.push(4); // DesktopRecordActive + if (openSources?.session_video) sources.push(2); // ScreenRecordActive + + setAvailableVideoSources(sources); + } + } catch (error) { + console.error('Error loading video sources:', error); + } + }; + + const handleStartStopRecording = async () => { + setIsLoading(true); + setError(null); + + try { + if (isRecording) { + // Stop recording + await recordingHelpers.stopRecording(); + } else { + // Start recording + const recordVideo = recordingType === 'audio-video'; + let recordVideoType = 0; // NoVideoRecordActive + + if (recordVideo) { + // Map video source selection to backend values + switch (videoSource) { + case 'webcam-only': + recordVideoType = 1; // WebCamRecordActive + break; + case 'webcam-only-2': + recordVideoType = 3; // WebCam2RecordActive + break; + case 'desktop-only': + recordVideoType = 4; // DesktopRecordActive + break; + case 'video-window': + recordVideoType = 2; // ScreenRecordActive + break; + } + + // Validate video source availability + if (!availableVideoSources.includes(recordVideoType)) { + setError(`Selected video source "${videoSourceOptions.find(opt => opt.key === videoSource)?.text}" is not available.`); + setIsLoading(false); + return; + } + } + + const recordSettings = { + recordingType: recordVideo ? 'JK.RECORD_TYPE_BOTH' : 'JK.RECORD_TYPE_AUDIO', + recordVideo: recordVideo, + recordChat: includeChat, + videoType: recordVideoType + }; + + await recordingHelpers.startRecording(recordSettings); + } + } catch (error) { + console.error('Recording operation failed:', error); + setError('Failed to start/stop recording. Please try again.'); + } finally { + setIsLoading(false); + } + }; + + const handleCancel = () => { + setError(null); + toggle(); + }; + + const getRecordingButtonText = () => { + if (isStarting) return 'Starting Recording...'; + if (isStopping) return 'Stopping Recording...'; + if (isRecording) return 'Stop Recording'; + return 'Start Recording'; + }; + + const getRecordingButtonColor = () => { + if (isRecording) return 'danger'; + return 'success'; + }; + + // Filter available video options + const availableVideoOptions = videoSourceOptions.filter(option => { + switch (option.key) { + case 'webcam-only': + return availableVideoSources.includes(1); + case 'webcam-only-2': + return availableVideoSources.includes(3); + case 'desktop-only': + return availableVideoSources.includes(4); + case 'video-window': + return availableVideoSources.includes(2); + default: + return false; + } + }); + + return ( + + + Recording Controls + + +
+ {/* Important Note */} +
+
Important Note
+
+ While playing in your session, you are listening to your own personal mix. This recording will use the master mix, + which may sound very different. To hear and adjust your master mix settings, click the MIXER button in the session toolbar. +
+
+ + {/* Recording Type Selection */} +
+ + Recording Type + + + + + + + + + {/* Video Source Selection */} + {recordingType === 'audio-video' && ( + + + setVideoSource(e.target.value)} + disabled={isRecording || isStarting || isStopping} + > + {availableVideoOptions.map(option => ( + + ))} + + + )} + + {/* Include Chat Option */} + + + +
+ + {/* Error Message */} + {error && ( + + {error} + + )} +
+
+ + + + +
+ ); +}; + +export default JKSessionRecordingModal; diff --git a/jam-ui/src/components/client/JKSessionScreen.js b/jam-ui/src/components/client/JKSessionScreen.js index 969f050f3..e01b53cfd 100644 --- a/jam-ui/src/components/client/JKSessionScreen.js +++ b/jam-ui/src/components/client/JKSessionScreen.js @@ -27,6 +27,7 @@ import SessionTrackVU from './SessionTrackVU.js'; import JKSessionSettingsModal from './JKSessionSettingsModal.js'; import JKSessionInviteModal from './JKSessionInviteModal.js'; import JKSessionVolumeModal from './JKSessionVolumeModal.js'; +import JKSessionRecordingModal from './JKSessionRecordingModal.js'; import { SESSION_PRIVACY_MAP } from '../../helpers/globals.js'; import { toast } from 'react-toastify'; @@ -87,6 +88,9 @@ const JKSessionScreen = () => { const [showVolumeModal, setShowVolumeModal] = useState(false); const [volumeLevel, setVolumeLevel] = useState(100); + //state for recording modal + const [showRecordingModal, setShowRecordingModal] = useState(false); + useEffect(() => { if (!isConnected || !jamClient) return; console.debug("JKSessionScreen: -DEBUG- isConnected changed to true"); @@ -465,7 +469,7 @@ const JKSessionScreen = () => { - + @@ -704,6 +708,11 @@ const JKSessionScreen = () => { isOpen={showVolumeModal} toggle={() => setShowVolumeModal(!showVolumeModal)} /> + + setShowRecordingModal(!showRecordingModal)} + /> ) }