374 lines
12 KiB
JavaScript
374 lines
12 KiB
JavaScript
//@ts-check
|
|
|
|
import JadefinIntegrity from '../JadefinIntegrity.js';
|
|
|
|
import Jadefin from "../Jadefin.js";
|
|
import JadefinMod from "../JadefinMod.js";
|
|
import JadefinModules from "../JadefinModules.js";
|
|
import JadefinUtils from "../JadefinUtils.js";
|
|
|
|
import { rd, rdom, rd$, RDOMListHelper } from "../utils/rdom.js";
|
|
|
|
export default JadefinIntegrity("InputEater", import.meta.url, () => new (class InputEater extends JadefinMod {
|
|
hasShownPopupThisSession = false;
|
|
canBeEating = false;
|
|
|
|
_isEnabled = true;
|
|
_isEating = false;
|
|
|
|
/** @type {Element | undefined} */
|
|
_lastOsdPage;
|
|
|
|
_btnRemote = rd$()`
|
|
<button is="paper-icon-button-light" class="btnRemote autoSize paper-icon-button-light" title="Grab remote">
|
|
<span class="xlargePaperIconButton material-icons settings_remote" aria-hidden="true"></span>
|
|
</button>
|
|
`;
|
|
|
|
constructor() {
|
|
super();
|
|
|
|
this._btnRemote.addEventListener("click", () => this.isEnabled = false);
|
|
|
|
this.initHookDocumentAddEventListener();
|
|
this.initHookDocumentRemoveEventListener();
|
|
}
|
|
|
|
get isEnabled() {
|
|
return this._isEnabled;
|
|
}
|
|
|
|
set isEnabled(value) {
|
|
this._isEnabled = value;
|
|
|
|
this.updateIsEating();
|
|
}
|
|
|
|
get shouldShowPopup() {
|
|
return this.storage.get("shouldShowPopup", true);
|
|
}
|
|
|
|
set shouldShowPopup(value) {
|
|
this.storage.set("shouldShowPopup", value);
|
|
}
|
|
|
|
get isEating() {
|
|
return this._isEating;
|
|
}
|
|
|
|
set isEating(value) {
|
|
if (this._isEating == value) {
|
|
return;
|
|
}
|
|
|
|
this.log.i(`isEating = ${value}`);
|
|
this._isEating = value;
|
|
|
|
this.update();
|
|
}
|
|
|
|
async init(name, url) {
|
|
await super.init(name, url);
|
|
|
|
await JadefinUtils.waitUntil(() => JadefinModules.syncPlay);
|
|
|
|
await this.initStyle();
|
|
this.initHookSyncPlayEnabled();
|
|
this.initHookSyncPlayDisabled();
|
|
this.initHookMediaSessionHandlers();
|
|
this.initHookInputManagerHandler();
|
|
|
|
document.addEventListener("viewshow", () => {
|
|
this.updateIsEating();
|
|
});
|
|
|
|
const ExtrasMenu = /** @type {import("./ExtrasMenu.js").default} */ (Jadefin.getMod("ExtrasMenu"));
|
|
|
|
ExtrasMenu.items.push({
|
|
name: "Grab remote",
|
|
secondaryText: "Take control over the party",
|
|
icon: "settings_remote",
|
|
in: ExtrasMenu.IN_CUSTOM,
|
|
inCustom: (current, item) => {
|
|
if (this.isEnabled) {
|
|
item.name = "Grab remote";
|
|
item.secondaryText = "Take control over the party";
|
|
} else {
|
|
item.name = "Disable controls";
|
|
item.secondaryText = "Give up control over the party";
|
|
}
|
|
|
|
return this.canBeEating;
|
|
},
|
|
cb: () => {
|
|
this.isEnabled = !this.isEnabled;
|
|
}
|
|
});
|
|
|
|
this.log.i("Ready");
|
|
}
|
|
|
|
initHookSyncPlayEnabled() {
|
|
const orig = this._enableSyncPlay = JadefinModules.syncPlay.Manager.enableSyncPlay.bind(JadefinModules.syncPlay.Manager);
|
|
JadefinModules.syncPlay.Manager.enableSyncPlay = (apiClient, groupInfo, showMessage) => {
|
|
const rv = orig(apiClient, groupInfo, showMessage);
|
|
|
|
this.updateIsEating();
|
|
|
|
return rv;
|
|
};
|
|
}
|
|
|
|
initHookSyncPlayDisabled() {
|
|
const orig = this._disableSyncPlay = JadefinModules.syncPlay.Manager.disableSyncPlay.bind(JadefinModules.syncPlay.Manager);
|
|
JadefinModules.syncPlay.Manager.disableSyncPlay = (showMessage) => {
|
|
const rv = orig(showMessage);
|
|
|
|
this.updateIsEating();
|
|
|
|
return rv;
|
|
};
|
|
}
|
|
|
|
initHookDocumentAddEventListener() {
|
|
const orig = this._addEventListener = document.addEventListener.bind(document);
|
|
document.addEventListener = (type, listener, options) => {
|
|
if (type == "keydown") {
|
|
const listenerStr = listener.toString();
|
|
|
|
// Anonymous function in playback-video
|
|
if (listenerStr.indexOf(".btnPause") != -1 &&
|
|
listenerStr.indexOf("32") != -1 &&
|
|
listenerStr.indexOf("playPause") != -1) {
|
|
this.log.i("Wrapping playback-video keydown listener");
|
|
this.log.dir(listener);
|
|
|
|
const origListener = this._playbackKeyDown = listener;
|
|
listener = this._playbackKeyDownWrap = (e) => {
|
|
if (this.isEating) {
|
|
if (e.keyCode == 32) {
|
|
return;
|
|
}
|
|
|
|
switch (e.key) {
|
|
case "ArrowLeft":
|
|
case "ArrowRight":
|
|
case "Enter":
|
|
case "Escape":
|
|
case "Back":
|
|
case "k":
|
|
case "l":
|
|
case "ArrowRight":
|
|
case "Right":
|
|
case "j":
|
|
case "ArrowLeft":
|
|
case "Left":
|
|
case "p":
|
|
case "P":
|
|
case "n":
|
|
case "N":
|
|
case "NavigationLeft":
|
|
case "GamepadDPadLeft":
|
|
case "GamepadLeftThumbstickLeft":
|
|
case "NavigationRight":
|
|
case "GamepadDPadRight":
|
|
case "GamepadLeftThumbstickRight":
|
|
case "Home":
|
|
case "End":
|
|
case "0":
|
|
case "1":
|
|
case "2":
|
|
case "3":
|
|
case "4":
|
|
case "5":
|
|
case "6":
|
|
case "7":
|
|
case "8":
|
|
case "9":
|
|
case ">":
|
|
case "<":
|
|
case "PageUp":
|
|
case "PageDown":
|
|
return;
|
|
}
|
|
}
|
|
|
|
return origListener(e);
|
|
};
|
|
}
|
|
}
|
|
|
|
return orig(type, listener, options);
|
|
};
|
|
}
|
|
|
|
initHookDocumentRemoveEventListener() {
|
|
const orig = this._removeEventListener = document.removeEventListener.bind(document);
|
|
document.removeEventListener = (type, listener, options) => {
|
|
if (listener == this._playbackKeyDown) {
|
|
listener = this._playbackKeyDownWrap;
|
|
}
|
|
|
|
return orig(type, listener, options);
|
|
};
|
|
}
|
|
|
|
initHookMediaSessionHandlers() {
|
|
const owner = Jadefin.findWebpackRawLoad(e => JadefinUtils.findDeep(e, 1, (_, o) => o.nextTrack && o.fastForward));
|
|
|
|
if (!owner || !("mediaSession" in navigator)) {
|
|
this.log.e("Couldn't hook media session handlers");
|
|
return;
|
|
}
|
|
|
|
const basic = (name, e) => {
|
|
if (this.isEating) {
|
|
return;
|
|
}
|
|
|
|
owner[name](owner.getCurrentPlayer());
|
|
}
|
|
|
|
const handlerTuples = [
|
|
["previoustrack", (e) => basic("previousTrack", e)],
|
|
["nexttrack", (e) => basic("nextTrack", e)],
|
|
["play", (e) => basic("unpause", e)],
|
|
["pause", (e) => basic("pause", e)],
|
|
["seekbackward", (e) => basic("rewind", e)],
|
|
["seekforward", (e) => basic("fastForward", e)],
|
|
["seekto", (e) => {
|
|
if (this.isEating) {
|
|
return;
|
|
}
|
|
|
|
const player = owner.getCurrentPlayer();
|
|
const item = owner.getPlayerState(player).NowPlayingItem;
|
|
const currentTime = Math.floor(item.RunTimeTicks ? item.RunTimeTicks / 10000 : 0);
|
|
const seekTime = 1000 * e.seekTime;
|
|
owner.seekPercent(seekTime / currentTime * 100, player);
|
|
}]
|
|
];
|
|
|
|
const handlers = this._handlers = {};
|
|
|
|
for (let handlerTuple of handlerTuples) {
|
|
this.log.i(`Replacing media session action handler ${handlerTuple[0]}`);
|
|
// @ts-ignore
|
|
navigator.mediaSession.setActionHandler(handlerTuple[0], handlers[handlerTuple[0]] = handlerTuple[1].bind(this));
|
|
}
|
|
}
|
|
|
|
initHookInputManagerHandler() {
|
|
const orig = this._handleCommand = JadefinModules.inputManager.handleCommand.bind(JadefinModules.inputManager);
|
|
JadefinModules.inputManager.handleCommand = (commandName, options) => {
|
|
if (this.isEating) {
|
|
switch (commandName) {
|
|
case "nextchapter":
|
|
case "next":
|
|
case "nexttrack":
|
|
case "previous":
|
|
case "previoustrack":
|
|
case "previouschapter":
|
|
case "play":
|
|
case "pause":
|
|
case "playpause":
|
|
case "stop":
|
|
case "increaseplaybackrate":
|
|
case "decreaseplaybackrate":
|
|
case "fastforward":
|
|
case "rewind":
|
|
case "seek":
|
|
case "repeatnone":
|
|
case "repeatall":
|
|
case "repeatone":
|
|
return;
|
|
}
|
|
}
|
|
|
|
return orig(commandName, options);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} [force]
|
|
*/
|
|
showPopup(force) {
|
|
if (!force && (!this.shouldShowPopup || this.hasShownPopupThisSession)) {
|
|
return false;
|
|
}
|
|
|
|
this.hasShownPopupThisSession = true;
|
|
|
|
const ExtrasMenu = /** @type {import("./ExtrasMenu.js").default} */ (Jadefin.getMod("ExtrasMenu"));
|
|
|
|
JadefinModules.actionSheet.show({
|
|
dialogClass: "inputEaterInfo",
|
|
positionTo: ExtrasMenu.headerExtrasEl,
|
|
title: "Controls disabled.",
|
|
text: "You can toggle it in this corner.",
|
|
items: [
|
|
{name: "Okay", icon: "tune"}
|
|
]
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
async updateIsEating() {
|
|
let isSyncPlayEnabled = JadefinModules.syncPlay.Manager.isSyncPlayEnabled();
|
|
let isSyncPlayOwner = true;
|
|
|
|
if (isSyncPlayEnabled) {
|
|
const groupInfo = JadefinModules.syncPlay.Manager.groupInfo;
|
|
const currentUser = await JadefinModules.ApiClient.getCurrentUser();
|
|
|
|
isSyncPlayOwner = groupInfo.GroupName.indexOf(currentUser.Name) != -1;
|
|
}
|
|
|
|
this.canBeEating = isSyncPlayEnabled && !isSyncPlayOwner && JadefinUtils.routePathIsVideo;
|
|
this.isEating = this.isEnabled && this.canBeEating;
|
|
}
|
|
|
|
update() {
|
|
document.body.setAttribute("input-eaten", this.isEating ? "true" : "false");
|
|
|
|
if (this.isEating) {
|
|
this.canBeEating = true;
|
|
|
|
if (!this.showPopup()) {
|
|
JadefinModules.toast("Controls disabled.");
|
|
}
|
|
}
|
|
|
|
this._btnRemote.classList.toggle("hide", !this.isEating);
|
|
|
|
const videoOsdPage = document.querySelector("div#videoOsdPage:not(.hide)");
|
|
if (!videoOsdPage) {
|
|
return;
|
|
}
|
|
|
|
if (this._lastOsdPage == videoOsdPage) {
|
|
return;
|
|
}
|
|
this._lastOsdPage = videoOsdPage;
|
|
|
|
this.log.i("Adding event listener to videoOsdPage");
|
|
this.log.dir(videoOsdPage);
|
|
|
|
videoOsdPage.addEventListener(window.PointerEvent ? "pointerdown" : "click", e => {
|
|
if (this.isEating) {
|
|
e.stopPropagation();
|
|
}
|
|
}, true);
|
|
|
|
const buttons = videoOsdPage.querySelector(".osdControls > .buttons");
|
|
if (this._btnRemote.parentElement != buttons && buttons) {
|
|
this.log.i("Adding remote button to osd buttons");
|
|
this.log.dir(buttons);
|
|
|
|
buttons.insertBefore(this._btnRemote, buttons.firstChild);
|
|
}
|
|
}
|
|
|
|
})());
|