dashcam 1.0.1-beta.33 → 1.0.1-beta.35
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 +19 -5
- package/lib/processManager.js +76 -90
- package/package.json +1 -1
|
@@ -55,8 +55,13 @@ function writeUploadResult(result) {
|
|
|
55
55
|
timestamp: Date.now()
|
|
56
56
|
}, null, 2));
|
|
57
57
|
logger.info('Successfully wrote upload result to file');
|
|
58
|
+
// Verify the file was written
|
|
59
|
+
if (fs.existsSync(RESULT_FILE)) {
|
|
60
|
+
const content = fs.readFileSync(RESULT_FILE, 'utf8');
|
|
61
|
+
logger.info('Verified upload result file exists and contains', { content: content.substring(0, 100) });
|
|
62
|
+
}
|
|
58
63
|
} catch (error) {
|
|
59
|
-
logger.error('Failed to write upload result file', { error });
|
|
64
|
+
logger.error('Failed to write upload result file', { error: error.message, stack: error.stack });
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
67
|
|
|
@@ -99,10 +104,11 @@ async function runBackgroundRecording() {
|
|
|
99
104
|
}
|
|
100
105
|
isShuttingDown = true;
|
|
101
106
|
|
|
102
|
-
logger.info(`Received ${signal}, stopping background recording
|
|
107
|
+
logger.info(`Received ${signal} signal, stopping background recording...`, { pid: process.pid });
|
|
103
108
|
|
|
104
109
|
try {
|
|
105
110
|
// Stop the recording
|
|
111
|
+
logger.info('Calling stopRecording...');
|
|
106
112
|
const stopResult = await stopRecording();
|
|
107
113
|
|
|
108
114
|
if (stopResult) {
|
|
@@ -128,10 +134,12 @@ async function runBackgroundRecording() {
|
|
|
128
134
|
logger.info('Upload complete', { shareLink: uploadResult.shareLink });
|
|
129
135
|
|
|
130
136
|
// Write upload result for stop command to read
|
|
137
|
+
logger.info('About to write upload result...');
|
|
131
138
|
writeUploadResult({
|
|
132
139
|
shareLink: uploadResult.shareLink,
|
|
133
140
|
replayId: uploadResult.replay?.id
|
|
134
141
|
});
|
|
142
|
+
logger.info('Upload result written successfully');
|
|
135
143
|
}
|
|
136
144
|
|
|
137
145
|
// Update status to indicate recording stopped
|
|
@@ -144,13 +152,19 @@ async function runBackgroundRecording() {
|
|
|
144
152
|
logger.info('Background process exiting successfully');
|
|
145
153
|
process.exit(0);
|
|
146
154
|
} catch (error) {
|
|
147
|
-
logger.error('Error during shutdown:', error);
|
|
155
|
+
logger.error('Error during shutdown:', { error: error.message, stack: error.stack });
|
|
148
156
|
process.exit(1);
|
|
149
157
|
}
|
|
150
158
|
};
|
|
151
159
|
|
|
152
|
-
process.on('SIGINT', () =>
|
|
153
|
-
|
|
160
|
+
process.on('SIGINT', () => {
|
|
161
|
+
logger.info('SIGINT handler triggered');
|
|
162
|
+
handleShutdown('SIGINT');
|
|
163
|
+
});
|
|
164
|
+
process.on('SIGTERM', () => {
|
|
165
|
+
logger.info('SIGTERM handler triggered');
|
|
166
|
+
handleShutdown('SIGTERM');
|
|
167
|
+
});
|
|
154
168
|
|
|
155
169
|
// Keep the process alive
|
|
156
170
|
logger.info('Background recording is now running. Waiting for stop signal...');
|
package/lib/processManager.js
CHANGED
|
@@ -7,7 +7,6 @@ import { logger } from './logger.js';
|
|
|
7
7
|
|
|
8
8
|
// Use a fixed directory in the user's home directory for cross-process communication
|
|
9
9
|
const PROCESS_DIR = path.join(os.homedir(), '.dashcam-cli');
|
|
10
|
-
const PID_FILE = path.join(PROCESS_DIR, 'recording.pid');
|
|
11
10
|
const STATUS_FILE = path.join(PROCESS_DIR, 'status.json');
|
|
12
11
|
const RESULT_FILE = path.join(PROCESS_DIR, 'upload-result.json');
|
|
13
12
|
|
|
@@ -42,7 +41,15 @@ class ProcessManager {
|
|
|
42
41
|
try {
|
|
43
42
|
if (!fs.existsSync(STATUS_FILE)) return null;
|
|
44
43
|
const data = fs.readFileSync(STATUS_FILE, 'utf8');
|
|
45
|
-
|
|
44
|
+
const status = JSON.parse(data);
|
|
45
|
+
|
|
46
|
+
// Check if status is stale (older than 24 hours)
|
|
47
|
+
if (status.timestamp && (Date.now() - status.timestamp) > 24 * 60 * 60 * 1000) {
|
|
48
|
+
logger.warn('Status file is stale, ignoring');
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return status;
|
|
46
53
|
} catch (error) {
|
|
47
54
|
logger.error('Failed to read status file', { error });
|
|
48
55
|
return null;
|
|
@@ -59,65 +66,46 @@ class ProcessManager {
|
|
|
59
66
|
logger.info('Successfully wrote upload result to file');
|
|
60
67
|
// Verify it was written
|
|
61
68
|
if (fs.existsSync(RESULT_FILE)) {
|
|
62
|
-
|
|
69
|
+
const fileSize = fs.statSync(RESULT_FILE).size;
|
|
70
|
+
logger.info('Verified upload result file exists', { size: fileSize });
|
|
63
71
|
} else {
|
|
64
72
|
logger.error('Upload result file does not exist after write!');
|
|
65
73
|
}
|
|
66
74
|
} catch (error) {
|
|
67
|
-
logger.error('Failed to write upload result file', { error });
|
|
75
|
+
logger.error('Failed to write upload result file', { error: error.message, stack: error.stack });
|
|
68
76
|
}
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
readUploadResult() {
|
|
72
80
|
try {
|
|
81
|
+
logger.debug('Checking for upload result file', { path: RESULT_FILE, exists: fs.existsSync(RESULT_FILE) });
|
|
73
82
|
if (!fs.existsSync(RESULT_FILE)) return null;
|
|
74
83
|
const data = fs.readFileSync(RESULT_FILE, 'utf8');
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
writePid(pid = process.pid) {
|
|
83
|
-
try {
|
|
84
|
-
fs.writeFileSync(PID_FILE, pid.toString());
|
|
85
|
-
} catch (error) {
|
|
86
|
-
logger.error('Failed to write PID file', { error });
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
readPid() {
|
|
91
|
-
try {
|
|
92
|
-
if (!fs.existsSync(PID_FILE)) return null;
|
|
93
|
-
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim());
|
|
94
|
-
return isNaN(pid) ? null : pid;
|
|
84
|
+
const result = JSON.parse(data);
|
|
85
|
+
logger.debug('Successfully read upload result', { shareLink: result.shareLink });
|
|
86
|
+
return result;
|
|
95
87
|
} catch (error) {
|
|
88
|
+
logger.error('Failed to read upload result file', { error: error.message, stack: error.stack });
|
|
96
89
|
return null;
|
|
97
90
|
}
|
|
98
91
|
}
|
|
99
92
|
|
|
100
|
-
isProcessRunning(pid) {
|
|
101
|
-
if (!pid) return false;
|
|
102
|
-
try {
|
|
103
|
-
process.kill(pid, 0); // Signal 0 just checks if process exists
|
|
104
|
-
return true;
|
|
105
|
-
} catch (error) {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
93
|
isRecordingActive() {
|
|
111
|
-
const pid = this.readPid();
|
|
112
94
|
const status = this.readStatus();
|
|
113
95
|
|
|
114
|
-
if
|
|
115
|
-
|
|
96
|
+
// Recording is active if we have a status file with isRecording=true
|
|
97
|
+
// and it's not stale
|
|
98
|
+
if (status && status.isRecording) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Clean up stale files if recording is not active
|
|
103
|
+
if (!status || !status.isRecording) {
|
|
116
104
|
this.cleanup({ preserveResult: true });
|
|
117
105
|
return false;
|
|
118
106
|
}
|
|
119
107
|
|
|
120
|
-
return
|
|
108
|
+
return false;
|
|
121
109
|
}
|
|
122
110
|
|
|
123
111
|
getActiveStatus() {
|
|
@@ -128,11 +116,19 @@ class ProcessManager {
|
|
|
128
116
|
cleanup(options = {}) {
|
|
129
117
|
const { preserveResult = false } = options;
|
|
130
118
|
try {
|
|
131
|
-
|
|
132
|
-
if (fs.existsSync(STATUS_FILE))
|
|
133
|
-
|
|
119
|
+
logger.debug('Cleanup called', { preserveResult, resultFileExists: fs.existsSync(RESULT_FILE) });
|
|
120
|
+
if (fs.existsSync(STATUS_FILE)) {
|
|
121
|
+
fs.unlinkSync(STATUS_FILE);
|
|
122
|
+
logger.debug('Deleted STATUS file');
|
|
123
|
+
}
|
|
124
|
+
if (!preserveResult && fs.existsSync(RESULT_FILE)) {
|
|
125
|
+
fs.unlinkSync(RESULT_FILE);
|
|
126
|
+
logger.debug('Deleted RESULT file');
|
|
127
|
+
} else if (preserveResult && fs.existsSync(RESULT_FILE)) {
|
|
128
|
+
logger.debug('Preserved RESULT file');
|
|
129
|
+
}
|
|
134
130
|
} catch (error) {
|
|
135
|
-
logger.error('Failed to cleanup process files', { error });
|
|
131
|
+
logger.error('Failed to cleanup process files', { error: error.message });
|
|
136
132
|
}
|
|
137
133
|
}
|
|
138
134
|
|
|
@@ -145,64 +141,50 @@ class ProcessManager {
|
|
|
145
141
|
this.isStopping = true;
|
|
146
142
|
|
|
147
143
|
try {
|
|
148
|
-
const pid = this.readPid();
|
|
149
144
|
const status = this.readStatus();
|
|
150
145
|
|
|
151
|
-
if (!
|
|
152
|
-
logger.warn('No active recording
|
|
146
|
+
if (!status || !status.isRecording) {
|
|
147
|
+
logger.warn('No active recording found in status file');
|
|
153
148
|
return false;
|
|
154
149
|
}
|
|
155
150
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// Send signal to background process
|
|
159
|
-
const isWindows = process.platform === 'win32';
|
|
151
|
+
const pid = status.pid;
|
|
152
|
+
logger.info('Stopping background recording process', { pid, hasStoredPid: !!pid });
|
|
160
153
|
|
|
161
|
-
if
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
154
|
+
// Try to gracefully stop the process if we have a PID
|
|
155
|
+
if (pid) {
|
|
156
|
+
const isWindows = process.platform === 'win32';
|
|
157
|
+
|
|
158
|
+
if (isWindows) {
|
|
159
|
+
logger.info('Windows detected, attempting graceful shutdown first');
|
|
167
160
|
try {
|
|
168
|
-
execSync
|
|
169
|
-
|
|
161
|
+
const { execSync } = await import('child_process');
|
|
162
|
+
try {
|
|
163
|
+
execSync(`taskkill /PID ${pid} /T`, { stdio: 'ignore', timeout: 5000 });
|
|
164
|
+
logger.info('Sent graceful shutdown signal to process');
|
|
165
|
+
} catch (error) {
|
|
166
|
+
logger.warn('Graceful shutdown failed, forcing termination');
|
|
167
|
+
execSync(`taskkill /PID ${pid} /F /T`, { stdio: 'ignore' });
|
|
168
|
+
}
|
|
170
169
|
} catch (error) {
|
|
171
|
-
|
|
172
|
-
logger.warn('Graceful shutdown failed, forcing termination');
|
|
173
|
-
execSync(`taskkill /PID ${pid} /F /T`, { stdio: 'ignore' });
|
|
170
|
+
logger.warn('Failed to kill process with taskkill', { error: error.message });
|
|
174
171
|
}
|
|
175
|
-
}
|
|
176
|
-
logger.warn('Failed to kill process with taskkill', { error: error.message });
|
|
177
|
-
}
|
|
178
|
-
} else {
|
|
179
|
-
try {
|
|
180
|
-
process.kill(pid, 'SIGINT');
|
|
181
|
-
} catch (error) {
|
|
182
|
-
logger.warn('Failed to send SIGINT', { error: error.message });
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Wait for the process to actually finish
|
|
187
|
-
const maxWaitTime = 30000; // 30 seconds
|
|
188
|
-
const startWait = Date.now();
|
|
189
|
-
|
|
190
|
-
while (this.isProcessRunning(pid) && (Date.now() - startWait) < maxWaitTime) {
|
|
191
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (this.isProcessRunning(pid)) {
|
|
195
|
-
logger.warn('Process did not stop within timeout');
|
|
196
|
-
if (!isWindows) {
|
|
172
|
+
} else {
|
|
197
173
|
try {
|
|
198
|
-
process.kill(pid, '
|
|
174
|
+
process.kill(pid, 'SIGINT');
|
|
199
175
|
} catch (error) {
|
|
200
|
-
logger.warn('Failed to send
|
|
176
|
+
logger.warn('Failed to send SIGINT', { error: error.message });
|
|
201
177
|
}
|
|
202
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
203
178
|
}
|
|
204
179
|
}
|
|
205
180
|
|
|
181
|
+
// Mark recording as stopped in status file immediately
|
|
182
|
+
this.writeStatus({
|
|
183
|
+
isRecording: false,
|
|
184
|
+
stoppedAt: Date.now(),
|
|
185
|
+
pid: pid
|
|
186
|
+
});
|
|
187
|
+
|
|
206
188
|
if (status) {
|
|
207
189
|
logger.info('Recording stopped, returning status', {
|
|
208
190
|
outputPath: status.outputPath,
|
|
@@ -256,9 +238,6 @@ class ProcessManager {
|
|
|
256
238
|
child.unref();
|
|
257
239
|
|
|
258
240
|
const pid = child.pid;
|
|
259
|
-
|
|
260
|
-
// Write PID file immediately so other commands can find the recording process
|
|
261
|
-
this.writePid(pid);
|
|
262
241
|
|
|
263
242
|
logger.info('Background process spawned successfully', {
|
|
264
243
|
pid,
|
|
@@ -266,17 +245,24 @@ class ProcessManager {
|
|
|
266
245
|
});
|
|
267
246
|
|
|
268
247
|
// Wait a moment for the background process to write its status
|
|
269
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
248
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
270
249
|
|
|
271
250
|
// Read the status to get output path and start time
|
|
272
251
|
const status = this.readStatus();
|
|
273
252
|
|
|
274
253
|
if (!status) {
|
|
254
|
+
logger.error('Background process failed to write status');
|
|
275
255
|
throw new Error('Background process failed to write status');
|
|
276
256
|
}
|
|
277
257
|
|
|
258
|
+
logger.info('Recording started successfully', {
|
|
259
|
+
pid: status.pid,
|
|
260
|
+
outputPath: status.outputPath,
|
|
261
|
+
startTime: status.startTime
|
|
262
|
+
});
|
|
263
|
+
|
|
278
264
|
return {
|
|
279
|
-
pid,
|
|
265
|
+
pid: status.pid,
|
|
280
266
|
outputPath: status.outputPath,
|
|
281
267
|
startTime: status.startTime
|
|
282
268
|
};
|