state.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import { getLikeButton, getDislikeButton, getButtons } from "./buttons";
  2. import { createRateBar } from "./bar";
  3. import {
  4. getBrowser,
  5. getVideoId,
  6. cLog,
  7. numberFormat,
  8. getColorFromTheme,
  9. } from "./utils";
  10. import { sendVideoIds } from "./events";
  11. //TODO: Do not duplicate here and in ryd.background.js
  12. const apiUrl = "https://returnyoutubedislikeapi.com";
  13. const LIKED_STATE = "LIKED_STATE";
  14. const DISLIKED_STATE = "DISLIKED_STATE";
  15. const NEUTRAL_STATE = "NEUTRAL_STATE";
  16. const DISLIKES_DISABLED_TEXT = "DISLIKES DISABLED";
  17. let extConfig = {
  18. disableVoteSubmission: false,
  19. coloredThumbs: false,
  20. coloredBar: false,
  21. colorTheme: "classic",
  22. numberDisplayFormat: "compactShort",
  23. numberDisplayRoundDown: true,
  24. numberDisplayReformatLikes: false,
  25. };
  26. let storedData = {
  27. likes: 0,
  28. dislikes: 0,
  29. previousState: NEUTRAL_STATE,
  30. };
  31. let likesDisabledState = true;
  32. function isMobile() {
  33. return location.hostname == "m.youtube.com";
  34. }
  35. function isShorts() {
  36. return location.pathname.startsWith("/shorts");
  37. }
  38. function isVideoLiked() {
  39. if (isMobile()) {
  40. return (
  41. getLikeButton().querySelector("button").getAttribute("aria-label") ==
  42. "true"
  43. );
  44. }
  45. return getLikeButton().classList.contains("style-default-active");
  46. }
  47. function isVideoDisliked() {
  48. if (isMobile()) {
  49. return (
  50. getDislikeButton().querySelector("button").getAttribute("aria-label") ==
  51. "true"
  52. );
  53. }
  54. return getDislikeButton().classList.contains("style-default-active");
  55. }
  56. function getState(storedData) {
  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. if (!likesDisabledState) {
  71. if (isMobile()) {
  72. getButtons().children[1].querySelector(
  73. ".button-renderer-text"
  74. ).innerText = dislikesCount;
  75. return;
  76. }
  77. getButtons().children[1].querySelector("#text").innerText = dislikesCount;
  78. } else {
  79. cLog("likes count disabled by creator");
  80. if (isMobile()) {
  81. getButtons().children[1].querySelector(
  82. ".button-renderer-text"
  83. ).innerText = DISLIKES_DISABLED_TEXT;
  84. return;
  85. }
  86. getButtons().children[1].querySelector("#text").innerText =
  87. DISLIKES_DISABLED_TEXT;
  88. }
  89. }
  90. function getLikeCountFromButton() {
  91. if (isShorts()) {
  92. //Youtube Shorts don't work with this query. It's not nessecary; we can skip it and still see the results.
  93. //It should be possible to fix this function, but it's not critical to showing the dislike count.
  94. return 0;
  95. }
  96. let likesStr = getLikeButton()
  97. .querySelector("button")
  98. .getAttribute("aria-label")
  99. .replace(/\D/g, "");
  100. return likesStr.length > 0 ? parseInt(likesStr) : false;
  101. }
  102. function processResponse(response, storedData) {
  103. const formattedDislike = numberFormat(response.dislikes);
  104. setDislikes(formattedDislike);
  105. if (extConfig.numberDisplayReformatLikes === true) {
  106. const nativeLikes = getLikeCountFromButton();
  107. if (nativeLikes !== false) {
  108. setLikes(numberFormat(nativeLikes));
  109. }
  110. }
  111. storedData.dislikes = parseInt(response.dislikes);
  112. storedData.likes = getLikeCountFromButton() || parseInt(response.likes);
  113. createRateBar(storedData.likes, storedData.dislikes);
  114. if (extConfig.coloredThumbs === true) {
  115. getLikeButton().style.color = getColorFromTheme(true);
  116. getDislikeButton().style.color = getColorFromTheme(false);
  117. }
  118. }
  119. async function setState(storedData) {
  120. storedData.previousState = isVideoDisliked()
  121. ? DISLIKED_STATE
  122. : isVideoLiked()
  123. ? LIKED_STATE
  124. : NEUTRAL_STATE;
  125. let statsSet = false;
  126. let videoId = getVideoId(window.location.href);
  127. let likeCount = getLikeCountFromButton() || null;
  128. let response = await fetch(
  129. `${apiUrl}/votes?videoId=${videoId}&likeCount=${likeCount || ""}`,
  130. {
  131. method: "GET",
  132. headers: {
  133. Accept: "application/json",
  134. },
  135. }
  136. )
  137. .then((response) => response.json())
  138. .catch();
  139. cLog("response from api:");
  140. cLog(JSON.stringify(response));
  141. likesDisabledState =
  142. numberFormat(response.dislikes) == 0 &&
  143. numberFormat(response.likes) == 0 &&
  144. numberFormat(response.viewCount) == 0;
  145. if (response !== undefined && !("traceId" in response) && !statsSet) {
  146. processResponse(response, storedData);
  147. }
  148. }
  149. function setInitialState() {
  150. setState(storedData);
  151. setTimeout(() => {
  152. sendVideoIds();
  153. }, 1500);
  154. }
  155. function initExtConfig() {
  156. initializeDisableVoteSubmission();
  157. initializeColoredThumbs();
  158. initializeColoredBar();
  159. initializeColorTheme();
  160. initializeNumberDisplayFormat();
  161. initializeNumberDisplayRoundDown();
  162. initializeNumberDisplayReformatLikes();
  163. }
  164. function initializeDisableVoteSubmission() {
  165. getBrowser().storage.sync.get(["disableVoteSubmission"], (res) => {
  166. if (res.disableVoteSubmission === undefined) {
  167. getBrowser().storage.sync.set({ disableVoteSubmission: false });
  168. } else {
  169. extConfig.disableVoteSubmission = res.disableVoteSubmission;
  170. }
  171. });
  172. }
  173. function initializeColoredThumbs() {
  174. getBrowser().storage.sync.get(["coloredThumbs"], (res) => {
  175. if (res.coloredThumbs === undefined) {
  176. getBrowser().storage.sync.set({ coloredThumbs: false });
  177. } else {
  178. extConfig.coloredThumbs = res.coloredThumbs;
  179. }
  180. });
  181. }
  182. function initializeColoredBar() {
  183. getBrowser().storage.sync.get(["coloredBar"], (res) => {
  184. if (res.coloredBar === undefined) {
  185. getBrowser().storage.sync.set({ coloredBar: false });
  186. } else {
  187. extConfig.coloredBar = res.coloredBar;
  188. }
  189. });
  190. }
  191. function initializeNumberDisplayRoundDown() {
  192. getBrowser().storage.sync.get(["numberDisplayRoundDown"], (res) => {
  193. if (res.numberDisplayRoundDown === undefined) {
  194. getBrowser().storage.sync.set({ numberDisplayRoundDown: true });
  195. } else {
  196. extConfig.numberDisplayRoundDown = res.numberDisplayRoundDown;
  197. }
  198. });
  199. }
  200. function initializeColorTheme() {
  201. getBrowser().storage.sync.get(["colorTheme"], (res) => {
  202. if (res.colorTheme === undefined) {
  203. getBrowser().storage.sync.set({ colorTheme: false });
  204. } else {
  205. extConfig.colorTheme = res.colorTheme;
  206. }
  207. });
  208. }
  209. function initializeNumberDisplayFormat() {
  210. getBrowser().storage.sync.get(["numberDisplayFormat"], (res) => {
  211. if (res.numberDisplayFormat === undefined) {
  212. getBrowser().storage.sync.set({ numberDisplayFormat: "compactShort" });
  213. } else {
  214. extConfig.numberDisplayFormat = res.numberDisplayFormat;
  215. }
  216. });
  217. }
  218. function initializeNumberDisplayReformatLikes() {
  219. getBrowser().storage.sync.get(["numberDisplayReformatLikes"], (res) => {
  220. if (res.numberDisplayReformatLikes === undefined) {
  221. getBrowser().storage.sync.set({ numberDisplayReformatLikes: false });
  222. } else {
  223. extConfig.numberDisplayReformatLikes = res.numberDisplayReformatLikes;
  224. }
  225. });
  226. }
  227. export {
  228. isMobile,
  229. isShorts,
  230. isVideoDisliked,
  231. isVideoLiked,
  232. getState,
  233. setState,
  234. setInitialState,
  235. setLikes,
  236. setDislikes,
  237. getLikeCountFromButton,
  238. LIKED_STATE,
  239. DISLIKED_STATE,
  240. NEUTRAL_STATE,
  241. extConfig,
  242. initExtConfig,
  243. storedData,
  244. likesDisabledState,
  245. };