· 2 years ago · Feb 07, 2023, 04:40 AM
1// ==UserScript==
2// @name Export List - MAL
3// @namespace MAL Automatic Anime/Manga List Exporter
4// @version 0.1
5// @description This is a tool to easily and quickly generate a list with the titles of what animes/mangas you have and export it or add to logged in account.
6// @author hacker09
7// @author ShaggyZE
8// @match https://myanimelist.net/animelist/*
9// @match https://myanimelist.net/mangalist/*
10// @icon https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://myanimelist.net&size=64
11// @run-at document-end
12// @grant none
13// ==/UserScript==
14(function() {
15 'use strict';
16 var $ = window.jQuery; //Defines That The Symbol $ Is A jQuery
17 var exportlistbtn = document.createElement("a"); //Creates an a element
18 var username = window.location.pathname.split('/')[2]; //Get the username on the url to use later
19 var TotalEntries = 543,
20 TotalReWatchedAnimes, TotalReReadMangas, type, interval, text, totalanimestwo, Condition, NEWStyle; //Make these variables global
21 var token = document.head.querySelector("[name='csrf_token']").content; //Get the user csrf token
22 const status = location.href.slice(-1); //Stores status number
23
24 window.location.pathname.split('/')[1] === 'animelist' ? (type = 'anime', text = 'Export Animes') : (type = 'manga', text = 'Export Mangas'); //Check If the user on an animelist or not and create some variables
25
26 exportlistbtn.setAttribute("id", "exportlistbtn"); //Adds the id exportlistbtn to the a element
27 exportlistbtn.setAttribute("style", "cursor: pointer;"); //Set the css for the button
28 type === 'anime' ? exportlistbtn.innerHTML = "Export Anime List" : exportlistbtn.innerHTML = "Export Manga List"; //Add the text on the Button
29
30 if (document.querySelector("#advanced-options-button") === null) //Checks if the Filters button on the modern list style doesn't exist,if not then the user is using an old classic list style
31 { //Starts the if condtion
32 document.querySelector("a.table_headerLink").parentElement.appendChild(exportlistbtn); //Defines that the 'Export List' button should appear close to the 'Anime Title' or 'Manga Title' text on the old classic style list.
33 exportlistbtn.onclick = function() { //Detects the mouse click on the 'Export List' button
34 NEWStyle = false; //Add the value false to the variable NEWStyle
35 setTimeout(scrape, 500); //Start the scrape function
36 }; //Shows a message in the console for dev purposes, and run the scrape function.Classic list styles doesn't need to be scrolled down.
37 } //Finishes the if condition
38 else //If the Filters button on the modern list style exists, then the user is using the modern list style
39 { //Starts the else condtion
40 document.querySelector("#advanced-options-button").parentElement.appendChild(exportlistbtn); //Defines that the 'Export List' button should appear close to the Filter button on the modern style list
41 document.body.insertAdjacentHTML('beforeend', '<div id="loadingScreen" style="display: none;z-index: 200;position: fixed;width: 100%;height: 100%;background-color: #00000054;top: 0;background-image: url(https://pa1.narvii.com/6258/61f5cd5c652efec508ff3c6e10798d26ccef6366_hq.gif);background-repeat: no-repeat;background-position: center;"></div>'); //Add the loading screen to the html body
42
43 exportlistbtn.onclick = async function() { //Detects the mouse click on the 'Export List' button
44 NEWStyle = true; //Add the value true to the variable NEWStyle
45 //TotalEntries = prompt("Please enter the Total Entrie on this page", "0"); //Create a variable to hold the Total Completed Entries Number
46 //TotalReWatchedAnimes = prompt("Please enter the Total ReWatched Animes", "0"); //Creates a variable to hold the actual TotalReWatchedAnimes value
47 //TotalReReadMangas = prompt("Please enter the Total ReRead Mangas", "0"); //Creates a variable to hold the actual TotalReReadMangas value
48 //await loadingscreen(); //Start the loading screen function
49
50 //if (Condition) //Run the codes below only if the user list has more than 300 entries
51 //{ //Starts the if condition
52 //console.log('Scrolling Down. Please Wait!'); //Shows a message in the console for dev purposes
53 //interval = setInterval(function() { //Starts the Function that automatically "Press the keyboard key End"
54 //if (document.querySelectorAll("td.data.number").length !== TotalEntries) //If condition that detect if the whole list is loaded or not
55 //{ //Starts the if condition
56 //window.scrollTo(0, document.body.scrollHeight); //Scrolls the website till the whole list is loaded
57 //} //Finishes the if condition
58 //else //When the whole list is loaded
59 //{ //Starts the else condition
60 //console.log('Full List Loaded! Stopping Scrolling Down Now!'); //Shows a message in the console for dev purposes
61 //clearInterval(interval); //Breaks the timer that scrolls the page down every 0 secs
62 scrape(); //Run the Scrapping Function
63 //} //Finishes the else condition
64 //}, 0); //Finishes the interval function that will run the function every 0 secs
65 //} //Finishes the if condition
66 }; //Finishes the onclick function
67 } //Finishes the else condition
68
69 async function loadingscreen() //Creates a loading screen function that also checks if the user is on the completed list or not, and get the needed variables
70 { //Starts the loadingscreen function
71 const loadingScreen = document.createElement("div"); //Creates a div element
72 loadingScreen.setAttribute("id", "loadingScreen"); //Adds an id to the element
73 loadingScreen.setAttribute("style", "position: fixed;width: 100%;height: 100%;background-color: black;top: 0;z-index: 1000;background-image: url(https://pa1.narvii.com/6258/61f5cd5c652efec508ff3c6e10798d26ccef6366_hq.gif);background-repeat: no-repeat;background-position: center;"); //Set the element css and img
74 document.body.appendChild(loadingScreen); //Add the loading screen to the html body
75
76 if (NEWStyle) //Run the codes below only if the list is using the new style
77 { //Starts the if condition
78 if (document.querySelectorAll("td.data.number").length < 300) //Check if the user list has less than 300 entries
79 { //Starts the if condition
80 Condition = false; //Add the value false to the var Condition
81 console.log('This user has less than 300 Completed Entries\nFull List is Already Loaded!'); //Shows a message in the console for dev purposes
82 scrape(); //Run the Scrapping Function
83 } //Finishes the if condition
84 else //If the user list has 300 or more entries
85 { //Starts the else condition
86 Condition = true; //Add the value true to the var Condition
87 while (true) { //Starts the while condition to get the Total Number of Entries on the user completed list
88 console.log('This user has more than 300 Completed Entries\nGetting the Total Completed Entries Number...'); //Shows a message in the console for dev purposes
89 if (TotalEntries === document.querySelectorAll("td.data.number").length) //If the next page has less than 300 completed entries stop looping the whlie condition
90 { //Starts the if condition
91 console.log('Finished Getting the Total Completed Entries Number!'); //Shows a message in the console for dev purposes
92 return; //Return whether or not the fetched page has less than 300 completed entries
93 } //Finishes the if condition
94 } //Finishes the while condition
95 } //Finishes the else condition
96 } //Finishes the if condition
97 } //Finishes the loadingscreen function
98
99 function scrape() //Function that will scrape the page
100 { //Starts the function scrape
101 if (isNaN(status) === true) //Checks if status is not a number
102 { //Starts the if condition
103 alert("Execute this on the every page except the 'All' page with filters clear! \nRedirecting. \nTry again after the page loads."); //Show an error alert message to the user, if the user is not on a valid list
104 window.location.replace(window.location.href.split('?')[0] + "?status=1"); //Redirects the user to status = 1
105 throw new Error("Redirecting"); //Show an error alert message on the dev console of the user
106 } else if (location.href.match('\\?status=7') === true || location.href.match('\\?status=') === null) { //Checks if the user is on the all animes/mangas tab or not
107 alert("Execute this on the every page except the 'All' page with filters clear! \nRedirecting. \nTry again after the page loads."); //Show an error alert message to the user, if the user is not on valid list
108 window.location.replace(window.location.href.split('?')[0] + "?status=1"); //Redirects the user to status = 1
109 throw new Error("Redirecting"); //Show an error alert message on the dev console of the user
110 } //Finishes the if condition
111 console.log('Starting To Scrape...Please Wait!'); //Shows a message in the console for dev purposes
112 if (confirm("If you've already added this status list to your anime list, press OK.")) { //Ask a question to the user
113 var FetchPage = "https://myanimelist.net/ownlist/" + type + "/edit.json"; //If the user have already added the entire franchise to his anime list
114 } else { //Starts the else condition
115 FetchPage = "https://myanimelist.net/ownlist/" + type + "/add.json"; //If the user doesn't have added the entire franchise on his anime list
116 }
117 document.querySelector("#loadingScreen").style.display = ''; //Shows the Loading Screen
118 var titles = []; //Creates a blank array to use later
119 var rewatches = []; //Creates a blank array to use later
120 var scores = []; //Creates a blank array to use later
121 var started = []; //Creates a blank array to use later
122 var finished = []; //Creates a blank array to use later
123 var progress = []; //Creates a blank array to use later
124 var chapters = []; //Creates a blank array to use later
125 var volumes = []; //Creates a blank array to use later
126 var notes = []; //Creates a blank array to use later
127 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
128 var titles_old = document.querySelectorAll('div table tbody tr a.animetitle span'); //Select only the anime title on the old style list
129 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
130 var scores_old = document.querySelectorAll('div table tbody tr a.animescore span'); //Select only the anime score on the old style list
131 var scores_new = document.querySelectorAll('.score > a > span'); //Select only the anime score on the Modern default style list
132 var started_old = document.querySelectorAll('div table tbody tr a.animestarted span'); //Select only the anime started on the old style list
133 var started_new = document.querySelectorAll('tbody.list-item tr.list-table-data td.data.started'); //Select only the anime started on the Modern default style list
134 var progress_new = document.querySelectorAll('.progress > div > span:nth-child(1) > a');
135 var chapters_new = document.querySelectorAll('.chapter > div > span');
136 var volumes_new = document.querySelectorAll('.volume > div > span');
137 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.
138
139 if (titles_old.length > titles_new.length) //Checks if the user list style is the old classic style or the new modern style
140 { //Starts the if condition
141 titles = titles_old; //If the user used the script on an old classic list style, the titles will be added to the titles array
142 scores - scores_old;
143 started - started_old;
144 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
145 } //Finishes the if condition
146 else //If the user used the script on a new modern list style
147 { //Starts the else condition
148 for (var i = 0; i < titles_new.length; i++) //This for condition is responsible for getting all the anime data
149 { //Starts the for condition
150 titles[i] = titles_new[i].text; //Add all titles to an array
151 scores[i] = scores_new[i]; //Add all scores to an array
152 started[i] = started_new[i]; //Add all started date to an array
153 if (type == "anime") { progress[i] = progress_new[i].text; };
154 if (type == "manga") { chapters[i] = chapters_new[i].text; };
155 if (type == "manga") { volumes[i] = volumes_new[i].text; };
156 } //Finishes the for condition
157 } //Finishes the else condition
158
159 if (old_list) //If the script is working on an old classic list style
160 { //Starts the if condition
161 //The 12 lines below Fetches the rewatch count information bypassing the 'More' link on old classic list styles
162 $("div.hide").each(function(index, value) {
163 var series_id = $(value).attr('id').split('more')[1];
164 $.post("/includes/ajax-no-auth.inc.php?t=6", {
165 color: 1,
166 id: series_id,
167 memId: $('#listUserId').val(),
168 type: $('#listType').val()
169 }, function(data) {
170 if (type == "anime") started[index] = $(data.html).find('strong')[0].innerHTML;
171 finished[index] = $(data.html).find('strong')[1].innerHTML; //If the type is anime start scrapping the anime rewatched values
172 if (type == "manga") //If the type is anime start scrapping the manga 'Start Date' values
173 { //Starts the if condition
174 var moreSection = $(data.html).find('td').html(); //Opens the more button on old classic style list
175 var DateIndex = moreSection.indexOf("Start Date"); //Detects date started
176 started[index] = moreSection.charAt(DateIndex + 11);
177 var moreSection1 = $(data.html).find('td').html(); //Opens the more button on old classic style list
178 var DateIndex1 = moreSection1.indexOf("End Date"); //Detects date started
179 finished[index] = moreSection1.charAt(DateIndex1 + 9);
180 } //Finishes the if condition
181 }, "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)
182 }); //Finishes the each condition
183 } //Finishes the if condition
184 else //If the script was run on a new modern list style
185 { //Starts the else condition
186 console.log('Opening And Scraping All "More" Buttons.Please Wait!'); //Shows a message in the console for dev purposes
187 //The 6 lines Below Will Click all links labeled 'More' to get the rewatch counts later on the page
188 for (i = moreLinks.length; i--;) { //Starts the for condition
189 if (moreLinks[i].innerHTML == 'More') { //If the moreLinks variable has the text More
190 moreLinks[i].click(); //Click on the moreLinks button
191 } //Finishes the if condition
192 } //Finishes the for condition
193 } //Finishes the else condition
194
195 document.querySelector("head").innerHTML = "<title>Almost Done...</title>"; //Change the tab title
196 console.log('Almost Done...'); //Shows a message on the console for dev purposes
197
198 wait(); //Repeats every 1 second until all More-sections are processed
199
200 function formatDate(userDate) {
201 console.log(userDate);
202 // split date string at '/'
203 var dateArr = userDate.split(' ');
204 var dateArr2 = dateArr[1].split(',')
205 //test results of split
206 console.log(dateArr[0]);
207 console.log(dateArr2[1]);
208 console.log(dateArr2[2]);
209 // check for single number day or month
210 // prepend '0' to single number day or month
211 if (dateArr[0].length == 1) {
212 dateArr[0] = '0' + dateArr[0];
213 } else if (dateArr[1].length == 1) {
214 dateArr[1] = '0' + dateArr[1];
215 }
216 // concatenate new values into one string
217 userDate = dateArr[0] + dateArr[1] + dateArr[2];
218 // test new string value
219 console.log(userDate);
220 // return value
221 return userDate;
222 }
223
224 async function AddEntry(id, current_score, manga_read_chapters, manga_read_volumes, watched_eps, comments) //Creates a function to Score + set as "Watching" the Franchise
225 { //Starts the function
226 if (type == "anime") {
227 const response = await fetch(FetchPage, { //Fetches the page
228 "headers": {
229 "content-type": "application/x-www-form-urlencoded; charset=UTF-8"
230 },
231 "body": "{\"" + type + "_id" + "\":" + id + ",\"status\":" + status + ",\"score\":" + current_score + ",\"num_watched_episodes\":" + watched_eps + ",\"comments\":\"" + comments + "\",\"csrf_token\":\"" + token + "\"}",
232 "method": "POST"
233 }); //Finishes the fetch
234 } else {
235 const response1 = await fetch(FetchPage, { //Fetches the page
236 "headers": {
237 "content-type": "application/x-www-form-urlencoded; charset=UTF-8"
238 },
239 "body": "{\"" + type + "_id" + "\":" + id + ",\"status\":" + status + ",\"score\":" + current_score + ",\"num_read_chapters\":" + manga_read_chapters + ",\"num_read_volumes\":" + manga_read_volumes + ",\"comments\":\"" + comments + "\",\"csrf_token\":\"" + token + "\"}",
240 "method": "POST"
241 }); //Finishes the fetch
242 }
243 } //Finishes the async function
244
245 async function AddEntry2(id, manga_read_chapters, manga_retail, manga_read_times, manga_reread_value, manga_read_volumes, watched_eps, current_score, month, day, year, finish_month, finish_day, finish_year, anime_tags, priority, storage_type, storage_value, rewatched_times, rewatch_value, comments, is_asked_to_discuss, sns_post_type) //Creates a function to edit entry
246 { //Starts the function AddEntry
247 const response = await fetch("https://myanimelist.net/ownlist/" + type + "/" + id + "/edit", {
248 "headers": {
249 "content-type": "application/x-www-form-urlencoded"
250 },
251 "body": "add_manga%5Bnum_read_chapters%5D=" + manga_read_chapters + "&add_manga%5Bnum_retail_volumes%5D=" + manga_retail + "&add_manga%5Bnum_read_times%5D=" + manga_read_times + "&add_manga%5Breread_value%5D=" + manga_reread_value + "&add_manga%5Bnum_read_volumes%5D=" + manga_read_volumes + "&add_" + type + "%5Bstatus%5D=1&add_anime%5Bnum_watched_episodes%5D=" + watched_eps + "&add_" + type + "%5Bscore%5D=" + current_score + "&add_" + type + "%5Bstart_date%5D%5Bmonth%5D=" + month + "&add_" + type + "%5Bstart_date%5D%5Bday%5D=" + day + "&add_" + type + "%5Bstart_date%5D%5Byear%5D=" + year + "&add_" + type + "%5Bfinish_date%5D%5Bmonth%5D=" + finish_month + "&add_" + type + "%5Bfinish_date%5D%5Bday%5D=" + finish_day + "&add_" + type + "%5Bfinish_date%5D%5Byear%5D=" + finish_year + "&add_" + type + "%5Btags%5D=" + anime_tags + "&add_" + type + "%5Bpriority%5D=" + priority + "&add_" + type + "%5Bstorage_type%5D=" + storage_type + "&add_anime%5Bstorage_value%5D=" + storage_value + "&add_anime%5Bnum_watched_times%5D=" + rewatched_times + "&add_anime%5Brewatch_value%5D=" + rewatch_value + "&add_" + type + "%5Bcomments%5D=" + comments + "&add_" + type + "%5Bis_asked_to_discuss%5D=" + is_asked_to_discuss + "&add_" + type + "%5Bsns_post_type%5D=" + sns_post_type + "&csrf_token=" + token,
252 "method": "POST",
253 }); //Finishes the fetch
254 }
255
256 function wait() //Creates the wait function
257 { //Starts the function wait
258 if (type == "manga" && document.querySelector("#advanced-options-button") !== null) //If the list type is manga and it's using the Modern Style
259 { //Starts the if condition
260 for (var i = document.querySelectorAll("td.td1.borderRBL").length; i--;) { //For condition to make the started values bold, otherwise the script won't detect started Mangas
261 document.querySelectorAll("td.td1.borderRBL")[i].outerHTML = "<strong>" + document.querySelectorAll("td > br:nth-last-of-type(9)")[i].nextSibling.textContent.replace("Start Date:", "") + "</strong><strong>" + document.querySelectorAll("td > br:nth-last-of-type(8)")[i].nextSibling.textContent.replace("End Date:", "") + "</strong><strong>" + document.querySelectorAll("td > br:nth-last-of-type(2)")[i].nextSibling.textContent.replace("Notes:", "") + "</strong>"; //Make the Date values bold
262 } //Finishes the for condition
263 } else { //Starts the if condition
264 for (var j = document.querySelectorAll("td.td1.borderRBL").length; j--;) { //For condition to make the started values bold, otherwise the script won't detect started Mangas
265 document.querySelectorAll("td.td1.borderRBL")[j].outerHTML = "<strong>" + document.querySelectorAll("td > br:nth-last-of-type(7)")[j].nextSibling.textContent.replace("Start Date:", "") + "</strong><strong>" + document.querySelectorAll("td > br:nth-last-of-type(6)")[j].nextSibling.textContent.replace("End Date:", "") + "</strong><strong>" + document.querySelectorAll("td > br:nth-last-of-type(2)")[j].nextSibling.textContent.replace("Notes:", "") + "</strong>"; //Make the Date values bold
266 } //Finishes the for condition
267 }
268 setTimeout(function() //Creates the timeout function
269 { //Starts the timeout function
270 if (!old_list) started = document.querySelectorAll('tbody.list-item tr.more-info strong:nth-child(1)');
271 finished = document.querySelectorAll('tbody.list-item tr.more-info strong:nth-child(2)');
272 notes = document.querySelectorAll('tbody.list-item tr.more-info strong:nth-child(3)'); //If the script was run on an new modern list style then use this command to set the variable started
273 if (started.length != titles.length) //Check if All sections were or not opened
274 { //Starts the if condition
275 wait(); //If All sections were not opened check it again after 2 seconds
276 } //Finishes the if condition
277 else //If All sections were opened
278 { //Starts the else condition
279 if (old_list) //Check if the script was run in an old classic list style or not
280 { //Starts the if condition
281 for (var i = 0; i < titles.length; i++) {
282 AddEntry(titles[i].parentElement.href.match(/\d+/)[0], "0", started[i])
283 } //Finishes the for condition
284 } //Finishes the if condition
285 else //If the script was run in on the new default modern list style
286 { //Starts the else condition
287 for (i = 0; i < titles.length; i++) {
288 console.log(started[i].innerHTML);
289 console.log(finished[i].innerHTML);
290 console.log(notes[i].innerHTML.replace(" ", ""));
291 formatDate(started[i].innerHTML);
292
293 AddEntry(titles_new[i].href.match(/\d+/)[0], "0", "0", "0", progress[i], notes[i].innerHTML.replace(" ", "").trim());
294 //AddEntry2(titles_new[i].href.match(/\d+/)[0], "0", "0", "0", "", "0", progress[i], scores[i], "11", "1", "2022", "12", "1", "2023", "", "0", "", "0", "0", "", notes[i].innerHTML.replace(" ", ""), "0", "0")
295
296 } //Finishes the for condition
297 } //Finishes the else condition
298 document.querySelector("#loadingScreen").style.display = 'none'; //Hides the Loading Screen
299 //document.querySelector("body").innerHTML = ('');
300 window.scrollTo(0, 0); //Scroll the page to the top
301 document.querySelector("head").innerHTML = "<title>Done! List Extracted!.</title>"; //Change the tab title
302 console.log('Done! List Extracted!'); //Shows a message in the console for dev purposes
303 alert("Done! List Extracted!"); //Shows completed message
304 //location.reload()
305 } //Finishes the else condition
306 }, 1000); //Finishes the settimeout function. Wait 1 second
307 } //Finishes the function wait
308 } //Finishes the scrape function
309})(); //Finishes the tampermonkey function