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

See the franchise vorrect watch order and all entries. Easily check if an entry has Live-Action/Dorama adaptations, and copy the entry or all franchise entries title(s).

// ==UserScript==
// @name         Find The Correct Watch Order And All Related Entries+Live-Actions+Doramas + Copy Entry Title
// @namespace    Search for Live-Actions\Doramas\All Related Entries + Correct Watch Order + Copy Entry Title
// @version      50
// @description  See the franchise vorrect watch order and all entries. Easily check if an entry has Live-Action/Dorama adaptations, and copy the entry or all franchise entries title(s).
// @author       hacker09
// @include      https://myanimelist.net/forum/?topicid=1863965
// @include      /^https:\/\/myanimelist\.net\/((anime|manga)(id=)?(\.php\?id=)?)(\/)?([\d]+)/
// @exclude      https://myanimelist.net/anime/genre/*
// @exclude      https://myanimelist.net/anime/producer/*
// @icon         https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://myanimelist.net&size=64
// @run-at       document-end
// @connect      chiaki.site
// @connect      mydramalist.com
// @grant        GM.xmlHttpRequest
// ==/UserScript==

(function() {
  'use strict';
  var hasRun = true; //Create a new variable
  async function Prog() { //Run the program
    if (document.hasFocus() && hasRun) //If the tab has focus and it's the first time the script runs
    { //Starts the if condition
      hasRun = false; //Change the var condition
      if (location.href === 'https://myanimelist.net/forum/?topicid=1863965') { //If the user is on the Official Guidex Index
        $("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

        for (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); ++i) { //For every charCode
          document.querySelector(`b#GoTo${String.fromCharCode(i)}`).onclick = () => document.querySelectorAll('b')[i - 36].scrollIntoView(); //Scroll the page until the letter can be seen
        } //Finishes the for condition
      } //Finishes the if condition
      else //If the user isn't on any Guide Index
      { //Starts the else condition

        const findButton = document.createElement("a"), copyButton = document.createElement("a"), chiakiButton = document.createElement("a"); //Creates an "a" element so the button will appear
        var ChiakiFranchiseTitle, ChiakiFranchiseTitleWithSymbols, MalClubText, ChiakiDocument, IMDBAsianWiki, hasAnime = '', ChiakiTextData = [], ChiakientryidSArray = [], MyDramaListText = ' and MyDramaList', MyDramaListCheck = '👍 Found on MyDramaList.'; //Create new global variables

        if (location.pathname.split('/')[1] === 'manga') { //If the user is in an manga entry
          const Relations = await (await fetch('https://api.jikan.moe/v4/' + location.href.split('/')[3] + '/' + location.pathname.match(/\d+/)[0] + '/relations')).json(); //Fetch
          hasAnime = Relations.data.flatMap(relation => relation.entry).find(entry => entry.type === "anime"); //Try getting 1 entry that is an anime
        } //Finishes the if condition

        var entryid = hasAnime !== undefined && hasAnime !== '' ? hasAnime.mal_id : location.pathname.match(/\d+/)[0]; //Get the anime id

        GM.xmlHttpRequest({ //Starts the xmlHttpRequest
          method: "GET",
          url: 'https://chiaki.site/?/tools/watch_order/id/' + entryid,
          onload: (response) => { //Starts the onload event listener
            ChiakiDocument = new DOMParser().parseFromString(response.responseText, 'text/html'); //Parses the fetch response

            if (ChiakiDocument.querySelector("h2") !== null) { //Starts the if condition
              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
              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
            } //Finishes the if condition

            ChiakiDocument.querySelectorAll("span.uk-text-muted.uk-text-small").forEach((TextElement, i) => { //Loop through the elements
              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

              const TotalRawDuration = TextElement.textContent.split("×")[1].split("|")[0].trim(); //Creates a variable to hold the total unprocessed times
              const ALLChiakiTitles = ChiakiDocument.querySelectorAll("span.wo_title")[i].innerText; //Creates a variable to get all the anime titles on chiaki site
              const TotalEpisodes = TextElement.textContent.split("|")[2].match(/\d+|\?/g)[0]; //Creates a variable to hold the total episodes
              const EpisodeType = TextElement.textContent.split("|")[1].trim(); //Creates a variable to get the episode types
              var eps = ' eps'; //Create a variable called eps
              var Duration = ''; //Creates a blank variable
              var PerEp = ' per ep'; //Create a variable called PerEp
              if (TotalEpisodes === '1') { //If the entry has only 1 ep
                eps = ' ' + EpisodeType; //Change the variable called eps
                PerEp = ''; //Change the variable called PerEp
              } //Finishes the if condition
              if (EpisodeType !== 'TV') { //If the entry type isn't TV
                if (TotalEpisodes !== '1') { //If the entry doesn't have only 1 ep
                  eps = ' ' + EpisodeType + 's'; //Change the variable called eps
                } //Finishes the if condition
                Duration = ' of ' + TotalRawDuration + PerEp; //Defines the Duration variable if the episode type isn't TV
              } //Finishes the if condition
              ChiakiTextData.push(ALLChiakiTitles + ',, ' + TotalEpisodes + eps + Duration + ',\n'); //Add Everything to an Array
            }); //Finishes the for condition

          } //Finishes the onload event listener
        }); //Finishes the xmlHttpRequest

        GM.xmlHttpRequest({ //Starts the xmlHttpRequest
          method: "GET",
          url: "https://mydramalist.com/search?q=" + (hasAnime !== undefined ? ChiakiFranchiseTitle : document.querySelector("[itemprop*='name']").innerText.split('\n')[0]) + '&adv=titles&ty=68,77,83,86',
          onload: (response) => { //Starts the onload event listener
            const MyDramaListDocument = new DOMParser().parseFromString(response.responseText, 'text/html'); //Parses the fetch response
            if (MyDramaListDocument.querySelector(".m-b-sm") === null) { //If MyDramaList did not return any results
              MyDramaListText = ''; //Display to the user that MyDramaList Won't be opened if OK is clicked
              MyDramaListCheck = '✖ NOT Found on MyDramaList.'; //Display the confirmation that the anime doesn't have any adaptations found on MyDramaList
            } //Finishes the if condition
          } //Finishes the onload event listener
        }); //Finishes the xmlHttpRequest

        copyButton.addEventListener("click", () => {
          navigator.clipboard.writeText(document.querySelector("[itemprop*='name']").innerText.split('\n')[0]); //Copy the entry title with symbols
        }); //Detect the single mouse click
        copyButton.addEventListener("dblclick", () => {
          navigator.clipboard.writeText(document.querySelector("[itemprop*='name']").innerText.split('\n')[0].replace(/[^a-zA-Z0-9]+/g, " ")); //Copy the entry title without symbols
        }); //Detect the double mouse click

        copyButton.addEventListener("contextmenu", (e) => { //Detect a mouse click
          hasAnime !== undefined ? navigator.clipboard.writeText(ChiakiTextData.join('').trim()) : ''; //Copy the array to the clipboard
          e.preventDefault(); //Don't show the right-click default context menu
        }); //Detect the mouse right click

        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

        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
        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

        findButton.addEventListener("click", async () => { //Detect the mouse click and search for the anime title
          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
            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)
            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)
          } //Finishes the if condition

          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
            const response = await (await fetch('https://myanimelist.net/clubs.php?cid=5450')).text(); //Fetch

            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

            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

            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
              open("https://www.imdb.com/find?s=tt&q=" + ChiakiFranchiseTitle + "&ref_=nv_sr_sm", "_blank"); //Open IMDB on a new tab
              open("https://asianwiki.com/index.php?title=Special%3ASearch&search=" + ChiakiFranchiseTitle, "_blank"); //Open AsianWiki on a new tab
              if (MyDramaListCheck.match('NOT') === null) { //If MyDramaList returned any results
                open("https://mydramalist.com/search?q=" + ChiakiFranchiseTitle + '&adv=titles&ty=68,77,83,86', "_blank"); //Open MyDramaList on a new tab
              } //Open MyDramaList on a new tab only if any results were found on the website
            } //Finishes the if condition
          } //Finishes the if condition
          else { //If the anime doesn't have any entry type = TV
            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
          } //Finishes the else condition
        }); //Finishes the event listener

        findButton.setAttribute("title", "Search for Live-Actions/Doramas"); //Detects a mouse hover on the button and show the text Find Live-Actions
        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
        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

        function Append(element) { //Creates a new Append function
          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
        } //Finishes the Append function

        chiakiButton.addEventListener('mousedown', async function(e) { //Detects when the user middle clicks on the chiakiButton
          if (e.button === 1 && hasAnime !== undefined) //If the middle mouse button was clicked
          { //Starts the if condition
            e.preventDefault(); //Prevent the default middle button action from executing
            var IsBroadcast = false; //Check if the franchise should be watched using the broadcast order or not
            const response = await (await fetch('https://myanimelist.net/forum/?topicid=1890672')).text(); //Fetch
            const GuideIndexnewDocument = new DOMParser().parseFromString(response, 'text/html'); //Parses the fetch response
            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

            if (GuideIndexLinkElement !== undefined) { //If the anime id was found on the guide index
              if (GuideIndexLinkElement.previousElementSibling.innerText.match('あ') !== null) { //If the anime name has the あ symbol in it on the guide index
                alert('Recommended watch order:\nBroadcast order.'); //Shows an alert
                IsBroadcast = true; //Check if the franchise should be watched using the broadcast order or not
                open("https://chiaki.site/?/tools/watch_order/id/" + entryid, "_self"); //Opens the chiaki.site in the same tab
              } //Finishes the if condition
              else { //If the anime name doesn't have the あ symbol in it on the guide index
                alert('Recommended watch order:\nAEGC Guide Order.'); //Shows a text
                open(GuideIndexLinkElement.previousElementSibling.href, "_self"); //Opens the GuideIndexLink in the same tab
              } //Finishes the if condition
            } //Finishes the if condition
            else { //If the anime ID was NOT found on the guide index
              const NotFoundMessage = document.createElement("a"); //Creates an a element
              NotFoundMessage.innerHTML = "<br>Not found on the AEGC Club!<br>Only chiaki.site will be opened.<br>"; //Defines the element text
              NotFoundMessage.setAttribute("style", "font-size: 80%;text-decoration: none;"); //Set the CSS for the button
              Append(NotFoundMessage); //Append the NotFoundMessage close to the title element
              setTimeout(function() {
                open("https://chiaki.site/?/tools/watch_order/id/" + entryid, "_self");
              }, 1000); //Open chiaki.site in the same tab
            } //Finishes the else condition

            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
            const GuideMissingIds = document.createElement("div"); //Creates a div element
            GuideMissingIds.setAttribute("style", "font-size: 80%;display: none;"); //Set the CSS for the button
            FinalArray.forEach(function(entryid) { //For every anime id that the guide index is missing
              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
            }); //Finishes the foreach condition

            if (IsBroadcast === false && FinalArray.length !== 0 && FinalArray.length !== ChiakientryidSArray.length) { //If the entry isn't broadcast and 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
              var LinksButton = document.createElement("button"); //Creates a button element
              LinksButton.innerHTML = 'Show AEGC Club Missing Links'; //Defines the element text
              LinksButton.setAttribute("style", "margin-left: 10px;"); //Set the CSS for the button
              LinksButton.onclick = function() { //Detects the mouse click on the Show Links Button
                if (GuideMissingIds.style.display === "none") { //If the Show missing links button is hidden
                  GuideMissingIds.style.display = ''; //Show the missing links button
                  LinksButton.innerHTML = 'Hide AEGC Club Missing Links'; //Defines the element text
                } else { //If the Show missing links button is being shown
                  GuideMissingIds.style.display = "none"; //Hide the missing links button
                  LinksButton.innerHTML = 'Show AEGC Club Missing Links'; //Defines the element text
                } //Finishes the else condition
              }; //FInishes the onclick event listener
              Append(LinksButton); //Display the button to show the IDs that chiaki.site has and the Guide is missing
              Append(GuideMissingIds); //Display the IDs that chiaki.site has and the Guide is missing
            } //Finishes the if condition

            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
              LinksButton.remove(); //Remove the button that shows the missing links
              const TwoFranchises = document.createElement("a"); //Creates an a element
              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
              TwoFranchises.setAttribute("style", "font-size: 80%;text-decoration: none;"); //Set the CSS for the button
              Append(TwoFranchises); //Append the NotFoundMessage close to the title element

              open("https://chiaki.site/?/tools/watch_order/id/" + entryid, "_self"); //Opens chiaki.site in the same 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
            } //Finishes the if condition

            if (GuideIndexnewDocument.querySelector(".body.clearfix").innerText.match(new RegExp('(?:\\|' + entryid + '\\|)', 'gi')).length > 1) { //If 2 identical anime ids were found on the guide index
              const OtherFranchiseMessage = document.createElement("a"); //Creates an a element
              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
              OtherFranchiseMessage.setAttribute("style", "font-size: 80%;text-decoration: none;"); //Set the CSS for the button
              Append(OtherFranchiseMessage); //Append the OtherFranchiseMessage close to the title element
            } //Finishes the if condition
          } //Finishes the if condition
        }); //Finishes the mousedown event listener

        chiakiButton.addEventListener("click", () => { //Detect the mouse click
          open(hasAnime !== undefined ? "https://chiaki.site/?/tools/watch_order/id/" + entryid : "https://relatedanime.com/manga/" + entryid, "_self"); //Opens chiaki.site in the same 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
        }); //Finishes the addEventListener click

        chiakiButton.addEventListener("contextmenu", (e) => { //Detect a mouse click
          open(hasAnime !== undefined ? "https://relatedanime.com/anime/" + entryid : "https://relatedanime.com/manga/" + entryid, "_self"); //Open relatedanime.com in the same tab to show all the related anime entries on MAL on the correct watch order for the anime franchise, including reading material
          e.preventDefault(); //Don't show the right-click default context menu
        }); //Detect the mouse right click

        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

        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

        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
        Append(copyButton); //Append the button next to the title element
        Append(findButton); //Append the button next to the title element
        Append(chiakiButton); //Append the button next to the title element
      } //Finishes the else condition
    } //Finishes the Prog function
  } //Finishes the if condition
  Prog(); //Run the program
  window.addEventListener('focus', () => { Prog(); }, { once: true }); //Run the program when the tab gets focus
})(); //Finishes the whole function