state.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. };
  25. let storedData = {
  26. likes: 0,
  27. dislikes: 0,
  28. previousState: NEUTRAL_STATE,
  29. };
  30. let likesDisabledState = true;
  31. function isMobile() {
  32. return location.hostname == "m.youtube.com";
  33. }
  34. function isShorts() {
  35. return location.pathname.startsWith("/shorts");
  36. }
  37. let mutationObserver = new Object();
  38. if (isShorts() && mutationObserver.exists !== true) {
  39. cLog('initializing mutation observer')
  40. mutationObserver.options = {
  41. childList: false,
  42. attributes: true,
  43. subtree: false
  44. };
  45. mutationObserver.exists = true;
  46. mutationObserver.observer = new MutationObserver( function(mutationList, observer) {
  47. mutationList.forEach( (mutation) => {
  48. if (mutation.type === 'attributes' &&
  49. mutation.target.nodeName === 'TP-YT-PAPER-BUTTON' &&
  50. mutation.target.id === 'button') {
  51. // cLog('Short thumb button status changed');
  52. if (mutation.target.getAttribute('aria-pressed') === 'true') {
  53. mutation.target.style.color =
  54. (mutation.target.parentElement.parentElement.id === 'like-button') ?
  55. getColorFromTheme(true) : getColorFromTheme(false);
  56. } else {
  57. mutation.target.style.color = 'unset';
  58. }
  59. return;
  60. }
  61. cLog('unexpected mutation observer event: ' + mutation.target + mutation.type);
  62. });
  63. });
  64. }
  65. function isVideoLiked() {
  66. if (isMobile()) {
  67. return (
  68. getLikeButton().querySelector("button").getAttribute("aria-label") ==
  69. "true"
  70. );
  71. }
  72. return getLikeButton().classList.contains("style-default-active");
  73. }
  74. function isVideoDisliked() {
  75. if (isMobile()) {
  76. return (
  77. getDislikeButton().querySelector("button").getAttribute("aria-label") ==
  78. "true"
  79. );
  80. }
  81. return getDislikeButton().classList.contains("style-default-active");
  82. }
  83. function getState(storedData) {
  84. if (isVideoLiked()) {
  85. return { current: LIKED_STATE, previous: storedData.previousState };
  86. }
  87. if (isVideoDisliked()) {
  88. return { current: DISLIKED_STATE, previous: storedData.previousState };
  89. }
  90. return { current: NEUTRAL_STATE, previous: storedData.previousState };
  91. }
  92. //--- Sets The Likes And Dislikes Values ---//
  93. function setLikes(likesCount) {
  94. getButtons().children[0].querySelector("#text").innerText = likesCount;
  95. }
  96. function setDislikes(dislikesCount) {
  97. if (!likesDisabledState) {
  98. if (isMobile()) {
  99. getButtons().children[1].querySelector(
  100. ".button-renderer-text"
  101. ).innerText = dislikesCount;
  102. return;
  103. }
  104. getButtons().children[1].querySelector("#text").innerText = dislikesCount;
  105. } else {
  106. cLog("likes count disabled by creator");
  107. if (isMobile()) {
  108. getButtons().children[1].querySelector(
  109. ".button-renderer-text"
  110. ).innerText = DISLIKES_DISABLED_TEXT;
  111. return;
  112. }
  113. getButtons().children[1].querySelector("#text").innerText =
  114. DISLIKES_DISABLED_TEXT;
  115. }
  116. }
  117. function getLikeCountFromButton() {
  118. if (isShorts()) {
  119. //Youtube Shorts don't work with this query. It's not nessecary; we can skip it and still see the results.
  120. //It should be possible to fix this function, but it's not critical to showing the dislike count.
  121. return 0;
  122. }
  123. let likesStr = getLikeButton()
  124. .querySelector("button")
  125. .getAttribute("aria-label")
  126. .replace(/\D/g, "");
  127. return likesStr.length > 0 ? parseInt(likesStr) : false;
  128. }
  129. function processResponse(response, storedData) {
  130. const formattedDislike = numberFormat(response.dislikes);
  131. setDislikes(formattedDislike);
  132. storedData.dislikes = parseInt(response.dislikes);
  133. storedData.likes = getLikeCountFromButton() || parseInt(response.likes);
  134. createRateBar(storedData.likes, storedData.dislikes);
  135. if (extConfig.coloredThumbs === true) {
  136. if (isShorts()) { // for shorts, leave deactived buttons in default color
  137. let shortLikeButton = getLikeButton().querySelector('tp-yt-paper-button#button');
  138. let shortDislikeButton = getDislikeButton().querySelector('tp-yt-paper-button#button');
  139. if (shortLikeButton.getAttribute('aria-pressed') === 'true') {
  140. shortLikeButton.style.color = getColorFromTheme(true);
  141. }
  142. if (shortDislikeButton.getAttribute('aria-pressed') === 'true') {
  143. shortDislikeButton.style.color = getColorFromTheme(false);
  144. }
  145. mutationObserver.observer.observe(shortLikeButton, mutationObserver.options);
  146. mutationObserver.observer.observe(shortDislikeButton, mutationObserver.options);
  147. } else {
  148. getLikeButton().style.color = getColorFromTheme(true);
  149. getDislikeButton().style.color = getColorFromTheme(false);
  150. }
  151. }
  152. }
  153. async function setState(storedData) {
  154. storedData.previousState = isVideoDisliked()
  155. ? DISLIKED_STATE
  156. : isVideoLiked()
  157. ? LIKED_STATE
  158. : NEUTRAL_STATE;
  159. let statsSet = false;
  160. let videoId = getVideoId(window.location.href);
  161. let likeCount = getLikeCountFromButton() || null;
  162. let response = await fetch(
  163. `${apiUrl}/votes?videoId=${videoId}&likeCount=${likeCount || ""}`,
  164. {
  165. method: "GET",
  166. headers: {
  167. Accept: "application/json",
  168. },
  169. }
  170. )
  171. .then((response) => response.json())
  172. .catch();
  173. cLog("response from api:");
  174. cLog(JSON.stringify(response));
  175. likesDisabledState =
  176. numberFormat(response.dislikes) == 0 &&
  177. numberFormat(response.likes) == 0 &&
  178. numberFormat(response.viewCount) == 0;
  179. if (response !== undefined && !("traceId" in response) && !statsSet) {
  180. processResponse(response, storedData);
  181. }
  182. }
  183. function setInitialState() {
  184. setState(storedData);
  185. setTimeout(() => {
  186. sendVideoIds();
  187. }, 1500);
  188. }
  189. function initExtConfig() {
  190. initializeDisableVoteSubmission();
  191. initializeColoredThumbs();
  192. initializeColoredBar();
  193. initializeColorTheme();
  194. initializeNumberDisplayFormat();
  195. initializeNumberDisplayRoundDown();
  196. }
  197. function initializeDisableVoteSubmission() {
  198. getBrowser().storage.sync.get(["disableVoteSubmission"], (res) => {
  199. if (res.disableVoteSubmission === undefined) {
  200. getBrowser().storage.sync.set({ disableVoteSubmission: false });
  201. } else {
  202. extConfig.disableVoteSubmission = res.disableVoteSubmission;
  203. }
  204. });
  205. }
  206. function initializeColoredThumbs() {
  207. getBrowser().storage.sync.get(["coloredThumbs"], (res) => {
  208. if (res.coloredThumbs === undefined) {
  209. getBrowser().storage.sync.set({ coloredThumbs: false });
  210. } else {
  211. extConfig.coloredThumbs = res.coloredThumbs;
  212. }
  213. });
  214. }
  215. function initializeColoredBar() {
  216. getBrowser().storage.sync.get(["coloredBar"], (res) => {
  217. if (res.coloredBar === undefined) {
  218. getBrowser().storage.sync.set({ coloredBar: false });
  219. } else {
  220. extConfig.coloredBar = res.coloredBar;
  221. }
  222. });
  223. }
  224. function initializeNumberDisplayRoundDown() {
  225. getBrowser().storage.sync.get(["numberDisplayRoundDown"], (res) => {
  226. if (res.numberDisplayRoundDown === undefined) {
  227. getBrowser().storage.sync.set({ numberDisplayRoundDown: true });
  228. } else {
  229. extConfig.numberDisplayRoundDown = res.numberDisplayRoundDown;
  230. }
  231. });
  232. }
  233. function initializeColorTheme() {
  234. getBrowser().storage.sync.get(["colorTheme"], (res) => {
  235. if (res.colorTheme === undefined) {
  236. getBrowser().storage.sync.set({ colorTheme: false });
  237. } else {
  238. extConfig.colorTheme = res.colorTheme;
  239. }
  240. });
  241. }
  242. function initializeNumberDisplayFormat() {
  243. getBrowser().storage.sync.get(["numberDisplayFormat"], (res) => {
  244. if (res.numberDisplayFormat === undefined) {
  245. getBrowser().storage.sync.set({ numberDisplayFormat: "compactShort" });
  246. } else {
  247. extConfig.numberDisplayFormat = res.numberDisplayFormat;
  248. }
  249. });
  250. }
  251. export {
  252. isMobile,
  253. isShorts,
  254. isVideoDisliked,
  255. isVideoLiked,
  256. getState,
  257. setState,
  258. setInitialState,
  259. setLikes,
  260. setDislikes,
  261. getLikeCountFromButton,
  262. LIKED_STATE,
  263. DISLIKED_STATE,
  264. NEUTRAL_STATE,
  265. extConfig,
  266. initExtConfig,
  267. storedData,
  268. likesDisabledState,
  269. };