123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- import { clamp } from "@sv443-network/userutils";
- import { error, getVideoTime, info, log, warn, getVideoSelector } from "../utils";
- import type { Domain } from "../types";
- import { isCfgMenuOpen } from "../menu/menu_old";
- import { disableBeforeUnload } from "./behavior";
- import { siteEvents } from "../siteEvents";
- import { featInfo } from "./index";
- import { getFeatures } from "../config";
- export const inputIgnoreTagNames = ["INPUT", "TEXTAREA", "SELECT", "BUTTON", "A"];
- //#region arrow key skip
- export async function initArrowKeySkip() {
- document.addEventListener("keydown", (evt) => {
- if(!getFeatures().arrowKeySupport)
- return;
- if(!["ArrowLeft", "ArrowRight"].includes(evt.code))
- return;
- const allowedClasses = ["bytm-generic-btn", "yt-spec-button-shape-next"];
- // discard the event when a (text) input is currently active, like when editing a playlist
- if(
- (inputIgnoreTagNames.includes(document.activeElement?.tagName ?? "") || ["volume-slider"].includes(document.activeElement?.id ?? ""))
- && !allowedClasses.some((cls) => document.activeElement?.classList.contains(cls))
- )
- return info(`Captured valid key to skip forward or backward but the current active element is <${document.activeElement?.tagName.toLowerCase()}>, so the keypress is ignored`);
- evt.preventDefault();
- evt.stopImmediatePropagation();
- let skipBy = getFeatures().arrowKeySkipBy ?? featInfo.arrowKeySkipBy.default;
- if(evt.code === "ArrowLeft")
- skipBy *= -1;
- log(`Captured arrow key '${evt.code}' - skipping by ${skipBy} seconds`);
-
- const vidElem = document.querySelector<HTMLVideoElement>(getVideoSelector());
-
- if(vidElem)
- vidElem.currentTime = clamp(vidElem.currentTime + skipBy, 0, vidElem.duration);
- });
- log("Added arrow key press listener");
- }
- //#region site switch
- /** switch sites only if current video time is greater than this value */
- const videoTimeThreshold = 3;
- let siteSwitchEnabled = true;
- /** Initializes the site switch feature */
- export async function initSiteSwitch(domain: Domain) {
- document.addEventListener("keydown", (e) => {
- if(!getFeatures().switchBetweenSites)
- return;
- const hk = getFeatures().switchSitesHotkey;
- if(siteSwitchEnabled && e.code === hk.code && e.shiftKey === hk.shift && e.ctrlKey === hk.ctrl && e.altKey === hk.alt)
- switchSite(domain === "yt" ? "ytm" : "yt");
- });
- siteEvents.on("hotkeyInputActive", (state) => {
- if(!getFeatures().switchBetweenSites)
- return;
- siteSwitchEnabled = !state;
- });
- log("Initialized site switch listener");
- }
- /** Switches to the other site (between YT and YTM) */
- async function switchSite(newDomain: Domain) {
- try {
- if(!(["/watch", "/playlist"].some(v => location.pathname.startsWith(v))))
- return warn("Not on a supported page, so the site switch is ignored");
- let subdomain;
- if(newDomain === "ytm")
- subdomain = "music";
- else if(newDomain === "yt")
- subdomain = "www";
- if(!subdomain)
- throw new Error(`Unrecognized domain '${newDomain}'`);
- disableBeforeUnload();
- const { pathname, search, hash } = new URL(location.href);
- const vt = await getVideoTime(0);
- log(`Found video time of ${vt} seconds`);
- const cleanSearch = search.split("&")
- .filter((param) => !param.match(/^\??t=/))
- .join("&");
- const newSearch = typeof vt === "number" && vt > videoTimeThreshold ?
- cleanSearch.includes("?")
- ? `${cleanSearch.startsWith("?")
- ? cleanSearch
- : "?" + cleanSearch
- }&t=${vt}`
- : `?t=${vt}`
- : cleanSearch;
- const newUrl = `https://${subdomain}.youtube.com${pathname}${newSearch}${hash}`;
- info(`Switching to domain '${newDomain}' at ${newUrl}`);
- location.assign(newUrl);
- }
- catch(err) {
- error("Error while switching site:", err);
- }
- }
- //#region num keys skip
- const numKeysIgnoreTagNames = [...inputIgnoreTagNames, "TP-YT-PAPER-TAB"];
- const numKeysIgnoreIds = ["progress-bar", "song-media-window"];
- /** Adds the ability to skip to a certain time in the video by pressing a number key (0-9) */
- export async function initNumKeysSkip() {
- document.addEventListener("keydown", (e) => {
- if(!getFeatures().numKeysSkipToTime)
- return;
- if(!e.key.trim().match(/^[0-9]$/))
- return;
- if(isCfgMenuOpen)
- return;
- // discard the event when an unexpected element is currently active or in focus, like when editing a playlist or when the search bar is focused
- if(
- document.activeElement !== document.body // short-circuit if nothing is active
- || numKeysIgnoreIds.includes(document.activeElement?.id ?? "") // video element or player bar active
- || numKeysIgnoreTagNames.includes(document.activeElement?.tagName ?? "") // other element active
- )
- return info("Captured valid key to skip video to, but ignored it since an unexpected element is active:", document.activeElement);
- const vidElem = document.querySelector<HTMLVideoElement>(getVideoSelector());
- if(!vidElem)
- return warn("Could not find video element, so the keypress is ignored");
- const newVidTime = vidElem.duration / (10 / Number(e.key));
- if(!isNaN(newVidTime)) {
- log(`Captured number key [${e.key}], skipping to ${Math.floor(newVidTime / 60)}m ${(newVidTime % 60).toFixed(1)}s`);
- vidElem.currentTime = newVidTime;
- }
- });
- log("Added number key press listener");
- }
|