Greasy Fork is available in English.

Auto Generate Your/Any Anime/Manga Re-Watched/Re-Read List!

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

נכון ליום 04-08-2020. ראה הגרסה האחרונה.

  1. // ==UserScript==
  2. // @name Auto Generate Your/Any Anime/Manga Re-Watched/Re-Read List!
  3. // @namespace MAL Automatic Anime/Manga List Generator
  4. // @version 0.2
  5. // @description This is a tool to easily and quickly generate a list of what animes/mangas you have ReWatched/ReRead and how many times.
  6. // @author hacker09 & (Reddit user SapphireFeast)
  7. // @match https://myanimelist.net/animelist/*
  8. // @match https://myanimelist.net/mangalist/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14. var $ = window.jQuery; //Defines That The Symbol $ Is A jQuery
  15. var rewatchedlistbtn = document.createElement("a"); // Creates an a element
  16. rewatchedlistbtn.setAttribute("id", "rewatchedlistbtn"); //Adds the id rewatchedlistbtn to the a element
  17. 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
  18. 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"
  19. {rewatchedlistbtn.innerHTML = "Generate ReWatched List";} // The text that should appear on the "Button"
  20. 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"
  21. {rewatchedlistbtn.innerHTML = "Generate ReRead List";} // The text that should appear on the "Button"
  22. 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
  23. {
  24. 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.
  25. document.querySelector("#rewatchedlistbtn").onclick = function (){console.log('Starting To Scrape...'); 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.
  26. } else //If the Filters button on the modern list style exists, then the user is using the modern list style
  27. { var bottomrewatchedlistbtn = document.createElement("a"); // Creates an a element
  28. bottomrewatchedlistbtn.setAttribute("id", "bottomrewatchedlistbtn"); //Adds the id bottomrewatchedlistbtn to the a element
  29. bottomrewatchedlistbtn.setAttribute("style", "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
  30. bottomrewatchedlistbtn.innerHTML = "Generate ReWatched List"; // The text that should appear on the "Button"
  31. 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
  32. document.querySelector("#bottomrewatchedlistbtn").onclick = function (){console.log('Starting To Scrape...'); scrape(); //Shows a message in the console for dev purposes
  33. godown(); var interval = setInterval(godown,10000); interval;} // Detects the mouse click on the 'Generate ReWatched/ReRead List' button, and then creates a variable named interval that will run the function godown every 10 secs, and will run the variable named interval to start running the godown function and the timer
  34. 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
  35. document.querySelector("#rewatchedlistbtn").onclick = function (){console.log('Starting To Scroll Down, Re-Checking in 10 secs...'); //Shows a message in the console for dev purposes
  36. godown(); var interval = setInterval(godown,10000); interval;} // Detects the mouse click on the 'Generate ReWatched/ReRead List' button, and then creates a variable named interval that will run the function godown every 10 secs, and will run the variable named interval to start running the godown function and the timer
  37. } //Finishes the if statement
  38. function godown() //Function to automatically "Press the keyboard key PgDn"
  39. {
  40. 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
  41. {
  42. window.scrollTo(0,document.body.scrollHeight); //Scrolls the website till the page reaches the bottom
  43. console.log('Page Bottom Reached, Re-Checking in 10 secs'); //Shows a message in the console for dev purposes
  44. } else //When the page bottom is reached
  45. {
  46. console.log('Full List Loaded! Stopping Scrolling Down Now!') //Shows a message in the console for dev purposes
  47. //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
  48. console.log('Opening All "More" Buttons and Starting to scrape.Please Wait!') //Shows a message in the console for dev purposes
  49. scrape(); //Run the Scrapping Function
  50. return; //Get out of this function
  51. }
  52. } //Finishes the if statement
  53.  
  54. function scrape() //Function that will scrape the page for rewatched/reread values
  55. {
  56. var url = window.location.href.split('?')[0]; //Saving in the variable named 'url' the full current url, for later use
  57. 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)
  58.  
  59. if (url.indexOf("myanimelist.net/animelist") > -1) //Detects if the user is on the url myanimelist.net/animelist or not
  60. {
  61. type = "anime"; //If the user is on the url above then the type to be scrapped will be animes
  62. } else if (url.indexOf("myanimelist.net/mangalist") > -1) //Detects if the user is on the url myanimelist.net/mangalist or not
  63. {
  64. type = "manga"; //If the user is on the url above then the type to be scrapped will be mangas
  65. } else //If the user doesn't have his manga or anime list opened then
  66. {
  67. alert("Execute this on a MyAnimeList.net anime/manga list page!"); //Show an error alert message to the user
  68. throw new Error("Execute this on a MyAnimeList.net anime/manga list page!"); //Show an error alert message on the dev console of the user
  69. } //Finishes the if statement
  70.  
  71. // After the user opened an anime or manga list, check if the completed section is opened or not
  72. if (getParameterByName('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)
  73. {
  74. 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
  75. window.location.replace(url + "?status=2"); //Redirects the user to the completed section of the list that the user was in
  76. throw new Error("Redirecting"); //Show an error alert message on the dev console of the user
  77. } //Finishes the if statement
  78.  
  79. var username = url.split('/').pop(); //Get the username on the url to use latter
  80.  
  81. var titles_old = document.querySelectorAll('div table tbody tr a.animetitle span'); //Select only the anime title on the old style list
  82. 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
  83. var titles = []; //Creates a blank node variable to use latter
  84. 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.
  85.  
  86. if (titles_old.length > titles_new.length) //Checks if the user list style is the old classic style or the new modern style
  87. {
  88. 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 = []; )
  89. 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
  90. } else //If the user used the script on a new modern list style
  91. {
  92. for (var i = 0; i < titles_new.length; i++) //This for condition is responsible for getting all the anime titles
  93. {
  94. titles[i] = titles_new[i].innerHTML; //This for condition is responsible for getting all the anime titles
  95. }
  96. } //Finishes the if statement
  97.  
  98. var rewatches = []; //Creates a blank node variable to use latter
  99. 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
  100. var resultArray = []; //Creates a blank node variable to use latter
  101. var result = '<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
  102.  
  103. 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
  104. 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
  105.  
  106. 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
  107. 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
  108.  
  109. if (old_list) //If the script is working on an old classic list style
  110. {
  111. // The 12 lines below Fetches the rewatch count information bypassing the 'More' link on old classic list styles
  112. $("div.hide").each(function(index,value){
  113. var series_id = $(value).attr('id').split('more')[1];
  114. $.post("/includes/ajax-no-auth.inc.php?t=6", {color:1,id:series_id,memId:$('#listUserId').val(),type:$('#listType').val()}, function(data) {
  115. if (type == "anime") rewatches[index] = $(data.html).find('strong')[0].innerHTML; //If the type is anime start scrapping the anime rewatched values
  116. if (type == "manga") //If the type is anime start scrapping the manga 'Times Read' values
  117. {
  118. var moreSection = $(data.html).find('td').html(); //Opens the more button on old classic style list
  119. var timesReadIndex = moreSection.indexOf("Times Read"); //Detects how many times a manga was read
  120. rewatches[index] = moreSection.charAt(timesReadIndex + 12);
  121. }
  122. }, "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)
  123. });
  124. } else //If the script was run on a new modern list style
  125. {
  126. // The 6 lines Below Will Click all links labeled 'More' to get the rewatch counts later on the page
  127. for( var i=moreLinks.length; i--; ) {
  128. if (moreLinks[i].innerHTML == 'More') {
  129. moreLinks[i].click();
  130. }
  131. }
  132. }
  133.  
  134. // Progress bar
  135. $('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
  136. .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
  137.  
  138. // Repeats every 3 seconds until all More-sections are processed
  139. wait();
  140.  
  141. function wait() //Creates the wait function
  142. {
  143. setTimeout(function () //Creates the timeout function
  144. {
  145. 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
  146. // Log the progress
  147. 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).
  148. $('#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
  149. $('#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
  150.  
  151. if (rewatches.length != titles.length) // Check if All sections were or not opened
  152. {
  153. wait(); //If All sections were not opened check it again after 3 seconds
  154. } else //If All sections were opened
  155. {
  156. if (old_list) //Check if the script was run in an old classic list style or not
  157. {
  158. for (var i = 0; i < titles.length; i++) {
  159. // 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
  160. if (rewatches[i] > 0) {
  161. if (resultArray[rewatches[i]]) {
  162. resultArray[rewatches[i]] = resultArray[rewatches[i]].concat("<li>" + titles[i].innerHTML + "</li>");
  163. } else {
  164. 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.
  165. resultArray[rewatches[i]] = resultArray[rewatches[i]].concat("<ul>");
  166. resultArray[rewatches[i]] = resultArray[rewatches[i]].concat("<li>" + titles[i].innerHTML + "</li>");
  167. }
  168. }
  169. }
  170. } else //If the script was run in on the new default modern list style
  171. {
  172. for (var i = 0; i < titles.length; i++) {
  173. // Parse rewatched shows into an array of arrays with rewatch count as index
  174. if (rewatches[i].innerHTML > 0) {
  175. if (resultArray[rewatches[i].innerHTML]) {
  176. resultArray[rewatches[i].innerHTML] = resultArray[rewatches[i].innerHTML].concat("<li>" + titles[i].trim() + "</li>");
  177. } else {
  178. 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.
  179. resultArray[rewatches[i].innerHTML] = resultArray[rewatches[i].innerHTML].concat("<ul>"); //Adds the divisories (div like html tags) between rewatched/reread numbers, and concatenates them
  180. 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
  181. }
  182. }
  183. }
  184. }
  185.  
  186. 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.
  187.  
  188. resultArray.forEach(function (value, index, array) {
  189. result += value.concat("</ul>");
  190. });
  191.  
  192. console.log('Done! Opening The Results Page!'); //Shows a message in the console for dev purposes
  193. 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
  194. var newTab = window.open(); //Open the scrapping HTML output results on a new browser tab
  195. newTab.document.body.innerHTML = result; //Adds the scrapping HTML output results on the html body tag on the new browser tab html content
  196. 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 opens the output on a new browser tab, 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.
  197. }
  198. }, 3000); //Wait 3 seconds
  199. }
  200.  
  201. //The function below scraps only the anime/manga title that were watched, and this removes the random html/json codes
  202. function getParameterByName(name) {
  203. name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
  204. var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
  205. results = regex.exec(location.search);
  206. return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
  207. } // Finishes the function getParameterByName
  208. } // Finishes the scrape function
  209. })(); // Finishes the tampermonkey for condition