return-youtube-dislike.script.js 9.6 KB

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