@hexar/biometric-identity-sdk-react-native 1.0.10 → 1.0.12
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/components/VideoRecorder.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAaxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,iBAAiB,EAA2B,MAAM,oCAAoC,CAAC;AAGlI,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,iCAAiC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA+CD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,
|
|
1
|
+
{"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/components/VideoRecorder.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAaxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,iBAAiB,EAA2B,MAAM,oCAAoC,CAAC;AAGlI,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,iCAAiC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA+CD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA80BtD,CAAC;AA4OF,eAAe,aAAa,CAAC"}
|
|
@@ -116,6 +116,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
116
116
|
const frameCaptureInterval = (0, react_1.useRef)(null);
|
|
117
117
|
const videoRecordingRef = (0, react_1.useRef)(null);
|
|
118
118
|
const isRecordingRef = (0, react_1.useRef)(false);
|
|
119
|
+
const recordingTimeoutRef = (0, react_1.useRef)(null);
|
|
119
120
|
const minDurationMs = 8000;
|
|
120
121
|
const totalDuration = duration || Math.max(minDurationMs, challenges.reduce((sum, c) => sum + c.duration_ms, 0) + 2000);
|
|
121
122
|
// Check camera permissions
|
|
@@ -274,12 +275,14 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
274
275
|
react_native_1.Alert.alert('Recording Error', 'Failed to record video. Please try again.', [{ text: 'OK', onPress: onCancel }]);
|
|
275
276
|
}, [onCancel]);
|
|
276
277
|
const handleVideoComplete = (0, react_1.useCallback)(async (video) => {
|
|
277
|
-
console.log('handleVideoComplete called with video:', video?.path);
|
|
278
|
-
if (
|
|
279
|
-
|
|
278
|
+
console.log('handleVideoComplete called with video:', video?.path, 'current phase:', phase, 'isRecording:', isRecordingRef.current);
|
|
279
|
+
// Don't process if we're already done (result screen or error)
|
|
280
|
+
if (phase === 'loading' && !video) {
|
|
281
|
+
console.log('Video completion in loading phase with no video, ignoring');
|
|
280
282
|
return;
|
|
281
283
|
}
|
|
282
284
|
try {
|
|
285
|
+
console.log('Setting phase to processing');
|
|
283
286
|
setPhase('processing');
|
|
284
287
|
setOverallProgress(100);
|
|
285
288
|
const actualDuration = Date.now() - recordingStartTime.current;
|
|
@@ -369,24 +372,36 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
369
372
|
}
|
|
370
373
|
}, [device]);
|
|
371
374
|
const stopRecording = (0, react_1.useCallback)(async () => {
|
|
372
|
-
console.log('
|
|
375
|
+
console.log('stopRecording called, isRecording:', isRecordingRef.current, 'hasVideoRef:', !!videoRecordingRef.current);
|
|
376
|
+
// Clear any pending timeouts
|
|
377
|
+
if (recordingTimeoutRef.current) {
|
|
378
|
+
clearTimeout(recordingTimeoutRef.current);
|
|
379
|
+
recordingTimeoutRef.current = null;
|
|
380
|
+
}
|
|
373
381
|
if (frameCaptureInterval.current) {
|
|
374
382
|
clearInterval(frameCaptureInterval.current);
|
|
375
383
|
frameCaptureInterval.current = null;
|
|
376
384
|
}
|
|
385
|
+
// Prevent multiple calls
|
|
386
|
+
if (!isRecordingRef.current) {
|
|
387
|
+
console.log('Recording already stopped, ignoring');
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
// Mark as not recording first to prevent re-entry
|
|
391
|
+
isRecordingRef.current = false;
|
|
377
392
|
if (videoRecordingRef.current) {
|
|
378
393
|
try {
|
|
379
|
-
console.log('Stopping video recording');
|
|
394
|
+
console.log('Stopping video recording, current ref exists');
|
|
380
395
|
const recording = videoRecordingRef.current;
|
|
381
396
|
videoRecordingRef.current = null;
|
|
382
|
-
isRecordingRef.current = false;
|
|
383
397
|
await recording.stop();
|
|
384
398
|
console.log('Video recording stopped - waiting for onRecordingFinished callback');
|
|
399
|
+
// onRecordingFinished callback will handle completion
|
|
385
400
|
}
|
|
386
401
|
catch (error) {
|
|
387
402
|
console.error('Error stopping video recording:', error);
|
|
388
|
-
isRecordingRef.current = false;
|
|
389
403
|
const actualDuration = Date.now() - recordingStartTime.current;
|
|
404
|
+
console.log('Error duration check:', actualDuration, 'minDurationMs:', minDurationMs, 'frames:', frames.length);
|
|
390
405
|
if (actualDuration >= minDurationMs && frames.length > 0) {
|
|
391
406
|
console.log('Video stopped with error, using captured frames');
|
|
392
407
|
const result = {
|
|
@@ -399,32 +414,76 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
399
414
|
};
|
|
400
415
|
onComplete(result);
|
|
401
416
|
}
|
|
417
|
+
else {
|
|
418
|
+
// Even if we don't have enough frames, try to complete with what we have
|
|
419
|
+
console.log('Completing with available data despite error');
|
|
420
|
+
const result = {
|
|
421
|
+
frames: frames.length > 0 ? frames : [],
|
|
422
|
+
duration: actualDuration,
|
|
423
|
+
instructionsFollowed: completedChallenges.length === challenges.length,
|
|
424
|
+
qualityScore: frames.length > 0 ? Math.min(100, (frames.length / 30) * 100) : 0,
|
|
425
|
+
challengesCompleted: completedChallenges,
|
|
426
|
+
sessionId,
|
|
427
|
+
};
|
|
428
|
+
onComplete(result);
|
|
429
|
+
}
|
|
402
430
|
}
|
|
403
431
|
}
|
|
404
432
|
else {
|
|
405
|
-
|
|
433
|
+
console.log('No video recording ref - completing with captured frames');
|
|
434
|
+
const actualDuration = Date.now() - recordingStartTime.current;
|
|
435
|
+
// Set phase to processing immediately
|
|
436
|
+
setPhase('processing');
|
|
437
|
+
setOverallProgress(100);
|
|
438
|
+
// Complete with frames we have
|
|
439
|
+
const result = {
|
|
440
|
+
frames: frames.length > 0 ? frames : [],
|
|
441
|
+
duration: actualDuration,
|
|
442
|
+
instructionsFollowed: completedChallenges.length === challenges.length,
|
|
443
|
+
qualityScore: frames.length > 0 ? Math.min(100, (frames.length / 30) * 100) : 0,
|
|
444
|
+
challengesCompleted: completedChallenges,
|
|
445
|
+
sessionId,
|
|
446
|
+
};
|
|
447
|
+
console.log('Completing without video ref:', {
|
|
448
|
+
frames: result.frames.length,
|
|
449
|
+
duration: (actualDuration / 1000).toFixed(1) + 's',
|
|
450
|
+
challenges: completedChallenges.length
|
|
451
|
+
});
|
|
452
|
+
// Small delay to ensure UI updates
|
|
453
|
+
setTimeout(() => {
|
|
454
|
+
onComplete(result);
|
|
455
|
+
}, 100);
|
|
406
456
|
}
|
|
407
457
|
}, [frames, completedChallenges, challenges, sessionId, onComplete, minDurationMs]);
|
|
408
458
|
const runChallenge = (0, react_1.useCallback)((index) => {
|
|
409
459
|
if (index >= challenges.length) {
|
|
410
|
-
console.log('All challenges completed,
|
|
460
|
+
console.log('All challenges completed, checking duration');
|
|
411
461
|
setOverallProgress(100);
|
|
462
|
+
// Clear the totalDuration timeout since we're handling it manually
|
|
463
|
+
if (recordingTimeoutRef.current) {
|
|
464
|
+
clearTimeout(recordingTimeoutRef.current);
|
|
465
|
+
recordingTimeoutRef.current = null;
|
|
466
|
+
console.log('Cleared totalDuration timeout');
|
|
467
|
+
}
|
|
412
468
|
if (isRecordingRef.current) {
|
|
413
469
|
const elapsed = Date.now() - recordingStartTime.current;
|
|
414
470
|
console.log('Checking duration - Elapsed:', (elapsed / 1000).toFixed(1), 's, Min required:', (minDurationMs / 1000).toFixed(1), 's');
|
|
415
471
|
if (elapsed < minDurationMs) {
|
|
416
472
|
const remaining = minDurationMs - elapsed;
|
|
417
473
|
console.log('Waiting additional', (remaining / 1000).toFixed(1), 's to meet minimum duration');
|
|
418
|
-
setTimeout(() => {
|
|
474
|
+
recordingTimeoutRef.current = setTimeout(() => {
|
|
475
|
+
console.log('Minimum duration wait completed, stopping recording');
|
|
419
476
|
if (isRecordingRef.current) {
|
|
420
477
|
stopRecording();
|
|
421
478
|
}
|
|
422
479
|
}, remaining);
|
|
423
480
|
return;
|
|
424
481
|
}
|
|
482
|
+
console.log('Duration requirement met, stopping immediately');
|
|
425
483
|
stopRecording();
|
|
426
484
|
}
|
|
427
485
|
else {
|
|
486
|
+
console.log('Recording already stopped, checking if we can complete');
|
|
428
487
|
const actualDuration = Date.now() - recordingStartTime.current;
|
|
429
488
|
if (actualDuration >= minDurationMs && frames.length > 0) {
|
|
430
489
|
console.log('Recording already stopped, completing with frames');
|
|
@@ -489,8 +548,15 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
489
548
|
videoRecordingRef.current = await cameraRef.current.startRecording({
|
|
490
549
|
flash: 'off',
|
|
491
550
|
onRecordingFinished: (video) => {
|
|
492
|
-
console.log('Video recording finished callback called, path:', video?.path || 'N/A');
|
|
493
|
-
|
|
551
|
+
console.log('Video recording finished callback called, path:', video?.path || 'N/A', 'phase:', phase);
|
|
552
|
+
// Ensure we're in the right state to process
|
|
553
|
+
if (phase === 'recording' || phase === 'processing') {
|
|
554
|
+
handleVideoComplete(video);
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
console.warn('Received onRecordingFinished but phase is', phase, '- calling handleVideoComplete anyway');
|
|
558
|
+
handleVideoComplete(video);
|
|
559
|
+
}
|
|
494
560
|
},
|
|
495
561
|
onRecordingError: (error) => {
|
|
496
562
|
console.error('Recording error:', error);
|
|
@@ -509,16 +575,24 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
509
575
|
startFrameCapture();
|
|
510
576
|
}
|
|
511
577
|
runChallenge(0);
|
|
512
|
-
|
|
513
|
-
|
|
578
|
+
// Set a safety timeout that's longer than totalDuration to ensure we stop eventually
|
|
579
|
+
// This will be cleared if challenges complete early
|
|
580
|
+
const maxDuration = Math.max(totalDuration, minDurationMs + 2000); // Add 2s buffer
|
|
581
|
+
recordingTimeoutRef.current = setTimeout(() => {
|
|
582
|
+
console.log('Safety timeout reached (max duration), stopping recording');
|
|
514
583
|
if (isRecordingRef.current) {
|
|
515
584
|
stopRecording().catch(err => {
|
|
516
|
-
console.error('Error stopping recording on timeout:', err);
|
|
585
|
+
console.error('Error stopping recording on safety timeout:', err);
|
|
517
586
|
});
|
|
518
587
|
}
|
|
519
|
-
},
|
|
520
|
-
return () =>
|
|
521
|
-
|
|
588
|
+
}, maxDuration);
|
|
589
|
+
return () => {
|
|
590
|
+
if (recordingTimeoutRef.current) {
|
|
591
|
+
clearTimeout(recordingTimeoutRef.current);
|
|
592
|
+
recordingTimeoutRef.current = null;
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
}, [device, totalDuration, minDurationMs, handleVideoComplete, handleRecordingError, runChallenge, stopRecording, startFrameCapture]);
|
|
522
596
|
// Current challenge
|
|
523
597
|
const currentChallenge = challenges[currentChallengeIndex];
|
|
524
598
|
// Get direction arrow for the current challenge
|
package/package.json
CHANGED
|
@@ -147,6 +147,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
147
147
|
const frameCaptureInterval = useRef<NodeJS.Timeout | null>(null);
|
|
148
148
|
const videoRecordingRef = useRef<any>(null);
|
|
149
149
|
const isRecordingRef = useRef<boolean>(false);
|
|
150
|
+
const recordingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
150
151
|
const minDurationMs = 8000;
|
|
151
152
|
const totalDuration = duration || Math.max(
|
|
152
153
|
minDurationMs,
|
|
@@ -328,14 +329,16 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
328
329
|
}, [onCancel]);
|
|
329
330
|
|
|
330
331
|
const handleVideoComplete = useCallback(async (video: any) => {
|
|
331
|
-
console.log('handleVideoComplete called with video:', video?.path);
|
|
332
|
+
console.log('handleVideoComplete called with video:', video?.path, 'current phase:', phase, 'isRecording:', isRecordingRef.current);
|
|
332
333
|
|
|
333
|
-
if (
|
|
334
|
-
|
|
334
|
+
// Don't process if we're already done (result screen or error)
|
|
335
|
+
if (phase === 'loading' && !video) {
|
|
336
|
+
console.log('Video completion in loading phase with no video, ignoring');
|
|
335
337
|
return;
|
|
336
338
|
}
|
|
337
339
|
|
|
338
340
|
try {
|
|
341
|
+
console.log('Setting phase to processing');
|
|
339
342
|
setPhase('processing');
|
|
340
343
|
setOverallProgress(100);
|
|
341
344
|
|
|
@@ -436,27 +439,42 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
436
439
|
}, [device]);
|
|
437
440
|
|
|
438
441
|
const stopRecording = useCallback(async () => {
|
|
439
|
-
console.log('
|
|
442
|
+
console.log('stopRecording called, isRecording:', isRecordingRef.current, 'hasVideoRef:', !!videoRecordingRef.current);
|
|
443
|
+
|
|
444
|
+
// Clear any pending timeouts
|
|
445
|
+
if (recordingTimeoutRef.current) {
|
|
446
|
+
clearTimeout(recordingTimeoutRef.current);
|
|
447
|
+
recordingTimeoutRef.current = null;
|
|
448
|
+
}
|
|
440
449
|
|
|
441
450
|
if (frameCaptureInterval.current) {
|
|
442
451
|
clearInterval(frameCaptureInterval.current);
|
|
443
452
|
frameCaptureInterval.current = null;
|
|
444
453
|
}
|
|
445
454
|
|
|
455
|
+
// Prevent multiple calls
|
|
456
|
+
if (!isRecordingRef.current) {
|
|
457
|
+
console.log('Recording already stopped, ignoring');
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Mark as not recording first to prevent re-entry
|
|
462
|
+
isRecordingRef.current = false;
|
|
463
|
+
|
|
446
464
|
if (videoRecordingRef.current) {
|
|
447
465
|
try {
|
|
448
|
-
console.log('Stopping video recording');
|
|
466
|
+
console.log('Stopping video recording, current ref exists');
|
|
449
467
|
const recording = videoRecordingRef.current;
|
|
450
468
|
videoRecordingRef.current = null;
|
|
451
|
-
isRecordingRef.current = false;
|
|
452
469
|
|
|
453
470
|
await recording.stop();
|
|
454
471
|
console.log('Video recording stopped - waiting for onRecordingFinished callback');
|
|
472
|
+
// onRecordingFinished callback will handle completion
|
|
455
473
|
} catch (error) {
|
|
456
474
|
console.error('Error stopping video recording:', error);
|
|
457
|
-
isRecordingRef.current = false;
|
|
458
475
|
|
|
459
476
|
const actualDuration = Date.now() - recordingStartTime.current;
|
|
477
|
+
console.log('Error duration check:', actualDuration, 'minDurationMs:', minDurationMs, 'frames:', frames.length);
|
|
460
478
|
if (actualDuration >= minDurationMs && frames.length > 0) {
|
|
461
479
|
console.log('Video stopped with error, using captured frames');
|
|
462
480
|
const result: VideoRecordingResult = {
|
|
@@ -468,18 +486,63 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
468
486
|
sessionId,
|
|
469
487
|
};
|
|
470
488
|
onComplete(result);
|
|
489
|
+
} else {
|
|
490
|
+
// Even if we don't have enough frames, try to complete with what we have
|
|
491
|
+
console.log('Completing with available data despite error');
|
|
492
|
+
const result: VideoRecordingResult = {
|
|
493
|
+
frames: frames.length > 0 ? frames : [],
|
|
494
|
+
duration: actualDuration,
|
|
495
|
+
instructionsFollowed: completedChallenges.length === challenges.length,
|
|
496
|
+
qualityScore: frames.length > 0 ? Math.min(100, (frames.length / 30) * 100) : 0,
|
|
497
|
+
challengesCompleted: completedChallenges,
|
|
498
|
+
sessionId,
|
|
499
|
+
};
|
|
500
|
+
onComplete(result);
|
|
471
501
|
}
|
|
472
502
|
}
|
|
473
503
|
} else {
|
|
474
|
-
|
|
504
|
+
console.log('No video recording ref - completing with captured frames');
|
|
505
|
+
const actualDuration = Date.now() - recordingStartTime.current;
|
|
506
|
+
|
|
507
|
+
// Set phase to processing immediately
|
|
508
|
+
setPhase('processing');
|
|
509
|
+
setOverallProgress(100);
|
|
510
|
+
|
|
511
|
+
// Complete with frames we have
|
|
512
|
+
const result: VideoRecordingResult = {
|
|
513
|
+
frames: frames.length > 0 ? frames : [],
|
|
514
|
+
duration: actualDuration,
|
|
515
|
+
instructionsFollowed: completedChallenges.length === challenges.length,
|
|
516
|
+
qualityScore: frames.length > 0 ? Math.min(100, (frames.length / 30) * 100) : 0,
|
|
517
|
+
challengesCompleted: completedChallenges,
|
|
518
|
+
sessionId,
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
console.log('Completing without video ref:', {
|
|
522
|
+
frames: result.frames.length,
|
|
523
|
+
duration: (actualDuration / 1000).toFixed(1) + 's',
|
|
524
|
+
challenges: completedChallenges.length
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// Small delay to ensure UI updates
|
|
528
|
+
setTimeout(() => {
|
|
529
|
+
onComplete(result);
|
|
530
|
+
}, 100);
|
|
475
531
|
}
|
|
476
532
|
}, [frames, completedChallenges, challenges, sessionId, onComplete, minDurationMs]);
|
|
477
533
|
|
|
478
534
|
const runChallenge = useCallback((index: number) => {
|
|
479
535
|
if (index >= challenges.length) {
|
|
480
|
-
console.log('All challenges completed,
|
|
536
|
+
console.log('All challenges completed, checking duration');
|
|
481
537
|
setOverallProgress(100);
|
|
482
538
|
|
|
539
|
+
// Clear the totalDuration timeout since we're handling it manually
|
|
540
|
+
if (recordingTimeoutRef.current) {
|
|
541
|
+
clearTimeout(recordingTimeoutRef.current);
|
|
542
|
+
recordingTimeoutRef.current = null;
|
|
543
|
+
console.log('Cleared totalDuration timeout');
|
|
544
|
+
}
|
|
545
|
+
|
|
483
546
|
if (isRecordingRef.current) {
|
|
484
547
|
const elapsed = Date.now() - recordingStartTime.current;
|
|
485
548
|
console.log('Checking duration - Elapsed:', (elapsed / 1000).toFixed(1), 's, Min required:', (minDurationMs / 1000).toFixed(1), 's');
|
|
@@ -487,7 +550,8 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
487
550
|
if (elapsed < minDurationMs) {
|
|
488
551
|
const remaining = minDurationMs - elapsed;
|
|
489
552
|
console.log('Waiting additional', (remaining / 1000).toFixed(1), 's to meet minimum duration');
|
|
490
|
-
setTimeout(() => {
|
|
553
|
+
recordingTimeoutRef.current = setTimeout(() => {
|
|
554
|
+
console.log('Minimum duration wait completed, stopping recording');
|
|
491
555
|
if (isRecordingRef.current) {
|
|
492
556
|
stopRecording();
|
|
493
557
|
}
|
|
@@ -495,8 +559,10 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
495
559
|
return;
|
|
496
560
|
}
|
|
497
561
|
|
|
562
|
+
console.log('Duration requirement met, stopping immediately');
|
|
498
563
|
stopRecording();
|
|
499
564
|
} else {
|
|
565
|
+
console.log('Recording already stopped, checking if we can complete');
|
|
500
566
|
const actualDuration = Date.now() - recordingStartTime.current;
|
|
501
567
|
if (actualDuration >= minDurationMs && frames.length > 0) {
|
|
502
568
|
console.log('Recording already stopped, completing with frames');
|
|
@@ -573,8 +639,14 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
573
639
|
videoRecordingRef.current = await cameraRef.current.startRecording({
|
|
574
640
|
flash: 'off',
|
|
575
641
|
onRecordingFinished: (video: any) => {
|
|
576
|
-
console.log('Video recording finished callback called, path:', video?.path || 'N/A');
|
|
577
|
-
|
|
642
|
+
console.log('Video recording finished callback called, path:', video?.path || 'N/A', 'phase:', phase);
|
|
643
|
+
// Ensure we're in the right state to process
|
|
644
|
+
if (phase === 'recording' || phase === 'processing') {
|
|
645
|
+
handleVideoComplete(video);
|
|
646
|
+
} else {
|
|
647
|
+
console.warn('Received onRecordingFinished but phase is', phase, '- calling handleVideoComplete anyway');
|
|
648
|
+
handleVideoComplete(video);
|
|
649
|
+
}
|
|
578
650
|
},
|
|
579
651
|
onRecordingError: (error: any) => {
|
|
580
652
|
console.error('Recording error:', error);
|
|
@@ -593,17 +665,25 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
593
665
|
|
|
594
666
|
runChallenge(0);
|
|
595
667
|
|
|
596
|
-
|
|
597
|
-
|
|
668
|
+
// Set a safety timeout that's longer than totalDuration to ensure we stop eventually
|
|
669
|
+
// This will be cleared if challenges complete early
|
|
670
|
+
const maxDuration = Math.max(totalDuration, minDurationMs + 2000); // Add 2s buffer
|
|
671
|
+
recordingTimeoutRef.current = setTimeout(() => {
|
|
672
|
+
console.log('Safety timeout reached (max duration), stopping recording');
|
|
598
673
|
if (isRecordingRef.current) {
|
|
599
674
|
stopRecording().catch(err => {
|
|
600
|
-
console.error('Error stopping recording on timeout:', err);
|
|
675
|
+
console.error('Error stopping recording on safety timeout:', err);
|
|
601
676
|
});
|
|
602
677
|
}
|
|
603
|
-
},
|
|
678
|
+
}, maxDuration);
|
|
604
679
|
|
|
605
|
-
return () =>
|
|
606
|
-
|
|
680
|
+
return () => {
|
|
681
|
+
if (recordingTimeoutRef.current) {
|
|
682
|
+
clearTimeout(recordingTimeoutRef.current);
|
|
683
|
+
recordingTimeoutRef.current = null;
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
}, [device, totalDuration, minDurationMs, handleVideoComplete, handleRecordingError, runChallenge, stopRecording, startFrameCapture]);
|
|
607
687
|
|
|
608
688
|
// Current challenge
|
|
609
689
|
const currentChallenge = challenges[currentChallengeIndex];
|