return-youtube-dislike.script.js 8.8 KB

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