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 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('Uploading recording...');
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', { found: !!uploadResult, shareLink: uploadResult?.shareLink });
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 filesExist = fs.existsSync(result.outputPath) &&
445
- (!result.gifPath || fs.existsSync(result.gifPath)) &&
446
- (!result.snapshotPath || fs.existsSync(result.snapshotPath));
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('Uploading recording...');
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
 
@@ -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
- // Clean up but preserve upload result in case the background process just finished uploading
99
- this.cleanup({ preserveResult: true });
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
- if (fs.existsSync(STATUS_FILE)) fs.unlinkSync(STATUS_FILE);
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.cleanup({ preserveResult: true });
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.cleanup(); // Fallback cleanup
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
- await got.post('https://testdriver-api.onrender.com/api/v1/replay/publish', {
440
- headers: {
441
- Authorization: `Bearer ${token}`
442
- },
443
- json: { id: newReplay.replay.id },
444
- timeout: 30000
445
- }).json();
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dashcam",
3
- "version": "1.3.13-beta",
3
+ "version": "1.3.14-beta",
4
4
  "description": "Minimal CLI version of Dashcam desktop app",
5
5
  "main": "bin/dashcam.js",
6
6
  "bin": {