dashcam 1.3.13-beta → 1.3.14-beta
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 +58 -8
- package/lib/processManager.js +29 -8
- package/lib/uploader.js +41 -7
- package/package.json +1 -1
package/bin/dashcam.js
CHANGED
|
@@ -404,55 +404,94 @@ program
|
|
|
404
404
|
const logFile = path.join(process.cwd(), '.dashcam', 'recording.log');
|
|
405
405
|
|
|
406
406
|
console.log('Stopping recording...');
|
|
407
|
+
logger.debug('Active status before stop:', activeStatus);
|
|
407
408
|
|
|
408
409
|
try {
|
|
410
|
+
logger.debug('Calling stopActiveRecording...');
|
|
409
411
|
const result = await processManager.stopActiveRecording();
|
|
410
412
|
|
|
411
413
|
if (!result) {
|
|
412
414
|
console.log('Failed to stop recording');
|
|
415
|
+
logger.error('stopActiveRecording returned null/false');
|
|
413
416
|
process.exit(1);
|
|
414
417
|
}
|
|
415
418
|
|
|
416
419
|
console.log('Recording stopped successfully');
|
|
420
|
+
logger.debug('Stop result:', result);
|
|
417
421
|
|
|
418
422
|
// Wait for upload to complete (background process handles this)
|
|
419
423
|
logger.debug('Waiting for background upload to complete...');
|
|
420
|
-
console.log('
|
|
424
|
+
console.log('Checking if background process uploaded...');
|
|
421
425
|
|
|
422
426
|
// Wait up to 2 minutes for upload result to appear
|
|
423
427
|
const maxWaitForUpload = 120000; // 2 minutes
|
|
424
428
|
const startWaitForUpload = Date.now();
|
|
425
429
|
let uploadResult = null;
|
|
430
|
+
let checkCount = 0;
|
|
426
431
|
|
|
427
432
|
while (!uploadResult && (Date.now() - startWaitForUpload) < maxWaitForUpload) {
|
|
428
433
|
uploadResult = processManager.readUploadResult();
|
|
434
|
+
checkCount++;
|
|
435
|
+
|
|
429
436
|
if (!uploadResult) {
|
|
437
|
+
// Log every 10 seconds to show progress
|
|
438
|
+
if (checkCount % 10 === 0) {
|
|
439
|
+
const elapsed = Math.round((Date.now() - startWaitForUpload) / 1000);
|
|
440
|
+
logger.debug(`Still waiting for background upload... (${elapsed}s elapsed)`);
|
|
441
|
+
console.log(`Waiting for background upload... (${elapsed}s)`);
|
|
442
|
+
}
|
|
430
443
|
await new Promise(resolve => setTimeout(resolve, 1000)); // Check every second
|
|
431
444
|
}
|
|
432
445
|
}
|
|
433
446
|
|
|
434
|
-
logger.debug('Upload result read attempt', {
|
|
447
|
+
logger.debug('Upload result read attempt', {
|
|
448
|
+
found: !!uploadResult,
|
|
449
|
+
shareLink: uploadResult?.shareLink,
|
|
450
|
+
checksPerformed: checkCount,
|
|
451
|
+
timeElapsed: Math.round((Date.now() - startWaitForUpload) / 1000) + 's'
|
|
452
|
+
});
|
|
435
453
|
|
|
436
454
|
if (uploadResult && uploadResult.shareLink) {
|
|
437
455
|
console.log('Watch your recording:', uploadResult.shareLink);
|
|
456
|
+
logger.info('Background process upload succeeded');
|
|
438
457
|
// Clean up the result file now that we've read it
|
|
439
458
|
processManager.cleanup();
|
|
440
459
|
process.exit(0);
|
|
441
460
|
}
|
|
442
461
|
|
|
462
|
+
logger.debug('No upload result from background process, checking files...');
|
|
463
|
+
|
|
443
464
|
// Check if files still exist - if not, background process already uploaded
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
465
|
+
const videoExists = fs.existsSync(result.outputPath);
|
|
466
|
+
const gifExists = !result.gifPath || fs.existsSync(result.gifPath);
|
|
467
|
+
const snapshotExists = !result.snapshotPath || fs.existsSync(result.snapshotPath);
|
|
468
|
+
|
|
469
|
+
logger.debug('File existence check:', {
|
|
470
|
+
video: videoExists,
|
|
471
|
+
gif: gifExists,
|
|
472
|
+
snapshot: snapshotExists,
|
|
473
|
+
outputPath: result.outputPath,
|
|
474
|
+
gifPath: result.gifPath,
|
|
475
|
+
snapshotPath: result.snapshotPath
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
const filesExist = videoExists && gifExists && snapshotExists;
|
|
447
479
|
|
|
448
480
|
if (!filesExist) {
|
|
449
|
-
console.log('Recording uploaded by background process');
|
|
450
|
-
logger.info('Files were cleaned up by background process');
|
|
481
|
+
console.log('Recording appears to be uploaded by background process (files deleted)');
|
|
482
|
+
logger.info('Files were cleaned up by background process, assuming upload succeeded');
|
|
451
483
|
process.exit(0);
|
|
452
484
|
}
|
|
453
485
|
|
|
454
486
|
// Always attempt to upload - let upload function find project if needed
|
|
455
|
-
console.log('
|
|
487
|
+
console.log('No upload result found, uploading from foreground process...');
|
|
488
|
+
logger.debug('Starting foreground upload with metadata:', {
|
|
489
|
+
title: activeStatus?.options?.title,
|
|
490
|
+
project: activeStatus?.options?.project,
|
|
491
|
+
duration: result.duration,
|
|
492
|
+
outputPath: result.outputPath
|
|
493
|
+
});
|
|
494
|
+
|
|
456
495
|
try {
|
|
457
496
|
const uploadResult = await upload(result.outputPath, {
|
|
458
497
|
title: activeStatus?.options?.title,
|
|
@@ -467,12 +506,23 @@ program
|
|
|
467
506
|
});
|
|
468
507
|
|
|
469
508
|
console.log('Watch your recording:', uploadResult.shareLink);
|
|
509
|
+
logger.info('Foreground upload succeeded');
|
|
470
510
|
} catch (uploadError) {
|
|
471
511
|
console.error('Upload failed:', uploadError.message);
|
|
512
|
+
logger.error('Upload error details:', {
|
|
513
|
+
message: uploadError.message,
|
|
514
|
+
stack: uploadError.stack,
|
|
515
|
+
code: uploadError.code,
|
|
516
|
+
statusCode: uploadError.response?.statusCode
|
|
517
|
+
});
|
|
472
518
|
console.log('Recording saved locally:', result.outputPath);
|
|
473
519
|
}
|
|
474
520
|
} catch (error) {
|
|
475
521
|
console.error('Failed to stop recording:', error.message);
|
|
522
|
+
logger.error('Stop recording error details:', {
|
|
523
|
+
message: error.message,
|
|
524
|
+
stack: error.stack
|
|
525
|
+
});
|
|
476
526
|
process.exit(1);
|
|
477
527
|
}
|
|
478
528
|
|
package/lib/processManager.js
CHANGED
|
@@ -51,6 +51,22 @@ class ProcessManager {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
markStatusCompleted(completionData = {}) {
|
|
55
|
+
try {
|
|
56
|
+
const status = this.readStatus();
|
|
57
|
+
if (status) {
|
|
58
|
+
fs.writeFileSync(STATUS_FILE, JSON.stringify({
|
|
59
|
+
...status,
|
|
60
|
+
isRecording: false,
|
|
61
|
+
completedAt: Date.now(),
|
|
62
|
+
...completionData
|
|
63
|
+
}, null, 2));
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
logger.error('Failed to mark status as completed', { error });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
54
70
|
writeUploadResult(result) {
|
|
55
71
|
try {
|
|
56
72
|
logger.info('Writing upload result to file', { path: RESULT_FILE, shareLink: result.shareLink });
|
|
@@ -95,8 +111,10 @@ class ProcessManager {
|
|
|
95
111
|
const status = this.readStatus();
|
|
96
112
|
|
|
97
113
|
if (!status || !status.pid || !this.isProcessRunning(status.pid)) {
|
|
98
|
-
//
|
|
99
|
-
|
|
114
|
+
// Mark as completed if process is dead but status exists
|
|
115
|
+
if (status && status.isRecording) {
|
|
116
|
+
this.markStatusCompleted({ reason: 'process_not_running' });
|
|
117
|
+
}
|
|
100
118
|
return false;
|
|
101
119
|
}
|
|
102
120
|
|
|
@@ -111,7 +129,7 @@ class ProcessManager {
|
|
|
111
129
|
cleanup(options = {}) {
|
|
112
130
|
const { preserveResult = false } = options;
|
|
113
131
|
try {
|
|
114
|
-
|
|
132
|
+
// Only delete result file, keep status file for history
|
|
115
133
|
if (!preserveResult && fs.existsSync(RESULT_FILE)) fs.unlinkSync(RESULT_FILE);
|
|
116
134
|
} catch (error) {
|
|
117
135
|
logger.error('Failed to cleanup process files', { error });
|
|
@@ -137,7 +155,7 @@ class ProcessManager {
|
|
|
137
155
|
const pid = status.pid;
|
|
138
156
|
if (!pid || !this.isProcessRunning(pid)) {
|
|
139
157
|
logger.warn('Background process not running');
|
|
140
|
-
this.
|
|
158
|
+
this.markStatusCompleted({ reason: 'process_already_stopped' });
|
|
141
159
|
return false;
|
|
142
160
|
}
|
|
143
161
|
|
|
@@ -172,6 +190,12 @@ class ProcessManager {
|
|
|
172
190
|
|
|
173
191
|
logger.info('Background process stopped');
|
|
174
192
|
|
|
193
|
+
// Mark status as completed
|
|
194
|
+
this.markStatusCompleted({
|
|
195
|
+
reason: 'stopped_by_user',
|
|
196
|
+
duration: Date.now() - status.startTime
|
|
197
|
+
});
|
|
198
|
+
|
|
175
199
|
// Return a minimal result indicating success
|
|
176
200
|
// The upload will be handled by the stop command checking the result file
|
|
177
201
|
return {
|
|
@@ -291,11 +315,8 @@ class ProcessManager {
|
|
|
291
315
|
logger.info('Recording stopped successfully during graceful exit');
|
|
292
316
|
} catch (error) {
|
|
293
317
|
logger.error('Failed to stop recording during graceful exit', { error });
|
|
294
|
-
this.
|
|
318
|
+
this.markStatusCompleted({ reason: 'graceful_exit_error' });
|
|
295
319
|
}
|
|
296
|
-
} else {
|
|
297
|
-
// Just cleanup if no recording is active
|
|
298
|
-
this.cleanup();
|
|
299
320
|
}
|
|
300
321
|
|
|
301
322
|
process.exit(0);
|
package/lib/uploader.js
CHANGED
|
@@ -110,6 +110,11 @@ class Uploader {
|
|
|
110
110
|
total: progress.total,
|
|
111
111
|
speedMBps: speedMBps.toFixed(2)
|
|
112
112
|
});
|
|
113
|
+
|
|
114
|
+
// Also output to console for user feedback
|
|
115
|
+
if (fileType === 'video') {
|
|
116
|
+
console.log(`Uploading video: ${percent}%`);
|
|
117
|
+
}
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
// Call progress callback if registered
|
|
@@ -125,6 +130,7 @@ class Uploader {
|
|
|
125
130
|
const uploadDuration = (Date.now() - upload.startTime) / 1000;
|
|
126
131
|
|
|
127
132
|
if (extension !== 'png') {
|
|
133
|
+
console.log(`Uploaded ${fileType} successfully (${uploadDuration.toFixed(1)}s)`);
|
|
128
134
|
logger.info(`Successfully uploaded ${fileType}`, {
|
|
129
135
|
key: result.Key,
|
|
130
136
|
location: result.Location,
|
|
@@ -143,10 +149,14 @@ class Uploader {
|
|
|
143
149
|
logExit();
|
|
144
150
|
return result;
|
|
145
151
|
} catch (error) {
|
|
152
|
+
console.error(`Failed to upload ${fileType}: ${error.message}`);
|
|
146
153
|
logger.error('Upload error:', {
|
|
147
154
|
fileType,
|
|
148
155
|
file: path.basename(file),
|
|
149
156
|
error: error.message,
|
|
157
|
+
code: error.code,
|
|
158
|
+
statusCode: error.$metadata?.httpStatusCode,
|
|
159
|
+
requestId: error.$metadata?.requestId,
|
|
150
160
|
stack: error.stack
|
|
151
161
|
});
|
|
152
162
|
|
|
@@ -254,6 +264,7 @@ export async function upload(filePath, metadata = {}) {
|
|
|
254
264
|
|
|
255
265
|
logger.verbose('Creating replay with config', replayConfig);
|
|
256
266
|
|
|
267
|
+
console.log('Creating replay on server...');
|
|
257
268
|
logger.info('Creating replay', replayConfig);
|
|
258
269
|
|
|
259
270
|
// Create the replay first
|
|
@@ -261,6 +272,7 @@ export async function upload(filePath, metadata = {}) {
|
|
|
261
272
|
|
|
262
273
|
let newReplay;
|
|
263
274
|
try {
|
|
275
|
+
logger.debug('Sending replay creation request...');
|
|
264
276
|
newReplay = await got.post('https://testdriver-api.onrender.com/api/v1/replay', {
|
|
265
277
|
headers: {
|
|
266
278
|
Authorization: `Bearer ${token}`
|
|
@@ -269,16 +281,19 @@ export async function upload(filePath, metadata = {}) {
|
|
|
269
281
|
timeout: 30000
|
|
270
282
|
}).json();
|
|
271
283
|
|
|
284
|
+
console.log('Replay created successfully');
|
|
272
285
|
logger.info('Replay created successfully', {
|
|
273
286
|
replayId: newReplay.replay.id,
|
|
274
287
|
shareKey: newReplay.replay.shareKey,
|
|
275
288
|
shareLink: newReplay.replay.shareLink
|
|
276
289
|
});
|
|
277
290
|
} catch (error) {
|
|
291
|
+
console.error('Failed to create replay on server');
|
|
278
292
|
logger.error('Failed to create replay', {
|
|
279
293
|
status: error.response?.statusCode,
|
|
280
294
|
statusText: error.response?.statusMessage,
|
|
281
295
|
body: error.response?.body,
|
|
296
|
+
message: error.message,
|
|
282
297
|
replayConfig: replayConfig
|
|
283
298
|
});
|
|
284
299
|
throw error;
|
|
@@ -310,8 +325,10 @@ export async function upload(filePath, metadata = {}) {
|
|
|
310
325
|
}
|
|
311
326
|
|
|
312
327
|
logger.verbose('Getting STS credentials for replay', { replayId: newReplay.replay.id });
|
|
328
|
+
console.log('Getting upload credentials...');
|
|
313
329
|
const sts = await auth.getStsCredentials(replayData);
|
|
314
330
|
|
|
331
|
+
console.log('Starting file uploads...');
|
|
315
332
|
logger.verbose('STS credentials received', {
|
|
316
333
|
hasVideo: !!sts.video,
|
|
317
334
|
hasImage: !!sts.image,
|
|
@@ -342,6 +359,7 @@ export async function upload(filePath, metadata = {}) {
|
|
|
342
359
|
}
|
|
343
360
|
|
|
344
361
|
logger.info('Starting asset uploads', { totalUploads: promises.length });
|
|
362
|
+
console.log(`Uploading ${promises.length} file(s)...`);
|
|
345
363
|
|
|
346
364
|
// Process and upload logs if available
|
|
347
365
|
if (metadata.logs && metadata.logs.length > 0) {
|
|
@@ -420,6 +438,8 @@ export async function upload(filePath, metadata = {}) {
|
|
|
420
438
|
});
|
|
421
439
|
}
|
|
422
440
|
|
|
441
|
+
logger.debug('Waiting for all uploads to complete...');
|
|
442
|
+
console.log('Finalizing uploads...');
|
|
423
443
|
await Promise.all(promises);
|
|
424
444
|
|
|
425
445
|
// Clean up uploaded files after all uploads complete successfully
|
|
@@ -436,13 +456,27 @@ export async function upload(filePath, metadata = {}) {
|
|
|
436
456
|
|
|
437
457
|
// Publish the replay (like the desktop app does)
|
|
438
458
|
logger.debug('Publishing replay...');
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
459
|
+
console.log('Publishing replay...');
|
|
460
|
+
|
|
461
|
+
try {
|
|
462
|
+
await got.post('https://testdriver-api.onrender.com/api/v1/replay/publish', {
|
|
463
|
+
headers: {
|
|
464
|
+
Authorization: `Bearer ${token}`
|
|
465
|
+
},
|
|
466
|
+
json: { id: newReplay.replay.id },
|
|
467
|
+
timeout: 30000
|
|
468
|
+
}).json();
|
|
469
|
+
|
|
470
|
+
console.log('Replay published successfully');
|
|
471
|
+
} catch (error) {
|
|
472
|
+
console.error('Failed to publish replay:', error.message);
|
|
473
|
+
logger.error('Publish error:', {
|
|
474
|
+
message: error.message,
|
|
475
|
+
statusCode: error.response?.statusCode,
|
|
476
|
+
body: error.response?.body
|
|
477
|
+
});
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
446
480
|
|
|
447
481
|
logger.info('Upload process completed successfully', {
|
|
448
482
|
replayId: newReplay.replay.id,
|