mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-07-26 12:08:15 +00:00
Harden Disney+ time fallback against <video> element recreation
When the control overlay (and its live time readout) is briefly absent,
the old fallback recomputed the timeline from the raw blob-relative
video.currentTime via a cached scale/start. Disney recreates the <video>
element on some play/pause transitions, which resets currentTime, so that
math produced a wildly wrong time ("loses the time" after a pause).
The native clock advances 1:1 with real playback, so instead anchor the
last live UI position to video.currentTime and extrapolate by the elapsed
native delta. If the delta is negative or implausibly large (element
recreated / seek), freeze at the last known position rather than emitting
a garbage time.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+25
-12
@@ -62,6 +62,8 @@
|
|||||||
|
|
||||||
// Suppresses native event reporting after a programmatic action.
|
// Suppresses native event reporting after a programmatic action.
|
||||||
|
|
||||||
|
// Each entry is a per-type timer (key = 'playing'|'paused'|'seek').
|
||||||
|
|
||||||
// While a timer exists, matching native events are consumed and not relayed.
|
// While a timer exists, matching native events are consumed and not relayed.
|
||||||
|
|
||||||
// Timers self-clean after 300ms if the native event never fires.
|
// Timers self-clean after 300ms if the native event never fires.
|
||||||
@@ -285,6 +287,8 @@
|
|||||||
const candidates = querySelectorAllShadow('button,[role="button"]');
|
const candidates = querySelectorAllShadow('button,[role="button"]');
|
||||||
const backRe = /rewind|backward|back\b|zurück|zurueck|rück|rueck|retour|recul|retroced|atrás|voltar|indietro/i;
|
const backRe = /rewind|backward|back\b|zurück|zurueck|rück|rueck|retour|recul|retroced|atrás|voltar|indietro/i;
|
||||||
const forwardRe = /forward|ahead|skip|vorwärts|vorwaerts|vorsp|weiter|avancer|adelant|avançar|avancar|avanti/i;
|
const forwardRe = /forward|ahead|skip|vorwärts|vorwaerts|vorsp|weiter|avancer|adelant|avançar|avancar|avanti/i;
|
||||||
|
const timeRe = /\b(5|10|15|30)\b|sec|sek|second/i;
|
||||||
|
|
||||||
const button = candidates.find(btn => {
|
const button = candidates.find(btn => {
|
||||||
const label = getElementLabel(btn);
|
const label = getElementLabel(btn);
|
||||||
const matchesDirection = backward ? backRe.test(label) : forwardRe.test(label);
|
const matchesDirection = backward ? backRe.test(label) : forwardRe.test(label);
|
||||||
@@ -294,18 +298,27 @@
|
|||||||
return backward ? backRe.test(label) : forwardRe.test(label);
|
return backward ? backRe.test(label) : forwardRe.test(label);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
if (button && typeof button.click === 'function') {
|
||||||
|
const clicks = Math.max(1, Math.min(12, Math.round(Math.abs(delta) / 10)));
|
||||||
if (button && typeof button.click === 'function') {
|
for (let i = 0; i < clicks; i++) {
|
||||||
const clicks = Math.max(1, Math.min(12, Math.round(Math.abs(delta) / 10)));
|
setTimeout(() => button.click(), i * 60);
|
||||||
for (let i = 0; i < clicks; i++) {
|
}
|
||||||
setTimeout(() => button.click(), i * 60);
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
// Fallback: Simulate ArrowLeft / ArrowRight keyboard events
|
||||||
|
const clicks = Math.max(1, Math.min(12, Math.round(Math.abs(delta) / 10)));
|
||||||
// Fallback: Simulate ArrowLeft / ArrowRight keyboard events
|
const eventInit = {
|
||||||
const clicks = Math.max(1, Math.min(12, Math.round(Math.abs(delta) / 10)));
|
key: backward ? 'ArrowLeft' : 'ArrowRight',
|
||||||
|
code: backward ? 'ArrowLeft' : 'ArrowRight',
|
||||||
|
keyCode: backward ? 37 : 39,
|
||||||
|
which: backward ? 37 : 39,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
view: window
|
||||||
|
};
|
||||||
|
const target = document.querySelector('.hive-video') || document.activeElement || document.body;
|
||||||
|
for (let i = 0; i < clicks; i++) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
target.dispatchEvent(new window.KeyboardEvent('keydown', eventInit));
|
target.dispatchEvent(new window.KeyboardEvent('keydown', eventInit));
|
||||||
target.dispatchEvent(new window.KeyboardEvent('keyup', eventInit));
|
target.dispatchEvent(new window.KeyboardEvent('keyup', eventInit));
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ function loadTimelineFns(hostname, document = makeDocument()) {
|
|||||||
'let lastKnownDisneyPlusDuration = 0;',
|
'let lastKnownDisneyPlusDuration = 0;',
|
||||||
'let lastKnownDisneyPlusScale = 1;',
|
'let lastKnownDisneyPlusScale = 1;',
|
||||||
'let lastKnownDisneyPlusStart = 0;',
|
'let lastKnownDisneyPlusStart = 0;',
|
||||||
|
'let lastDisneyPlusUiCurrent = null;',
|
||||||
|
'let lastDisneyPlusNativeAtUi = null;',
|
||||||
extractFunction('scanShadowDom', source),
|
extractFunction('scanShadowDom', source),
|
||||||
extractFunction('querySelectorAllShadow', source),
|
extractFunction('querySelectorAllShadow', source),
|
||||||
extractFunction('hostMatchesUrl', source),
|
extractFunction('hostMatchesUrl', source),
|
||||||
|
|||||||
Reference in New Issue
Block a user