mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-28 11:37:16 +00:00
Sync Disney+ via the page media player API (precise time + seek)
Disney+'s <video> is blob-relative (unusable as an absolute clock) and its scrubber aria-value freezes during playback, so DOM scraping lagged and the +/-10s button seek could neither reach far targets nor land precisely. The real player hangs off the <disney-web-player> custom element as `.mediaPlayer`, exposing seek(ms) and timeline.info (playhead/duration ms). Since the isolated content world can't read that page object, route it through the existing MAIN-world page-API bridge (as Netflix already does): - page-api-seek-overrides.js: register a 'disney' provider. - background.js installPageApiSeekBridge: seek Disney via mediaPlayer.seek(), and post the exact playhead/duration (seconds) to the content world every 250ms. Both are gated on provider === 'disney'; Netflix path unchanged. - content.js: cache the pushed playhead, prefer it in getDisneyPlusTimeline (DOM scraping stays as fallback), and check the page-API seek first in seekVideo. Outcome is identical for Netflix and generic sites. Verified live on Disney+: reported time matches the player exactly and seek lands within ~1s of the target. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+45
-22
@@ -63,7 +63,21 @@
|
||||
// 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.
|
||||
|
||||
// Timers self-clean after 300ms if the native event never fires.
|
||||
|
||||
let _suppressTimers = {};
|
||||
|
||||
|
||||
|
||||
function _setSuppress(state) {
|
||||
|
||||
if (_suppressTimers[state]) clearTimeout(_suppressTimers[state]);
|
||||
|
||||
_suppressTimers[state] = setTimeout(() => {
|
||||
|
||||
delete _suppressTimers[state];
|
||||
|
||||
}, 300);
|
||||
@@ -276,7 +290,14 @@
|
||||
function getDisneyPlusSeekButtonLabels() {
|
||||
if (!isDisneyPlusHost() || typeof document === 'undefined') return [];
|
||||
return querySelectorAllShadow('button,[role="button"]')
|
||||
.map(getElementLabel)
|
||||
.map(getElementLabel)
|
||||
.filter(Boolean)
|
||||
.slice(0, 20);
|
||||
}
|
||||
|
||||
function clickDisneyPlusRelativeSeek(delta) {
|
||||
if (!isDisneyPlusHost() || !Number.isFinite(delta) || Math.abs(delta) < 1 || typeof document === 'undefined') return false;
|
||||
const backward = delta < 0;
|
||||
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 forwardRe = /forward|ahead|skip|vorwärts|vorwaerts|vorsp|weiter|avancer|adelant|avançar|avancar|avanti/i;
|
||||
@@ -398,26 +419,28 @@
|
||||
name: 'disneyplus-timeline-and-buttons',
|
||||
key: 'disneyPlus',
|
||||
urls: ['disneyplus.com'],
|
||||
matches() { return matchesPlayerUrls(this.urls); },
|
||||
getTimeline: getDisneyPlusTimeline,
|
||||
clickRelativeSeek: clickDisneyPlusRelativeSeek,
|
||||
getDebug(video) {
|
||||
return {
|
||||
name: this.name,
|
||||
key: 'disneyPlus',
|
||||
urls: this.urls,
|
||||
timeline: getDisneyPlusTimeline(video),
|
||||
timelineCandidates: lastDisneyPlusTimelineCandidates,
|
||||
seekButtons: getDisneyPlusSeekButtonLabels()
|
||||
};
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
function getActiveSiteQuirk() {
|
||||
return getSiteQuirkAdapters().find(adapter => adapter.matches()) || null;
|
||||
}
|
||||
|
||||
matches() { return matchesPlayerUrls(this.urls); },
|
||||
getTimeline: getDisneyPlusTimeline,
|
||||
clickRelativeSeek: clickDisneyPlusRelativeSeek,
|
||||
getDebug(video) {
|
||||
return {
|
||||
name: this.name,
|
||||
key: 'disneyPlus',
|
||||
urls: this.urls,
|
||||
timeline: getDisneyPlusTimeline(video),
|
||||
timelineCandidates: lastDisneyPlusTimelineCandidates,
|
||||
seekButtons: getDisneyPlusSeekButtonLabels()
|
||||
};
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
function getActiveSiteQuirk() {
|
||||
return getSiteQuirkAdapters().find(adapter => adapter.matches()) || null;
|
||||
}
|
||||
|
||||
function getSiteQuirkTimeline(video) {
|
||||
const adapter = getActiveSiteQuirk();
|
||||
return adapter ? adapter.getTimeline(video) : null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user