22 * Recording-related IPC handlers for start/stop recording
33 */
44
5- import { app , ipcMain , BrowserWindow } from 'electron' ;
6- import { join , dirname } from 'path' ;
7- import { existsSync , mkdirSync , copyFileSync } from 'fs' ;
5+ import { ipcMain , BrowserWindow } from 'electron' ;
86import type { Platform } from '../../platform' ;
97import type { RecordingConfig , CursorConfig , ZoomConfig , MouseEffectsConfig } from '../../types' ;
10- import { MetadataExporter } from '../../processing/metadata-exporter' ;
118import { createLogger } from '../../utils/logger' ;
129import { hasRequiredRecordingPermissions } from '../services/permissions-service' ;
10+ import {
11+ createTempRecordingPaths ,
12+ finalizeRecordingOutput ,
13+ stopCaptureAndCollectData ,
14+ } from '../services/recording-service' ;
1315import {
1416 getRecordingState ,
1517 getCurrentRecordingConfig ,
@@ -61,16 +63,7 @@ export function registerRecordingHandlers(
6163 const mouseTracker = createMouseTracker ( ) ;
6264
6365 // Generate temp file paths
64- const tempDir = join ( app . getPath ( 'temp' ) , 'screen-recorder' ) ;
65- logger . debug ( 'Temp directory:' , tempDir ) ;
66- if ( ! existsSync ( tempDir ) ) {
67- logger . debug ( 'Creating temp directory' ) ;
68- mkdirSync ( tempDir , { recursive : true } ) ;
69- }
70-
71- const timestamp = Date . now ( ) ;
72- const tempVideoPath = join ( tempDir , `recording_${ timestamp } .mkv` ) ;
73- const tempMouseDataPath = join ( tempDir , `mouse_${ timestamp } .json` ) ;
66+ const { tempVideoPath, tempMouseDataPath } = createTempRecordingPaths ( ) ;
7467 logger . debug ( 'Temp video path:' , tempVideoPath ) ;
7568 logger . debug ( 'Temp mouse data path:' , tempMouseDataPath ) ;
7669
@@ -190,14 +183,12 @@ export function registerRecordingHandlers(
190183 // Initialize platform
191184 const platform = await initPlatform ( ) ;
192185
193- // Stop screen recording
194- logger . info ( 'Stopping screen recording...' ) ;
195- const videoPath = await screenCapture ?. stopRecording ( ) ;
196- logger . info ( 'Screen recording stopped, video path:' , videoPath ) ;
197-
198- // Show system cursor again
199- logger . info ( 'Showing system cursor...' ) ;
200- await platform . cursor . ensureVisible ( ) ;
186+ const { videoPath, mouseEvents } = await stopCaptureAndCollectData ( {
187+ platform,
188+ screenCapture,
189+ mouseTracker,
190+ recordingState,
191+ } ) ;
201192
202193 // Hide recording bar and show main window
203194 hideRecordingBar ( ) ;
@@ -211,83 +202,17 @@ export function registerRecordingHandlers(
211202 mainWindow . setContentProtection ( false ) ;
212203 }
213204
214- if ( ! videoPath ) {
215- throw new Error ( 'Failed to stop recording' ) ;
216- }
217-
218- // Stop mouse tracking
219- logger . info ( 'Stopping mouse tracking...' ) ;
220- mouseTracker ?. stopTracking ( ) ;
221- logger . info ( 'Mouse tracking stopped' ) ;
222-
223- // Save mouse data
224- if ( mouseTracker && recordingState . tempMouseDataPath ) {
225- logger . debug ( 'Saving mouse data to:' , recordingState . tempMouseDataPath ) ;
226- mouseTracker . saveToFile ( recordingState . tempMouseDataPath ) ;
227- }
228-
229- // Get mouse events
230- const mouseEvents = mouseTracker ?. getEvents ( ) || [ ] ;
231- logger . debug ( 'Mouse events count:' , mouseEvents . length ) ;
232- const recordingDuration = Date . now ( ) - ( recordingState . startTime || 0 ) ;
233- logger . debug ( 'Recording duration:' , recordingDuration , 'ms' ) ;
234-
235- // Determine final output path
236- const finalOutputPath =
237- recordingState . outputPath ||
238- join ( app . getPath ( 'downloads' ) , `recording_${ Date . now ( ) } .mp4` ) ;
239- logger . debug ( 'Final output path:' , finalOutputPath ) ;
240-
241- // Ensure output directory exists
242- const outputDir = dirname ( finalOutputPath ) ;
243- if ( ! existsSync ( outputDir ) ) {
244- mkdirSync ( outputDir , { recursive : true } ) ;
245- }
246-
247- // Copy video to final location (preserve original extension or use .mkv)
248- const videoExtension = videoPath . split ( '.' ) . pop ( ) || 'mkv' ;
249- const finalVideoPath = finalOutputPath . replace ( / \. ( m p 4 | m o v | m k v | a v i | w e b m ) $ / i, `.${ videoExtension } ` ) ;
250-
251- logger . info ( 'Copying video to final location:' , finalVideoPath ) ;
252- copyFileSync ( videoPath , finalVideoPath ) ;
253- logger . info ( 'Video copied successfully' ) ;
254-
255- // Export metadata alongside the final video
256- logger . info ( 'Exporting metadata...' ) ;
257-
258- // Get screen dimensions for coordinate conversion (handles Retina displays)
259- let screenDimensions : { width : number ; height : number } | undefined ;
260- try {
261- const { getScreenDimensions } = await import ( '../../processing/video-utils' ) ;
262- screenDimensions = await getScreenDimensions ( ) ;
263- logger . debug ( 'Screen dimensions for metadata export:' , screenDimensions ) ;
264- } catch ( error ) {
265- logger . warn ( 'Could not get screen dimensions for metadata export:' , error ) ;
266- }
267-
268- const exporter = new MetadataExporter ( ) ;
269- // Apply mouse-to-video timing offset to sync cursor with video
270- const mouseToVideoOffset = recordingState . mouseToVideoOffset || 0 ;
271- logger . debug ( `Applying mouse-to-video offset: ${ mouseToVideoOffset } ms to ${ mouseEvents . length } events` ) ;
272- const adjustedMouseEvents = mouseEvents . map ( event => ( {
273- ...event ,
274- timestamp : Math . max ( 0 , event . timestamp - mouseToVideoOffset ) ,
275- } ) ) ;
276-
277- const metadataPath = await exporter . exportMetadata ( {
278- videoPath : finalVideoPath , // Use final video path so metadata is saved alongside it
279- mouseEvents : adjustedMouseEvents ,
205+ const { finalVideoPath, metadataPath } = await finalizeRecordingOutput ( {
206+ videoPath,
207+ mouseEvents,
208+ recordingState,
209+ currentRecordingConfig,
210+ userConfig,
280211 cursorConfig,
281212 zoomConfig,
282213 mouseEffectsConfig,
283- frameRate : parseInt ( userConfig . frameRate , 10 ) || 60 ,
284- videoDuration : recordingDuration ,
285- screenDimensions,
286- recordingRegion : currentRecordingConfig ?. region ,
287214 } ) ;
288215
289- logger . info ( 'Metadata exported successfully to:' , metadataPath ) ;
290-
291216 setRecordingState ( {
292217 isRecording : false ,
293218 tempVideoPath : videoPath ,
0 commit comments