Generate a List With The Animes/Mangas Titles that were Re-Watched/Re-Read

This is a tool to easily and quickly generate a list with the titles of what animes/mangas you have ReWatched/ReRead and how many times.

Version au 13/08/2020. Voir la dernière version.

// ==UserScript==
// @name         Generate a List With The Animes/Mangas Titles that were Re-Watched/Re-Read
// @namespace    MAL Automatic Anime/Manga List Generator
// @version      0.3
// @description  This is a tool to easily and quickly generate a list with the titles of what animes/mangas you have ReWatched/ReRead and how many times.
// @author       hacker09 & (Reddit user SapphireFeast)
// @match        https://myanimelist.net/animelist/*
// @match        https://myanimelist.net/mangalist/*
// @grant        none
// @run-at       document-end
// ==/UserScript==
(function() {
    'use strict';
    var $ = window.jQuery; //Defines That The Symbol $ Is A jQuery

    var rewatchedlistbtn = document.createElement("a"); // Creates an a element
    rewatchedlistbtn.setAttribute("id", "rewatchedlistbtn"); //Adds the id rewatchedlistbtn to the a element
    rewatchedlistbtn.setAttribute("style", "margin-left: 10px;transform: scale(1.8);vertical-align: top;margin-top: 7px;", "id", "rewatchedlistbtn"); //Adds the css to the a element
    if (window.location.pathname.split('/')[1] === 'animelist') //Check If The URL Is https://myanimelist.net/animelist/  ,And If Yes Make A Button Called "Generate My ReWatched List"
    {
        rewatchedlistbtn.innerHTML = "Generate ReWatched List";
    } // The text that should appear on the "Button"
    if (window.location.pathname.split('/')[1] === 'mangalist') //Check If The URL Is https://myanimelist.net/animelist/  ,And If Yes Make A Button Called "Generate My ReRead List"
    {
        rewatchedlistbtn.innerHTML = "Generate ReRead List"; // The text that should appear on the "Button"
    } //Finishes the if condition
    if (document.querySelector("#advanced-options-button") === null) //Checks if the Filters button on the modern list style exists,if not then the user is using an old classic list style
    {
        document.querySelector("a.table_headerLink").parentElement.appendChild(rewatchedlistbtn); //Defines that the 'Generate ReWatched/ReRead List' button should appear close to the 'Anime Title' or 'Manga Title' text on the old classic style list.
        document.querySelector("#rewatchedlistbtn").onclick = function() {
            scrape();
        }; //Shows a message in the console for dev purposes, and run the scrape function.Classic list styles doesn't need to be scrolled down.
        document.querySelector("#rewatchedlistbtn").setAttribute("style", "cursor: pointer;"); //Set the css for the button
    } //Finishes the if condition
    else //If the Filters button on the modern list style exists, then the user is using the modern list style
    {
        var bottomrewatchedlistbtn = document.createElement("a"); // Creates an a element
        bottomrewatchedlistbtn.setAttribute("id", "bottomrewatchedlistbtn"); //Adds the id bottomrewatchedlistbtn to the a element
        bottomrewatchedlistbtn.setAttribute("style", "cursor: pointer;margin-left: 10px;transform: scale(1.8);vertical-align: top;margin-top: 7px;font-size: 20px;", "id", "rewatchedlistbtn"); //Adds the css to the a element
        bottomrewatchedlistbtn.innerHTML = "Generate ReWatched List"; // The text that should appear on the "Button"
        document.querySelector("body > footer").parentElement.appendChild(bottomrewatchedlistbtn); //Defines that the 'Generate ReWatched/ReRead List' button should appear close to the Footer on the modern style list
        document.querySelector("#bottomrewatchedlistbtn").onclick = function() //Detects the mouse click on the 'Generate ReWatched/ReRead List' button
        {
            scrape(); //Start the scrape function
        };
        document.querySelector("#advanced-options-button").parentElement.appendChild(rewatchedlistbtn); //Defines that the 'Generate ReWatched/ReRead List' button should appear close to the Filter button on the modern style list
        document.querySelector("#rewatchedlistbtn").setAttribute("style", "cursor: pointer;"); //Set the css for the button
        document.querySelector("#rewatchedlistbtn").onclick = function() // Detects the mouse click on the 'Generate ReWatched/ReRead List' button
        {
            console.log('Starting To Scroll Down'); //Shows a message in the console for dev purposes
            godown(); //Run the godown function
            var interval = setInterval(godown, 10000); //Creates a variable named interval that will run the function godown every 10 secs
            interval; //Run the variable named interval to start running the godown function and the timer
        }; //Finishes the onclick function
    } //Finishes the else statement

    function godown() //Function that automatically "Press the keyboard key End"
    {
        check(); //Check if the user is on the completed anime/manga page
        if (window.innerHeight + window.scrollY >= document.body.scrollHeight === false) //If condition that detects if the website is scrolled 100% at the bottom or not, and calculates how much the page needs to be scrolled to reach the bottom
        {
            window.scrollTo(0, document.body.scrollHeight); //Scrolls the website till the page reaches the bottom
            console.log('Page Bottom Reached, Re-Checking in 10 secs'); //Shows a message in the console for dev purposes
        } else //When the page bottom is reached
        {
            console.log('Full List Loaded! Stopping Scrolling Down Now!'); //Shows a message in the console for dev purposes
            //console.log('Stopping the timer that scrolls the page down'); //Shows a message in the console for dev purposes. clearInterval(interval); //Breaks the timer that scrolls the page down every 10 secs
            scrape(); //Run the Scrapping Function
            return; //Get out of this function
        } //Finishes the else condition
    } //Finishes the function godown

    function check()  //Function to check if the user is on the completed anime/manga page
    {
        var username = window.location.pathname.split('/')[2]; //Get the username on the url to use latter

        // Before running, check if the completed section is opened or not
        if (window.location.href.split('/')[4] !== '' + username + '?status=2') //Checks if the user is on https://myanimelist.net/animelist/USERNAMEHERE?status=2 opened or not (the status 2 means that the user is on an completed list)
        {
            alert("Execute this on the 'Completed' page! \nRedirecting. \nTry again after the page loads."); //Show an error alert message to the user, if the user is not on an completed list
            window.location.replace(window.location.href.split('?')[0] + "?status=2"); //Redirects the user to the completed section of the list that the user was in
            throw new Error("Redirecting"); //Show an error alert message on the dev console of the user
        } //Finishes the if statement
    } //Finishes the function check

    function scrape() //Function that will scrape the page for rewatched/reread values
    {
        check(); //Check if the user is on the completed anime/manga page
        console.log('Starting To Scrape...Please Wait!');
        var type = ""; //Null variable that will be used to declare later what is the type that will be scrapped (if it's anime or manga)
        var titles_old = document.querySelectorAll('div table tbody tr a.animetitle span'); //Select only the anime title on the old style list
        var titles_new = document.querySelectorAll('tbody.list-item tr.list-table-data td.data.title a.link.sort'); //Select only the anime title on the Modern default style list
        var titles = []; //Creates a blank node variable to use latter
        var old_list = false; //Variable that can be changed latter to the value 'true' if the user used the script on an old classic style list.The value 'false' will be kept if the user used the script on the new modern list style.
        var username = window.location.pathname.split('/')[2]; //Get the username on the url to use latter

        if (window.location.pathname.split('/')[1]) //Detects if the user is on an animelist or not
        {
            type = "anime"; //Set the type as anime
        } //If the user is on an animelist, then the type to be scrapped will be animes
        else //Detects if the user is not on an animelist
        {
            type = "manga"; //The user is on an manga list, then the type to be scrapped will be mangas
        } //Finishes the else condition

        if (titles_old.length > titles_new.length) //Checks if the user list style is the old classic style or the new modern style
        {
            titles = titles_old; //If the user used the script on an old classic list style, the blank node created later named 'titles' will be changed to an variable named 'titles_old' that will posses the blank nodes value (In dev words  var titles_old = [];  )
            old_list = true; ////Variable old_list will be changed to the value 'true' if the user used the script on an old classic style list
        } else //If the user used the script on a new modern list style
        {
            for (var i = 0; i < titles_new.length; i++) //This for condition is responsible for getting all the anime titles
            {
                titles[i] = titles_new[i].innerHTML; //This for condition is responsible for getting all the anime titles
            } //Finishes the for condition
        } //Finishes the if statement

        var rewatches = []; //Creates a blank node variable to use latter
        var moreLinks = document.querySelectorAll('a'); //Defines a variable named 'moreLinks' that will be used to click on all the more buttons on the completed page
        var resultArray = []; //Creates a blank node variable to use latter
        var result = 'data:text/html;charset=utf-8,<style>html,body{margin: 0;padding: 0;}</style><div style="max-width:650px;font-size: 18px;margin:0px auto;">'; //The HTML and CSS that will be added to the new browser tab when the script is done

        if (type == "anime") result += "<h1> " + username + " ReWatched Anime List</h1>"; //If the type is anime then add 'Rewatched anime' to The HTML and CSS that will be added to the new browser tab when the script is done
        if (type == "manga") result += "<h1> " + username + " ReRead Manga List</h1>"; //If the type is manga then add 'Rewatched manga' to The HTML and CSS that will be added to the new browser tab when the script is done
        if (type == "anime") result += "<h3><em>List of Animes that " + username + " has watched and ReWatched:</em></h3>"; //If the type is anime then add 'How many times username has watched and ReWatched' to The HTML and CSS that will be added to the new browser tab when the script is done
        if (type == "manga") result += "<h3><em>List of Mangas that " + username + " has read and ReRead:</em></h3>"; //If the type is manga then add ''How many times username has read and ReRead' to The HTML and CSS that will be added to the new browser tab when the script is done

        if (old_list) //If the script is working on an old classic list style
        {
            // The 12 lines below Fetches the rewatch count information bypassing the 'More' link on old classic list styles
            $("div.hide").each(function(index, value) {
                var series_id = $(value).attr('id').split('more')[1];
                $.post("/includes/ajax-no-auth.inc.php?t=6", {
                    color: 1,
                    id: series_id,
                    memId: $('#listUserId').val(),
                    type: $('#listType').val()
                }, function(data) {
                    if (type == "anime") rewatches[index] = $(data.html).find('strong')[0].innerHTML; //If the type is anime start scrapping the anime rewatched values
                    if (type == "manga") //If the type is anime start scrapping the manga 'Times Read' values
                    {
                        var moreSection = $(data.html).find('td').html(); //Opens the more button on old classic style list
                        var timesReadIndex = moreSection.indexOf("Times Read"); //Detects how many times a manga was read
                        rewatches[index] = moreSection.charAt(timesReadIndex + 12);
                    } //Finishes the if condition
                }, "json"); //The scrapping isn't done using HTML,it's done by scrapping only the json file that's loaded when the user goes down (loads more animes/mangas) ('XHR Get' Method)
            });
        } else //If the script was run on a new modern list style
        {
            console.log('Opening And Scraping All "More" Buttons.Please Wait!'); //Shows a message in the console for dev purposes
            // The 6 lines Below Will Click all links labeled 'More' to get the rewatch counts later on the page
            for (var i = moreLinks.length; i--;) {
                if (moreLinks[i].innerHTML == 'More') {
                    moreLinks[i].click();
                }
            } //Finishes the for condition
        } //Finishes the else condition

        // Progress bar
        $('body').append('<div id="progress" style="width: 600px;height: 20px;position: fixed;top:30%;left:50%;margin-left:-300px;border: 5px solid #ccc;background-color: #479af9;"></div>') //Creates and show an blue retangular box on the screen
            .append($('<div id="bar" style="text-align: left;width: 0px;height: 20px;position: fixed;top:30%;left:50%;margin-left:-295px;margin-top:5px;white-space:pre;color:white;font-weight:bold;font-size:16px"></div>')); //Creates and show the total animes number on the completed list on the blue retangular box on the screen that were processed by the script

        wait(); // Repeats every 1 seconds until all More-sections are processed

        function wait() //Creates the wait function
        {
            setTimeout(function() //Creates the timeout function
                {
                    if (!old_list) rewatches = document.querySelectorAll('tbody.list-item tr.more-info strong'); //If the script was run on an new modern list style then use this command to select all the "more info" buttons
                    // Log the progress
                    console.log(rewatches.length + "/" + titles.length + " opened"); //Shows the total number of completed entries that were opened on the dev console.(This should be the same amount of your total completed entries).
                    $('#bar').text("Total entries processed: " + rewatches.length + "/" + titles.length); //Adds the total number of completed entries that were processed to the HTML DIV ID named 'bar', on the new browser tab output
                    $('#bar').css('width', rewatches.length / titles.length * 900 + 'px'); //Adds the css for the total number of completed entries that were processed to the HTML DIV ID named 'bar', on the new browser tab output

                    if (rewatches.length != titles.length) // Check if All sections were or not opened
                    {
                        wait(); //If All sections were not opened check it again after 1 seconds
                    } else //If All sections were opened
                    {
                        if (old_list) //Check if the script was run in an old classic list style or not
                        {
                            for (var i = 0; i < titles.length; i++) {
                                // Parse rewatched shows into an array of arrays with rewatch count as index and add them to the new browser tab when the script is done
                                if (rewatches[i] > 0) {
                                    if (resultArray[rewatches[i]]) {
                                        resultArray[rewatches[i]] = resultArray[rewatches[i]].concat("<li>" + titles[i].innerHTML + "</li>");
                                    } else {
                                        resultArray[rewatches[i]] = "<b>" + (parseInt(rewatches[i]) + 1) + " times:</b>"; // +1 shows the total watched times number. -1 shows the total Re-Watched times only.
                                        resultArray[rewatches[i]] = resultArray[rewatches[i]].concat("<ul>");
                                        resultArray[rewatches[i]] = resultArray[rewatches[i]].concat("<li>" + titles[i].innerHTML + "</li>");
                                    } //Finishes the else condition
                                } //Finishes the if condition
                            } //Finishes the for condition
                        } else //If the script was run in on the new default modern list style
                        {
                            for (var i = 0; i < titles.length; i++) {
                                // Parse rewatched shows into an array of arrays with rewatch count as index
                                if (rewatches[i].innerHTML > 0) {
                                    if (resultArray[rewatches[i].innerHTML]) {
                                        resultArray[rewatches[i].innerHTML] = resultArray[rewatches[i].innerHTML].concat("<li>" + titles[i].trim() + "</li>");
                                    } else {
                                        resultArray[rewatches[i].innerHTML] = "<b>" + (parseInt(rewatches[i].innerHTML) + 1) + " times:</b>"; // +1 shows the Re-Watched/Re-Read and the watched/read total numbers. -1 shows only the total times an anime/manga was Re-Watched/Re-Read.
                                        resultArray[rewatches[i].innerHTML] = resultArray[rewatches[i].innerHTML].concat("<ul>"); //Adds the divisories (div like html tags) between rewatched/reread numbers, and concatenates them
                                        resultArray[rewatches[i].innerHTML] = resultArray[rewatches[i].innerHTML].concat("<li>" + titles[i].trim() + "</li>"); //Adds the rewatched/reread titles inside the tags <li> , and concatenates them
                                    } //Finishes the else condition
                                }
                            }
                        } //Finishes the else condition

                        resultArray.reverse(); //This command makes result on the new browser tab be organized by starting the list with the most rewatched/reread values, if this line is removed, the result on the new browser tab will start the list with animes rewatched once and the last animes on the list will be the most rewatched/reread one's.
                        resultArray.forEach(function(value, index, array) {
                            result += value.concat("</ul>");
                        });

                        console.log('Done! Opening The Results Page!'); //Shows a message in the console for dev purposes
                        console.log('Redirecting you to the profile page of the owner of this anime/manga list.'); //Shows a message in the console for dev purposes
                        var link = document.createElement('a'); //Create an a element to hold the results and to be clicked later to download the result
                        link.download = username + ' ReWatched_ReRead List!'; //Sets the downloaded filename to the user of the scrapped list + ReWatched_ReRead List!
                        link.href = result; //Adds the scrapped results as a link on the a element
                        link.click(); //Clicks on the a element to download the scrapped results file
                        window.location.assign("https://myanimelist.net/profile/" + username); //Redirects the user to the profile page of the owner of the anime/manga list that was scrapped.This is done to get out of the 'interval' variable loop, because when the script finishes and downloads the output, the script won't stop looping if the user leaves the anime/manga completed page opened on his browser.This redirection command avoids this problem.
                    } //Finishes the else condition
                }, 1000); //Finishes the settimeout function.Wait 1 second
        } //Finishes the function wait
    } // Finishes the scrape function
})(); // Finishes the tampermonkey for condition