Anarios / return-youtube-dislike

Chrome extension to return youtube dislikes
https://returnyoutubedislike.com/
GNU General Public License v3.0
12.51k stars 557 forks source link

(Bug): "Dislike" is not replaced by the dislike count #666

Open shadoxxhd opened 2 years ago

shadoxxhd commented 2 years ago

Browser

Cent Browser

Browser Version

Chromium 86

Extension or Userscript?

Userscript

Extension/Userscript Version

3.0.1

Video link where you see the problem

any

What happened?

No dislike count is shown, just "Dislike".

How to reproduce/recreate?

Open any video. The console shows "Received Count", but the "Dislike" text is not replaced.

The issue wasn't there yesterday, and the html structure appears unchanged.

Manually executing the code leads to document.getElementById("menu-container")?.querySelector("#top-level-buttons-computed").children[1] returning a valid element, eg:

<ytd-toggle-button-renderer class="style-scope ytd-menu-renderer force-icon-button style-text" use-keyboard-focused="" system-icons="" button-renderer="true" style-action-button="" is-icon-button=""><a class="yt-simple-endpoint style-scope ytd-toggle-button-renderer" tabindex="-1"><yt-icon-button id="button" class="style-scope ytd-toggle-button-renderer style-text"><!--css-build:shady--><button id="button" class="style-scope yt-icon-button" aria-label="Dislike this video" aria-pressed="false"><yt-icon class="style-scope ytd-toggle-button-renderer"><svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope yt-icon" style="pointer-events: none; display: block; width: 100%; height: 100%;"><g class="style-scope yt-icon"><path d="M17,4h-1H6.57C5.5,4,4.59,4.67,4.38,5.61l-1.34,6C2.77,12.85,3.82,14,5.23,14h4.23l-1.52,4.94C7.62,19.97,8.46,21,9.62,21 c0.58,0,1.14-0.24,1.52-0.65L17,14h4V4H17z M10.4,19.67C10.21,19.88,9.92,20,9.62,20c-0.26,0-0.5-0.11-0.63-0.3 c-0.07-0.1-0.15-0.26-0.09-0.47l1.52-4.94l0.4-1.29H9.46H5.23c-0.41,0-0.8-0.17-1.03-0.46c-0.12-0.15-0.25-0.4-0.18-0.72l1.34-6 C5.46,5.35,5.97,5,6.57,5H16v8.61L10.4,19.67z M20,13h-3V5h3V13z" class="style-scope yt-icon"></path></g></svg><!--css-build:shady--></yt-icon></button><yt-interaction id="interaction" class="circular style-scope yt-icon-button"><!--css-build:shady--><div class="stroke style-scope yt-interaction"></div><div class="fill style-scope yt-interaction"></div></yt-interaction></yt-icon-button><yt-formatted-string id="text" class="style-scope ytd-toggle-button-renderer style-text">Dislike</yt-formatted-string></a></ytd-toggle-button-renderer>

but executing .querySelector("#text") on it returns null.

In a tab that was open before the issue appeared, .querySelector("#text") returns the text element as expected.

cyrildtm commented 2 years ago

<yt-formatted-string id="text" class="style-scope ytd-toggle-button-renderer style-text">Dislike</

It's right there. querySelector should not give you null. Something went wrong. Did you try refreshing the page by clicking the circle-arrow button? Or open the same URL in a new tab? Sometimes youtube decides not to reload the entire page for a new video, causing a lot of pain. See https://github.com/Anarios/return-youtube-dislike/issues/645#issuecomment-1152774324

shadoxxhd commented 2 years ago

I did try all that, even opening youtube in an incognito window didn't change that strange behavior...

shadoxxhd commented 2 years ago

When I assign document.getElementById("menu-container")?.querySelector("#top-level-buttons-computed").children[1] to a variable x, copy the element with right-click and assign it to x.innerHTML, x.querySelector("#text") no longer returns null. Does that make any sense to you?

edit: and document.getElementById("menu-container")?.querySelector("#top-level-buttons-computed").children[1].querySelector("#text") also correctly returns the text element after I reassign innerHTML...

cyrildtm commented 2 years ago

It does not. I've never seen querySelector malfunctions like that. It seems your first result (in x) is incorrect.

Browser

Browser Version

Chromium 86

Did you try with a newer browser? Just run the code manually, no need to install extension or log in for now

shadoxxhd commented 2 years ago

The browser I'm using has no newer version available atm, but the strange thing it that it was working until now, and tabs that were not reloaded since yesterday still continue to show the correct behavior...

Firefox 101 does not show this issue for me.

cyrildtm commented 2 years ago

I mean, it sucks it happened to you, but it can't be recreated by following certain steps, and it doesn't sound like an obvious oversight in our extension. Somehow query selector quit working. Maybe someone else here who's more familiar can help you?

shadoxxhd commented 2 years ago

After a browser restart, the issue persists.

shadoxxhd commented 2 years ago

I noticed there were 2 other suggestions when I typed in element.querySelector : __shady_querySelector and __shady_native_querySelector. The former 2 don't appear to work, while the last returns the text element properly. I set up my script like this now:

function setDislikes(dislikesCount) {
  if (isMobile) {
    mobileDislikes = dislikesCount;
    return;
  }
  var txt = getButtons().children[1].querySelector("#text");
  if(txt === null){
      txt = getButtons().children[1].__shady_querySelector("#text");
  }
  if(txt === null){
      txt = getButtons().children[1].__shady_native_querySelector("#text");
  }
  if(txt === null){
      cLog("3rd try failed...")
      return
  }
  txt.innerText = dislikesCount;
}

which fixes the issue, but the question remains where the replacement of the querySelector method with a disfunctional one comes from. Am I hit by early A/B testing targeting sponsorblock, or could this be another benign script that is malfunctioning?

shadoxxhd commented 2 years ago

__shady_native_queryselector gives just 18 google results, __shady_queryselector 19...

the method replacement appears to happen in webcomponents-sd.js, loaded from https://www.youtube.com/s/desktop/1422277c/jsbin/webcomponents-sd.vflset/webcomponents-sd.js.

In firefox, these methods also exist, but querySelector and __shady_native_querySelector both work correctly. The __shady_querySelector returns null in firefox as well.

shadoxxhd commented 2 years ago

apparently related: https://web.dev/shadowdom-v1/ https://www.polymer-project.org/blog/shadydom

I don't really know what to do with this information to find out why this specific tag was affected the way it was though...

cyrildtm commented 2 years ago

What about this? https://github.com/Polymer/polymer/issues/4172

Polymer.dom(document.getElementById("menu-container")?.querySelector("#top-level-buttons-computed").children[1])['node'].querySelector('#text')
shadoxxhd commented 2 years ago

What about this? Polymer/polymer#4172

Polymer.dom(document.getElementById("menu-container")?.querySelector("#top-level-buttons-computed").children[1])['node'].querySelector('#text')

This also returns null. Replacing querySelector with __shady_native_querySelector makes it return the correct element, just as without the Polymer.dom function.

shadoxxhd commented 2 years ago

getElementsByTagName("yt-formatted-string")[0] also works in place of querySelector...

cyrildtm commented 2 years ago

Seems to me to be a bug in the old version. Do we need backward compatibility?

https://en.wikipedia.org/wiki/Google_Chrome_version_history

90.0.4430 2021-04-14 A new way to implement and use Shadow DOM directly in HTML 86.0.4240 2020-10-06

http://www.centbrowser.com/history.html

v4.3.9.226 [2020-12-08] Upgraded to Chromium 86.0.4240.198

https://polymer-library.polymer-project.org/1.0/docs/release-notes#v-1-7-1

Release 1.7.1 (2016-12-14)

this is the first version after https://github.com/Polymer/polymer/issues/4172 No commits were linked to this issue. Perhaps it's only the Chrome change leading to your problem @shadoxxhd

https://github.com/Polymer/polymer/blob/master/CHANGELOG.md v3.3.1 (2019-11-08)

Make Closure compiler happier about ShadyDOM access (commit)

(not sure if this commit is related, but it's the last version before Chrome 90)

shadoxxhd commented 2 years ago

The strange thing is that until 5 days ago, everything was working properly, to the point where in unreloaded tabs querySelector did return the correct element instead of null, and in new tabs it didn't. And I can't imagine a browser bug being present only in some tabs...

And to me it doesn't seem too likely that either of these changes could be the reason for the strange selector behavior (I'm not a JS/web developer though, so take my impression with a grain of salt)...

Didn't the Polymer.dom() test disprove https://github.com/Polymer/polymer/issues/4172 being the issue?

shadoxxhd commented 2 years ago

About backward compatibility, the question seems to be whether getElementsByTagName("yt-formatted-string")[0] would have any downsides compared to querySelector("#text"). If backwards compatability doesn't come with any performance cost, I think it is generally a good idea. Otherwise, just reference this issue if someone else has the same problem as I had...

Since this isn't really a bug and more of a rare edge case (and whatever the underlying bug is, it is nothing to do with RYD), I wouldn't mind if you want to close this issue as irrelevant.


edit or is it? I don't think it's certain yet that this isn't some A/B test that changes the like/dislike buttons to be in shady/shadow DOM... so what would be the best way to check for that?

sy-b commented 2 years ago

I can't imagine a browser bug being present only in some tabs...

bitakshat — 6:08 PM UTC Hi, I'm working on a firefox extension to calculate video playback time for youtube. The extension works fine when its loaded on tab with youtube for the first time. When I open multiple youtube tabs it only works for the recent tab. How can I fix this? (its been a while since I'm working on this extension I've tried the most I can but still can't figure out. So I randomly joined this server. sorry If I'm in the wrong server or wrong channel. I just need some help)

source: https://discord.com/channels/909435648170160229/909435648665071660/993941459441037362

related?

shadoxxhd commented 2 years ago

related?

I don't think so, since multiple tabs were working before, and now the issue seems to be present in all tabs (certainly also in the most recent one).

cyrildtm commented 2 years ago

quick question: do you open a new tab and immediately see it? or do you open a new tab "in background" first, and then immediately switch to it?

cyrildtm commented 2 years ago

The strange thing is that until 5 days ago, everything was working properly

I agree, this is very strange. If I insist on the backward compatibility bug as the reason, I can argue that youtube recently made some changes in shady doms, and that triggered the old bug in the old browser version.

Why do they make it so complicated. I don't get it. It's a waste of time. That means they are paying people to do unnecessary work.

Maybe we can try this for now:

https://github.com/Anarios/return-youtube-dislike/blob/16b1c2ccf0ac7d419ae1df40bdd313234557807f/Extensions/combined/src/buttons.js#L39

Insert two new functions:

getLikeTextContainer() {
  return getLikeButton().querySelector("#text") ?? getLikeButton().getElementsByTagName("yt-formatted-string")[0]
}

getDislikeTextContainer() {
  return getDislikeButton().querySelector("#text") ?? getDislikeButton().getElementsByTagName("yt-formatted-string")[0]
}

Nullish coalescing prevents "0" being interpreted as invalid container

https://github.com/Anarios/return-youtube-dislike/blob/16b1c2ccf0ac7d419ae1df40bdd313234557807f/Extensions/combined/src/state.js#L124 change to:

  getLikeTextContainer().innerText = likesCount;

https://github.com/Anarios/return-youtube-dislike/blob/16b1c2ccf0ac7d419ae1df40bdd313234557807f/Extensions/combined/src/state.js#L135 change to:

    getDislikeTextContainer().innerText = dislikesCount;

https://github.com/Anarios/return-youtube-dislike/blob/16b1c2ccf0ac7d419ae1df40bdd313234557807f/Extensions/combined/src/state.js#L206 change to:

  getDislikeTextContainer().innerText = localize(
cyrildtm commented 2 years ago

I can't imagine a browser bug being present only in some tabs...

bitakshat — 6:08 PM UTC Hi, I'm working on a firefox extension to calculate video playback time for youtube. The extension works fine when its loaded on tab with youtube for the first time. When I open multiple youtube tabs it only works for the recent tab. How can I fix this? (its been a while since I'm working on this extension I've tried the most I can but still can't figure out. So I randomly joined this server. sorry If I'm in the wrong server or wrong channel. I just need some help)

source: https://discord.com/channels/909435648170160229/909435648665071660/993941459441037362

related?

As far as I can see, no. The cited extension has its own bad practices (many) causing problems. Shady DOMs is their last concern.

shadoxxhd commented 2 years ago

quick question: do you open a new tab and immediately see it? or do you open a new tab "in background" first, and then immediately switch to it?

I tried it early on on tabs that were loaded in the background as well as some that were reloaded/URL-pasted in new tab. The issue occured in all cases.

Maybe we can try this for now:

That looks like it would fix the issue; I personally used the .__shady_native_querySelector("#text") as a fallback in my private tampermonkey script, but don't know which would be more stable and/or performant.

cyrildtm commented 2 years ago

I wouldn't trust "undocumented" APIs (it's "shady" anyway) since one day or another it will change, and time spent now fixing it will become wasted.

shadoxxhd commented 2 years ago

I can confirm now that it is some sort of A/B test - when not logged in, the incognito tab behaved correctly (as in querySelector returning the element) on multiple video pages, but as soon as I log in it starts returning null. Even on current firefox, when I am logged in querySelector returns null, so it has nothing to do with browser/version.

cyrildtm commented 2 years ago

Wow thank you @shadoxxhd This issue has been so long. For now, we can implemented the changes from above, until this A/B testing ends and whatever version is to be used from then on.

mbc07 commented 2 years ago

I started experiencing this issue recently, on both Chrome 105.0.5148.2 and Edge 103.0.1264.49.

I'm using version 3.0.1 of the userscript and had the same symptoms: Dev Tools console shows Return Youtube Dislike successfully setting up, registering button listeners, fetching votes and the received count, immediately followed by a TypeError: Cannot set properties of null (setting 'innerText').

I also tried an incognito tab, without logging to my account, but the issue persisted, so I'm not sure if whatever YouTube change that broke the extension still is in A/B testing or if it started rolling out widely now...

regs01 commented 2 years ago

Also Firefox 102.0.1 / Userscript 3.0.1 from GreasyFork

With every video

01:45:46.677 [Return YouTube Dislikes] Received count: 54 2 Return YouTube Dislike.user.js:50:11
01:45:46.688 Uncaught (in promise) TypeError: getButtons().children[1].querySelector(...) is null
    setDislikes moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:197
    setState moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:325
    promise callback*setState/< moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:319
    promise callback*setState moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:318
    setInitialState moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:399
    checkForJS_Finish moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:534
    setEventListeners moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:540
    VMin041blywd9 moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:546
    VMin041blywd9 moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:547
    VMin041blywd9 moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:560
    a moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/sandbox/injected-web.js:1
    v moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/sandbox/injected-web.js:1
    set moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/sandbox/injected-web.js:1
    <anonymous> moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:1
    c moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/sandbox/injected-web.js:1
    ScriptData moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/sandbox/injected-web.js:1
    onHandle moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/sandbox/injected-web.js:1
    c moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/sandbox/injected-web.js:1
watch:197:28
01:45:46.691 Uncaught (in promise) TypeError: getButtons().children[1].querySelector(...) is null
    setDislikes moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:197
    setState moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:325
    promise callback*setState/< moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:319
    promise callback*setState moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:318
    setInitialState moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:399
    checkForJS_Finish moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:534
    setEventListeners moz-extension://538351f7-2f69-433e-ae5b-62d10fc42979/ Return YouTube Dislike.user.js#5:540
    xb https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/webcomponents-sd.vflset/webcomponents-sd.js:41
    dispatchEvent https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:4188
    fireEvent https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:4186
    e https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:4177
    61473_onFulfilled https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:877
    fia https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:887
    bia https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:887
    executeCallbacks_ https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:886
    xha https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:786
    promise callback*Hg https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:786
    Ig https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:785
    cia https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:884
    Zha https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:875
    Yha https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:879
    then https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:871
    loadData https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:20714
    yy https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:5079
    Fy https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:5168
    AXa https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:5172
    ua https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:48
    next https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:50
    uaa https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:56
    uaa https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:55
    t https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:56
    AXa https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:5170
    job https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:4913
    d https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/desktop_polymer.vflset/desktop_polymer.js:4900
    I https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/scheduler.vflset/scheduler.js:29
    O https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/scheduler.vflset/scheduler.js:38
    P https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/scheduler.vflset/scheduler.js:31
    requestIdleCallback handler*f.start https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/scheduler.vflset/scheduler.js:39
    O https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/scheduler.vflset/scheduler.js:38
    P https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/scheduler.vflset/scheduler.js:31
    requestIdleCallback handler*f.start https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/scheduler.vflset/scheduler.js:39
    O https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/scheduler.vflset/scheduler.js:38
    S https://www.youtube.com/s/desktop/2cbeb7d0/jsbin/scheduler.vflset/scheduler.js:34
watch:197:28
saemideluxe commented 2 years ago

Same problem here :+1: Looks like A/B testing to me. qutebrowser has the problem when I login to youtube, but a private BraveBrowser-Tab show the "old" UI. Btw, it would help the issue to add a proper title, I first thought I am the first person to experience the issue.

Edit: The following script from above works for me too:

function setDislikes(dislikesCount) {
  if (isMobile) {
    mobileDislikes = dislikesCount;
    return;
  }
  var txt = getButtons().children[1].querySelector("#text");
  if(txt === null){
      txt = getButtons().children[1].__shady_querySelector("#text");
  }
  if(txt === null){
      txt = getButtons().children[1].__shady_native_querySelector("#text");
  }
  if(txt === null){
      cLog("3rd try failed...")
      return
  }
  txt.innerText = dislikesCount;
}
sy-b commented 2 years ago

Can you please improve the title to make it easier to find & understand?