dashcam 1.0.1-beta.30 → 1.0.1-beta.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/dashcam.js CHANGED
@@ -136,55 +136,8 @@ async function recordingAction(options, command) {
136
136
  log('Use "dashcam status" to check progress');
137
137
  log('Use "dashcam stop" to stop recording and upload');
138
138
 
139
- // Keep the process alive so recording continues
140
- // Set up graceful shutdown handlers
141
- const handleShutdown = async (signal) => {
142
- log(`\nReceived ${signal}, stopping recording...`);
143
- try {
144
- const result = await processManager.stopActiveRecording();
145
-
146
- if (result) {
147
- log('Recording stopped successfully');
148
- log('Uploading recording...');
149
-
150
- try {
151
- const uploadResult = await upload(result.outputPath, {
152
- title: options.title || 'Dashcam Recording',
153
- description: description,
154
- project: options.project || options.k,
155
- duration: result.duration,
156
- clientStartDate: result.clientStartDate,
157
- apps: result.apps,
158
- icons: result.icons,
159
- gifPath: result.gifPath,
160
- snapshotPath: result.snapshotPath
161
- });
162
-
163
- // Write upload result for stop command to read
164
- processManager.writeUploadResult({
165
- shareLink: uploadResult.shareLink,
166
- replayId: uploadResult.replay?.id
167
- });
168
-
169
- log('📹 Watch your recording:', uploadResult.shareLink);
170
- } catch (uploadError) {
171
- logError('Upload failed:', uploadError.message);
172
- log('Recording saved locally:', result.outputPath);
173
- }
174
- }
175
-
176
- process.exit(0);
177
- } catch (error) {
178
- logError('Failed to stop recording:', error.message);
179
- process.exit(1);
180
- }
181
- };
182
-
183
- process.on('SIGINT', handleShutdown);
184
- process.on('SIGTERM', handleShutdown);
185
-
186
- // Keep process alive indefinitely until stopped
187
- await new Promise(() => {}); // Wait forever
139
+ // Process can exit now - background process is detached
140
+ process.exit(0);
188
141
 
189
142
  } catch (error) {
190
143
  logError('Failed to start recording:', error.message);
@@ -1,6 +1,8 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import { spawn } from 'child_process';
5
+ import { fileURLToPath } from 'url';
4
6
  import { logger } from './logger.js';
5
7
 
6
8
  // Use a fixed directory in the user's home directory for cross-process communication
@@ -151,91 +153,69 @@ class ProcessManager {
151
153
  return false;
152
154
  }
153
155
 
154
- // Check if this is the same process (direct recording)
155
- if (pid === process.pid) {
156
- logger.info('Stopping recording in current process');
157
-
158
- // Import recorder module
159
- const { stopRecording: stopRecorderRecording } = await import('./recorder.js');
160
-
161
- // Stop recording directly
162
- const result = await stopRecorderRecording();
163
-
164
- // Update status to indicate recording stopped
165
- this.writeStatus({
166
- isRecording: false,
167
- completedTime: Date.now(),
168
- pid: process.pid
169
- });
170
-
171
- this.cleanup({ preserveResult: true });
172
- return result;
156
+ logger.info('Stopping detached background process', { pid });
157
+
158
+ // Send signal to background process
159
+ const isWindows = process.platform === 'win32';
160
+
161
+ if (isWindows) {
162
+ logger.info('Windows detected, using taskkill to stop process');
163
+ try {
164
+ // Use taskkill to forcefully stop the process tree on Windows
165
+ const { execSync } = await import('child_process');
166
+ execSync(`taskkill /PID ${pid} /F /T`, { stdio: 'ignore' });
167
+ } catch (error) {
168
+ logger.warn('Failed to kill process with taskkill', { error: error.message });
169
+ }
173
170
  } else {
174
- // Different process - need to stop it
175
- logger.info('Stopping active recording process', { pid });
176
-
177
- // On Windows, signals don't work the same way, so we need to forcefully terminate
178
- const isWindows = process.platform === 'win32';
179
-
180
- if (isWindows) {
181
- logger.info('Windows detected, using taskkill to stop process');
182
- try {
183
- // Use taskkill to forcefully stop the process tree on Windows
184
- const { execSync } = await import('child_process');
185
- execSync(`taskkill /PID ${pid} /F /T`, { stdio: 'ignore' });
186
- } catch (error) {
187
- logger.warn('Failed to kill process with taskkill', { error: error.message });
188
- }
189
- } else {
171
+ try {
172
+ process.kill(pid, 'SIGINT');
173
+ } catch (error) {
174
+ logger.warn('Failed to send SIGINT', { error: error.message });
175
+ }
176
+ }
177
+
178
+ // Wait for the process to actually finish
179
+ const maxWaitTime = 30000; // 30 seconds
180
+ const startWait = Date.now();
181
+
182
+ while (this.isProcessRunning(pid) && (Date.now() - startWait) < maxWaitTime) {
183
+ await new Promise(resolve => setTimeout(resolve, 500));
184
+ }
185
+
186
+ if (this.isProcessRunning(pid)) {
187
+ logger.warn('Process did not stop within timeout');
188
+ if (!isWindows) {
190
189
  try {
191
- process.kill(pid, 'SIGINT');
190
+ process.kill(pid, 'SIGKILL');
192
191
  } catch (error) {
193
- logger.warn('Failed to send SIGINT', { error: error.message });
192
+ logger.warn('Failed to send SIGKILL', { error: error.message });
194
193
  }
194
+ await new Promise(resolve => setTimeout(resolve, 1000));
195
195
  }
196
+ }
197
+
198
+ if (status) {
199
+ logger.info('Recording stopped, returning status', {
200
+ outputPath: status.outputPath,
201
+ duration: Date.now() - status.startTime
202
+ });
196
203
 
197
- // Wait for the process to actually finish
198
- const maxWaitTime = 30000; // 30 seconds
199
- const startWait = Date.now();
200
-
201
- while (this.isProcessRunning(pid) && (Date.now() - startWait) < maxWaitTime) {
202
- await new Promise(resolve => setTimeout(resolve, 500));
203
- }
204
-
205
- if (this.isProcessRunning(pid)) {
206
- logger.warn('Process did not stop within timeout');
207
- if (!isWindows) {
208
- try {
209
- process.kill(pid, 'SIGKILL');
210
- } catch (error) {
211
- logger.warn('Failed to send SIGKILL', { error: error.message });
212
- }
213
- await new Promise(resolve => setTimeout(resolve, 1000));
214
- }
215
- }
204
+ const basePath = status.outputPath.substring(0, status.outputPath.lastIndexOf('.'));
205
+ const result = {
206
+ outputPath: status.outputPath,
207
+ gifPath: `${basePath}.gif`,
208
+ snapshotPath: `${basePath}.png`,
209
+ duration: Date.now() - status.startTime,
210
+ clientStartDate: status.startTime,
211
+ apps: [],
212
+ logs: []
213
+ };
216
214
 
217
- if (status) {
218
- logger.info('Recording stopped, returning status', {
219
- outputPath: status.outputPath,
220
- duration: Date.now() - status.startTime
221
- });
222
-
223
- const basePath = status.outputPath.substring(0, status.outputPath.lastIndexOf('.'));
224
- const result = {
225
- outputPath: status.outputPath,
226
- gifPath: `${basePath}.gif`,
227
- snapshotPath: `${basePath}.png`,
228
- duration: Date.now() - status.startTime,
229
- clientStartDate: status.startTime,
230
- apps: [],
231
- logs: []
232
- };
233
-
234
- this.cleanup({ preserveResult: true });
235
- return result;
236
- } else {
237
- throw new Error('No status information available for active recording');
238
- }
215
+ this.cleanup({ preserveResult: true });
216
+ return result;
217
+ } else {
218
+ throw new Error('No status information available for active recording');
239
219
  }
240
220
  } catch (error) {
241
221
  logger.error('Failed to stop recording', { error });
@@ -251,41 +231,46 @@ class ProcessManager {
251
231
  throw new Error('Recording already in progress');
252
232
  }
253
233
 
254
- // Import recorder module
255
- const { startRecording: startRecorderRecording } = await import('./recorder.js');
234
+ logger.info('Starting recording in detached background process');
256
235
 
257
- logger.info('Starting recording directly');
236
+ // Get the path to the background script
237
+ const __filename = fileURLToPath(import.meta.url);
238
+ const __dirname = path.dirname(__filename);
239
+ const backgroundScript = path.join(__dirname, '..', 'bin', 'dashcam-background.js');
258
240
 
259
- // Start recording using the recorder module
260
- const recordingOptions = {
261
- fps: parseInt(options.fps) || 10,
262
- includeAudio: options.audio || false,
263
- customOutputPath: options.output || null
264
- };
265
-
266
- const result = await startRecorderRecording(recordingOptions);
267
-
268
- // Write status to track the recording
269
- this.writeStatus({
270
- isRecording: true,
271
- startTime: result.startTime,
272
- options,
273
- pid: process.pid,
274
- outputPath: result.outputPath
241
+ // Spawn a detached background process
242
+ const child = spawn(process.execPath, [backgroundScript, JSON.stringify(options)], {
243
+ detached: true,
244
+ stdio: 'ignore'
275
245
  });
246
+
247
+ // Unref so the parent process can exit
248
+ child.unref();
249
+
250
+ const pid = child.pid;
251
+
252
+ // Write PID file immediately so other commands can find the recording process
253
+ this.writePid(pid);
276
254
 
277
- // Write PID file so other commands can find the recording process
278
- this.writePid(process.pid);
279
-
280
- logger.info('Recording started successfully', {
281
- pid: process.pid,
282
- outputPath: result.outputPath
255
+ logger.info('Background process spawned successfully', {
256
+ pid,
257
+ backgroundScript
283
258
  });
259
+
260
+ // Wait a moment for the background process to write its status
261
+ await new Promise(resolve => setTimeout(resolve, 1000));
262
+
263
+ // Read the status to get output path and start time
264
+ const status = this.readStatus();
265
+
266
+ if (!status) {
267
+ throw new Error('Background process failed to write status');
268
+ }
284
269
 
285
270
  return {
286
- pid: process.pid,
287
- outputPath: result.outputPath,
288
- startTime: result.startTime
271
+ pid,
272
+ outputPath: status.outputPath,
273
+ startTime: status.startTime
289
274
  };
290
275
  }
291
276
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dashcam",
3
- "version": "1.0.1-beta.30",
3
+ "version": "1.0.1-beta.31",
4
4
  "description": "Minimal CLI version of Dashcam desktop app",
5
5
  "main": "bin/index.js",
6
6
  "bin": {