Browse Source

fix: race conditions

Sven 9 months ago
parent
commit
d325e9926d
4 changed files with 69 additions and 44 deletions
  1. 7 7
      assets/translations/README.md
  2. 5 6
      src/features/input.ts
  3. 21 8
      src/observers.ts
  4. 36 23
      src/siteEvents.ts

+ 7 - 7
assets/translations/README.md

@@ -95,7 +95,7 @@ This means to figure out which keys are untranslated, you will need to manually
 | `vote_ratio_green_red` | `Green and red` |
 | `vote_ratio_blue_gray` | `Blue and gray` |
 | `votes_format_short` | `Shortened` |
-| `votes_format_full` | `Full number` |
+| `votes_format_long` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
 | `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
@@ -173,7 +173,7 @@ This means to figure out which keys are untranslated, you will need to manually
 | `vote_ratio_green_red` | `Green and red` |
 | `vote_ratio_blue_gray` | `Blue and gray` |
 | `votes_format_short` | `Shortened` |
-| `votes_format_full` | `Full number` |
+| `votes_format_long` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
 | `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
@@ -251,7 +251,7 @@ This means to figure out which keys are untranslated, you will need to manually
 | `vote_ratio_green_red` | `Green and red` |
 | `vote_ratio_blue_gray` | `Blue and gray` |
 | `votes_format_short` | `Shortened` |
-| `votes_format_full` | `Full number` |
+| `votes_format_long` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
 | `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
@@ -329,7 +329,7 @@ This means to figure out which keys are untranslated, you will need to manually
 | `vote_ratio_green_red` | `Green and red` |
 | `vote_ratio_blue_gray` | `Blue and gray` |
 | `votes_format_short` | `Shortened` |
-| `votes_format_full` | `Full number` |
+| `votes_format_long` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
 | `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
@@ -407,7 +407,7 @@ This means to figure out which keys are untranslated, you will need to manually
 | `vote_ratio_green_red` | `Green and red` |
 | `vote_ratio_blue_gray` | `Blue and gray` |
 | `votes_format_short` | `Shortened` |
-| `votes_format_full` | `Full number` |
+| `votes_format_long` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
 | `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
@@ -485,7 +485,7 @@ This means to figure out which keys are untranslated, you will need to manually
 | `vote_ratio_green_red` | `Green and red` |
 | `vote_ratio_blue_gray` | `Blue and gray` |
 | `votes_format_short` | `Shortened` |
-| `votes_format_full` | `Full number` |
+| `votes_format_long` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
 | `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
@@ -563,7 +563,7 @@ This means to figure out which keys are untranslated, you will need to manually
 | `vote_ratio_green_red` | `Green and red` |
 | `vote_ratio_blue_gray` | `Blue and gray` |
 | `votes_format_short` | `Shortened` |
-| `votes_format_full` | `Full number` |
+| `votes_format_long` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
 | `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |

+ 5 - 6
src/features/input.ts

@@ -282,14 +282,14 @@ export async function initAutoLike() {
       });
 
       siteEvents.on("pathChanged", (path) => {
-        if(path.match(/(\/?@|\/channel\/).+/)) {
+        if(path.match(/(\/?@|\/?channel\/)\S+/)) {
           const chanId = getCurrentChannelId();
           if(!chanId)
             return error("Couldn't extract channel ID from URL");
 
           document.querySelectorAll<HTMLElement>(".bytm-auto-like-toggle-btn").forEach((btn) => clearNode(btn));
 
-          addSelectorListener<0, "yt">("ytChannelHeader", "#channel-header-container", {
+          addSelectorListener<0, "yt">("ytAppHeader", "#channel-header-container, #page-header", {
             listener(headerCont) {
               const titleCont = headerCont.querySelector<HTMLElement>("ytd-channel-name #container");
               if(!titleCont)
@@ -299,10 +299,9 @@ export async function initAutoLike() {
 
               const buttonsCont = headerCont.querySelector<HTMLElement>("#inner-header-container #buttons");
               if(buttonsCont) {
-                addSelectorListener<0, "yt">("ytChannelHeader", "#channel-header-container #other-buttons", {
-                  listener(otherBtns) {
-                    addAutoLikeToggleBtn(otherBtns, chanId, chanName, ["left-margin"]);
-                  }
+                addSelectorListener<0, "yt">("ytAppHeader", "#channel-header-container #other-buttons, yt-subscribe-button-view-model", {
+                  listener: (otherBtns) =>
+                    addAutoLikeToggleBtn(otherBtns, chanId, chanName, ["left-margin"]),
                 });
               }
               else if(titleCont)

+ 21 - 8
src/observers.ts

@@ -39,7 +39,7 @@ export type YTObserverName =
   | "ytMasthead"       // the masthead (title bar) at the top of the page
   | "ytGuide"          // the left sidebar menu
   | "ytdBrowse"        // channel pages for example
-  | "ytChannelHeader"  // header of a channel page
+  | "ytAppHeader"      // header of the page
   | "ytWatchFlexy"     // the main content of the /watch page
   | "ytWatchMetadata"; // the metadata section of the /watch page
 
@@ -55,11 +55,14 @@ const defaultObserverOptions: SelectorObserverOptions = {
 
 /** Global SelectorObserver instances usable throughout the script for improved performance */
 export const globservers = {} as Record<ObserverName, SelectorObserver>;
+/** Whether all observers have been initialized */
+export let globserversReady = false;
 
 //#region add listener func
 
 /**
  * Interface function for adding listeners to the {@linkcode globservers}  
+ * If the observers haven't been initialized yet, the function will queue calls until the `bytm:observersReady` event is emitted
  * @param selector Relative to the observer's root element, so the selector can only start at of the root element's children at the earliest!
  * @param options Options for the listener
  * @template TElem The type of the element that the listener will be attached to. If set to `0`, the default type `HTMLElement` will be used.
@@ -77,7 +80,16 @@ export function addSelectorListener<
       : TElem
   >,
 ) {
-  globservers[observerName].addListener(selector, options);
+  try {
+    if(!globserversReady) {
+      window.addEventListener("bytm:observersReady", () => addSelectorListener(observerName, selector, options), { once: true });
+      return;
+    }
+    globservers[observerName].addListener(selector, options);
+  }
+  catch(err) {
+    error(`Couldn't add listener to globserver '${observerName}':`, err);
+  }
 }
 
 //#region init
@@ -280,17 +292,17 @@ export function initObservers() {
         listener: () => globservers.ytdBrowse.enable(),
       });
 
-      //#region ytChannelHeader
-      // -> header of a channel page
+      //#region ytAppHeader
+      // -> header of the page
       //    enabled by "ytdBrowse"
-      const ytChannelHeaderSelector = "#header tp-yt-app-header #channel-header";
-      globservers.ytChannelHeader = new SelectorObserver(ytChannelHeaderSelector, {
+      const ytAppHeaderSelector = "#header tp-yt-app-header";
+      globservers.ytAppHeader = new SelectorObserver(ytAppHeaderSelector, {
         ...defaultObserverOptions,
         subtree: true,
       });
 
-      globservers.ytdBrowse.addListener(ytChannelHeaderSelector, {
-        listener: () => globservers.ytChannelHeader.enable(),
+      globservers.ytdBrowse.addListener(ytAppHeaderSelector, {
+        listener: () => globservers.ytAppHeader.enable(),
       });
 
       //#region ytWatchFlexy
@@ -336,6 +348,7 @@ export function initObservers() {
 
     //#region finalize
 
+    globserversReady = true;
     emitInterface("bytm:observersReady");
   }
   catch(err) {

+ 36 - 23
src/siteEvents.ts

@@ -1,7 +1,7 @@
 import { NanoEmitter, error, getDomain, info } from "./utils/index.js";
 import { FeatureConfig } from "./types.js";
 import { emitInterface } from "./interface.js";
-import { addSelectorListener } from "./observers.js";
+import { addSelectorListener, globserversReady } from "./observers.js";
 
 export interface SiteEventsMap {
   //#region misc:
@@ -156,33 +156,46 @@ export async function initSiteEvents() {
         }
       });
 
-      addSelectorListener("mainPanel", "ytmusic-player#player", {
-        listener: (el) => {
-          playerFullscreenObs.observe(el, {
-            attributeFilter: ["player-ui-state"],
-          });
-        },
-      });
+      if(getDomain() === "ytm") {
+        const registerFullScreenObs = () => addSelectorListener("mainPanel", "ytmusic-player#player", {
+          listener: (el) => {
+            playerFullscreenObs.observe(el, {
+              attributeFilter: ["player-ui-state"],
+            });
+          },
+        });
+
+        if(globserversReady)
+          registerFullScreenObs();
+        else
+          window.addEventListener("bytm:observersReady", registerFullScreenObs, { once: true });
+      }
     }
 
     window.addEventListener("bytm:ready", () => {
       runIntervalChecks();
       setInterval(runIntervalChecks, 100);
 
-      addSelectorListener<HTMLAnchorElement>("mainPanel", "ytmusic-player #song-video #movie_player .ytp-title-text > a", {
-        listener(el) {
-          const urlRefObs = new MutationObserver(([ { target } ]) => {
-            if(!target || !(target as HTMLAnchorElement)?.href?.includes("/watch"))
-              return;
-            const watchId = new URL((target as HTMLAnchorElement).href).searchParams.get("v");
-            checkWatchIdChange(watchId);
-          });
-
-          urlRefObs.observe(el, {
-            attributeFilter: ["href"],
-          });
-        }
-      });
+      if(getDomain() === "ytm") {
+        addSelectorListener<HTMLAnchorElement>("mainPanel", "ytmusic-player #song-video #movie_player .ytp-title-text > a", {
+          listener(el) {
+            const urlRefObs = new MutationObserver(([ { target } ]) => {
+              if(!target || !(target as HTMLAnchorElement)?.href?.includes("/watch"))
+                return;
+              const watchId = new URL((target as HTMLAnchorElement).href).searchParams.get("v");
+              checkWatchIdChange(watchId);
+            });
+
+            urlRefObs.observe(el, {
+              attributeFilter: ["href"],
+            });
+          }
+        });
+      }
+      if(getDomain() === "ytm") {
+        setInterval(checkWatchIdChange, 250);
+        checkWatchIdChange();
+      }
     }, {
       once: true,
     });
@@ -214,9 +227,9 @@ export function emitSiteEvent<TKey extends keyof SiteEventsMap>(key: TKey, ...ar
 function checkWatchIdChange(newId?: string | null) {
   const newWatchId = newId ?? new URL(location.href).searchParams.get("v");
   if(newWatchId && newWatchId !== lastWatchId) {
+    lastWatchId = newWatchId;
     info(`Detected watch ID change - old ID: "${lastWatchId}" - new ID: "${newWatchId}"`);
     emitSiteEvent("watchIdChanged", newWatchId, lastWatchId);
-    lastWatchId = newWatchId;
   }
 }