Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New YouTube UI fix for the latest version 3.0.0.11 #952

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

tdreams2
Copy link

I changed this line of code in createDislikeTextContainer() function:
const textNodeClone = (getLikeButton().querySelector("button > div[class*='cbox']") || getLikeButton().querySelector('div > span[role="text"]').parentNode).cloneNode(true);

changed to this:
const textNodeClone = (getLikeButton() || getLikeButton().parentNode).cloneNode(true);

Selectors: ("button > div[class*='cbox']") and ('div > span[role="text"]') can't be found on the page

Copy link
Contributor

@AminoffZ AminoffZ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should get some info on which browsers / devices this works on. Here are a few on which these changes make it work:

Windows 10:

  • Chromium Version 116.0.5845.141 (Official Build, ungoogled-chromium) (64-bit)
  • Brave Version 1.59.117 Chromium: 118.0.5993.70 (Official Build) (64-bit).
  • Firefox Developer Edition 119.0b2 (64-bit)

Windows 11:

  • Google Chrome Version 118.0.5993.71 (Official Build) (64-bit)

@tdreams2
Copy link
Author

I get this error when I open a short video but dislike count still there no issue:

Cannot read properties of undefined (reading 'classList')

getDislikeButton().classList.contains("style-default-active")

From:

function isVideoDisliked() {
  if (isMobile()) {
    return (
      getDislikeButton().querySelector("button").getAttribute("aria-label") === "true"
    );
  }
  return getDislikeButton().classList.contains("style-default-active") || getDislikeButton().querySelector('button')?.getAttribute('aria-pressed') === 'true';
}

Windows 11:

  • Google Chrome Version 118.0.5993.71 (Official Build) (64-bit)

So I think with this error if I dislike a video, it will not count my dislike through API

@AminoffZ
Copy link
Contributor

AminoffZ commented Oct 16, 2023

Cannot read properties of undefined (reading 'classList')

getDislikeButton().classList.contains("style-default-active")

I'm getting it sometimes on both Windows 10 and Windows 11. Both on:

  • Brave Version 1.59.117 Chromium: 118.0.5993.70 (Official Build) (64-bit)

I think this should be fine since if you do:

function getDislikeButton() {
  console.log("getDislikeButton");
  const dislikeButton =
    getButtons().children[0].tagName ===
    "YTD-SEGMENTED-LIKE-DISLIKE-BUTTON-RENDERER"
      ? getButtons().children[0].children[1] === undefined
        ? document.querySelector("#segmented-dislike-button")
        : getButtons().children[0].children[1]
      : getButtons().children[1];
  console.log(dislikeButton);
  return dislikeButton;
}

You end up getting what seems like calls to get the buttons until they exist.

ryd.content-script.js:786 getDislikeButton
ryd.content-script.js:788 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'children')
at getDislikeButton (ryd.content-script.js:788:17)
at isVideoDisliked (ryd.content-script.js:488:10)
at setState (ryd.content-script.js:604:30)
at setInitialState (ryd.content-script.js:637:3)
at checkForJS_Finish (ryd.content-script.js:1021:9)
getDislikeButton @ ryd.content-script.js:788
...
ryd.content-script.js:786 getDislikeButton
ryd.content-script.js:794 <ytd-toggle-button-renderer id=​"dislike-button" is-shorts class=​"style-scope ytd-like-button-renderer" vertically-aligned button-renderer=​"true">​…​​
ryd.content-script.js:786 getDislikeButton
ryd.content-script.js:794 <ytd-toggle-button-renderer id=​"dislike-button" is-shorts class=​"style-scope ytd-like-button-renderer" vertically-aligned button-renderer=​"true">​…​​
ryd.content-script.js:786 getDislikeButton
ryd.content-script.js:794 <ytd-toggle-button-renderer id=​"dislike-button" is-shorts class=​"style-scope ytd-like-button-renderer" vertically-aligned button-renderer=​"true">​…​​
ryd.content-script.js:786 getDislikeButton
ryd.content-script.js:794 <ytd-toggle-button-renderer id=​"dislike-button" is-shorts class=​"style-scope ytd-like-button-renderer" vertically-aligned button-renderer=​"true">​…​​
ryd.content-script.js:786 getDislikeButton

image

Think there is some underlying logic in state where we re-querySelect if we error or something or it might be some mutationobserver implementation that I haven't looked into. If someone has insight into this, let us know.

To summarize: This should be fine 😅.

EDIT: More importantly, when inspecting the service worker's network tab we can see the request succeeding even if we had the error in the console. API is still getting the info it needs.

Copy link

@elmobark elmobark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i can confirm it's work in chrome 118.0.5993.70 macOS

@tdreams2
Copy link
Author

Think there is some underlying logic in state where we re-querySelect if we error or something or it might be some mutationobserver implementation that I haven't looked into. If someone has insight into this, let us know.

Actually there is a mutationObserver I think and it causing issues

To produce the error:

  1. go to YouTube home page and refresh the page.
  2. go to any short video from home page and the error will come up.
    if you go back to the home page and click another short video the error shows and so on.

To stop the error:

  1. go to any short video from home page
  2. refresh the page
    you can go back and forth without any issue until you refresh the home page again

mutationObserver code:

let mutationObserver = new Object();

  if (isShorts() && mutationObserver.exists !== true) {
  cLog("initializing mutation observer");
  mutationObserver.options = {
    childList: false,
    attributes: true,
    subtree: false,
  };
  mutationObserver.exists = true;
  mutationObserver.observer = new MutationObserver(function (
    mutationList,
    observer
  ) {
    mutationList.forEach((mutation) => {
      if (
        mutation.type === "attributes" &&
        mutation.target.nodeName === "TP-YT-PAPER-BUTTON" &&
        mutation.target.id === "button"
      ) {
        // cLog('Short thumb button status changed');
        if (mutation.target.getAttribute("aria-pressed") === "true") {
          mutation.target.style.color =
            mutation.target.parentElement.parentElement.id === "like-button"
              ? getColorFromTheme(true)
              : getColorFromTheme(false);
        } else {
          mutation.target.style.color = "unset";
        }
        return;
      }
      cLog(
        "unexpected mutation observer event: " + mutation.target + mutation.type
      );
    });
  });
}

for some reason the MutationObserver doesn't initiate if you produce the error steps above, and the only way to initialize it is by refreshing the page while we are on shorts path

@AminoffZ
Copy link
Contributor

Actually there is a mutationObserver I think and it causing issues

Unless there are more issues than the errors in the console from before, I don't think it's a huge priority but something we should look at next.

@PatrickG
Copy link

This change doesn't really make any sense, does it?
If getLikeButton() returns something, || getLikeButton().parentNode is not used.
If getLikeButton() returns nothing, || getLikeButton().parentNode will throw an error.
Also, textNodeClone is not supposed to be the whole like button.

I fixed it locally like this:

const textNodeClone = (getLikeButton().querySelector("button > div[class*='cbox']") || (getLikeButton().querySelector('div > span[role="text"]') || document.querySelector('button > div.yt-spec-button-shape-next__button-text-content > span[role="text"]')).parentNode).cloneNode(true);

Since the like button does not have any <span role="text"> elements anymore for me, I simply use any button element on the page (they all have the same structure).

@AminoffZ
Copy link
Contributor

Fair point. It's interesting to note that just const textNodeClone = getLikeButton().cloneNode(true); seems to be working for me now also. Do we know when and why the specific selectors work or don't?
In which situations are we using:

  • getLikeButton().querySelector("button > div[class*='cbox']").cloneNode(true);
  • getLikeButton().querySelector('div > span[role="text"]').parentNode.cloneNode(true);
  • document.querySelector('button > div.yt-spec-button-shape-next__button-text-content > span[role="text"]').parentNode.cloneNode(true);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants