fix: improve playback timing and progress tracking in player.handlebars
Signed-off-by: si458 <simonsmith5521@gmail.com>
si458 committed
Mar 23, 2026 at 14:53 UTC
b99a21cf13f4ae153dd7726d87c3c20da331800e
1 file changed
+55
-5
views/player.handlebars
+55
-5
@@ -115,6 +115,10 @@
115
var readState = 0;
116
var waitTimer = null;
117
var waitTimerArgs = null;
118
+ var waitTickTimer = null;
119
+ var waitTickStartWall = 0;
120
+ var waitTickStartRec = 0;
121
+ var waitTickTargetRec = 0;
122
var deskAspectRatio = 0;
123
var currentDeltaTimeTotalSec = 0;
124
var videoWriter = null;
@@ -504,14 +508,41 @@
508
QS('progressbar').width = '0px';
509
}
510
511
+ function updateWaitTick() {
512
+ var elapsed = Date.now() - waitTickStartWall;
513
+ var speed = parseFloat(Q('PlaySpeed').value);
514
+ var recNow = waitTickStartRec + (elapsed / speed);
515
+ if (recNow > waitTickTargetRec) recNow = waitTickTargetRec;
516
+ var deltaTimeTotalSec = Math.floor((recNow - recFileStartTime) / 1000);
517
+ if (currentDeltaTimeTotalSec != deltaTimeTotalSec) {
518
+ currentDeltaTimeTotalSec = deltaTimeTotalSec;
519
+ var hrs = Math.floor(deltaTimeTotalSec / 3600);
520
+ var mins = Math.floor((deltaTimeTotalSec % 3600) / 60);
521
+ var secs = Math.floor(deltaTimeTotalSec % 60);
522
+ QH('timespan', pad2(hrs) + ':' + pad2(mins) + ':' + pad2(secs));
523
+ }
524
+ if (recFileEndTime > recFileStartTime) {
525
+ QS('progressbar').width = Math.floor(((recNow - recFileStartTime) / (recFileEndTime - recFileStartTime)) * 100) + '%';
526
+ }
527
+ }
528
+
529
function processBlock(type, flags, time, data) {
530
if (type < 0) { pause(); return; }
531
var waitTime = Math.round((time - recFileLastTime) * parseFloat(Q('PlaySpeed').value));
532
if ((waitTime < 5) || (videoWriter != null)) {
533
processBlockEx(type, flags, time, data);
534
} else {
513
- waitTimerArgs = [type, flags, time, data]
514
- waitTimer = setTimeout(function () { waitTimer = null; if (waitTimerArgs) { processBlockEx(waitTimerArgs[0], waitTimerArgs[1], waitTimerArgs[2], waitTimerArgs[3]); } }, waitTime);
535
+ waitTimerArgs = [type, flags, time, data];
536
+ waitTickStartWall = Date.now();
537
+ waitTickStartRec = recFileLastTime;
538
+ waitTickTargetRec = time;
539
+ if (waitTickTimer) { clearInterval(waitTickTimer); }
540
+ waitTickTimer = setInterval(updateWaitTick, 250);
541
+ waitTimer = setTimeout(function () {
542
+ if (waitTickTimer) { clearInterval(waitTickTimer); waitTickTimer = null; }
543
+ waitTimer = null;
544
+ if (waitTimerArgs) { processBlockEx(waitTimerArgs[0], waitTimerArgs[1], waitTimerArgs[2], waitTimerArgs[3]); }
545
+ }, waitTime);
546
}
547
}
548
@@ -631,6 +662,7 @@
662
currentDeltaTimeTotalSec = 0;
663
recFileEndTime = 0;
664
agentTerminal = null;
665
+ if (waitTickTimer != null) { clearInterval(waitTickTimer); waitTickTimer = null; }
666
if (waitTimer != null) { clearTimeout(waitTimer); waitTimer = null; }
667
QH('deskstatus', '');
668
QE('PlayButton', false);
@@ -800,7 +832,14 @@
832
//agentTerminal = CreateAmtRemoteTerminal('Term', {});
833
//agentTerminal.State = 3;
834
}
803
- readNextBlock(processBlock);
835
+ if (waitTimerArgs != null) {
836
+ // Resuming mid-gap: restart timer for remaining time from current position
837
+ var resumeArgs = waitTimerArgs;
838
+ waitTimerArgs = null;
839
+ processBlock(resumeArgs[0], resumeArgs[1], resumeArgs[2], resumeArgs[3]);
840
+ } else {
841
+ readNextBlock(processBlock);
842
+ }
843
}
844
845
function pause() {
@@ -811,11 +850,15 @@
850
QE('PlayButton', recFilePtr != recFile.size);
851
QE('PauseButton', false);
852
QE('RestartButton', recFilePtr != 0);
853
+ if (waitTickTimer != null) { clearInterval(waitTickTimer); waitTickTimer = null; }
854
if (waitTimer != null) {
855
clearTimeout(waitTimer);
856
waitTimer = null;
817
- processBlockEx(waitTimerArgs[0], waitTimerArgs[1], waitTimerArgs[2], waitTimerArgs[3]);
818
- waitTimerArgs = null;
857
+ // Save current virtual time so resume starts from here, not from the pre-gap time
858
+ var elapsed = Date.now() - waitTickStartWall;
859
+ var speed = parseFloat(Q('PlaySpeed').value);
860
+ recFileLastTime = Math.min(waitTickStartRec + (elapsed / speed), waitTickTargetRec);
861
+ // Keep waitTimerArgs so play() can resume the gap from this point
862
}
863
}
864
@@ -825,6 +868,7 @@
868
if (playing == true) return;
869
recFilePtr = 0;
870
readState = 0;
871
+ waitTimerArgs = null;
872
currentDeltaTimeTotalSec = 0;
873
QV('metadatadiv', true);
874
QE('PlayButton', true);
@@ -836,8 +880,14 @@
880
QV('TermParent', false);
881
QV('XTermParent', false);
882
clearXTerm();
883
+ recFileLastTime = recFileStartTime;
884
if (agentDesktop) {
885
agentDesktop.Canvas.clearRect(0, 0, agentDesktop.CanvasId.width, agentDesktop.CanvasId.height);
886
+ agentDesktop = CreateAgentRemoteDesktop('Desk');
887
+ agentDesktop.onScreenSizeChange = deskAdjust;
888
+ agentDesktop.onPreDrawImage = preCanvasDraw;
889
+ agentDesktop.State = 3;
890
+ deskAdjust();
891
} else if (amtDesktop) {
892
amtDesktop.canvas.clearRect(0, 0, amtDesktop.CanvasId.width, amtDesktop.CanvasId.height);
893
amtDesktop = CreateAmtRemoteDesktop('Desk');