Find The Correct Watch Order And All Related Entries+Live-Actions+Doramas + Copy Entry Title

Find the Correct Watch Order And All Related Entries. Also, find out if an anime has any Live-Action or Dorama adaptations, and Copy The entry Title.

当前为 2023-12-10 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Find The Correct Watch Order And All Related Entries+Live-Actions+Doramas + Copy Entry Title
  3. // @namespace Search for Live-Actions\Doramas\All Related Entries + Correct Watch Order + Copy Entry Title
  4. // @version 48
  5. // @description Find the Correct Watch Order And All Related Entries. Also, find out if an anime has any Live-Action or Dorama adaptations, and Copy The entry Title.
  6. // @author hacker09
  7. // @include https://myanimelist.net/forum/?topicid=1863965
  8. // @include /^https:\/\/myanimelist\.net\/((anime|manga)(id=)?(\.php\?id=)?)(\/)?([\d]+)/
  9. // @exclude https://myanimelist.net/anime/genre/*
  10. // @exclude https://myanimelist.net/anime/producer/*
  11. // @icon https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://myanimelist.net&size=64
  12. // @run-at document-end
  13. // @connect chiaki.site
  14. // @connect mydramalist.com
  15. // @grant GM.xmlHttpRequest
  16. // ==/UserScript==
  17.  
  18. (function() {
  19. 'use strict';
  20. var hasRun = true; //Create a new variable
  21. async function Prog() { //Run the program
  22. if (document.hasFocus() && hasRun) //If the tab has focus and it's the first time the script runs
  23. { //Starts the if condition
  24. hasRun = false; //Change the var condition
  25. if (location.href === 'https://myanimelist.net/forum/?topicid=1863965') { //If the user is on the Official Guidex Index
  26. $("b:contains('Guides available:')")[0].innerHTML = '<b style="font-weight: normal;">(Click on the letter you want to jump to.)</b><br><div style="cursor: pointer;"><b id="GoToA">Guides available: A</b> | <b id="GoToB">B</b> | <b id="GoToC">C</b> | <b id="GoToD">D</b> | <b id="GoToE">E</b> | <b id="GoToF">F</b> | <b id="GoToG">G</b> | <b id="GoToH">H</b> | <b id="GoToI">I</b> | <b id="GoToJ">J</b> | <b id="GoToK">K</b> | <b id="GoToL">L</b> | <b id="GoToM">M</b> | <b id="GoToN">N</b> | <b id="GoToO">O</b> | <b id="GoToP">P</b> | <b id="GoToQ">Q</b> | <b id="GoToR">R</b> | <b id="GoToS">S</b> | <b id="GoToT">T</b> | <b id="GoToU">U</b> | <b id="GoToV">V</b> | <b id="GoToW">W</b> | <b id="GoToX">X</b> | <b id="GoToY">Y</b> | <b id="GoToZ">Z</b><div> <style>#topBtn {display: block;position: fixed;bottom: 20px;right: 30px;z-index: 99;font-size: 18px;border: none;outline: none;background-color: #2e51a2;color: white;cursor: pointer;padding: 15px;border-radius: 4px;}</style><button onclick="document.documentElement.scrollTop = 0;" id="topBtn" title="Go to top" style="transform: rotate(90deg); display: block;">&lt;</button>'; //Adds a text and makes the letters clickable. Also adds a scroll to top button on the page
  27.  
  28. for (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); ++i) { //For every charCode
  29. document.querySelector(`b#GoTo${String.fromCharCode(i)}`).onclick = () => document.querySelectorAll('b')[i - 36].scrollIntoView(); //Scroll the page until the letter can be seen
  30. } //Finishes the for condition
  31. } //Finishes the if condition
  32. else //If the user isn't on any Guide Index
  33. { //Starts the else condition
  34.  
  35. const findButton = document.createElement("a"), copyButton = document.createElement("a"), chiakiButton = document.createElement("a"); //Creates an "a" element so the button will appear
  36. var ChiakiFranchiseTitle, ChiakiFranchiseTitleWithSymbols, MalClubText, ChiakiDocument, IMDBAsianWiki, hasAnime = '', ChiakiTextData = [], ChiakientryidSArray = [], MyDramaListText = ' and MyDramaList', MyDramaListCheck = '👍 Found on MyDramaList.'; //Create new global variables
  37.  
  38. if (location.pathname.split('/')[1] === 'manga') { //If the user is in an manga entry
  39. const Relations = await (await fetch('https://api.jikan.moe/v4/' + location.href.split('/')[3] + '/' + location.pathname.match(/\d+/)[0] + '/relations')).json(); //Fetch
  40. hasAnime = Relations.data.flatMap(relation => relation.entry).find(entry => entry.type === "anime"); //Try getting 1 entry that is an anime
  41. } //Finishes the if condition
  42.  
  43. var entryid = hasAnime !== undefined && hasAnime !== '' ? hasAnime.mal_id : location.pathname.match(/\d+/)[0]; //Get the anime id
  44.  
  45. GM.xmlHttpRequest({ //Starts the xmlHttpRequest
  46. method: "GET",
  47. url: 'https://chiaki.site/?/tools/watch_order/id/' + entryid,
  48. onload: (response) => { //Starts the onload event listener
  49. ChiakiDocument = new DOMParser().parseFromString(response.responseText, 'text/html'); //Parses the fetch response
  50.  
  51. if (ChiakiDocument.querySelector("h2") !== null) { //Starts the if condition
  52. ChiakiFranchiseTitle = ChiakiDocument.querySelector("h2").innerText.split(' Watch Order')[0].replace(/[^a-zA-Z0-9]+/g, " ").trim(); //Get the anime title on the h2 element and remove the Watch Order text, symbols, and whitespaces
  53. ChiakiFranchiseTitleWithSymbols = ChiakiDocument.querySelector("h2").innerText.split(' Watch Order')[0].trim(); //Get the anime title on the h2 element (with symbols) and remove the Watch Order text, and whitespaces
  54. } //Finishes the if condition
  55.  
  56. ChiakiDocument.querySelectorAll("span.uk-text-muted.uk-text-small").forEach((TextElement, i) => { //Loop through the elements
  57. ChiakientryidSArray.push(ChiakiDocument.querySelectorAll("span.uk-text-muted.uk-text-small > a:nth-child(1)")[i].href.match(/\d+/)[0]); //Add All Anime Links on chiaki to an Array
  58.  
  59. const TotalRawDuration = TextElement.textContent.split("×")[1].split("|")[0].trim(); //Creates a variable to hold the total unprocessed times
  60. const ALLChiakiTitles = ChiakiDocument.querySelectorAll("span.wo_title")[i].innerText; //Creates a variable to get all the anime titles on chiaki site
  61. const TotalEpisodes = TextElement.textContent.split("|")[2].match(/\d+|\?/g)[0]; //Creates a variable to hold the total episodes
  62. const EpisodeType = TextElement.textContent.split("|")[1].trim(); //Creates a variable to get the episode types
  63. var eps = ' eps'; //Create a variable called eps
  64. var Duration = ''; //Creates a blank variable
  65. var PerEp = ' per ep'; //Create a variable called PerEp
  66. if (TotalEpisodes === '1') { //If the entry has only 1 ep
  67. eps = ' ' + EpisodeType; //Change the variable called eps
  68. PerEp = ''; //Change the variable called PerEp
  69. } //Finishes the if condition
  70. if (EpisodeType !== 'TV') { //If the entry type isn't TV
  71. if (TotalEpisodes !== '1') { //If the entry doesn't have only 1 ep
  72. eps = ' ' + EpisodeType + 's'; //Change the variable called eps
  73. } //Finishes the if condition
  74. Duration = ' of ' + TotalRawDuration + PerEp; //Defines the Duration variable if the episode type isn't TV
  75. } //Finishes the if condition
  76. ChiakiTextData.push(ALLChiakiTitles + ',, ' + TotalEpisodes + eps + Duration + ',\n'); //Add Everything to an Array
  77. }); //Finishes the for condition
  78.  
  79. } //Finishes the onload event listener
  80. }); //Finishes the xmlHttpRequest
  81.  
  82. GM.xmlHttpRequest({ //Starts the xmlHttpRequest
  83. method: "GET",
  84. url: "https://mydramalist.com/search?q=" + (hasAnime !== undefined ? ChiakiFranchiseTitle : document.querySelector("[itemprop*='name']").innerText.split('\n')[0]) + '&adv=titles&ty=68,77,83,86',
  85. onload: (response) => { //Starts the onload event listener
  86. const MyDramaListDocument = new DOMParser().parseFromString(response.responseText, 'text/html'); //Parses the fetch response
  87. if (MyDramaListDocument.querySelector(".m-b-sm") === null) { //If MyDramaList did not return any results
  88. MyDramaListText = ''; //Display to the user that MyDramaList Won't be opened if OK is clicked
  89. MyDramaListCheck = '✖ NOT Found on MyDramaList.'; //Display the confirmation that the anime doesn't have any adaptations found on MyDramaList
  90. } //Finishes the if condition
  91. } //Finishes the onload event listener
  92. }); //Finishes the xmlHttpRequest
  93.  
  94. copyButton.addEventListener("click", () => {
  95. navigator.clipboard.writeText(document.querySelector("[itemprop*='name']").innerText.split('\n')[0]); //Copy the entry title with symbols
  96. }); //Detect the single mouse click
  97. copyButton.addEventListener("dblclick", () => {
  98. navigator.clipboard.writeText(document.querySelector("[itemprop*='name']").innerText.split('\n')[0].replace(/[^a-zA-Z0-9]+/g, " ")); //Copy the entry title without symbols
  99. }); //Detect the double mouse click
  100.  
  101. copyButton.addEventListener("contextmenu", (e) => { //Detect a mouse click
  102. hasAnime !== undefined ? navigator.clipboard.writeText(ChiakiTextData.join('').trim()) : ''; //Copy the array to the clipboard
  103. e.preventDefault(); //Don't show the right-click default context menu
  104. }); //Detect the mouse right click
  105.  
  106. hasAnime !== undefined ? copyButton.setAttribute("title", "Click To Copy Entry Title (+ Symbols)\n2 Clicks To Copy Entry Title (Without Symbols)\n\nRight click to Copy ALL Anime Only Entry Titles on The Broadcast Order With EP Numbers, Entry Types and Duration Times") : copyButton.setAttribute("title", "1 Click To Copy Entry Title (+ Symbols)\n2 Clicks To Copy Entry Title (Without Symbols)"); //Detect a mouse hover on the button and shows an explanation text
  107.  
  108. copyButton.setAttribute("style", "cursor: pointer;margin-left: 13px;height: 10px;width: 10px;background-size: cover;display: inline-block;transform: scale(1.8);vertical-align: top;margin-top: 7px;"); //The CSS for the copy button
  109. copyButton.style.backgroundImage = `url(${document.querySelector(".dark-mode") !== null ? 'https://i.imgur.com/hIfOM22.png' : 'https://i.imgur.com/vU0m0ye.png'})`; //The copy button image
  110.  
  111. findButton.addEventListener("click", async () => { //Detect the mouse click and search for the anime title
  112. if (location.pathname.split('/')[1] === 'manga' || confirm('If you want to search using the Entry Title instead of the Franchise Title\nPress OK')) { //Show the confirmation alert box text
  113. ChiakiFranchiseTitle = document.querySelector("[itemprop*='name']").innerText.split('\n')[0]; //Change the Franchise title we got from Chiaki to the entry title (to search on mydramalist)
  114. ChiakiFranchiseTitleWithSymbols = document.querySelector("[itemprop*='name']").innerText.split('\n')[0]; //Change the Franchise title we got from Chiaki to the entry title (to search on the mal club)
  115. } //Finishes the if condition
  116.  
  117. if ([...ChiakiDocument.querySelectorAll('span.uk-text-muted.uk-text-small')].find(el => el.innerText.includes('TV')) !== null) { //If the Franchise has at least 1 entry that the type is TV
  118. const response = await (await fetch('https://myanimelist.net/clubs.php?cid=5450')).text(); //Fetch
  119.  
  120. new DOMParser().parseFromString(response, 'text/html').body.innerText.search(ChiakiFranchiseTitleWithSymbols) > -1 ? MalClubText = '👍 Found on the [[ Live Action Adaptations ]] MAL Club' : MalClubText = '✖ NOT found on the [[ Live Action Adaptations ]] MAL Club'; //If the title is found on the MALClub, display the confirmation whether or not the anime has adaptations found on the MALClub
  121.  
  122. MyDramaListCheck.match('👍') !== null || MalClubText.match('👍') !== null ? IMDBAsianWiki = 'IMDB, AsianWiki' : IMDBAsianWiki = 'IMDB and AsianWiki'; //Change the IMDBAsianWiki variable depending on if mydramalist or the mal club returned any results or not
  123.  
  124. if (confirm('Franchise Title: ' + ChiakiFranchiseTitle + '\n\n' + MyDramaListCheck + '\n' + MalClubText + '\n\nDo you want to open ' + IMDBAsianWiki + MyDramaListText + ' to confirm that information and get more detailed info?')) { //Show the confirmation alert box text
  125. open("https://www.imdb.com/find?s=tt&q=" + ChiakiFranchiseTitle + "&ref_=nv_sr_sm", "_blank"); //Open IMDB on a new tab
  126. open("https://asianwiki.com/index.php?title=Special%3ASearch&search=" + ChiakiFranchiseTitle, "_blank"); //Open AsianWiki on a new tab
  127. if (MyDramaListCheck.match('NOT') === null) { //If MyDramaList returned any results
  128. open("https://mydramalist.com/search?q=" + ChiakiFranchiseTitle + '&adv=titles&ty=68,77,83,86', "_blank"); //Open MyDramaList on a new tab
  129. } //Open MyDramaList on a new tab only if any results were found on the website
  130. } //Finishes the if condition
  131. } //Finishes the if condition
  132. else { //If the anime doesn't have any entry type = TV
  133. alert("This Franchise doesn't even have any TV type entries, it's very likely that there are adaptations of any kind for this Franchise, so there's no need to search."); //Show a message to the user
  134. } //Finishes the else condition
  135. }); //Finishes the event listener
  136.  
  137. findButton.setAttribute("title", "Search for Live-Actions/Doramas"); //Detects a mouse hover on the button and show the text Find Live-Actions
  138. findButton.setAttribute("style", "cursor: pointer;margin-left: 15px;height: 10px;width: 10px;background-size: cover;display: inline-block;transform: scale(1.8);vertical-align: top;margin-top: 7px;"); //The CSS for the findButton
  139. findButton.style.backgroundImage = `url(${document.querySelector(".dark-mode") !== null ? 'https://i.imgur.com/TEPmlyF.png' : 'https://i.imgur.com/2XQm3qI.png'})`; //The find button image
  140.  
  141. function Append(element) { //Creates a new Append function
  142. document.querySelector(".title-english") === null ? document.querySelector("[itemprop*='name']").append(element) : document.querySelector(".title-english").previousElementSibling.parentNode.insertBefore(element, document.querySelector(".title-english").previousElementSibling); //Append depending on if the entry has an English title or not
  143. } //Finishes the Append function
  144.  
  145. chiakiButton.addEventListener('mousedown', async function(e) { //Detects when the user middle clicks on the chiakiButton
  146. if (e.button === 1 && hasAnime !== undefined) //If the middle mouse button was clicked
  147. { //Starts the if condition
  148. e.preventDefault(); //Prevent the default middle button action from executing
  149. const response = await (await fetch('https://myanimelist.net/forum/?topicid=1890672')).text(); //Fetch
  150. const GuideIndexnewDocument = new DOMParser().parseFromString(response, 'text/html'); //Parses the fetch response
  151. const GuideIndexLinkElement = [...GuideIndexnewDocument.querySelectorAll('b')].find(el => el.innerText.includes('|' + entryid + '|')); //Gets the topic element that probably has the link of the Franchise and adds that to a variable
  152.  
  153. if (GuideIndexLinkElement !== undefined) { //If the anime id was found on the guide index
  154. if (GuideIndexLinkElement.previousElementSibling.innerText.match('あ') !== null) { //If the anime name has the あ symbol in it on the guide index
  155. alert('Recommended watch order:\nBroadcast order.'); //Shows an alert
  156. } //Finishes the if condition
  157. else { //If the anime name doesn't have the あ symbol in it on the guide index
  158. alert('Recommended watch order:\nAEGC Guide Order.'); //Shows a text
  159. } //Finishes the if condition
  160. open(GuideIndexLinkElement.previousElementSibling.href, "_blank"); //Opens the GuideIndexLink on a new tab, and specifies that the GuideIndexLink should be opened on a new tab
  161. } //Finishes the if condition
  162. else { //If the anime ID was NOT found on the guide index
  163. const NotFoundMessage = document.createElement("a"); //Creates an a element
  164. NotFoundMessage.innerHTML = "<br>Not found on the AEGC Club!<br>Only chiaki.site will be opened.<br>"; //Defines the element text
  165. NotFoundMessage.setAttribute("style", "font-size: 80%;text-decoration: none;"); //Set the CSS for the button
  166. Append(NotFoundMessage); //Append the NotFoundMessage close to the title element
  167. setTimeout(function() {
  168. open("https://chiaki.site/?/tools/watch_order/id/" + entryid, "_blank");
  169. }, 1000); //Open chiaki.site on a new tab
  170. } //Finishes the else condition
  171.  
  172. const FinalArray = ChiakientryidSArray.filter(d => !GuideIndexnewDocument.querySelector(".body.clearfix").innerText.match(/(?<=\|\b)\d+/gi).includes(d)); //Get the ids that chiaki.site has and the Guide is missing
  173. const GuideMissingIds = document.createElement("div"); //Creates a div element
  174. GuideMissingIds.setAttribute("style", "font-size: 80%;display: none;"); //Set the CSS for the button
  175. FinalArray.forEach(function(entryid) { //For every anime id that the guide index is missing
  176. GuideMissingIds.innerHTML += GuideMissingIds.innerHTML = `<br><a href="https://myanimelist.net/anime/${entryid}">https://myanimelist.net/anime/${entryid}</a>`; //Add to the GuideMissingIds div a line break + the anime link with the link as text too
  177. }); //Finishes the foreach condition
  178.  
  179. if (FinalArray.length !== 0 && FinalArray.length !== ChiakientryidSArray.length) { //If there's at least 1 missing id on the guide index and if the guide index is not missing the same amount of total links that Chiaki has for the franchise
  180. var LinksButton = document.createElement("button"); //Creates a button element
  181. LinksButton.innerHTML = 'Show AEGC Club Missing Links'; //Defines the element text
  182. LinksButton.setAttribute("style", "margin-left: 10px;"); //Set the CSS for the button
  183. LinksButton.onclick = function() { //Detects the mouse click on the Show Links Button
  184. if (GuideMissingIds.style.display === "none") { //If the Show missing links button is hidden
  185. GuideMissingIds.style.display = ''; //Show the missing links button
  186. LinksButton.innerHTML = 'Hide AEGC Club Missing Links'; //Defines the element text
  187. } else { //If the Show missing links button is being shown
  188. GuideMissingIds.style.display = "none"; //Hide the missing links button
  189. LinksButton.innerHTML = 'Show AEGC Club Missing Links'; //Defines the element text
  190. } //Finishes the else condition
  191. }; //FInishes the onclick event listener
  192. Append(LinksButton); //Display the button to show the IDs that chiaki.site has and the Guide is missing
  193. Append(GuideMissingIds); //Display the IDs that chiaki.site has and the Guide is missing
  194. } //Finishes the if condition
  195.  
  196. if (GuideIndexLinkElement !== undefined && FinalArray.length === ChiakientryidSArray.length - 1) { //If the anime id was found on the guide index and the missing links are equal to all of the chiaki.site total links -1
  197. LinksButton.remove(); //Remove the button that shows the missing links
  198. const TwoFranchises = document.createElement("a"); //Creates an a element
  199. TwoFranchises.innerHTML = "<br>It seems that this entry is related to 2 Anime Franchises.<br>Both chiaki.site and the AEGC Club will be opened."; //Defines the element text
  200. TwoFranchises.setAttribute("style", "font-size: 80%;text-decoration: none;"); //Set the CSS for the button
  201. Append(TwoFranchises); //Append the NotFoundMessage close to the title element
  202.  
  203. open("https://chiaki.site/?/tools/watch_order/id/" + entryid, "_blank"); //Opens chiaki.site on a new tab to show all the related anime entries on MAL on the correct watch order for the anime franchise and specifies that chiaki.site should be opened on a new tab
  204. } //Finishes the if condition
  205.  
  206. if (GuideIndexnewDocument.querySelector(".body.clearfix").innerText.match(new RegExp('(?:\\|' + entryid + '\\|)', 'gi')).length > 1) { //If 2 identical anime ids were found on the guide index
  207. const OtherFranchiseMessage = document.createElement("a"); //Creates an a element
  208. OtherFranchiseMessage.innerHTML = "<br>According to the AEGC club this entry also has another related entry that chiaki.site consider as being from another franchise."; //Defines the element text
  209. OtherFranchiseMessage.setAttribute("style", "font-size: 80%;text-decoration: none;"); //Set the CSS for the button
  210. Append(OtherFranchiseMessage); //Append the OtherFranchiseMessage close to the title element
  211. } //Finishes the if condition
  212. } //Finishes the if condition
  213. }); //Finishes the mousedown event listener
  214.  
  215. chiakiButton.addEventListener("click", () => { //Detect the mouse click
  216. open(hasAnime !== undefined ? "https://chiaki.site/?/tools/watch_order/id/" + entryid : "https://relatedanime.com/manga/" + entryid, "_blank"); //Opens chiaki.site on a new tab to show all the related anime entries on MAL on the correct watch order for the anime franchise and specifies that chiaki.site should be opened on a new tab
  217. }); //Finishes the addEventListener click
  218.  
  219. chiakiButton.addEventListener("contextmenu", (e) => { //Detect a mouse click
  220. open(hasAnime !== undefined ? "https://relatedanime.com/anime/" + entryid : "https://relatedanime.com/manga/" + entryid, "_blank"); //Open relatedanime.com on a new tab to show all the related anime entries on MAL on the correct watch order for the anime franchise, including reading material
  221. e.preventDefault(); //Don't show the right-click default context menu
  222. }); //Detect the mouse right click
  223.  
  224. chiakiButton.setAttribute("style", "cursor: pointer;margin-left: 15px;height: 10px;width: 10px;background-size: cover;display: inline-block;transform: scale(1.8);vertical-align: top;margin-top: 7px;"); //The CSS for the chiakiButton
  225.  
  226. chiakiButton.setAttribute("title", hasAnime !== undefined ? "Click to see all related anime entries only on the Broadcast Watch Order\nMiddle Click to see all related anime entries only on the Broadcast/Chronological Watch Order\nRight Click to see all related entries on the Broadcast Watch Order. (Including reading material)" : "This franchise has no anime adaptations!\nClick to open relatedanime.com to show all related entries on the Broadcast Watch Order. (Including reading material)"); //Detects a mouse hover on the button and shows some text info
  227.  
  228. chiakiButton.style.backgroundImage = `url(${hasAnime !== undefined ? 'https://i.imgur.com/i635kBp.png' : 'https://i.imgur.com/7tUhvqf.png'})`; //The chiaki.site/relatedanime.com button favicon
  229. Append(copyButton); //Append the button next to the title element
  230. Append(findButton); //Append the button next to the title element
  231. Append(chiakiButton); //Append the button next to the title element
  232. } //Finishes the else condition
  233. } //Finishes the Prog function
  234. } //Finishes the if condition
  235. Prog(); //Run the program
  236. window.addEventListener('focus', () => { Prog(); }, { once: true }); //Run the program when the tab gets focus
  237. })(); //Finishes the whole function