123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- import { getVideoTime, info, log, warn } from "../utils";
- import type { Domain } from "../types";
- import { getFeatures } from "../config";
- //#MARKER arrow key skip
- export function initArrowKeySkip() {
- document.addEventListener("keydown", onKeyDown);
- log("Added key press listener");
- }
- /** Called when the user presses any key, anywhere */
- function onKeyDown(evt: KeyboardEvent) {
- if(["ArrowLeft", "ArrowRight"].includes(evt.code)) {
- // discard the event when a (text) input is currently active, like when editing a playlist
- if(["INPUT", "TEXTAREA", "SELECT"].includes(document.activeElement?.tagName ?? "_"))
- return info(`Captured valid key but the current active element is <${document.activeElement!.tagName.toLowerCase()}>, so the keypress is ignored`);
- log(`Captured key '${evt.code}' in proxy listener`);
- // ripped this stuff from the console, most of these are probably unnecessary but this was finnicky af and I am sick and tired of trial and error
- const defaultProps = {
- altKey: false,
- ctrlKey: false,
- metaKey: false,
- shiftKey: false,
- target: document.body,
- currentTarget: document.body,
- originalTarget: document.body,
- explicitOriginalTarget: document.body,
- srcElement: document.body,
- type: "keydown",
- bubbles: true,
- cancelBubble: false,
- cancelable: true,
- isTrusted: true,
- repeat: false,
- view: unsafeWindow,
- };
- let invalidKey = false;
- let keyProps = {};
- switch(evt.code) {
- case "ArrowLeft":
- keyProps = {
- code: "KeyH",
- key: "h",
- keyCode: 72,
- which: 72,
- };
- break;
- case "ArrowRight":
- keyProps = {
- code: "KeyL",
- key: "l",
- keyCode: 76,
- which: 76,
- };
- break;
- default:
- invalidKey = true;
- break;
- }
- if(!invalidKey) {
- // TODO: check if the code prop is correct
- const proxyProps = { code: "", ...defaultProps, ...keyProps };
- document.body.dispatchEvent(new KeyboardEvent("keydown", proxyProps));
- log(`Dispatched proxy keydown event: [${evt.code}] -> [${proxyProps.code}]`);
- }
- else
- warn(`Captured key '${evt.code}' has no defined behavior`);
- }
- }
- //#MARKER site switch
- /** Initializes the site switch feature */
- export function initSiteSwitch(domain: Domain) {
- // TODO:
- // extra features:
- // - keep video time
- // - configurable hotkey
- document.addEventListener("keydown", (e) => {
- if(e.key === "F9")
- switchSite(domain === "yt" ? "ytm" : "yt");
- });
- log("Initialized site switch listener");
- }
- /** Switches to the other site (between YT and YTM) */
- function switchSite(newDomain: Domain) {
- try {
- let subdomain;
- if(newDomain === "ytm")
- subdomain = "music";
- else if(newDomain === "yt")
- subdomain = "www";
- if(!subdomain)
- throw new TypeError(`Unrecognized domain '${newDomain}'`);
- const { pathname, search, hash } = new URL(location.href);
- const vt = getVideoTime() ?? 0;
- log(`Found video time of ${vt} seconds`);
- const newSearch = search.includes("?") ? `${search}&t=${vt}` : `?t=${vt}`;
- const url = `https://${subdomain}.youtube.com${pathname}${newSearch}${hash}`;
- console.info(`BetterYTM - switching to domain '${newDomain}' at ${url}`);
- disableBeforeUnload();
- setTimeout(() => { location.href = url; }, 0);
- }
- catch(err) {
- console.error("Error while switching site:", err);
- }
- }
- //#MARKER beforeunload popup
- let beforeUnloadEnabled = true;
- /** Disables the popup before leaving the site */
- export function disableBeforeUnload() {
- beforeUnloadEnabled = false;
- info("Disabled popup before leaving the site");
- }
- /** (Re-)enables the popup before leaving the site */
- export function enableBeforeUnload() {
- beforeUnloadEnabled = true;
- info("Enabled popup before leaving the site");
- }
- /** Adds a spy function into `window.__proto__.addEventListener` to selectively discard events before they can be captured by the original site's listeners */
- export function initBeforeUnloadHook() {
- Error.stackTraceLimit = Infinity;
- (function(original) {
- // @ts-ignore
- window.__proto__.addEventListener = function(...args) {
- const [type, listener, ...rest] = args;
- if(type === "beforeunload") {
- return original.apply(this, [
- type,
- // @ts-ignore
- (...a) => {
- if(beforeUnloadEnabled)
- listener(...a);
- },
- ...rest,
- ]);
- }
- else
- return original.apply(this, args);
- };
- // @ts-ignore
- })(window.__proto__.addEventListener);
- getFeatures().then(feats => {
- if(feats.disableBeforeUnloadPopup)
- disableBeforeUnload();
- });
- // (function(original) {
- // window.__proto__.removeEventListener = function(type, listener, useCapture) {
- // if(evtNames.includes(type)){
- // console.log("------> removeEventListener " + type, listener, useCapture);
- // }
- // return original.apply(this, arguments);
- // };
- // })(window.__proto__.removeEventListener);
- }
|