Skip to content

Commit 1fd61d1

Browse files
committed
extract services
1 parent 8980782 commit 1fd61d1

3 files changed

Lines changed: 187 additions & 174 deletions

File tree

src/main/ipc/recording-bar.ts

Lines changed: 19 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
* Recording bar IPC handlers
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';
86
import type { Platform } from '../../platform';
97
import type { CursorConfig, ZoomConfig } from '../../types';
10-
import { MetadataExporter } from '../../processing/metadata-exporter';
118
import { createLogger } from '../../utils/logger';
129
import { hasRequiredRecordingPermissions } from '../services/permissions-service';
10+
import {
11+
createTempRecordingPaths,
12+
finalizeRecordingOutput,
13+
stopCaptureAndCollectData,
14+
} from '../services/recording-service';
1315
import {
1416
getRecordingState,
1517
getCurrentRecordingConfig,
@@ -80,14 +82,12 @@ export function registerRecordingBarHandlers(
8082
try {
8183
const platform = await initPlatform();
8284

83-
// Stop screen recording
84-
logger.info('Stopping screen recording...');
85-
const videoPath = await screenCapture?.stopRecording();
86-
logger.info('Screen recording stopped, video path:', videoPath);
87-
88-
// Show system cursor again
89-
logger.info('Showing system cursor...');
90-
await platform.cursor.ensureVisible();
85+
const { videoPath, mouseEvents } = await stopCaptureAndCollectData({
86+
platform,
87+
screenCapture,
88+
mouseTracker,
89+
recordingState,
90+
});
9191

9292
// Disable content protection on main window if it exists
9393
if (mainWindow && !mainWindow.isDestroyed()) {
@@ -97,66 +97,14 @@ export function registerRecordingBarHandlers(
9797
// Show recording bar in idle mode
9898
showRecordingBarIdle();
9999

100-
if (!videoPath) {
101-
throw new Error('Failed to stop recording');
102-
}
103-
104-
// Stop mouse tracking
105-
logger.info('Stopping mouse tracking...');
106-
mouseTracker?.stopTracking();
107-
108-
// Save mouse data
109-
if (mouseTracker && recordingState.tempMouseDataPath) {
110-
mouseTracker.saveToFile(recordingState.tempMouseDataPath);
111-
}
112-
113-
// Get mouse events
114-
const mouseEvents = mouseTracker?.getEvents() || [];
115-
const recordingDuration = Date.now() - (recordingState.startTime || 0);
116-
117-
// Determine final output path
118-
const finalOutputPath =
119-
recordingState.outputPath ||
120-
join(app.getPath('downloads'), `recording_${Date.now()}.mp4`);
121-
122-
// Ensure output directory exists
123-
const outputDir = dirname(finalOutputPath);
124-
if (!existsSync(outputDir)) {
125-
mkdirSync(outputDir, { recursive: true });
126-
}
127-
128-
// Copy video to final location
129-
const videoExtension = videoPath.split('.').pop() || 'mkv';
130-
const finalVideoPath = finalOutputPath.replace(/\.(mp4|mov|mkv|avi|webm)$/i, `.${videoExtension}`);
131-
132-
logger.info('Copying video to final location:', finalVideoPath);
133-
copyFileSync(videoPath, finalVideoPath);
134-
135-
// Export metadata
136-
let screenDimensions: { width: number; height: number } | undefined;
137-
try {
138-
const { getScreenDimensions } = await import('../../processing/video-utils');
139-
screenDimensions = await getScreenDimensions();
140-
} catch (error) {
141-
logger.warn('Could not get screen dimensions:', error);
142-
}
143-
144-
const exporter = new MetadataExporter();
145-
const mouseToVideoOffset = recordingState.mouseToVideoOffset || 0;
146-
const adjustedMouseEvents = mouseEvents.map(event => ({
147-
...event,
148-
timestamp: Math.max(0, event.timestamp - mouseToVideoOffset),
149-
}));
150-
151-
const metadataPath = await exporter.exportMetadata({
152-
videoPath: finalVideoPath,
153-
mouseEvents: adjustedMouseEvents,
100+
const { finalVideoPath, metadataPath } = await finalizeRecordingOutput({
101+
videoPath,
102+
mouseEvents,
103+
recordingState,
104+
currentRecordingConfig,
105+
userConfig,
154106
cursorConfig,
155107
zoomConfig,
156-
frameRate: parseInt(userConfig.frameRate, 10) || 60,
157-
videoDuration: recordingDuration,
158-
screenDimensions,
159-
recordingRegion: currentRecordingConfig?.region,
160108
});
161109

162110
logger.info('Recording completed successfully');
@@ -336,16 +284,7 @@ export function registerRecordingBarHandlers(
336284
const mouseTracker = createMouseTracker();
337285

338286
// Generate temp file paths
339-
const tempDir = join(app.getPath('temp'), 'screen-recorder');
340-
logger.debug('Temp directory:', tempDir);
341-
if (!existsSync(tempDir)) {
342-
logger.debug('Creating temp directory');
343-
mkdirSync(tempDir, { recursive: true });
344-
}
345-
346-
const timestamp = Date.now();
347-
const tempVideoPath = join(tempDir, `recording_${timestamp}.mkv`);
348-
const tempMouseDataPath = join(tempDir, `mouse_${timestamp}.json`);
287+
const { tempVideoPath, tempMouseDataPath } = createTempRecordingPaths();
349288

350289
setRecordingState({
351290
isRecording: true,

src/main/ipc/recording.ts

Lines changed: 19 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
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';
86
import type { Platform } from '../../platform';
97
import type { RecordingConfig, CursorConfig, ZoomConfig, MouseEffectsConfig } from '../../types';
10-
import { MetadataExporter } from '../../processing/metadata-exporter';
118
import { createLogger } from '../../utils/logger';
129
import { hasRequiredRecordingPermissions } from '../services/permissions-service';
10+
import {
11+
createTempRecordingPaths,
12+
finalizeRecordingOutput,
13+
stopCaptureAndCollectData,
14+
} from '../services/recording-service';
1315
import {
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(/\.(mp4|mov|mkv|avi|webm)$/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

Comments
 (0)