Explorar o código

feat: add lyrics hotkey & remove forceReboundNextAndPrevious

Sv443 hai 10 horas
pai
achega
277b28987f

+ 9 - 9
assets/translations/README.md

@@ -16,15 +16,15 @@ To submit or edit a translation, please follow [this guide](../../contributing.m
 ### Translation progress:
 |   | Locale | Translated keys | Based on |
 | :----: | ------ | --------------- | :------: |
-|  | [`en-US`](./en-US.json) | `361` (default locale) |  |
-| ✅ | [`de-DE`](./de-DE.json) | `361/361` (100%) | ─ |
-|  | [`en-GB`](./en-GB.json) | `361/361` (100%) | `en-US` |
-| ‼️ | [`es-ES`](./es-ES.json) | `342/361` (94.7%) | ─ |
-| ‼️ | [`fr-FR`](./fr-FR.json) | `342/361` (94.7%) | ─ |
-| ‼️ | [`hi-IN`](./hi-IN.json) | `342/361` (94.7%) | ─ |
-| ‼️ | [`ja-JP`](./ja-JP.json) | `342/361` (94.7%) | ─ |
-| ‼️ | [`pt-BR`](./pt-BR.json) | `342/361` (94.7%) | ─ |
-| ‼️ | [`zh-CN`](./zh-CN.json) | `342/361` (94.7%) | ─ |
+|  | [`en-US`](./en-US.json) | `364` (default locale) |  |
+| ✅ | [`de-DE`](./de-DE.json) | `364/364` (100%) | ─ |
+|  | [`en-GB`](./en-GB.json) | `364/364` (100%) | `en-US` |
+| ‼️ | [`es-ES`](./es-ES.json) | `342/364` (94%) | ─ |
+| ‼️ | [`fr-FR`](./fr-FR.json) | `342/364` (94%) | ─ |
+| ‼️ | [`hi-IN`](./hi-IN.json) | `342/364` (94%) | ─ |
+| ‼️ | [`ja-JP`](./ja-JP.json) | `342/364` (94%) | ─ |
+| ‼️ | [`pt-BR`](./pt-BR.json) | `342/364` (94%) | ─ |
+| ‼️ | [`zh-CN`](./zh-CN.json) | `342/364` (94%) | ─ |
 
 <sub>
 ✅ - Fully translated

+ 5 - 2
assets/translations/de-DE.json

@@ -342,12 +342,15 @@
   "feature_desc_likeDislikeHotkeys": "Füge Hotkeys hinzu, um das aktuell spielende Video/Lied zu liken oder disliken",
   "feature_desc_likeHotkey": "Der Hotkey, um das aktuell spielende Video/Lied zu liken",
   "feature_desc_dislikeHotkey": "Der Hotkey, um das aktuell spielende Video/Lied zu disliken",
+  "feature_desc_currentLyricsHotkeyEnabled": "Einen Hotkey hinzufügen, um den Songtext des aktuell spielenden Songs in einem neuen Tab zu öffnen",
+  "feature_desc_currentLyricsHotkey": "Der Hotkey, um den Songtext des aktuellen Songs zu öffnen",
   "feature_desc_rebindNextAndPrevious": "Die Hotkeys neu belegen, die zum nächsten (J) oder vorherigen (K) Video/Lied springen",
-  "feature_desc_forceReboundNextAndPrevious": "Erlaube nur das Springen zum nächsten oder vorherigen Video/Lied mit den neuen Hotkeys",
   "feature_desc_nextHotkey": "Der Hotkey, um zum nächsten Video/Lied zu springen",
   "feature_desc_previousHotkey": "Der Hotkey, um zum vorherigen Video/Lied zu springen",
+  "feature_desc_rebindPlayPause": "Den Hotkey neu belegen, um das aktuelle Video/Lied abzuspielen oder zu pausieren (Leertaste)",
+  "feature_desc_playPauseHotkey": "Der Hotkey zum Abspielen oder Pausieren",
 
-  "feature_desc_geniusLyrics": "Füge einen Knopf zu dem aktuell spielenden Song hinzu, um den Songtext auf genius.com zu öffnen",
+  "feature_desc_geniusLyrics": "Füge einen Knopf zu dem aktuellen Song hinzu, um den Songtext auf genius.com zu öffnen",
   "feature_desc_errorOnLyricsNotFound": "Zeige einen Error, wenn die Songtext-Seite für den aktuell spielenden Song nicht gefunden werden konnte",
   "feature_desc_geniUrlBase": "Base URL deiner geniURL Instanz, siehe https://github.com/Sv443/geniURL",
   "feature_helptext_geniUrlBase": "Wenn du deine eigene geniURL Instanz laufen hast (zum Beispiel um Rate Limiting zu umgehen), kannst du hier ihre Base URL eingeben, um sie für die genius.com Songtext-Suche zu benutzen.\nWenn du nicht weißt, was das ist, kannst du diese Option so lassen, wie sie ist.",

+ 4 - 1
assets/translations/en-US.json

@@ -342,10 +342,13 @@
   "feature_desc_likeDislikeHotkeys": "Add hotkeys to like and dislike the currently playing video/song",
   "feature_desc_likeHotkey": "The hotkey to like the current video/song",
   "feature_desc_dislikeHotkey": "The hotkey to dislike the current video/song",
+  "feature_desc_currentLyricsHotkeyEnabled": "Add a hotkey to open the current song's lyrics in a new tab",
+  "feature_desc_currentLyricsHotkey": "The hotkey to open the song's lyrics",
   "feature_desc_rebindNextAndPrevious": "Rebind the hotkeys to skip to the next (J) or previous (K) video/song",
-  "feature_desc_forceReboundNextAndPrevious": "Only allow skipping to the next or previous video/song with the new hotkeys",
   "feature_desc_nextHotkey": "The hotkey to skip to the next video/song",
   "feature_desc_previousHotkey": "The hotkey to skip to the previous video/song",
+  "feature_desc_rebindPlayPause": "Rebind the hotkey to play or pause the current video/song (Space)",
+  "feature_desc_playPauseHotkey": "The hotkey to play or pause",
 
   "feature_desc_geniusLyrics": "Add a button to the media controls of the currently playing song to open its lyrics on genius.com",
   "feature_desc_errorOnLyricsNotFound": "Show an error when the lyrics page for the currently playing song couldn't be found",

+ 4 - 2
src/config.ts

@@ -144,8 +144,10 @@ export const migrations: DataMigrationsDict = {
         "frameSkipAmount", "watchPageFullSize",
         "arrowKeyVolumeStep", "likeDislikeHotkeys",
         "likeHotkey", "dislikeHotkey",
-        "rebindNextAndPrevious", "forceReboundNextAndPrevious",
-        "nextHotkey", "previousHotkey",
+        "currentLyricsHotkeyEnabled", "currentLyricsHotkey",
+        "rebindNextAndPrevious", "nextHotkey",
+        "previousHotkey", "rebindPlayPause",
+        "playPauseHotkey",
       ]), [
         { key: "lyricsCacheMaxSize", oldDefault: 2000 },
       ],

+ 68 - 40
src/features/hotkeys.ts

@@ -12,6 +12,7 @@ export async function initHotkeys() {
   const promises: Promise<void>[] = [];
 
   promises.push(initLikeDislikeHotkeys());
+  promises.push(initLyricsHotkey());
   promises.push(initSiteSwitch());
   promises.push(initProxyHotkeys());
 
@@ -110,13 +111,32 @@ async function initLikeDislikeHotkeys() {
   });
 }
 
-//#region rebound hotkeys
+//#region lyrics
+
+async function initLyricsHotkey() {
+  document.addEventListener("keydown", (e) => {
+    if(!getFeature("currentLyricsHotkeyEnabled"))
+      return;
+    if(inputIgnoreTagNames.includes(document.activeElement?.tagName ?? ""))
+      return;
+
+    if(hotkeyMatches(e, getFeature("currentLyricsHotkey"))) {
+      e.preventDefault();
+      e.stopImmediatePropagation();
+
+      const lyricsBtn = document.getElementById("bytm-player-bar-lyrics-btn");
+      lyricsBtn?.click();
+    }
+  }, { capture: true });
+}
+
+//#region proxy hotkeys
 
 type HotkeyProxyGroup = {
   /** The feature key that contains the hotkey object */
   hkFeatKey: FeatKeysOfType<HotkeyObj>;
-  /** Called when the hotkey was pressed, regardless of the individual hotkey feature's enabled state */
-  onBeforePress?: (e: KeyboardEvent) => void | Promise<void>;
+  /** Which key should have its default action and propagation prevented */
+  preventKey?: string;
   /** Called when the hotkey was pressed and the feature is toggled on */
   onPress: (e: KeyboardEvent) => void | Promise<void>;
 };
@@ -127,64 +147,72 @@ let lastProxyHk = 0;
 
 /** Handles all proxy hotkeys, which trigger other hotkeys instead of their own actions */
 async function initProxyHotkeys() {
-  const suppressForceRebind = (e: KeyboardEvent) => {
-    if(["KeyJ", "KeyK"].includes(e.code) && getFeature("forceReboundNextAndPrevious")) {
-      e.preventDefault();
-      e.stopImmediatePropagation();
-    }
+  const dispatchProxyKey = (hkProps: Pick<KeyboardEventInit, "code" | "key" | "keyCode" | "which" | "shiftKey" | "ctrlKey" | "altKey" | "metaKey">) => {
+    document.body.dispatchEvent(new KeyboardEvent("keydown", {
+      ...hkProps,
+      bubbles: true,
+      cancelable: false,
+      view: getUnsafeWindow(),
+    }));
+    log("Dispatched proxy hotkey:", hkProps);
   };
 
   /** All proxy hotkey groups, identified by the feature key that toggles them off or on */
   const proxyHotkeys: ProxyHotkeys = {
-    "rebindNextAndPrevious": [
+    rebindNextAndPrevious: [
       {
         hkFeatKey: "nextHotkey",
-        onBeforePress: suppressForceRebind,
-        onPress() {
-          document.body.dispatchEvent(new KeyboardEvent("keydown", {
-            code: "KeyJ",
-            key: "j",
-            keyCode: 74,
-            which: 74,
-            bubbles: true,
-            cancelable: false,
-            view: getUnsafeWindow(),
-          }));
-        },
+        preventKey: "KeyJ",
+        onPress: () => dispatchProxyKey({
+          code: "KeyJ",
+          key: "j",
+          keyCode: 74,
+          which: 74,
+        }),
       },
       {
         hkFeatKey: "previousHotkey",
-        onBeforePress: suppressForceRebind,
-        onPress() {
-          document.body.dispatchEvent(new KeyboardEvent("keydown", {
-            code: "KeyK",
-            key: "k",
-            keyCode: 75,
-            which: 75,
-            bubbles: true,
-            cancelable: false,
-            view: getUnsafeWindow(),
-          }));
-        },
+        preventKey: "KeyK",
+        onPress: () => dispatchProxyKey({
+          code: "KeyK",
+          key: "k",
+          keyCode: 75,
+          which: 75,
+        }),
       },
     ],
+    rebindPlayPause: [
+      {
+        hkFeatKey: "playPauseHotkey",
+        preventKey: "Space",
+        onPress: () => dispatchProxyKey({
+          code: "Space",
+          key: " ",
+          keyCode: 32,
+          which: 32,
+        }),
+      },
+    ]
   } as const;
 
   document.addEventListener("keydown", (e) => {
-    for(const [featKey, group] of Object.entries(proxyHotkeys)) {
-      if(!getFeature(featKey as "_"))
+    for(const [featKey, proxyGroup] of Object.entries(proxyHotkeys)) {
+      if(getFeature(featKey as "_") !== true)
         continue;
 
-      for(const { hkFeatKey, onPress, ...rest } of group) {
+      for(const { hkFeatKey, onPress, ...rest } of proxyGroup) {
         // prevent hotkeys from triggering each other:
         if(Date.now() - lastProxyHk < 15) // (holding keys makes them repeat every ~30ms)
-          return;
-        lastProxyHk = Date.now();
+          continue;
+        const nowTs = Date.now();
 
-        if("onBeforePress" in rest)
-          rest.onBeforePress?.(e);
+        if("preventKey" in rest && e.code === rest.preventKey) {
+          e.preventDefault();
+          e.stopImmediatePropagation();
+        }
 
         if(hotkeyMatches(e, getFeature(hkFeatKey))) {
+          lastProxyHk = nowTs;
           !e.defaultPrevented && e.preventDefault();
           e.bubbles && e.stopImmediatePropagation();
           onPress(e);

+ 42 - 6
src/features/index.ts

@@ -733,24 +733,37 @@ export const featInfo = {
     reloadRequired: false,
     enable: noop,
   },
-  rebindNextAndPrevious: {
+  currentLyricsHotkeyEnabled: {
     type: "toggle",
     category: "hotkeys",
     supportedSites: ["ytm"],
-    default: false,
+    default: true,
     reloadRequired: false,
     enable: noop,
     textAdornment: adornments.ytmOnly,
   },
-  forceReboundNextAndPrevious: {
+  currentLyricsHotkey: {
+    type: "hotkey",
+    category: "hotkeys",
+    supportedSites: ["ytm"],
+    default: {
+      code: "KeyO",
+      shift: false,
+      ctrl: false,
+      alt: false,
+    },
+    reloadRequired: false,
+    enable: noop,
+    textAdornment: adornments.ytmOnly,
+  },
+  rebindNextAndPrevious: {
     type: "toggle",
     category: "hotkeys",
     supportedSites: ["ytm"],
-    default: true,
+    default: false,
     reloadRequired: false,
     enable: noop,
-    advanced: true,
-    textAdornment: () => combineAdornments([adornments.ytmOnly, adornments.advanced]),
+    textAdornment: adornments.ytmOnly,
   },
   nextHotkey: {
     type: "hotkey",
@@ -780,6 +793,29 @@ export const featInfo = {
     enable: noop,
     textAdornment: adornments.ytmOnly,
   },
+  rebindPlayPause: {
+    type: "toggle",
+    category: "hotkeys",
+    supportedSites: ["ytm"],
+    default: false,
+    reloadRequired: false,
+    enable: noop,
+    textAdornment: adornments.ytmOnly,
+  },
+  playPauseHotkey: {
+    type: "hotkey",
+    category: "hotkeys",
+    supportedSites: ["ytm"],
+    default: {
+      code: "Pause",
+      shift: false,
+      ctrl: false,
+      alt: false,
+    },
+    reloadRequired: false,
+    enable: noop,
+    textAdornment: adornments.ytmOnly,
+  },
 
   //#region cat:lyrics
   geniusLyrics: {

+ 8 - 2
src/types.ts

@@ -645,14 +645,20 @@ export interface FeatureConfig {
   likeHotkey: HotkeyObj;
   /** The hotkey that needs to be pressed to dislike the current video/song */
   dislikeHotkey: HotkeyObj;
+  /** Add a hotkey to open the current song's lyrics in a new tab */
+  currentLyricsHotkeyEnabled: boolean;
+  /** The hotkey that needs to be pressed to open the current song's lyrics in a new tab */
+  currentLyricsHotkey: HotkeyObj;
   /** Whether to rebind the next [J] and previous [K] keys */
   rebindNextAndPrevious: boolean;
-  /** Whether to only allow the new hotkeys to skip to the next or previous video/song */
-  forceReboundNextAndPrevious: boolean;
   /** The hotkey that needs to be pressed to skip to the next video/song */
   nextHotkey: HotkeyObj;
   /** The hotkey that needs to be pressed to skip to the previous video/song */
   previousHotkey: HotkeyObj;
+  /** Whether to rebind the play/pause hotkey */
+  rebindPlayPause: boolean;
+  /** The hotkey that needs to be pressed to play/pause the current video/song */
+  playPauseHotkey: HotkeyObj;
 
   //#region lyrics
   /** Add a button to the media controls to open the current song's lyrics on genius.com in a new tab */