MAL Images Backup

Backups all images on forum topics, blogs and all images on anime/manga lists CSS styles.

目前为 2023-05-07 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name MAL Images Backup
  3. // @namespace MALImgsBackup
  4. // @description Backups all images on forum topics, blogs and all images on anime/manga lists CSS styles.
  5. // @version 2
  6. // @match https://myanimelist.net/animelist/*
  7. // @match https://myanimelist.net/mangalist/*
  8. // @match https://myanimelist.net/blog/*&cat=*
  9. // @match https://myanimelist.net/forum/?topicid=*
  10. // @icon https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://myanimelist.net&size=64
  11. // @connect i.imgur.com
  12. // @connect cdn.myanimelist.net
  13. // @grant GM_registerMenuCommand
  14. // @grant GM.xmlHttpRequest
  15. // @run-at document-end
  16. // ==/UserScript==
  17.  
  18. GM_registerMenuCommand("Backup HTML/CSS", Backup); //Adds an option to the menu
  19. async function Backup() { //Creates a new Backup function
  20. const imagePromises = []; //Creates a new array
  21. if (location.pathname.match('list') !== null) //If the user is on a manga/anime list
  22. { //Starts the if condition
  23. document.querySelector("style:nth-child(2), #custom-css").innerText.match(/http.+?(?=(?:"|'|`)?\))/gm).forEach((URL) => { //For each image URL in the CSS code
  24. const promise = new Promise((resolve) => { //Convert URL to DataURI using a Promise
  25. GM.xmlHttpRequest({ //Starts the xmlHttpRequest
  26. method: "GET",
  27. url: URL,
  28. responseType: "blob",
  29. onload: async (response) => {
  30. const reader = new FileReader();
  31. reader.onload = () => resolve(reader.result);
  32. reader.readAsDataURL(response.response);
  33. } //Finishes the onload
  34. }); //Finishes the xmlHttpRequest
  35. }); //Finishes the promise const
  36. imagePromises.push(promise); //Add the promise to the array
  37. promise.then((dataURI) => { //After the promise resolves, replace URL with DataURI in the CSS code
  38. document.querySelector("style:nth-child(2), #custom-css").innerText = document.querySelector("style:nth-child(2), #custom-css").innerText.replace(URL, dataURI);
  39. }); //Finishes the dataURI const
  40. }); //Finishes the forEach loop
  41. await Promise.all(imagePromises); //Wait for all image URLs to be fetched and their corresponding data URIs to be generated
  42.  
  43. if (document.querySelector("#advanced-options") !== null && document.querySelector("#custom-css").innerText !== '\n \n ') //If the user is on a modern list style and has a custom css
  44. { //Starts the if condition
  45. document.body.insertAdjacentHTML('afterbegin', `<a id='dwnldLnk' download="${location.pathname.split('/')[2] + ' Modern Custom Style'}.css" href="data:text/css;charset=utf-8,${encodeURIComponent(document.querySelector("#custom-css").innerText)}"></a>`); //Generate the download button
  46. } //Finishes the if condition
  47.  
  48. if (document.querySelector("#advanced-options") !== null) //If the user is on a modern list style and has no custom css
  49. { //Starts the if condition
  50. document.body.insertAdjacentHTML('afterbegin', `<a id='dwnldLnk' download="${location.pathname.split('/')[2] + ' Modern Style'}.css" href="data:text/css;charset=utf-8,${encodeURIComponent(document.querySelector("style:nth-child(2)").innerText)}"></a>`); //Generate the download button
  51. document.getElementById('dwnldLnk').click(); //Download the css
  52. } //Finishes the if condition
  53. else //If the user is on a classic list style
  54. { //Starts the else condition
  55. document.body.insertAdjacentHTML('afterbegin', `<a id='dwnldLnk' download="${location.pathname.split('/')[2] + ' Classic Style'}.css" href="data:text/css;charset=utf-8,${encodeURIComponent(document.querySelector("style:nth-child(2)").innerText)}"></a>`); //Generate the download button
  56. document.getElementById('dwnldLnk').click(); //Download the css
  57. } //Finishes the else condition
  58. } //Finishes the if condition
  59. else //If the user is on a forum or blog page
  60. { //Starts the else condition
  61. document.querySelectorAll(".message-text > img, .userimg").forEach(function(URL) { //ForEach image URL on the page
  62. const imagePromise = new Promise((resolve) => GM.xmlHttpRequest({ //Starts the xmlHttpRequest
  63. method: "GET",
  64. url: URL.src,
  65. responseType: "blob",
  66. onload: async (response) => {
  67. const dataURI = await new Promise((resolve) => {
  68. const reader = new FileReader();
  69. reader.onload = () => resolve(reader.result);
  70. reader.readAsDataURL(response.response);
  71. });
  72. dataURI !== 'data:' ? URL.src = dataURI : ''; //Change the url link with the data URI
  73. resolve(); //Resolve the promise once the data URI is generated
  74. } //Finishes the onload
  75. })); //Finishes the xmlHttpRequest
  76. imagePromises.push(imagePromise); //Add the promise to the array
  77. }); //Finishes the forEach loop
  78. await Promise.all(imagePromises); //Wait for all image URLs to be fetched and their corresponding data URIs to be generated
  79. document.body.insertAdjacentHTML('afterbegin', `<a id='dwnldLnk' download="https myanimelist.net ${location.href.split(/t\//)[1].replaceAll(/[/?]/gm, ' ').replaceAll(/ /gm, ' ')}.html" href="data:text/html;charset=utf-8,${encodeURIComponent(document.documentElement.outerHTML)}"></a>`); //Generate the download button
  80. document.getElementById('dwnldLnk').click(); //Download the website html
  81. } //Finishes the else condition
  82. }; //Finishes the Backup function