dashcam 1.0.1-beta.35 → 1.0.1-beta.36
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-background.js +61 -65
- package/bin/dashcam.js +35 -40
- package/lib/processManager.js +22 -30
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ import os from 'os';
|
|
|
15
15
|
const PROCESS_DIR = path.join(os.homedir(), '.dashcam-cli');
|
|
16
16
|
const STATUS_FILE = path.join(PROCESS_DIR, 'status.json');
|
|
17
17
|
const RESULT_FILE = path.join(PROCESS_DIR, 'upload-result.json');
|
|
18
|
+
const STOP_SIGNAL_FILE = path.join(PROCESS_DIR, 'stop-signal.json');
|
|
18
19
|
|
|
19
20
|
// Parse options from command line argument
|
|
20
21
|
const optionsJson = process.argv[2];
|
|
@@ -96,79 +97,74 @@ async function runBackgroundRecording() {
|
|
|
96
97
|
startTime: recordingResult.startTime
|
|
97
98
|
});
|
|
98
99
|
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
logger.info(`Received ${signal} signal, stopping background recording...`, { pid: process.pid });
|
|
108
|
-
|
|
100
|
+
// Poll for stop signal file instead of relying on system signals
|
|
101
|
+
// This works reliably across all platforms including Windows
|
|
102
|
+
logger.info('Background recording is now running. Polling for stop signal...');
|
|
103
|
+
|
|
104
|
+
const pollInterval = 1000; // Check every second
|
|
105
|
+
|
|
106
|
+
while (true) {
|
|
109
107
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
duration: stopResult.duration
|
|
118
|
-
});
|
|
108
|
+
if (fs.existsSync(STOP_SIGNAL_FILE)) {
|
|
109
|
+
if (isShuttingDown) {
|
|
110
|
+
logger.info('Shutdown already in progress...');
|
|
111
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
isShuttingDown = true;
|
|
119
115
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
116
|
+
logger.info('Stop signal file detected, initiating graceful shutdown');
|
|
117
|
+
|
|
118
|
+
// Read and log the signal
|
|
119
|
+
try {
|
|
120
|
+
const signalData = JSON.parse(fs.readFileSync(STOP_SIGNAL_FILE, 'utf8'));
|
|
121
|
+
logger.info('Stop signal received', signalData);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
logger.warn('Could not parse stop signal file');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Stop the recording
|
|
127
|
+
logger.info('Calling stopRecording...');
|
|
128
|
+
const stopResult = await stopRecording();
|
|
133
129
|
|
|
134
|
-
|
|
130
|
+
if (stopResult) {
|
|
131
|
+
logger.info('Recording stopped successfully', {
|
|
132
|
+
outputPath: stopResult.outputPath,
|
|
133
|
+
duration: stopResult.duration
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Write the recording result so stop command can upload it
|
|
137
|
+
logger.info('Writing recording result for stop command to upload');
|
|
138
|
+
writeUploadResult({
|
|
139
|
+
outputPath: stopResult.outputPath,
|
|
140
|
+
duration: stopResult.duration,
|
|
141
|
+
clientStartDate: stopResult.clientStartDate,
|
|
142
|
+
apps: stopResult.apps,
|
|
143
|
+
logs: stopResult.logs,
|
|
144
|
+
gifPath: stopResult.gifPath,
|
|
145
|
+
snapshotPath: stopResult.snapshotPath,
|
|
146
|
+
recordingStopped: true
|
|
147
|
+
});
|
|
148
|
+
logger.info('Recording result written successfully');
|
|
149
|
+
}
|
|
135
150
|
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
151
|
+
// Update status to indicate recording stopped
|
|
152
|
+
writeStatus({
|
|
153
|
+
isRecording: false,
|
|
154
|
+
completedTime: Date.now(),
|
|
155
|
+
pid: process.pid
|
|
141
156
|
});
|
|
142
|
-
|
|
157
|
+
|
|
158
|
+
logger.info('Background process exiting successfully');
|
|
159
|
+
process.exit(0);
|
|
143
160
|
}
|
|
144
|
-
|
|
145
|
-
// Update status to indicate recording stopped
|
|
146
|
-
writeStatus({
|
|
147
|
-
isRecording: false,
|
|
148
|
-
completedTime: Date.now(),
|
|
149
|
-
pid: process.pid
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
logger.info('Background process exiting successfully');
|
|
153
|
-
process.exit(0);
|
|
154
161
|
} catch (error) {
|
|
155
|
-
logger.error('Error during
|
|
156
|
-
process.exit(1);
|
|
162
|
+
logger.error('Error during stop signal handling', { error: error.message, stack: error.stack });
|
|
157
163
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
handleShutdown('SIGINT');
|
|
163
|
-
});
|
|
164
|
-
process.on('SIGTERM', () => {
|
|
165
|
-
logger.info('SIGTERM handler triggered');
|
|
166
|
-
handleShutdown('SIGTERM');
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// Keep the process alive
|
|
170
|
-
logger.info('Background recording is now running. Waiting for stop signal...');
|
|
171
|
-
await new Promise(() => {}); // Wait indefinitely for signals
|
|
164
|
+
|
|
165
|
+
// Wait before next poll
|
|
166
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
167
|
+
}
|
|
172
168
|
|
|
173
169
|
} catch (error) {
|
|
174
170
|
logger.error('Background recording failed:', error);
|
package/bin/dashcam.js
CHANGED
|
@@ -419,61 +419,56 @@ program
|
|
|
419
419
|
|
|
420
420
|
console.log('Recording stopped successfully');
|
|
421
421
|
|
|
422
|
-
// Wait for
|
|
423
|
-
logger.debug('Waiting for background
|
|
424
|
-
console.log('⏳ Uploading recording...');
|
|
422
|
+
// Wait for background process to finish recording and write result
|
|
423
|
+
logger.debug('Waiting for background process to finish recording...');
|
|
425
424
|
|
|
426
|
-
// Wait up to
|
|
427
|
-
const
|
|
428
|
-
const
|
|
429
|
-
let
|
|
425
|
+
// Wait up to 30 seconds for recording result to appear
|
|
426
|
+
const maxWaitForRecording = 30000;
|
|
427
|
+
const startWaitForRecording = Date.now();
|
|
428
|
+
let recordingResult = null;
|
|
430
429
|
|
|
431
|
-
while (!
|
|
432
|
-
|
|
433
|
-
if (
|
|
434
|
-
|
|
430
|
+
while (!recordingResult && (Date.now() - startWaitForRecording) < maxWaitForRecording) {
|
|
431
|
+
const resultData = processManager.readUploadResult();
|
|
432
|
+
if (resultData && resultData.recordingStopped) {
|
|
433
|
+
recordingResult = resultData;
|
|
434
|
+
logger.debug('Recording result received from background process');
|
|
435
|
+
break;
|
|
435
436
|
}
|
|
437
|
+
await new Promise(resolve => setTimeout(resolve, 500)); // Check every 500ms
|
|
436
438
|
}
|
|
437
439
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
// Clean up the result file now that we've read it
|
|
443
|
-
processManager.cleanup();
|
|
444
|
-
process.exit(0);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Check if files still exist - if not, background process already uploaded
|
|
448
|
-
const filesExist = fs.existsSync(result.outputPath) &&
|
|
449
|
-
(!result.gifPath || fs.existsSync(result.gifPath)) &&
|
|
450
|
-
(!result.snapshotPath || fs.existsSync(result.snapshotPath));
|
|
451
|
-
|
|
452
|
-
if (!filesExist) {
|
|
453
|
-
console.log('✅ Recording uploaded by background process');
|
|
454
|
-
logger.info('Files were cleaned up by background process');
|
|
455
|
-
process.exit(0);
|
|
440
|
+
if (!recordingResult || !recordingResult.outputPath) {
|
|
441
|
+
logger.error('Failed to get recording result from background process');
|
|
442
|
+
console.log('⚠️ Failed to get recording from background process');
|
|
443
|
+
process.exit(1);
|
|
456
444
|
}
|
|
457
445
|
|
|
458
|
-
//
|
|
446
|
+
// Now upload the recording ourselves
|
|
447
|
+
logger.info('Uploading recording from stop command', { outputPath: recordingResult.outputPath });
|
|
459
448
|
console.log('Uploading recording...');
|
|
449
|
+
|
|
460
450
|
try {
|
|
461
|
-
const uploadResult = await upload(
|
|
462
|
-
title: activeStatus?.options?.title,
|
|
451
|
+
const uploadResult = await upload(recordingResult.outputPath, {
|
|
452
|
+
title: activeStatus?.options?.title || 'Dashcam Recording',
|
|
463
453
|
description: activeStatus?.options?.description,
|
|
464
|
-
project: activeStatus?.options?.project,
|
|
465
|
-
duration:
|
|
466
|
-
clientStartDate:
|
|
467
|
-
apps:
|
|
468
|
-
|
|
469
|
-
gifPath:
|
|
470
|
-
snapshotPath:
|
|
454
|
+
project: activeStatus?.options?.project,
|
|
455
|
+
duration: recordingResult.duration,
|
|
456
|
+
clientStartDate: recordingResult.clientStartDate,
|
|
457
|
+
apps: recordingResult.apps,
|
|
458
|
+
logs: recordingResult.logs,
|
|
459
|
+
gifPath: recordingResult.gifPath,
|
|
460
|
+
snapshotPath: recordingResult.snapshotPath
|
|
471
461
|
});
|
|
472
462
|
|
|
473
463
|
console.log('📹 Watch your recording:', uploadResult.shareLink);
|
|
464
|
+
|
|
465
|
+
// Clean up result file
|
|
466
|
+
processManager.cleanup();
|
|
467
|
+
process.exit(0);
|
|
474
468
|
} catch (uploadError) {
|
|
475
469
|
console.error('Upload failed:', uploadError.message);
|
|
476
|
-
console.log('Recording saved locally:',
|
|
470
|
+
console.log('Recording saved locally:', recordingResult.outputPath);
|
|
471
|
+
process.exit(1);
|
|
477
472
|
}
|
|
478
473
|
} catch (error) {
|
|
479
474
|
console.error('Failed to stop recording:', error.message);
|
package/lib/processManager.js
CHANGED
|
@@ -9,6 +9,7 @@ import { logger } from './logger.js';
|
|
|
9
9
|
const PROCESS_DIR = path.join(os.homedir(), '.dashcam-cli');
|
|
10
10
|
const STATUS_FILE = path.join(PROCESS_DIR, 'status.json');
|
|
11
11
|
const RESULT_FILE = path.join(PROCESS_DIR, 'upload-result.json');
|
|
12
|
+
const STOP_SIGNAL_FILE = path.join(PROCESS_DIR, 'stop-signal.json');
|
|
12
13
|
|
|
13
14
|
// Ensure process directory exists
|
|
14
15
|
if (!fs.existsSync(PROCESS_DIR)) {
|
|
@@ -121,6 +122,10 @@ class ProcessManager {
|
|
|
121
122
|
fs.unlinkSync(STATUS_FILE);
|
|
122
123
|
logger.debug('Deleted STATUS file');
|
|
123
124
|
}
|
|
125
|
+
if (fs.existsSync(STOP_SIGNAL_FILE)) {
|
|
126
|
+
fs.unlinkSync(STOP_SIGNAL_FILE);
|
|
127
|
+
logger.debug('Deleted STOP_SIGNAL file');
|
|
128
|
+
}
|
|
124
129
|
if (!preserveResult && fs.existsSync(RESULT_FILE)) {
|
|
125
130
|
fs.unlinkSync(RESULT_FILE);
|
|
126
131
|
logger.debug('Deleted RESULT file');
|
|
@@ -148,41 +153,27 @@ class ProcessManager {
|
|
|
148
153
|
return false;
|
|
149
154
|
}
|
|
150
155
|
|
|
151
|
-
|
|
152
|
-
logger.info('Stopping background recording process', { pid, hasStoredPid: !!pid });
|
|
156
|
+
logger.info('Signaling background process to stop recording');
|
|
153
157
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
} catch (error) {
|
|
166
|
-
logger.warn('Graceful shutdown failed, forcing termination');
|
|
167
|
-
execSync(`taskkill /PID ${pid} /F /T`, { stdio: 'ignore' });
|
|
168
|
-
}
|
|
169
|
-
} catch (error) {
|
|
170
|
-
logger.warn('Failed to kill process with taskkill', { error: error.message });
|
|
171
|
-
}
|
|
172
|
-
} else {
|
|
173
|
-
try {
|
|
174
|
-
process.kill(pid, 'SIGINT');
|
|
175
|
-
} catch (error) {
|
|
176
|
-
logger.warn('Failed to send SIGINT', { error: error.message });
|
|
177
|
-
}
|
|
178
|
-
}
|
|
158
|
+
// Write stop signal file instead of killing the process
|
|
159
|
+
// The background process polls for this file
|
|
160
|
+
try {
|
|
161
|
+
fs.writeFileSync(STOP_SIGNAL_FILE, JSON.stringify({
|
|
162
|
+
timestamp: Date.now(),
|
|
163
|
+
requestedBy: 'stop-command'
|
|
164
|
+
}));
|
|
165
|
+
logger.info('Stop signal file written successfully');
|
|
166
|
+
} catch (error) {
|
|
167
|
+
logger.error('Failed to write stop signal file', { error: error.message });
|
|
168
|
+
throw new Error('Failed to signal background process to stop');
|
|
179
169
|
}
|
|
180
170
|
|
|
181
|
-
// Mark recording as
|
|
171
|
+
// Mark recording as stopping in status file
|
|
182
172
|
this.writeStatus({
|
|
183
173
|
isRecording: false,
|
|
174
|
+
stopping: true,
|
|
184
175
|
stoppedAt: Date.now(),
|
|
185
|
-
pid: pid
|
|
176
|
+
pid: status.pid
|
|
186
177
|
});
|
|
187
178
|
|
|
188
179
|
if (status) {
|
|
@@ -202,7 +193,8 @@ class ProcessManager {
|
|
|
202
193
|
logs: []
|
|
203
194
|
};
|
|
204
195
|
|
|
205
|
-
|
|
196
|
+
// Don't cleanup here - the background process needs to read the stop signal file
|
|
197
|
+
// Cleanup will happen after we get the recording result
|
|
206
198
|
return result;
|
|
207
199
|
} else {
|
|
208
200
|
throw new Error('No status information available for active recording');
|