return-youtube-dislike.script.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. const LIKED_STATE = "LIKED_STATE";
  2. const DISLIKED_STATE = "DISLIKED_STATE";
  3. const NEUTRAL_STATE = "NEUTRAL_STATE";
  4. if (!storedData) {
  5. var storedData = {
  6. dislikes: 0,
  7. previousState: NEUTRAL_STATE,
  8. };
  9. }
  10. function cLog(message, writer) {
  11. message = `[return youtube dislike]: ${message}`;
  12. if (writer) {
  13. writer(message);
  14. } else {
  15. console.log(message);
  16. }
  17. }
  18. function getButtons() {
  19. //--- If Menu Element Is Displayed: ---//
  20. if (document.getElementById("menu-container")?.offsetParent === null) {
  21. return document.querySelector("ytd-menu-renderer.ytd-watch-metadata > div");
  22. //--- If Menu Element Isnt Displayed: ---//
  23. } else {
  24. return document
  25. .getElementById("menu-container")
  26. ?.querySelector("#top-level-buttons-computed");
  27. }
  28. }
  29. function getLikeButton() {
  30. return getButtons().children[0];
  31. }
  32. function getDislikeButton() {
  33. return getButtons().children[1];
  34. }
  35. function isVideoLiked() {
  36. return getLikeButton().classList.contains("style-default-active");
  37. }
  38. function isVideoDisliked() {
  39. return getDislikeButton().classList.contains("style-default-active");
  40. }
  41. function isVideoNotLiked() {
  42. return getLikeButton().classList.contains("style-text");
  43. }
  44. function isVideoNotDisliked() {
  45. return getDislikeButton().classList.contains("style-text");
  46. }
  47. function getState() {
  48. if (isVideoLiked()) {
  49. return { current: LIKED_STATE, previous: storedData.previousState };
  50. }
  51. if (isVideoDisliked()) {
  52. return { current: DISLIKED_STATE, previous: storedData.previousState };
  53. }
  54. return { current: NEUTRAL_STATE, previous: storedData.previousState };
  55. }
  56. //--- Sets The Likes And Dislikes Values ---//
  57. function setLikes(likesCount) {
  58. getButtons().children[0].querySelector("#text").innerText = likesCount;
  59. }
  60. function setDislikes(dislikesCount) {
  61. getButtons().children[1].querySelector("#text").innerText = dislikesCount;
  62. }
  63. function setState() {
  64. let statsSet = false;
  65. browser.runtime.sendMessage(
  66. {
  67. message: "fetch_from_youtube",
  68. videoId: getVideoId(window.location.href),
  69. },
  70. function (response) {
  71. if (response != undefined) {
  72. cLog("response from youtube:");
  73. cLog(JSON.stringify(response));
  74. try {
  75. if ("likes" in response && "dislikes" in response) {
  76. const formattedDislike = numberFormat(response.dislikes);
  77. setDislikes(formattedDislike);
  78. storedData.dislikes = parseInt(response.dislikes);
  79. createRateBar(response.likes, response.dislikes);
  80. statsSet = true;
  81. }
  82. } catch (e) {
  83. statsSet = false;
  84. }
  85. }
  86. }
  87. );
  88. browser.runtime.sendMessage(
  89. {
  90. message: "set_state",
  91. videoId: getVideoId(window.location.href),
  92. state: getState().current,
  93. },
  94. function (response) {
  95. cLog("response from api:");
  96. cLog(JSON.stringify(response));
  97. if (response != undefined && !("traceId" in response) && !statsSet) {
  98. const formattedDislike = numberFormat(response.dislikes);
  99. storedData.dislikes = response.dislikes;
  100. // setLikes(response.likes);
  101. console.log(response);
  102. setDislikes(formattedDislike);
  103. createRateBar(response.likes, response.dislikes);
  104. } else {
  105. }
  106. }
  107. );
  108. }
  109. function likeClicked() {
  110. if (storedData.previousState === "disliked") {
  111. storedData.dislikes--;
  112. setDislikes(numberFormat(storedData.dislikes));
  113. storedData.previousState = "liked";
  114. }
  115. }
  116. function dislikeClicked() {
  117. let state = getState().current;
  118. if (state == DISLIKED_STATE) {
  119. storedData.dislikes++;
  120. setDislikes(numberFormat(storedData.dislikes));
  121. storedData.previousState = DISLIKED_STATE;
  122. } else if (state == NEUTRAL_STATE) {
  123. storedData.dislikes--;
  124. setDislikes(numberFormat(storedData.dislikes));
  125. storedData.previousState = NEUTRAL_STATE;
  126. }
  127. // setState();
  128. }
  129. function setInitialState() {
  130. setState();
  131. // setTimeout(() => sendVideoIds(), 1500);
  132. }
  133. function getVideoId(url) {
  134. const urlObject = new URL(url);
  135. const pathname = urlObject.pathname;
  136. if (pathname.startsWith("/clips")) {
  137. return document.querySelector("meta[itemprop='videoId']").content;
  138. } else {
  139. return urlObject.searchParams.get("v");
  140. }
  141. }
  142. function isVideoLoaded() {
  143. const videoId = getVideoId(window.location.href);
  144. return (
  145. document.querySelector(`ytd-watch-flexy[video-id='${videoId}']`) !== null
  146. );
  147. }
  148. function roundDown(num) {
  149. if (num < 1000) return num;
  150. const int = Math.floor(Math.log10(num) - 2);
  151. const decimal = int + (int % 3 ? 1 : 0);
  152. const value = Math.floor(num / 10 ** decimal);
  153. return value * 10 ** decimal;
  154. }
  155. function numberFormat(numberState) {
  156. const userLocales = new URL(
  157. Array.from(document.querySelectorAll("head > link[rel='search']"))
  158. ?.find((n) => n?.getAttribute("href")?.includes("?locale="))
  159. ?.getAttribute("href")
  160. )?.searchParams?.get("locale");
  161. const formatter = Intl.NumberFormat(document.documentElement.lang || userLocales || navigator.language, {
  162. notation: "compact",
  163. });
  164. return formatter.format(roundDown(numberState));
  165. }
  166. function setEventListeners(evt) {
  167. function checkForJS_Finish() {
  168. if (getButtons()?.offsetParent && isVideoLoaded()) {
  169. clearInterval(jsInitChecktimer);
  170. const buttons = getButtons();
  171. if (!window.returnDislikeButtonlistenersSet) {
  172. buttons.children[0].addEventListener("click", likeClicked);
  173. buttons.children[1].addEventListener("click", dislikeClicked);
  174. let lastKnownScrollPosition = 0;
  175. let ticking = false;
  176. // document.addEventListener('scroll', function(e) {
  177. // lastKnownScrollPosition = window.scrollY;
  178. //
  179. // if (!ticking) {
  180. // window.requestAnimationFrame(function() {
  181. // // sendVideoIds();
  182. // ticking = false;
  183. // });
  184. //
  185. // ticking = true;
  186. // }
  187. // });
  188. window.returnDislikeButtonlistenersSet = true;
  189. }
  190. setInitialState();
  191. }
  192. }
  193. if (window.location.href.indexOf("watch?") >= 0) {
  194. var jsInitChecktimer = setInterval(checkForJS_Finish, 111);
  195. }
  196. }
  197. function createRateBar(likes, dislikes) {
  198. var rateBar = document.getElementById("return-youtube-dislike-bar-container");
  199. const widthPx =
  200. getButtons().children[0].clientWidth +
  201. getButtons().children[1].clientWidth +
  202. 8;
  203. const widthPercent =
  204. likes + dislikes > 0 ? (likes / (likes + dislikes)) * 100 : 50;
  205. if (!rateBar) {
  206. (
  207. document.querySelector("#actions-inner") ||
  208. document.getElementById("menu-container")
  209. ).insertAdjacentHTML(
  210. "beforeend",
  211. `
  212. <div class="ryd-tooltip" style="width: ${widthPx}px">
  213. <div class="ryd-tooltip-bar-container">
  214. <div
  215. id="return-youtube-dislike-bar-container"
  216. style="width: 100%; height: 2px;"
  217. >
  218. <div
  219. id="return-youtube-dislike-bar"
  220. style="width: ${widthPercent}%; height: 100%"
  221. ></div>
  222. </div>
  223. </div>
  224. <tp-yt-paper-tooltip position="top" id="ryd-dislike-tooltip" class="style-scope ytd-sentiment-bar-renderer" role="tooltip" tabindex="-1">
  225. <div>
  226. <!--css-build:shady-->${likes.toLocaleString()}&nbsp;/&nbsp;${dislikes.toLocaleString()}
  227. </tp-yt-paper-tooltip>
  228. </div>
  229. `
  230. );
  231. } else {
  232. document.getElementById(
  233. "return-youtube-dislike-bar-container"
  234. ).style.width = widthPx + "px";
  235. document.getElementById("return-youtube-dislike-bar").style.width =
  236. widthPercent + "%";
  237. document.querySelector(
  238. "#ryd-dislike-tooltip > #tooltip"
  239. ).innerHTML = `${likes.toLocaleString()}&nbsp;/&nbsp;${dislikes.toLocaleString()}`;
  240. }
  241. }
  242. function sendVideoIds() {
  243. const ids = Array.from(
  244. document.getElementsByClassName(
  245. "yt-simple-endpoint ytd-compact-video-renderer"
  246. )
  247. )
  248. .concat(
  249. Array.from(
  250. document.getElementsByClassName("yt-simple-endpoint ytd-thumbnail")
  251. )
  252. )
  253. .filter((x) => x.href && x.href.indexOf("/watch?v=") > 0)
  254. .map((x) => getVideoId(x.href));
  255. browser.runtime.sendMessage({
  256. message: "send_links",
  257. videoIds: ids,
  258. });
  259. }
  260. setEventListeners();
  261. setTimeout(() => sendVideoIds(), 1500);