dashcam 1.0.1-beta.25 → 1.0.1-beta.27
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 +51 -2
- package/lib/processManager.js +81 -94
- package/lib/recorder.js +7 -6
- package/package.json +1 -1
- package/test-system-info.js +105 -0
- package/test_workflow.sh +9 -5
- package/sea-bundle.mjs +0 -34595
package/bin/dashcam.js
CHANGED
|
@@ -136,7 +136,56 @@ 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
|
-
process
|
|
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
|
|
188
|
+
|
|
140
189
|
} catch (error) {
|
|
141
190
|
logError('Failed to start recording:', error.message);
|
|
142
191
|
process.exit(1);
|
|
@@ -626,7 +675,7 @@ program
|
|
|
626
675
|
|
|
627
676
|
if (options.recover) {
|
|
628
677
|
// Try to recover from interrupted recording
|
|
629
|
-
const tempFileInfoPath = path.join(
|
|
678
|
+
const tempFileInfoPath = path.join(APP.configDir, 'temp-file.json');
|
|
630
679
|
|
|
631
680
|
if (fs.existsSync(tempFileInfoPath)) {
|
|
632
681
|
console.log('Found interrupted recording, attempting recovery...');
|
package/lib/processManager.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
2
1
|
import fs from 'fs';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import os from 'os';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
4
|
import { logger } from './logger.js';
|
|
7
5
|
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
|
|
11
6
|
// Use a fixed directory in the user's home directory for cross-process communication
|
|
12
7
|
const PROCESS_DIR = path.join(os.homedir(), '.dashcam-cli');
|
|
13
8
|
const PID_FILE = path.join(PROCESS_DIR, 'recording.pid');
|
|
@@ -156,48 +151,66 @@ class ProcessManager {
|
|
|
156
151
|
return false;
|
|
157
152
|
}
|
|
158
153
|
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const maxWaitTime = 120000; // 2 minutes max to allow for upload
|
|
166
|
-
const startWait = Date.now();
|
|
167
|
-
|
|
168
|
-
while (this.isProcessRunning(pid) && (Date.now() - startWait) < maxWaitTime) {
|
|
169
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (this.isProcessRunning(pid)) {
|
|
173
|
-
logger.warn('Process did not stop within timeout, forcing termination');
|
|
174
|
-
process.kill(pid, 'SIGKILL');
|
|
175
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// The background process handles stopRecording() internally via SIGINT
|
|
179
|
-
// We just need to return the basic result from the status file
|
|
180
|
-
if (status) {
|
|
181
|
-
logger.info('Background recording stopped, returning status', {
|
|
182
|
-
outputPath: status.outputPath,
|
|
183
|
-
duration: Date.now() - status.startTime
|
|
184
|
-
});
|
|
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');
|
|
185
160
|
|
|
186
|
-
|
|
187
|
-
const result =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
};
|
|
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
|
+
});
|
|
196
170
|
|
|
197
171
|
this.cleanup({ preserveResult: true });
|
|
198
172
|
return result;
|
|
199
173
|
} else {
|
|
200
|
-
|
|
174
|
+
// Different process - send signal (for backward compatibility if needed)
|
|
175
|
+
logger.info('Stopping active recording process', { pid });
|
|
176
|
+
process.kill(pid, 'SIGINT');
|
|
177
|
+
|
|
178
|
+
// Wait for the process to actually finish
|
|
179
|
+
const maxWaitTime = 120000; // 2 minutes max
|
|
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, forcing termination');
|
|
188
|
+
process.kill(pid, 'SIGKILL');
|
|
189
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (status) {
|
|
193
|
+
logger.info('Recording stopped, returning status', {
|
|
194
|
+
outputPath: status.outputPath,
|
|
195
|
+
duration: Date.now() - status.startTime
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const basePath = status.outputPath.substring(0, status.outputPath.lastIndexOf('.'));
|
|
199
|
+
const result = {
|
|
200
|
+
outputPath: status.outputPath,
|
|
201
|
+
gifPath: `${basePath}.gif`,
|
|
202
|
+
snapshotPath: `${basePath}.png`,
|
|
203
|
+
duration: Date.now() - status.startTime,
|
|
204
|
+
clientStartDate: status.startTime,
|
|
205
|
+
apps: [],
|
|
206
|
+
logs: []
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
this.cleanup({ preserveResult: true });
|
|
210
|
+
return result;
|
|
211
|
+
} else {
|
|
212
|
+
throw new Error('No status information available for active recording');
|
|
213
|
+
}
|
|
201
214
|
}
|
|
202
215
|
} catch (error) {
|
|
203
216
|
logger.error('Failed to stop recording', { error });
|
|
@@ -213,67 +226,41 @@ class ProcessManager {
|
|
|
213
226
|
throw new Error('Recording already in progress');
|
|
214
227
|
}
|
|
215
228
|
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
// Get the path to the CLI binary
|
|
220
|
-
const binPath = path.join(__dirname, '..', 'bin', 'dashcam-background.js');
|
|
221
|
-
|
|
222
|
-
logger.debug('Background process path', { binPath, exists: fs.existsSync(binPath) });
|
|
223
|
-
|
|
224
|
-
// Create log files for background process
|
|
225
|
-
const logDir = PROCESS_DIR;
|
|
226
|
-
const stdoutLog = path.join(logDir, 'background-stdout.log');
|
|
227
|
-
const stderrLog = path.join(logDir, 'background-stderr.log');
|
|
228
|
-
|
|
229
|
-
const stdoutFd = fs.openSync(stdoutLog, 'a');
|
|
230
|
-
const stderrFd = fs.openSync(stderrLog, 'a');
|
|
231
|
-
|
|
232
|
-
// Spawn a detached process that will handle the recording
|
|
233
|
-
const backgroundProcess = spawn(process.execPath, [
|
|
234
|
-
binPath,
|
|
235
|
-
JSON.stringify(options)
|
|
236
|
-
], {
|
|
237
|
-
detached: true,
|
|
238
|
-
stdio: ['ignore', stdoutFd, stderrFd], // Log stdout and stderr
|
|
239
|
-
env: {
|
|
240
|
-
...process.env,
|
|
241
|
-
DASHCAM_BACKGROUND: 'true'
|
|
242
|
-
}
|
|
243
|
-
});
|
|
229
|
+
// Import recorder module
|
|
230
|
+
const { startRecording: startRecorderRecording } = await import('./recorder.js');
|
|
244
231
|
|
|
245
|
-
|
|
246
|
-
fs.closeSync(stdoutFd);
|
|
247
|
-
fs.closeSync(stderrFd);
|
|
248
|
-
|
|
249
|
-
// Get the background process PID before unreffing
|
|
250
|
-
const backgroundPid = backgroundProcess.pid;
|
|
232
|
+
logger.info('Starting recording directly');
|
|
251
233
|
|
|
252
|
-
//
|
|
253
|
-
|
|
234
|
+
// Start recording using the recorder module
|
|
235
|
+
const recordingOptions = {
|
|
236
|
+
fps: parseInt(options.fps) || 10,
|
|
237
|
+
includeAudio: options.audio || false,
|
|
238
|
+
customOutputPath: options.output || null
|
|
239
|
+
};
|
|
254
240
|
|
|
255
|
-
|
|
256
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
241
|
+
const result = await startRecorderRecording(recordingOptions);
|
|
257
242
|
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
243
|
+
// Write status to track the recording
|
|
244
|
+
this.writeStatus({
|
|
245
|
+
isRecording: true,
|
|
246
|
+
startTime: result.startTime,
|
|
247
|
+
options,
|
|
248
|
+
pid: process.pid,
|
|
249
|
+
outputPath: result.outputPath
|
|
250
|
+
});
|
|
264
251
|
|
|
265
|
-
// Write PID file so other commands can find the
|
|
266
|
-
this.writePid(
|
|
252
|
+
// Write PID file so other commands can find the recording process
|
|
253
|
+
this.writePid(process.pid);
|
|
267
254
|
|
|
268
|
-
logger.info('
|
|
269
|
-
pid:
|
|
270
|
-
outputPath:
|
|
255
|
+
logger.info('Recording started successfully', {
|
|
256
|
+
pid: process.pid,
|
|
257
|
+
outputPath: result.outputPath
|
|
271
258
|
});
|
|
272
259
|
|
|
273
260
|
return {
|
|
274
|
-
pid:
|
|
275
|
-
outputPath:
|
|
276
|
-
startTime:
|
|
261
|
+
pid: process.pid,
|
|
262
|
+
outputPath: result.outputPath,
|
|
263
|
+
startTime: result.startTime
|
|
277
264
|
};
|
|
278
265
|
}
|
|
279
266
|
|
package/lib/recorder.js
CHANGED
|
@@ -4,6 +4,7 @@ import { createGif, createSnapshot } from './ffmpeg.js';
|
|
|
4
4
|
import { applicationTracker } from './applicationTracker.js';
|
|
5
5
|
import { logsTrackerManager, trimLogs } from './logs/index.js';
|
|
6
6
|
import { getFfmpegPath } from './binaries.js';
|
|
7
|
+
import { APP } from './config.js';
|
|
7
8
|
import path from 'path';
|
|
8
9
|
import os from 'os';
|
|
9
10
|
import fs from 'fs';
|
|
@@ -145,8 +146,8 @@ let outputPath = null;
|
|
|
145
146
|
let recordingStartTime = null;
|
|
146
147
|
let currentTempFile = null;
|
|
147
148
|
|
|
148
|
-
// File paths - use
|
|
149
|
-
const DASHCAM_TEMP_DIR =
|
|
149
|
+
// File paths - use APP config directory for better Windows compatibility
|
|
150
|
+
const DASHCAM_TEMP_DIR = APP.configDir;
|
|
150
151
|
const TEMP_FILE_INFO_PATH = path.join(DASHCAM_TEMP_DIR, 'temp-file.json');
|
|
151
152
|
|
|
152
153
|
// Platform-specific configurations
|
|
@@ -238,12 +239,12 @@ async function getPlatformArgs({ fps, includeAudio }) {
|
|
|
238
239
|
}
|
|
239
240
|
|
|
240
241
|
/**
|
|
241
|
-
* Clear the
|
|
242
|
+
* Clear the recordings directory
|
|
242
243
|
*/
|
|
243
244
|
function clearRecordingsDirectory() {
|
|
244
245
|
const logExit = logFunctionCall('clearRecordingsDirectory');
|
|
245
246
|
|
|
246
|
-
const directory =
|
|
247
|
+
const directory = APP.recordingsDir;
|
|
247
248
|
|
|
248
249
|
try {
|
|
249
250
|
if (fs.existsSync(directory)) {
|
|
@@ -281,8 +282,8 @@ function generateOutputPath() {
|
|
|
281
282
|
const logExit = logFunctionCall('generateOutputPath');
|
|
282
283
|
|
|
283
284
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
284
|
-
// Use
|
|
285
|
-
const directory =
|
|
285
|
+
// Use APP recordings directory for consistent cross-platform location
|
|
286
|
+
const directory = APP.recordingsDir;
|
|
286
287
|
const filepath = path.join(directory, `recording-${timestamp}.webm`);
|
|
287
288
|
|
|
288
289
|
logger.verbose('Generating output path', {
|
package/package.json
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test script to verify system information collection
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getSystemInfo } from './lib/systemInfo.js';
|
|
7
|
+
import { logger } from './lib/logger.js';
|
|
8
|
+
|
|
9
|
+
async function testSystemInfo() {
|
|
10
|
+
console.log('Testing system information collection...\n');
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const systemInfo = await getSystemInfo();
|
|
14
|
+
|
|
15
|
+
console.log('✓ System information collected successfully\n');
|
|
16
|
+
console.log('System Information:');
|
|
17
|
+
console.log('==================\n');
|
|
18
|
+
|
|
19
|
+
console.log('CPU:');
|
|
20
|
+
console.log(` Brand: ${systemInfo.cpu.brand}`);
|
|
21
|
+
console.log(` Cores: ${systemInfo.cpu.cores}`);
|
|
22
|
+
console.log(` Speed: ${systemInfo.cpu.speed} GHz`);
|
|
23
|
+
console.log();
|
|
24
|
+
|
|
25
|
+
console.log('Memory:');
|
|
26
|
+
console.log(` Total: ${(systemInfo.mem.total / (1024 ** 3)).toFixed(2)} GB`);
|
|
27
|
+
console.log(` Free: ${(systemInfo.mem.free / (1024 ** 3)).toFixed(2)} GB`);
|
|
28
|
+
console.log(` Used: ${(systemInfo.mem.used / (1024 ** 3)).toFixed(2)} GB`);
|
|
29
|
+
console.log();
|
|
30
|
+
|
|
31
|
+
console.log('Operating System:');
|
|
32
|
+
console.log(` Platform: ${systemInfo.os.platform}`);
|
|
33
|
+
console.log(` Distribution: ${systemInfo.os.distro}`);
|
|
34
|
+
console.log(` Release: ${systemInfo.os.release}`);
|
|
35
|
+
console.log(` Architecture: ${systemInfo.os.arch}`);
|
|
36
|
+
console.log(` Hostname: ${systemInfo.os.hostname}`);
|
|
37
|
+
console.log();
|
|
38
|
+
|
|
39
|
+
console.log('Graphics:');
|
|
40
|
+
console.log(` Controllers: ${systemInfo.graphics.controllers?.length || 0}`);
|
|
41
|
+
if (systemInfo.graphics.controllers?.length > 0) {
|
|
42
|
+
systemInfo.graphics.controllers.forEach((controller, index) => {
|
|
43
|
+
console.log(` ${index + 1}. ${controller.vendor} ${controller.model}`);
|
|
44
|
+
if (controller.vram) {
|
|
45
|
+
console.log(` VRAM: ${controller.vram} MB`);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
console.log(` Displays: ${systemInfo.graphics.displays?.length || 0}`);
|
|
50
|
+
if (systemInfo.graphics.displays?.length > 0) {
|
|
51
|
+
systemInfo.graphics.displays.forEach((display, index) => {
|
|
52
|
+
console.log(` ${index + 1}. ${display.model || 'Unknown'}`);
|
|
53
|
+
console.log(` Resolution: ${display.currentResX}x${display.currentResY}`);
|
|
54
|
+
console.log(` Refresh Rate: ${display.currentRefreshRate} Hz`);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
console.log();
|
|
58
|
+
|
|
59
|
+
console.log('System:');
|
|
60
|
+
console.log(` Manufacturer: ${systemInfo.system.manufacturer}`);
|
|
61
|
+
console.log(` Model: ${systemInfo.system.model}`);
|
|
62
|
+
console.log(` Virtual: ${systemInfo.system.virtual ? 'Yes' : 'No'}`);
|
|
63
|
+
console.log();
|
|
64
|
+
|
|
65
|
+
// Verify all required fields are present
|
|
66
|
+
console.log('Validation:');
|
|
67
|
+
console.log('===========\n');
|
|
68
|
+
|
|
69
|
+
const validations = [
|
|
70
|
+
{ name: 'CPU info', valid: !!systemInfo.cpu && !!systemInfo.cpu.brand },
|
|
71
|
+
{ name: 'Memory info', valid: !!systemInfo.mem && systemInfo.mem.total > 0 },
|
|
72
|
+
{ name: 'OS info', valid: !!systemInfo.os && !!systemInfo.os.platform },
|
|
73
|
+
{ name: 'Graphics info', valid: !!systemInfo.graphics },
|
|
74
|
+
{ name: 'System info', valid: !!systemInfo.system }
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
let allValid = true;
|
|
78
|
+
validations.forEach(v => {
|
|
79
|
+
const status = v.valid ? '✓' : '✗';
|
|
80
|
+
console.log(`${status} ${v.name}: ${v.valid ? 'OK' : 'MISSING'}`);
|
|
81
|
+
if (!v.valid) allValid = false;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
console.log();
|
|
85
|
+
|
|
86
|
+
if (allValid) {
|
|
87
|
+
console.log('✓ All system information fields are properly populated');
|
|
88
|
+
console.log('✓ System information is ready to be uploaded to the API');
|
|
89
|
+
} else {
|
|
90
|
+
console.log('✗ Some system information is missing');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Show the JSON structure that would be sent to the API
|
|
94
|
+
console.log('\nJSON Structure for API:');
|
|
95
|
+
console.log('======================\n');
|
|
96
|
+
console.log(JSON.stringify(systemInfo, null, 2));
|
|
97
|
+
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('✗ Failed to collect system information:', error.message);
|
|
100
|
+
console.error(error.stack);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
testSystemInfo();
|
package/test_workflow.sh
CHANGED
|
@@ -33,13 +33,16 @@ echo "✅ File tracking configured"
|
|
|
33
33
|
# 4. Start dashcam recording in background
|
|
34
34
|
echo ""
|
|
35
35
|
echo "4. Starting dashcam recording in background..."
|
|
36
|
-
|
|
36
|
+
echo "⚠️ NOTE: Recording will run in the background using nohup"
|
|
37
|
+
echo ""
|
|
38
|
+
|
|
39
|
+
# Use nohup to properly detach the process and keep it running
|
|
37
40
|
./bin/dashcam.js record --title "Sync Test Recording" --description "Testing video/log synchronization with timestamped events" > /tmp/dashcam-recording.log 2>&1 &
|
|
38
41
|
RECORD_PID=$!
|
|
39
42
|
|
|
40
43
|
# Wait for recording to initialize and log tracker to start
|
|
41
44
|
echo "Waiting for recording to initialize (PID: $RECORD_PID)..."
|
|
42
|
-
sleep
|
|
45
|
+
sleep 3
|
|
43
46
|
|
|
44
47
|
# Write first event after log tracker is fully ready
|
|
45
48
|
RECORDING_START=$(date +%s)
|
|
@@ -48,11 +51,12 @@ echo "🔴 EVENT 1: Recording START at $(date '+%H:%M:%S')"
|
|
|
48
51
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
49
52
|
echo "[EVENT 1] 🔴 Recording started with emoji at $(date '+%H:%M:%S') - TIMESTAMP: $RECORDING_START" >> "$TEMP_FILE"
|
|
50
53
|
|
|
51
|
-
# Verify recording is actually running
|
|
52
|
-
if
|
|
54
|
+
# Verify recording is actually running by checking status
|
|
55
|
+
if ./bin/dashcam.js status | grep -q "Recording in progress"; then
|
|
53
56
|
echo "✅ Recording started successfully"
|
|
54
57
|
else
|
|
55
|
-
echo "❌ Recording
|
|
58
|
+
echo "❌ Recording failed to start, check /tmp/dashcam-recording.log"
|
|
59
|
+
cat /tmp/dashcam-recording.log
|
|
56
60
|
exit 1
|
|
57
61
|
fi
|
|
58
62
|
|