Переглянути джерело

fix: non-resolving ft init func hanging the entire script

Sv443 11 місяців тому
батько
коміт
f6fd3e39ca
2 змінених файлів з 168 додано та 156 видалено
  1. 138 135
      src/features/layout.ts
  2. 30 21
      src/index.ts

+ 138 - 135
src/features/layout.ts

@@ -478,161 +478,164 @@ export async function initThumbnailOverlay() {
   if(getFeatures().thumbnailOverlayBehavior === "never" && !toggleBtnShown)
     return;
 
-  await waitVideoElementReady();
+  // so the script doesn't wait until a /watch page is loaded
+  (async () => {
+    await waitVideoElementReady();
 
-  const playerSelector = "ytmusic-player#player";
-  const playerEl = document.querySelector<HTMLElement>(playerSelector);
+    const playerSelector = "ytmusic-player#player";
+    const playerEl = document.querySelector<HTMLElement>(playerSelector);
 
-  if(!playerEl)
-    return error("Couldn't find video player element while adding thumbnail overlay");
+    if(!playerEl)
+      return error("Couldn't find video player element while adding thumbnail overlay");
 
-  /** Checks and updates the overlay and toggle button states based on the current song type (yt video or ytm song) */
-  const updateOverlayVisibility = async () => {
-    if(!domLoaded)
-      return;
+    /** Checks and updates the overlay and toggle button states based on the current song type (yt video or ytm song) */
+    const updateOverlayVisibility = async () => {
+      if(!domLoaded)
+        return;
 
-    const behavior = getFeatures().thumbnailOverlayBehavior;
+      const behavior = getFeatures().thumbnailOverlayBehavior;
 
-    let showOverlay = behavior === "always";
-    const isVideo = currentMediaType() === "video";
+      let showOverlay = behavior === "always";
+      const isVideo = currentMediaType() === "video";
 
-    if(behavior === "videosOnly" && isVideo)
-      showOverlay = true;
-    else if(behavior === "songsOnly" && !isVideo)
-      showOverlay = true;
+      if(behavior === "videosOnly" && isVideo)
+        showOverlay = true;
+      else if(behavior === "songsOnly" && !isVideo)
+        showOverlay = true;
 
-    showOverlay = invertOverlay ? !showOverlay : showOverlay;
+      showOverlay = invertOverlay ? !showOverlay : showOverlay;
 
-    const overlayElem = document.querySelector<HTMLElement>("#bytm-thumbnail-overlay");
-    const thumbElem = document.querySelector<HTMLElement>("#bytm-thumbnail-overlay-img");
-    const indicatorElem = document.querySelector<HTMLElement>("#bytm-thumbnail-overlay-indicator");
+      const overlayElem = document.querySelector<HTMLElement>("#bytm-thumbnail-overlay");
+      const thumbElem = document.querySelector<HTMLElement>("#bytm-thumbnail-overlay-img");
+      const indicatorElem = document.querySelector<HTMLElement>("#bytm-thumbnail-overlay-indicator");
 
-    if(overlayElem)
-      overlayElem.style.display = showOverlay ? "block" : "none";
-    if(thumbElem)
-      thumbElem.ariaHidden = String(!showOverlay);
-    if(indicatorElem) {
-      indicatorElem.style.display = showOverlay ? "block" : "none";
-      indicatorElem.ariaHidden = String(!showOverlay);
-    }
+      if(overlayElem)
+        overlayElem.style.display = showOverlay ? "block" : "none";
+      if(thumbElem)
+        thumbElem.ariaHidden = String(!showOverlay);
+      if(indicatorElem) {
+        indicatorElem.style.display = showOverlay ? "block" : "none";
+        indicatorElem.ariaHidden = String(!showOverlay);
+      }
 
-    if(getFeatures().thumbnailOverlayToggleBtnShown) {
-      const toggleBtnElem = document.querySelector<HTMLImageElement>("#bytm-thumbnail-overlay-toggle");
-      const toggleBtnImgElem = document.querySelector<HTMLImageElement>("#bytm-thumbnail-overlay-toggle > img");
+      if(getFeatures().thumbnailOverlayToggleBtnShown) {
+        const toggleBtnElem = document.querySelector<HTMLImageElement>("#bytm-thumbnail-overlay-toggle");
+        const toggleBtnImgElem = document.querySelector<HTMLImageElement>("#bytm-thumbnail-overlay-toggle > img");
 
-      if(toggleBtnImgElem)
-        toggleBtnImgElem.src = await getResourceUrl(`icon-image${showOverlay ? "_filled" : ""}` as "icon-image" | "icon-image_filled");
-      if(toggleBtnElem)
-        toggleBtnElem.ariaLabel = toggleBtnElem.title = t(`thumbnail_overlay_toggle_btn_tooltip${showOverlay ? "_hide" : "_show"}`);
-    }
-  };
+        if(toggleBtnImgElem)
+          toggleBtnImgElem.src = await getResourceUrl(`icon-image${showOverlay ? "_filled" : ""}` as "icon-image" | "icon-image_filled");
+        if(toggleBtnElem)
+          toggleBtnElem.ariaLabel = toggleBtnElem.title = t(`thumbnail_overlay_toggle_btn_tooltip${showOverlay ? "_hide" : "_show"}`);
+      }
+    };
 
-  const applyThumbUrl = async (watchId: string) => {
-    const thumbUrl = await getBestThumbnailUrl(watchId);
-    if(thumbUrl) {
-      const toggleBtnElem = document.querySelector<HTMLAnchorElement>("#bytm-thumbnail-overlay-toggle");
-      const thumbImgElem = document.querySelector<HTMLImageElement>("#bytm-thumbnail-overlay-img");
-      if(toggleBtnElem)
-        toggleBtnElem.href = thumbUrl;
-      if(thumbImgElem)
-        thumbImgElem.src = thumbUrl;
-    }
-  };
+    const applyThumbUrl = async (watchId: string) => {
+      const thumbUrl = await getBestThumbnailUrl(watchId);
+      if(thumbUrl) {
+        const toggleBtnElem = document.querySelector<HTMLAnchorElement>("#bytm-thumbnail-overlay-toggle");
+        const thumbImgElem = document.querySelector<HTMLImageElement>("#bytm-thumbnail-overlay-img");
+        if(toggleBtnElem)
+          toggleBtnElem.href = thumbUrl;
+        if(thumbImgElem)
+          thumbImgElem.src = thumbUrl;
+      }
+    };
 
-  const unsubWatchIdChanged = siteEvents.on("watchIdChanged", (watchId) => {
-    unsubWatchIdChanged();
-    addSelectorListener("body", "#bytm-thumbnail-overlay", {
-      listener: () => {
-        applyThumbUrl(watchId);
-        updateOverlayVisibility();
-      },
+    const unsubWatchIdChanged = siteEvents.on("watchIdChanged", (watchId) => {
+      unsubWatchIdChanged();
+      addSelectorListener("body", "#bytm-thumbnail-overlay", {
+        listener: () => {
+          applyThumbUrl(watchId);
+          updateOverlayVisibility();
+        },
+      });
     });
-  });
 
-  const createElements = async () => {
-    // overlay
-    const overlayElem = document.createElement("div");
-    overlayElem.id = "bytm-thumbnail-overlay";
-    overlayElem.title = ""; // prevent child titles from propagating
-    overlayElem.classList.add("bytm-no-select");
-    overlayElem.style.display = "none";
-
-    let indicatorElem: HTMLImageElement | undefined;
-    if(getFeatures().thumbnailOverlayShowIndicator) {
-      indicatorElem = document.createElement("img");
-      indicatorElem.id = "bytm-thumbnail-overlay-indicator";
-      indicatorElem.src = await getResourceUrl("icon-image");
-      indicatorElem.role = "presentation";
-      indicatorElem.title = indicatorElem.ariaLabel = t("thumbnail_overlay_indicator_tooltip");
-      indicatorElem.ariaHidden = "true";
-      indicatorElem.style.display = "none";
-      indicatorElem.style.opacity = String(getFeatures().thumbnailOverlayIndicatorOpacity / 100);
-    }
-  
-    const thumbImgElem = document.createElement("img");
-    thumbImgElem.id = "bytm-thumbnail-overlay-img";
-    thumbImgElem.role = "presentation";
-    thumbImgElem.ariaHidden = "true";
-    thumbImgElem.style.objectFit = getFeatures().thumbnailOverlayImageFit;
-  
-    overlayElem.appendChild(thumbImgElem);
-    playerEl.appendChild(overlayElem);
-    indicatorElem && playerEl.appendChild(indicatorElem);
-
-
-    siteEvents.on("watchIdChanged", async (watchId) => {
-      invertOverlay = false;
-      applyThumbUrl(watchId);
-      updateOverlayVisibility();
-    });
-  
-    // toggle button
-    if(toggleBtnShown) {
-      const toggleBtnElem = document.createElement("a");
-      toggleBtnElem.id = "bytm-thumbnail-overlay-toggle";
-      toggleBtnElem.role = "button";
-      toggleBtnElem.tabIndex = 0;
-      toggleBtnElem.classList.add("ytmusic-player-bar", "bytm-generic-btn", "bytm-no-select");
-  
-      onInteraction(toggleBtnElem, (e) => {
-        if(e.shiftKey)
-          return openInTab(toggleBtnElem.href, e instanceof MouseEvent);
-        invertOverlay = !invertOverlay;
+    const createElements = async () => {
+      // overlay
+      const overlayElem = document.createElement("div");
+      overlayElem.id = "bytm-thumbnail-overlay";
+      overlayElem.title = ""; // prevent child titles from propagating
+      overlayElem.classList.add("bytm-no-select");
+      overlayElem.style.display = "none";
+
+      let indicatorElem: HTMLImageElement | undefined;
+      if(getFeatures().thumbnailOverlayShowIndicator) {
+        indicatorElem = document.createElement("img");
+        indicatorElem.id = "bytm-thumbnail-overlay-indicator";
+        indicatorElem.src = await getResourceUrl("icon-image");
+        indicatorElem.role = "presentation";
+        indicatorElem.title = indicatorElem.ariaLabel = t("thumbnail_overlay_indicator_tooltip");
+        indicatorElem.ariaHidden = "true";
+        indicatorElem.style.display = "none";
+        indicatorElem.style.opacity = String(getFeatures().thumbnailOverlayIndicatorOpacity / 100);
+      }
+    
+      const thumbImgElem = document.createElement("img");
+      thumbImgElem.id = "bytm-thumbnail-overlay-img";
+      thumbImgElem.role = "presentation";
+      thumbImgElem.ariaHidden = "true";
+      thumbImgElem.style.objectFit = getFeatures().thumbnailOverlayImageFit;
+    
+      overlayElem.appendChild(thumbImgElem);
+      playerEl.appendChild(overlayElem);
+      indicatorElem && playerEl.appendChild(indicatorElem);
+
+
+      siteEvents.on("watchIdChanged", async (watchId) => {
+        invertOverlay = false;
+        applyThumbUrl(watchId);
         updateOverlayVisibility();
       });
-  
-      const imgElem = document.createElement("img");
-      imgElem.classList.add("bytm-generic-btn-img");
-  
-      toggleBtnElem.appendChild(imgElem);
-  
-      addSelectorListener("playerBarMiddleButtons", "ytmusic-like-button-renderer#like-button-renderer", {
-        listener: (likeContainer) => insertAfter(likeContainer, toggleBtnElem),
-      });
-    }
-
-    log("Added thumbnail overlay");
-  };
-
-  addSelectorListener("mainPanel", playerSelector, {
-    listener(playerEl) {
-      if(playerEl.getAttribute("player-ui-state") === "INACTIVE") {
-        const obs = new MutationObserver(() => {
-          if(playerEl.getAttribute("player-ui-state") === "INACTIVE")
-            return;
-          createElements();
-          obs.disconnect();
+    
+      // toggle button
+      if(toggleBtnShown) {
+        const toggleBtnElem = document.createElement("a");
+        toggleBtnElem.id = "bytm-thumbnail-overlay-toggle";
+        toggleBtnElem.role = "button";
+        toggleBtnElem.tabIndex = 0;
+        toggleBtnElem.classList.add("ytmusic-player-bar", "bytm-generic-btn", "bytm-no-select");
+    
+        onInteraction(toggleBtnElem, (e) => {
+          if(e.shiftKey)
+            return openInTab(toggleBtnElem.href, e instanceof MouseEvent);
+          invertOverlay = !invertOverlay;
+          updateOverlayVisibility();
         });
-
-        obs.observe(playerEl, {
-          attributes: true,
-          attributeFilter: ["player-ui-state"],
+    
+        const imgElem = document.createElement("img");
+        imgElem.classList.add("bytm-generic-btn-img");
+    
+        toggleBtnElem.appendChild(imgElem);
+    
+        addSelectorListener("playerBarMiddleButtons", "ytmusic-like-button-renderer#like-button-renderer", {
+          listener: (likeContainer) => insertAfter(likeContainer, toggleBtnElem),
         });
       }
-      else
-        createElements();
-    },
-  });
+
+      log("Added thumbnail overlay");
+    };
+
+    addSelectorListener("mainPanel", playerSelector, {
+      listener(playerEl) {
+        if(playerEl.getAttribute("player-ui-state") === "INACTIVE") {
+          const obs = new MutationObserver(() => {
+            if(playerEl.getAttribute("player-ui-state") === "INACTIVE")
+              return;
+            createElements();
+            obs.disconnect();
+          });
+
+          obs.observe(playerEl, {
+            attributes: true,
+            attributeFilter: ["player-ui-state"],
+          });
+        }
+        else
+          createElements();
+      },
+    });
+  })();
 }
 
 //#region hide cursor on idle

+ 30 - 21
src/index.ts

@@ -1,4 +1,4 @@
-import { compress, decompress, type Stringifiable } from "@sv443-network/userutils";
+import { compress, decompress, pauseFor, type Stringifiable } from "@sv443-network/userutils";
 import { addStyleFromResource, domLoaded, reserialize, warn } from "./utils";
 import { clearConfig, fixMissingCfgKeys, getFeatures, initConfig, setFeatures } from "./config";
 import { buildNumber, compressionFormat, defaultLogLevel, mode, scriptInfo } from "./constants";
@@ -117,7 +117,7 @@ async function init() {
 async function onDomLoad() {
   const domain = getDomain();
   const features = getFeatures();
-  const ftInit = [] as Promise<void>[];
+  const ftInit = [] as [string, Promise<void>][];
 
   try {
     initObservers();
@@ -138,7 +138,7 @@ async function onDomLoad() {
     if(domain === "ytm") {
       //#region (ytm) misc
 
-      ftInit.push(initSiteEvents());
+      ftInit.push(["initSiteEvents", initSiteEvents()]);
 
       //#region (ytm) welcome dlg
 
@@ -154,52 +154,52 @@ async function onDomLoad() {
       //#region (ytm) layout
 
       if(features.watermarkEnabled)
-        ftInit.push(addWatermark());
+        ftInit.push(["addWatermark", addWatermark()]);
 
       if(features.fixSpacing)
-        ftInit.push(fixSpacing());
+        ftInit.push(["fixSpacing", fixSpacing()]);
 
       if(features.removeUpgradeTab)
-        ftInit.push(removeUpgradeTab());
+        ftInit.push(["removeUpgradeTab", removeUpgradeTab()]);
 
-      ftInit.push(initThumbnailOverlay());
+      ftInit.push(["initThumbnailOverlay", initThumbnailOverlay()]);
 
       if(features.hideCursorOnIdle)
-        ftInit.push(initHideCursorOnIdle());
+        ftInit.push(["initHideCursorOnIdle", initHideCursorOnIdle()]);
 
       if(features.fixHdrIssues)
-        ftInit.push(fixHdrIssues());
+        ftInit.push(["fixHdrIssues", fixHdrIssues()]);
 
       //#region (ytm) volume
 
-      ftInit.push(initVolumeFeatures());
+      ftInit.push(["initVolumeFeatures", initVolumeFeatures()]);
 
       //#region (ytm) song lists
 
       if(features.lyricsQueueButton || features.deleteFromQueueButton)
-        ftInit.push(initQueueButtons());
+        ftInit.push(["initQueueButtons", initQueueButtons()]);
 
       if(features.scrollToActiveSongBtn)
-        ftInit.push(initAboveQueueBtns());
+        ftInit.push(["initAboveQueueBtns", initAboveQueueBtns()]);
 
       //#region (ytm) behavior
 
       if(features.closeToastsTimeout > 0)
-        ftInit.push(initAutoCloseToasts());
+        ftInit.push(["initAutoCloseToasts", initAutoCloseToasts()]);
 
       //#region (ytm) input
 
-      ftInit.push(initArrowKeySkip());
+      ftInit.push(["initArrowKeySkip", initArrowKeySkip()]);
 
       if(features.anchorImprovements)
-        ftInit.push(addAnchorImprovements());
+        ftInit.push(["addAnchorImprovements", addAnchorImprovements()]);
 
-      ftInit.push(initNumKeysSkip());
+      ftInit.push(["initNumKeysSkip", initNumKeysSkip()]);
 
       //#region (ytm) lyrics
 
       if(features.geniusLyrics)
-        ftInit.push(addMediaCtrlLyricsBtn());
+        ftInit.push(["addMediaCtrlLyricsBtn", addMediaCtrlLyricsBtn()]);
     }
 
     //#region (ytm+yt) cfg menu option
@@ -220,11 +220,11 @@ async function onDomLoad() {
       disableDarkReader();
 
       if(features.removeShareTrackingParamSites && (features.removeShareTrackingParamSites === domain || features.removeShareTrackingParamSites === "all"))
-        ftInit.push(initRemShareTrackParam());
+        ftInit.push(["initRemShareTrackParam", initRemShareTrackParam()]);
 
       //#region (ytm+yt) input
 
-      ftInit.push(initSiteSwitch(domain));
+      ftInit.push(["initSiteSwitch", initSiteSwitch(domain)]);
 
       // TODO: for hot reloading features
       // ftInit.push(new Promise((resolve) => {
@@ -256,9 +256,16 @@ async function onDomLoad() {
       // }));
     }
 
-    await Promise.allSettled(ftInit);
+    const initStartTs = Date.now();
+
+    // wait for feature init or timeout (in case an init function is hung up on a promise)
+    await Promise.race([
+      pauseFor(10_000),
+      Promise.allSettled(ftInit.map(([, p]) => p)),
+    ]);
 
     emitInterface("bytm:ready");
+    info("Done initializing all features after", Math.floor(Date.now() - initStartTs), "ms");
 
     try {
       initPlugins();
@@ -294,7 +301,7 @@ async function onDomLoad() {
 //       if(res instanceof Promise)
 //         ftInit.push(res);
 //       else
-//         ftInit.push(Promise.resolve());
+//         ftInit.push(["Promise.resolve", Promise.resolve()]);
 //     }
 //     catch(err) {
 //       error(`Couldn't initialize feature "${ftKey}" due to error:`, err);
@@ -473,6 +480,8 @@ function registerDevMenuCommands() {
       console.log(`Decompresion result (${input.length} chars -> ${decompressed.length} chars)\nValue: ${decompressed}`);
     }
   });
+
+  log("Registered dev menu commands");
 }
 
 preInit();