return-youtube-dislike.script.js 9.7 KB

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