dashcam 1.4.0-beta → 1.4.1-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/lib/recorder.js +110 -13
- package/package.json +1 -1
package/lib/recorder.js
CHANGED
|
@@ -3,7 +3,7 @@ import { logger, logFunctionCall } from './logger.js';
|
|
|
3
3
|
import { createGif, createSnapshot } from './ffmpeg.js';
|
|
4
4
|
import { applicationTracker } from './applicationTracker.js';
|
|
5
5
|
import { logsTrackerManager, trimLogs } from './logs/index.js';
|
|
6
|
-
import { getFfmpegPath } from './binaries.js';
|
|
6
|
+
import { getFfmpegPath, getFfprobePath } from './binaries.js';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import os from 'os';
|
|
9
9
|
import fs from 'fs';
|
|
@@ -536,6 +536,9 @@ export async function stopRecording() {
|
|
|
536
536
|
}
|
|
537
537
|
|
|
538
538
|
try {
|
|
539
|
+
const platform = os.platform();
|
|
540
|
+
const isWindows = platform === 'win32';
|
|
541
|
+
|
|
539
542
|
// First try to gracefully stop FFmpeg by sending 'q'
|
|
540
543
|
if (currentRecording && currentRecording.stdin) {
|
|
541
544
|
logger.debug('Sending quit signal to FFmpeg...');
|
|
@@ -546,18 +549,36 @@ export async function stopRecording() {
|
|
|
546
549
|
// Wait for FFmpeg to finish gracefully with realtime encoding
|
|
547
550
|
const gracefulTimeout = setTimeout(() => {
|
|
548
551
|
if (currentRecording && !currentRecording.killed) {
|
|
549
|
-
logger.warn('FFmpeg did not exit gracefully after 10s,
|
|
550
|
-
//
|
|
551
|
-
|
|
552
|
+
logger.warn('FFmpeg did not exit gracefully after 10s, forcing termination...');
|
|
553
|
+
// Use platform-appropriate termination
|
|
554
|
+
if (isWindows) {
|
|
555
|
+
// On Windows, use taskkill for clean termination
|
|
556
|
+
try {
|
|
557
|
+
execa.sync('taskkill', ['/PID', currentRecording.pid.toString(), '/T']);
|
|
558
|
+
} catch (e) {
|
|
559
|
+
logger.warn('taskkill failed, ffmpeg may have already exited');
|
|
560
|
+
}
|
|
561
|
+
} else {
|
|
562
|
+
// On Unix, use SIGTERM
|
|
563
|
+
process.kill(currentRecording.pid, 'SIGTERM');
|
|
564
|
+
}
|
|
552
565
|
}
|
|
553
566
|
}, 10000); // Wait longer for graceful shutdown
|
|
554
567
|
|
|
555
|
-
// Wait up to 20 seconds for
|
|
568
|
+
// Wait up to 20 seconds for termination to work
|
|
556
569
|
const hardKillTimeout = setTimeout(() => {
|
|
557
570
|
if (currentRecording && !currentRecording.killed) {
|
|
558
|
-
logger.error('FFmpeg still running after
|
|
559
|
-
//
|
|
560
|
-
|
|
571
|
+
logger.error('FFmpeg still running after termination, forcing kill...');
|
|
572
|
+
// Use platform-appropriate forced kill
|
|
573
|
+
if (isWindows) {
|
|
574
|
+
try {
|
|
575
|
+
execa.sync('taskkill', ['/F', '/PID', currentRecording.pid.toString(), '/T']);
|
|
576
|
+
} catch (e) {
|
|
577
|
+
logger.warn('taskkill /F failed, ffmpeg may have already exited');
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
process.kill(currentRecording.pid, 'SIGKILL');
|
|
581
|
+
}
|
|
561
582
|
}
|
|
562
583
|
}, 20000); // Longer timeout to avoid killing prematurely
|
|
563
584
|
|
|
@@ -571,20 +592,39 @@ export async function stopRecording() {
|
|
|
571
592
|
clearTimeout(hardKillTimeout);
|
|
572
593
|
|
|
573
594
|
// Wait for filesystem to sync and file to be written
|
|
595
|
+
// This is critical on Windows where file writes may be buffered
|
|
596
|
+
|
|
597
|
+
// Windows needs extra time for file system sync
|
|
598
|
+
const extraSyncTime = isWindows ? 3000 : 1000;
|
|
599
|
+
logger.debug(`Waiting ${extraSyncTime}ms for file system to sync...`);
|
|
600
|
+
await new Promise(resolve => setTimeout(resolve, extraSyncTime));
|
|
601
|
+
|
|
574
602
|
// Poll for the file to exist with content
|
|
575
603
|
logger.debug('Waiting for temp file to be written...');
|
|
576
|
-
const maxWaitTime =
|
|
604
|
+
const maxWaitTime = 15000; // Increased from 10s to 15s for Windows
|
|
577
605
|
const startWait = Date.now();
|
|
578
606
|
let tempFileReady = false;
|
|
607
|
+
let lastSize = 0;
|
|
608
|
+
let stableCount = 0;
|
|
579
609
|
|
|
580
610
|
while (!tempFileReady && (Date.now() - startWait) < maxWaitTime) {
|
|
581
611
|
const tempFile = currentTempFile;
|
|
582
612
|
if (tempFile && fs.existsSync(tempFile)) {
|
|
583
613
|
const stats = fs.statSync(tempFile);
|
|
584
614
|
if (stats.size > 0) {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
615
|
+
// Check if file size is stable (hasn't changed)
|
|
616
|
+
if (stats.size === lastSize) {
|
|
617
|
+
stableCount++;
|
|
618
|
+
// Consider ready after size is stable for 2 consecutive checks
|
|
619
|
+
if (stableCount >= 2) {
|
|
620
|
+
logger.debug('Temp file is ready and stable', { size: stats.size });
|
|
621
|
+
tempFileReady = true;
|
|
622
|
+
break;
|
|
623
|
+
}
|
|
624
|
+
} else {
|
|
625
|
+
stableCount = 0;
|
|
626
|
+
}
|
|
627
|
+
lastSize = stats.size;
|
|
588
628
|
}
|
|
589
629
|
}
|
|
590
630
|
// Wait a bit before checking again
|
|
@@ -592,7 +632,7 @@ export async function stopRecording() {
|
|
|
592
632
|
}
|
|
593
633
|
|
|
594
634
|
if (!tempFileReady) {
|
|
595
|
-
logger.warn('Temp file not
|
|
635
|
+
logger.warn('Temp file not confirmed stable after waiting, proceeding anyway', { lastSize });
|
|
596
636
|
}
|
|
597
637
|
|
|
598
638
|
// Read temp file path from disk (for cross-process access)
|
|
@@ -641,6 +681,63 @@ export async function stopRecording() {
|
|
|
641
681
|
path: tempFile
|
|
642
682
|
});
|
|
643
683
|
|
|
684
|
+
// Validate that the file is readable by ffprobe before attempting re-mux
|
|
685
|
+
logger.debug('Validating temp file integrity with ffprobe...');
|
|
686
|
+
const ffprobePath = await getFfprobePath();
|
|
687
|
+
try {
|
|
688
|
+
const { exitCode, stderr } = await execa(ffprobePath, [
|
|
689
|
+
'-v', 'error',
|
|
690
|
+
'-select_streams', 'v:0',
|
|
691
|
+
'-show_entries', 'stream=codec_type',
|
|
692
|
+
'-of', 'default=noprint_wrappers=1:nokey=1',
|
|
693
|
+
tempFile
|
|
694
|
+
], { reject: false });
|
|
695
|
+
|
|
696
|
+
if (exitCode !== 0) {
|
|
697
|
+
logger.warn('Temp file validation failed', { exitCode, stderr });
|
|
698
|
+
// File may be corrupted, but we'll try to process it anyway
|
|
699
|
+
// Skip re-muxing and just copy the file
|
|
700
|
+
logger.warn('Skipping re-mux due to validation failure, copying file directly');
|
|
701
|
+
fs.copyFileSync(tempFile, outputPath);
|
|
702
|
+
|
|
703
|
+
// Clean up temp file
|
|
704
|
+
try {
|
|
705
|
+
fs.unlinkSync(tempFile);
|
|
706
|
+
} catch (e) {
|
|
707
|
+
logger.debug('Failed to delete temp file:', e);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Skip the re-mux process below
|
|
711
|
+
const result = {
|
|
712
|
+
outputPath,
|
|
713
|
+
gifPath: `${outputPath.substring(0, outputPath.lastIndexOf('.'))}.gif`,
|
|
714
|
+
snapshotPath: `${outputPath.substring(0, outputPath.lastIndexOf('.'))}.png`,
|
|
715
|
+
duration: Date.now() - recordingStartTime,
|
|
716
|
+
fileSize: fs.existsSync(outputPath) ? fs.statSync(outputPath).size : 0,
|
|
717
|
+
clientStartDate: recordingStartTime,
|
|
718
|
+
apps: applicationTracker.stop().apps,
|
|
719
|
+
icons: applicationTracker.stop().icons,
|
|
720
|
+
logs: await logsTrackerManager.stop({ recorderId: path.basename(outputPath).replace('.webm', ''), screenId: '1' })
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
currentRecording = null;
|
|
724
|
+
recordingStartTime = null;
|
|
725
|
+
currentTempFile = null;
|
|
726
|
+
|
|
727
|
+
if (fs.existsSync(TEMP_FILE_INFO_PATH)) {
|
|
728
|
+
fs.unlinkSync(TEMP_FILE_INFO_PATH);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
logExit();
|
|
732
|
+
return result;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
logger.debug('Temp file validation passed');
|
|
736
|
+
} catch (error) {
|
|
737
|
+
logger.warn('Failed to validate temp file', { error: error.message });
|
|
738
|
+
// Continue with re-mux attempt anyway
|
|
739
|
+
}
|
|
740
|
+
|
|
644
741
|
// Re-mux to ensure proper container metadata (duration, seekability, etc.)
|
|
645
742
|
logger.debug('Re-muxing temp file to fix container metadata...');
|
|
646
743
|
|