MAL VS

Adds video search buttons to anime lists on MAL.

  1. // ==UserScript==
  2. // @name MAL VS
  3. // @version 1.0
  4. // @description Adds video search buttons to anime lists on MAL.
  5. // @author Ivan Yancharkin
  6. // @license MIT
  7. // @namespace http://github.com/yancharkin/malvs
  8. // @homepageURL https://github.com/yancharkin/malvs
  9. // @supportURL https://github.com/yancharkin/malvs/issues
  10. // @icon https://github.com/yancharkin/malvs/blob/webextension/assets/icons/icon-48.png?raw=true
  11. // @match *://myanimelist.net/animelist/*
  12. // @match *://myanimelist.net/anime/*
  13. // @grant GM.setValue
  14. // @grant GM.getValue
  15. // ==/UserScript==
  16.  
  17. (async () => {
  18. let icon = 'data:image/webp;base64,UklGRoYFAABXRUJQVlA4WAoAAAAQAAAAKQAAKQAAQUxQSEsAAAABYNxGkqLKP+laPOiFd0RMAOdU/c23H6h2fvnJ94DDgIcDNkMeAeSwN3dk4s7cC3Geiq2RA/zGjZEEv/B6QK/AEz5UeT4i0Z5UuUoAVlA4IBQFAADwGQCdASoqACoAPjESh0KiIQuO/mYQAYJbACdMuz/h7hxcHHXgDJz6iPqA2wHmA/V71kvQf0LnqAegB5cHsR/ut6TmaAdJPnyu3/rv6ATM/9c/HjzAN8A/jX+A3gji/83/uv2s8Ll6mf4d/kfyn9gOMA/jH8b/vf5j/5L6E/1j/Sfd37GvyD+0/6T3AP43/Jf8V/eP2+/t//6+of1q/qr7EH6hmDYASG/vJOZnHOGfTO8xl1SD6fN2Sej0qMXOHDbpFfEZtKwY98sfcy/aDwqlO+rz+7Rr78AA/vjhzSetupX8vvGApvh05L4/4x6f5Z3M7JANOJzEdxNV2PwXSgDrBPHj/cD1uPp8qZxsZWvTEWFNl8iW9kUSwDkKTHTw4N6XdCfhvaM6IdYM9xLo36t+hECYXO50OEjIMxTGgIcMf9mGUJz0KD9VnJDmS4eMy1911Cb9N+4lloThs3saJ/KcgLVU/a62URE4bB9MUjj3sO1c2x6s7XNVHXTCH9uovrfl5yjOm5WYYIGkE/KRQfALgfsyGxahj9Wf/udZupAyYIZmgR/qDd7QQl9Ri0MJNc4SkfkVcLVXTZjoj/uq3BTsx+7APN4hY9QeqBCL0Iz7FbFuD6ldBUB2hwuM16JTOxKTTNwTMhmNHzvXQYLACTeckkbWuywCfO5a0gxr0i4f/IdD/1pJ3b/1r4ZwqArtzl6fkfZbNnAbXDC5paGpV97L1VS9BPvb+CBTofoH+tG81FcBW3+1xvi0BZoYq+S/ui340Q35iwJlJhhcSI9sZ6Ie+KJIBCl6GFhIBcaZts1UYRu/pl3Wfpk9R7DBa4fGttRHLS/8XWezpoVxTGyFGAyaiDp//Mc73yk1oO3nPxO0q1y+hHIJ1PtqWG7Gqw0CUaJEu//3OneWPr/x8Of/1vU//81/nVzDb4zlA8QwfPH29WZxAMmctOqaN7RQOifmzmHUYfsPO08Z7hrwfgBvxRXVS9eO6HLmuazO3n3unIclW26bL8cAsju4Tyoy6uDYh3t1NIWgDL5qJI5s/Hu1n+TzGf3XedmML6EplvZk3Mkhm8npoaaF9v/R2jUAdvtBD8OhjPZgiCZKL03Vh392NTK+L17DT3Igg4D4R48q/PMLMmPnm9wwkTLJvEvOxnuZMJ4R8sswHVqV+6Lb2wvm5rigtMwfljnDhtM2AAo+088uSXIIYuRGNLuPY/0YXYUXJfh+cQLyRW3bhA/aX1CxVp8MfZdqE7fH6uchZmB7+MrecMm3H9RP/9cx4v1r89UfIzHvMnCcETeXn/rdNVYnFU3qnz/whrIhZhMZqJEq+vSu4IVybV/Pmp4zTq02Pgpn9bTjvm8XA9QYD+ctVnbenCn//yUG9IMRIQ0pZyKDybaZUzVY4ok6C2v+9CVxifsrFbuei7Uk97s+PS0/HUiEWmiHvGjpW56n5j/nZCbR/70rHRjCZl00HYm8f0C/7htdYbSBMJB7rGR1of3V77vmsAUKr33Ui/ACDKstKxmCK1DMU8EFpnzzX7/mttikenlzLwaAMkE4yysGxtbCISId1evr8lr8yaOioc3+Sqv/aeR1s+ij4Q1V1ZK7mZIFK9V/99lans9UbcsnlxfQdvze2RY1Bh1f5fkSR1iR9e54HQiRxwVekvxyKaeaxwY9Wna1rLuu9yYWNjHUXQsO/mU4D6znNh/k/LTMTbzvRklk0qfRWRnUDBwiXdGMSV5/5/l20x5Uu4AA';
  19. let sitesList = await GM.getValue('sitesList', "https://www.crunchyroll.com/search?from=&q=");
  20. sitesList = sitesList.split(",")
  21.  
  22. function removeButtons() {
  23. let buttons = document.getElementsByClassName("searchanimebutton");
  24. while (buttons.length > 0) {
  25. buttons[0].parentNode.removeChild(buttons[0]);
  26. };
  27. };
  28.  
  29. function setupButton(button, title){
  30. button.src = icon;
  31. button.alt = title;
  32. button.style.width = "32px";
  33. button.style.cursor = "pointer";
  34. button.onmouseover = function(){
  35. this.style.filter="brightness(1.5)";
  36. this.style.transform="scale(-1.1, 1.1)";
  37. };
  38. button.onmouseout = function(){
  39. this.style.filter="brightness(1)";
  40. this.style.transform="scale(1, 1)";
  41. };
  42. button.onclick = function(){loadDataAndSearch(this.alt)};
  43. return button;
  44. };
  45.  
  46. function addButtons() {
  47. let allAnimes = document.getElementsByClassName("data title clearfix");
  48. let existingButtons = document.getElementsByClassName("searchanimebutton");
  49. if (existingButtons.length < allAnimes.length) {
  50. for (let anime of allAnimes) {
  51. if (anime.parentElement.getElementsByClassName("searchanimebutton").length === 0) {
  52. let title = anime.firstChild.innerHTML;
  53. let button = document.createElement("img");
  54. button = setupButton(button, title);
  55. let malvsTd = document.createElement("td");
  56. malvsTd.className = "data searchanimebutton";
  57. malvsTd.appendChild(button);
  58. anime.parentElement.appendChild(malvsTd);
  59. }
  60. }
  61. }
  62. };
  63.  
  64. function loadDataAndSearch(title) {
  65. for (let site of sitesList) {
  66. if (!site.startsWith("-")) {
  67. window.open(site + title);
  68. }
  69. };
  70. };
  71.  
  72. function addSettingsButton() {
  73. async function settingsEdit() {
  74. let newList = prompt("Sites list: ", await GM.getValue('sitesList', "https://www.crunchyroll.com/search?from=&q="));
  75. if (newList) {
  76. await GM.setValue('sitesList', newList);
  77. newList = newList.split(",")
  78. sitesList = newList;
  79. };
  80. };
  81.  
  82. let settingsDiv = document.createElement("div");
  83. settingsDiv.className = "icon-menu";
  84. let settingsText = document.createElement("span");
  85. settingsText.className = "text";
  86. settingsText.innerHTML = "MAL VS";
  87. settingsText.style.cursor = "pointer";
  88. settingsText.onclick = settingsEdit;
  89. let settingsButton = document.createElement("img");
  90. settingsButton.src = icon;
  91. settingsButton.style.transform="scaleX(-1)";
  92. settingsButton.alt = "MAL VS Settings";
  93. settingsButton.style.width = "24px";
  94. settingsButton.style.height = "24px";
  95. settingsButton.style.position = "absolute";
  96. settingsButton.style.left = "13px";
  97. settingsButton.style.top = "13px";
  98. settingsButton.style.cursor = "pointer";
  99. settingsButton.onmouseover = function() {this.style.filter="brightness(1.5)"};
  100. settingsButton.onmouseout = function() {this.style.filter="brightness(1)"};
  101. settingsButton.onclick = settingsEdit;
  102. settingsDiv.appendChild(settingsButton);
  103. settingsDiv.appendChild(settingsText);
  104. let listMenu = document.getElementsByClassName("list-menu-float")[0];
  105. let settingsMenu;
  106. if (!listMenu) {
  107. settingsDiv.className = "icon-menu";
  108. let menuPlaceholder = document.createElement("div");
  109. menuPlaceholder.className = "list-menu-float";
  110. menuPlaceholder.appendChild(settingsDiv);
  111. document.body.appendChild(menuPlaceholder);
  112. settingsMenu = menuPlaceholder;
  113. } else {
  114. listMenu.appendChild(settingsDiv);
  115. settingsMenu = listMenu;
  116. }
  117. let listMenuOffset = Math.round((window.innerHeight - settingsMenu.offsetHeight)/2);
  118. if (listMenuOffset < 0) listMenuOffset = 0;
  119. settingsMenu.style.top = listMenuOffset + "px";
  120. };
  121.  
  122. const regexp = RegExp("myanimelist.net/animelist//*")
  123. let curUrl = window.location.href;
  124. if (regexp.test(curUrl)) {
  125. addSettingsButton();
  126. if (document.getElementsByClassName("malvs-header").length === 0) {
  127. let tableHeader = document.getElementsByClassName("list-table-header")[0]
  128. let malvsHeader = document.createElement("th");
  129. malvsHeader.className = "header-title malvs-header";
  130. malvsHeader.innerHTML ='<a class="link hover_info">MAL VS</a>';
  131. tableHeader.appendChild(malvsHeader);
  132. };
  133. removeButtons();
  134. setInterval(addButtons, 1000);
  135. } else {
  136. for (let malvsButton of document.getElementsByClassName("searchanimebutton")) {
  137. malvsButton.parentNode.removeChild(malvsButton);
  138. }
  139. let titleDiv = document.getElementsByClassName("h1-title")[0]
  140. titleDiv.style.verticalAlign = "middle";
  141. titleDiv.style.float = "none";
  142. let targetElement = document.getElementsByClassName("h1 edit-info")[0];
  143. targetElement.style.minHeight = "32px";
  144. let title;
  145. try {
  146. title = document.getElementsByClassName("title-name h1_bold_none")[0].firstElementChild.textContent;
  147. } catch(error) {
  148. console.log(error);
  149. title = document.getElementsByClassName("title-name")[0].innerHTML;
  150. }
  151. let malvsDiv = document.createElement("div");
  152. malvsDiv.className = "searchanimebutton";
  153. malvsDiv.style.display = "table-cell";
  154. malvsDiv.style.verticalAlign = "middle";
  155. malvsDiv.style.width = "48px";
  156. malvsDiv.style.transform = "scaleX(-1)";
  157. malvsDiv.style.textAlign = "right";
  158. let button = document.createElement("img");
  159. button = setupButton(button, title);
  160. malvsDiv.appendChild(button);
  161. targetElement.prepend(malvsDiv);
  162. };
  163.  
  164. })();