Allow the intro skip button to be confirmed with the Enter key (#189)

* allow the intro skip button to be confirmed with the Enter key

tested with LG webOS

* add some comments

* Update inject.js (#173)

* Update inject.js

* Update inject.js

---------

Co-authored-by: Kilian von Pflugk <github@jumoog.io>
Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com>
This commit is contained in:
rlauuzo 2024-06-01 14:15:30 +02:00 committed by GitHub
parent 87272075ed
commit 50529c4a0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 77 additions and 13 deletions

View File

@ -1,4 +1,5 @@
let introSkipper = { let introSkipper = {
allowEnter: true,
skipSegments: {}, skipSegments: {},
videoPlayer: {}, videoPlayer: {},
// .bind() is used here to prevent illegal invocation errors // .bind() is used here to prevent illegal invocation errors
@ -60,6 +61,7 @@ introSkipper.d = function (msg) {
if (introSkipper.videoPlayer != null) { if (introSkipper.videoPlayer != null) {
introSkipper.d("Hooking video timeupdate"); introSkipper.d("Hooking video timeupdate");
introSkipper.videoPlayer.addEventListener("timeupdate", introSkipper.videoPositionChanged); introSkipper.videoPlayer.addEventListener("timeupdate", introSkipper.videoPositionChanged);
document.body.addEventListener('keydown', introSkipper.eventHandler, true);
} }
} }
/** /**
@ -169,13 +171,28 @@ introSkipper.getCurrentSegment = function (position) {
} }
return { "SegmentType": "None" }; return { "SegmentType": "None" };
} }
introSkipper.overrideBlur = function(embyButton) {
if (!embyButton.originalBlur) {
embyButton.originalBlur = embyButton.blur;
}
embyButton.blur = function () {
if (!introSkipper.osdVisible() || !embyButton.contains(document.activeElement)) {
embyButton.originalBlur.call(this);
}
};
};
introSkipper.restoreBlur = function(embyButton) {
if (embyButton.originalBlur) {
embyButton.blur = embyButton.originalBlur;
delete embyButton.originalBlur;
}
};
/** Playback position changed, check if the skip button needs to be displayed. */ /** Playback position changed, check if the skip button needs to be displayed. */
introSkipper.videoPositionChanged = function () { introSkipper.videoPositionChanged = function () {
const skipButton = document.querySelector("#skipIntro"); const skipButton = document.querySelector("#skipIntro");
if (!skipButton) { if (introSkipper.videoPlayer.currentTime === 0 || !skipButton || !introSkipper.allowEnter) return;
return;
}
const embyButton = skipButton.querySelector(".emby-button"); const embyButton = skipButton.querySelector(".emby-button");
const tvLayout = document.documentElement.classList.contains("layout-tv");
const segment = introSkipper.getCurrentSegment(introSkipper.videoPlayer.currentTime); const segment = introSkipper.getCurrentSegment(introSkipper.videoPlayer.currentTime);
switch (segment.SegmentType) { switch (segment.SegmentType) {
case "None": case "None":
@ -184,6 +201,10 @@ introSkipper.videoPositionChanged = function () {
embyButton.style.opacity = '0'; embyButton.style.opacity = '0';
embyButton.addEventListener("transitionend", () => { embyButton.addEventListener("transitionend", () => {
skipButton.classList.add("hide"); skipButton.classList.add("hide");
if (tvLayout) {
introSkipper.restoreBlur(embyButton);
embyButton.blur();
}
}, { once: true }); }, { once: true });
return; return;
case "Introduction": case "Introduction":
@ -196,22 +217,44 @@ introSkipper.videoPositionChanged = function () {
if (!skipButton.classList.contains("hide")) return; if (!skipButton.classList.contains("hide")) return;
skipButton.classList.remove("hide"); skipButton.classList.remove("hide");
embyButton.offsetWidth; // Force reflow embyButton.style.opacity = '1';
requestAnimationFrame(() => {
embyButton.style.opacity = '1'; if (tvLayout) {
}); introSkipper.overrideBlur(embyButton);
embyButton.focus({ focusVisible: true });
}
}
introSkipper.throttle = function (func, limit) {
let inThrottle;
return function(...args) {
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
} }
/** Seeks to the end of the intro. */ /** Seeks to the end of the intro. */
introSkipper.doSkip = function (e) { introSkipper.doSkip = introSkipper.throttle(function (e) {
introSkipper.d("Skipping intro"); introSkipper.d("Skipping intro");
introSkipper.d(introSkipper.skipSegments); introSkipper.d(introSkipper.skipSegments);
const segment = introSkipper.getCurrentSegment(introSkipper.videoPlayer.currentTime); const segment = introSkipper.getCurrentSegment(introSkipper.videoPlayer.currentTime);
if (segment["SegmentType"] === "None") { if (segment.SegmentType === "None") {
console.warn("[intro skipper] doSkip() called without an active segment"); console.warn("[intro skipper] doSkip() called without an active segment");
return; return;
} }
introSkipper.videoPlayer.currentTime = segment["IntroEnd"]; // Disable keydown events
} introSkipper.allowEnter = false;
introSkipper.videoPlayer.currentTime = segment.IntroEnd;
// Listen for the seeked event to re-enable keydown events
const onSeeked = async () => {
await new Promise(resolve => setTimeout(resolve, 50)); // Wait 50ms
introSkipper.allowEnter = true;
introSkipper.videoPlayer.removeEventListener('seeked', onSeeked);
};
introSkipper.videoPlayer.addEventListener('seeked', onSeeked);
}, 3000);
/** Tests if an element with the provided selector exists. */ /** Tests if an element with the provided selector exists. */
introSkipper.testElement = function (selector) { return document.querySelector(selector); } introSkipper.testElement = function (selector) { return document.querySelector(selector); }
/** Make an authenticated fetch to the Jellyfin server and parse the response body as JSON. */ /** Make an authenticated fetch to the Jellyfin server and parse the response body as JSON. */
@ -222,4 +265,25 @@ introSkipper.secureFetch = async function (url) {
if (res.status !== 200) { throw new Error(`Expected status 200 from ${url}, but got ${res.status}`); } if (res.status !== 200) { throw new Error(`Expected status 200 from ${url}, but got ${res.status}`); }
return await res.json(); return await res.json();
} }
/** Handle keydown events. */
introSkipper.eventHandler = function (e) {
const skipButton = document.querySelector("#skipIntro");
if (!skipButton || skipButton.classList.contains("hide")) return;
// Ignore all keydown events
if (!introSkipper.allowEnter) {
e.preventDefault();
return;
}
if (e.key !== "Enter") return;
const embyButton = skipButton.querySelector(".emby-button");
if (document.documentElement.classList.contains("layout-tv") && embyButton.contains(document.activeElement)) {
e.stopPropagation();
return;
}
if (document.documentElement.classList.contains("layout-desktop")) {
e.preventDefault();
e.stopPropagation();
introSkipper.doSkip();
}
}
introSkipper.setup(); introSkipper.setup();

View File

@ -92,14 +92,14 @@ public class SkipIntroController : ControllerBase
if (config.PersistSkipButton) if (config.PersistSkipButton)
{ {
segment.ShowSkipPromptAt = segment.IntroStart; segment.ShowSkipPromptAt = segment.IntroStart;
segment.HideSkipPromptAt = segment.IntroEnd - 1; segment.HideSkipPromptAt = segment.IntroEnd;
} }
else else
{ {
segment.ShowSkipPromptAt = Math.Max(0, segment.IntroStart - config.ShowPromptAdjustment); segment.ShowSkipPromptAt = Math.Max(0, segment.IntroStart - config.ShowPromptAdjustment);
segment.HideSkipPromptAt = Math.Min( segment.HideSkipPromptAt = Math.Min(
segment.IntroStart + config.HidePromptAdjustment, segment.IntroStart + config.HidePromptAdjustment,
segment.IntroEnd - 1); segment.IntroEnd);
} }
return segment; return segment;