dashcam 1.0.1-beta.34 → 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/lib/processManager.js +56 -86
- package/package.json +1 -1
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;
|
|
@@ -83,45 +90,22 @@ class ProcessManager {
|
|
|
83
90
|
}
|
|
84
91
|
}
|
|
85
92
|
|
|
86
|
-
writePid(pid = process.pid) {
|
|
87
|
-
try {
|
|
88
|
-
fs.writeFileSync(PID_FILE, pid.toString());
|
|
89
|
-
} catch (error) {
|
|
90
|
-
logger.error('Failed to write PID file', { error });
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
readPid() {
|
|
95
|
-
try {
|
|
96
|
-
if (!fs.existsSync(PID_FILE)) return null;
|
|
97
|
-
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim());
|
|
98
|
-
return isNaN(pid) ? null : pid;
|
|
99
|
-
} catch (error) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
isProcessRunning(pid) {
|
|
105
|
-
if (!pid) return false;
|
|
106
|
-
try {
|
|
107
|
-
process.kill(pid, 0); // Signal 0 just checks if process exists
|
|
108
|
-
return true;
|
|
109
|
-
} catch (error) {
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
93
|
isRecordingActive() {
|
|
115
|
-
const pid = this.readPid();
|
|
116
94
|
const status = this.readStatus();
|
|
117
95
|
|
|
118
|
-
if
|
|
119
|
-
|
|
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) {
|
|
120
104
|
this.cleanup({ preserveResult: true });
|
|
121
105
|
return false;
|
|
122
106
|
}
|
|
123
107
|
|
|
124
|
-
return
|
|
108
|
+
return false;
|
|
125
109
|
}
|
|
126
110
|
|
|
127
111
|
getActiveStatus() {
|
|
@@ -133,10 +117,6 @@ class ProcessManager {
|
|
|
133
117
|
const { preserveResult = false } = options;
|
|
134
118
|
try {
|
|
135
119
|
logger.debug('Cleanup called', { preserveResult, resultFileExists: fs.existsSync(RESULT_FILE) });
|
|
136
|
-
if (fs.existsSync(PID_FILE)) {
|
|
137
|
-
fs.unlinkSync(PID_FILE);
|
|
138
|
-
logger.debug('Deleted PID file');
|
|
139
|
-
}
|
|
140
120
|
if (fs.existsSync(STATUS_FILE)) {
|
|
141
121
|
fs.unlinkSync(STATUS_FILE);
|
|
142
122
|
logger.debug('Deleted STATUS file');
|
|
@@ -161,64 +141,50 @@ class ProcessManager {
|
|
|
161
141
|
this.isStopping = true;
|
|
162
142
|
|
|
163
143
|
try {
|
|
164
|
-
const pid = this.readPid();
|
|
165
144
|
const status = this.readStatus();
|
|
166
145
|
|
|
167
|
-
if (!
|
|
168
|
-
logger.warn('No active recording
|
|
146
|
+
if (!status || !status.isRecording) {
|
|
147
|
+
logger.warn('No active recording found in status file');
|
|
169
148
|
return false;
|
|
170
149
|
}
|
|
171
150
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// Send signal to background process
|
|
175
|
-
const isWindows = process.platform === 'win32';
|
|
151
|
+
const pid = status.pid;
|
|
152
|
+
logger.info('Stopping background recording process', { pid, hasStoredPid: !!pid });
|
|
176
153
|
|
|
177
|
-
if
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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');
|
|
183
160
|
try {
|
|
184
|
-
execSync
|
|
185
|
-
|
|
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
|
+
}
|
|
186
169
|
} catch (error) {
|
|
187
|
-
|
|
188
|
-
logger.warn('Graceful shutdown failed, forcing termination');
|
|
189
|
-
execSync(`taskkill /PID ${pid} /F /T`, { stdio: 'ignore' });
|
|
170
|
+
logger.warn('Failed to kill process with taskkill', { error: error.message });
|
|
190
171
|
}
|
|
191
|
-
}
|
|
192
|
-
logger.warn('Failed to kill process with taskkill', { error: error.message });
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
try {
|
|
196
|
-
process.kill(pid, 'SIGINT');
|
|
197
|
-
} catch (error) {
|
|
198
|
-
logger.warn('Failed to send SIGINT', { error: error.message });
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Wait for the process to actually finish
|
|
203
|
-
const maxWaitTime = 30000; // 30 seconds
|
|
204
|
-
const startWait = Date.now();
|
|
205
|
-
|
|
206
|
-
while (this.isProcessRunning(pid) && (Date.now() - startWait) < maxWaitTime) {
|
|
207
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (this.isProcessRunning(pid)) {
|
|
211
|
-
logger.warn('Process did not stop within timeout');
|
|
212
|
-
if (!isWindows) {
|
|
172
|
+
} else {
|
|
213
173
|
try {
|
|
214
|
-
process.kill(pid, '
|
|
174
|
+
process.kill(pid, 'SIGINT');
|
|
215
175
|
} catch (error) {
|
|
216
|
-
logger.warn('Failed to send
|
|
176
|
+
logger.warn('Failed to send SIGINT', { error: error.message });
|
|
217
177
|
}
|
|
218
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
219
178
|
}
|
|
220
179
|
}
|
|
221
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
|
+
|
|
222
188
|
if (status) {
|
|
223
189
|
logger.info('Recording stopped, returning status', {
|
|
224
190
|
outputPath: status.outputPath,
|
|
@@ -272,9 +238,6 @@ class ProcessManager {
|
|
|
272
238
|
child.unref();
|
|
273
239
|
|
|
274
240
|
const pid = child.pid;
|
|
275
|
-
|
|
276
|
-
// Write PID file immediately so other commands can find the recording process
|
|
277
|
-
this.writePid(pid);
|
|
278
241
|
|
|
279
242
|
logger.info('Background process spawned successfully', {
|
|
280
243
|
pid,
|
|
@@ -282,17 +245,24 @@ class ProcessManager {
|
|
|
282
245
|
});
|
|
283
246
|
|
|
284
247
|
// Wait a moment for the background process to write its status
|
|
285
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
248
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
286
249
|
|
|
287
250
|
// Read the status to get output path and start time
|
|
288
251
|
const status = this.readStatus();
|
|
289
252
|
|
|
290
253
|
if (!status) {
|
|
254
|
+
logger.error('Background process failed to write status');
|
|
291
255
|
throw new Error('Background process failed to write status');
|
|
292
256
|
}
|
|
293
257
|
|
|
258
|
+
logger.info('Recording started successfully', {
|
|
259
|
+
pid: status.pid,
|
|
260
|
+
outputPath: status.outputPath,
|
|
261
|
+
startTime: status.startTime
|
|
262
|
+
});
|
|
263
|
+
|
|
294
264
|
return {
|
|
295
|
-
pid,
|
|
265
|
+
pid: status.pid,
|
|
296
266
|
outputPath: status.outputPath,
|
|
297
267
|
startTime: status.startTime
|
|
298
268
|
};
|