Return Youtube Dislike On Mobile

Return dislikes on youtube mobile page. Uses returnyoutubedislike.com API

当前为 2024-01-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Return Youtube Dislike On Mobile
  3. // @namespace https://gitlab.com/Dwyriel
  4. // @version 1.2.1
  5. // @description Return dislikes on youtube mobile page. Uses returnyoutubedislike.com API
  6. // @author Dwyriel
  7. // @license MIT
  8. // @match *://*.youtube.com/*
  9. // @grant none
  10. // @homepageURL https://gitlab.com/Dwyriel/Greasyfork-Scripts
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15. const scriptName = "[Return Youtube Dislike On Mobile]";
  16. const API_URL = "https://returnyoutubedislikeapi.com/votes?videoId=";
  17. const buttonSegmentClass = "YtSegmentedLikeDislikeButtonViewModelSegmentedButtonsWrapper";
  18. const dislikeButtonID = "dislikeButtonID_198wa16df78ms1d";
  19.  
  20. let dislikeCache = {};
  21. let oldURL = "";
  22. let videoID;
  23. let fetching = false;
  24.  
  25. const config = { attributes: true, childList: true, subtree: true };
  26. let mutationObserver = new MutationObserver(() => { });
  27.  
  28. const getVideoID = () => {
  29. return (new Proxy(new URLSearchParams(window.location.search), { get: (UrlSearchParams, key) => UrlSearchParams.get(key) })).v;
  30. }
  31.  
  32. const formatedDislikeNumber = () => {
  33. let dislikes = dislikeCache[videoID];
  34. let formattedNum = 0, character = '';
  35. if (dislikes / 1000000000 >= 1) {
  36. formattedNum = Math.round(dislikes / 1000000000);
  37. character = 'B';
  38. } else if (dislikes / 1000000 >= 1) {
  39. formattedNum = Math.round(dislikes / 1000000);
  40. character = 'M';
  41. } else if (dislikes / 10000 >= 1) {
  42. formattedNum = Math.round(dislikes / 1000);
  43. character = 'K';
  44. } else
  45. formattedNum = dislikes;
  46. return `${formattedNum}${character}`;
  47. }
  48.  
  49. const modifyDislikeButton = () => { //check explanation at the end of the file
  50. let buttons = document.getElementsByClassName(buttonSegmentClass)[0].children;
  51. if (buttons.length == 0)
  52. return;
  53. document.getElementById(dislikeButtonID)?.remove();
  54. let dislikeButton = buttons[1].children[0].children[0];
  55. dislikeButton.children[0].style = "margin: 0 6px 0 -6px";
  56. let dislikes = buttons[0].children[0].children[0].children[1].cloneNode(true);
  57. dislikes.id = dislikeButtonID;
  58. dislikeButton.appendChild(dislikes);
  59. dislikeButton.appendChild(dislikeButton.children[1]);
  60. let dislikeString = formatedDislikeNumber();
  61. dislikes.innerHTML = dislikeString;
  62. dislikeButton.style = `width: ${56 + (8 * dislikeString.length)}px !important`;
  63. }
  64.  
  65. let hookObserver = async () => {
  66. let buttons = document.getElementsByClassName(buttonSegmentClass);
  67. if (buttons.length > 0 && buttons[0].children != undefined) {
  68. mutationObserver.disconnect();
  69. modifyDislikeButton();
  70. mutationObserver.observe(buttons[0].children[0].parentNode, config);
  71. }
  72. else
  73. await new Promise(() => setTimeout(hookObserver, 100));
  74. }
  75.  
  76. const callback = () => {
  77. let currURL = window.location.href;
  78. if (window.location.pathname != "/watch") {
  79. oldURL = currURL;
  80. return;
  81. }
  82. if (fetching || (oldURL == currURL))
  83. return;
  84. fetching = true;
  85. oldURL = currURL;
  86.  
  87. videoID = getVideoID();
  88. if (typeof videoID != 'string') {
  89. fetching = false;
  90. return;
  91. }
  92.  
  93. if (dislikeCache[videoID] != undefined) {
  94. fetching = false;
  95. hookObserver();
  96. return;
  97. }
  98.  
  99. let request = new Request(API_URL + videoID);
  100. fetch(request).then(response => response.json(), (reason) => { fetching = false; console.error("Couldn't fetch dislikes", reason) }).then(response => {
  101. console.log(`${scriptName} response from api: \n${JSON.stringify(response)}`);
  102. dislikeCache[videoID] = response.dislikes;
  103. fetching = false;
  104. hookObserver();
  105. }, (reason) => { fetching = false; console.error("Couldn't fetch dislikes", reason) });
  106. };
  107.  
  108. mutationObserver = new MutationObserver(() => {
  109. hookObserver();
  110. });
  111.  
  112. const old_pushState = history.pushState;
  113. history.pushState = function pushState() {
  114. let origFuncReturn = old_pushState.apply(this, arguments);
  115. window.dispatchEvent(new Event('historyChanged'));
  116. return origFuncReturn;
  117. };
  118.  
  119. window.addEventListener('popstate', () => window.dispatchEvent(new Event('historyChanged')));
  120. window.addEventListener('load', () => callback());
  121. window.addEventListener('historyChanged', () => {
  122. mutationObserver.disconnect();
  123. callback();
  124. });
  125. })();
  126.  
  127. /* modifyDislikeButton function explanation
  128.  
  129. let buttons = document.getElementsByClassName(buttonSegmentClass)[0].children; //get both like and dislike buttons if they exist
  130. if (buttons.length == 0)
  131. return;
  132.  
  133. document.getElementById(dislikeButtonID)?.remove(); //remove if it was already created before
  134.  
  135. let dislikeButton = buttons[1].children[0].children[0]; //the dislike "button" tag
  136. dislikeButton.children[0].style = "margin: 0 6px 0 -6px"; //fix margin to accomodate changes
  137.  
  138. let dislikes = buttons[0].children[0].children[0].children[1].cloneNode(true); //clone the text tag of the like button to be added to the dislike
  139.  
  140. dislikes.id = dislikeButtonID; //set custom ID
  141.  
  142. dislikeButton.appendChild(dislikes); //append cloned node to dislike button
  143.  
  144. dislikeButton.appendChild(dislikeButton.children[1]); //move nodes around to be in the same order as the like button
  145.  
  146. let dislikeString = formatedDislikeNumber(); //formats and adds the formated string to the cloned node's inner HTML
  147. dislikes.innerHTML = dislikeString;
  148.  
  149. dislikeButton.style = `width: ${56 + (8 * dislikeString.length)}px`; //adjust button width based on formated string
  150. */