MAL Images Backup

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

目前為 2023-05-07 提交的版本,檢視 最新版本

// ==UserScript==
// @name      MAL Images Backup
// @namespace MALImgsBackup
// @description Backups all images on forum topics, blogs and all images on anime/manga lists CSS styles.
// @version  2
// @match    https://myanimelist.net/animelist/*
// @match    https://myanimelist.net/mangalist/*
// @match    https://myanimelist.net/blog/*&cat=*
// @match    https://myanimelist.net/forum/?topicid=*
// @icon     https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://myanimelist.net&size=64
// @connect  i.imgur.com
// @connect  cdn.myanimelist.net
// @grant    GM_registerMenuCommand
// @grant    GM.xmlHttpRequest
// @run-at   document-end
// ==/UserScript==

GM_registerMenuCommand("Backup HTML/CSS", Backup); //Adds an option to the menu
async function Backup() { //Creates a new Backup function
  const imagePromises = []; //Creates a new array
  if (location.pathname.match('list') !== null) //If the user is on a manga/anime list
  { //Starts the if condition
    document.querySelector("style:nth-child(2), #custom-css").innerText.match(/http.+?(?=(?:"|'|`)?\))/gm).forEach((URL) => { //For each image URL in the CSS code
      const promise = new Promise((resolve) => { //Convert URL to DataURI using a Promise
        GM.xmlHttpRequest({ //Starts the xmlHttpRequest
          method: "GET",
          url: URL,
          responseType: "blob",
          onload: async (response) => {
            const reader = new FileReader();
            reader.onload = () => resolve(reader.result);
            reader.readAsDataURL(response.response);
          } //Finishes the onload
        }); //Finishes the xmlHttpRequest
      }); //Finishes the promise const
      imagePromises.push(promise); //Add the promise to the array
      promise.then((dataURI) => { //After the promise resolves, replace URL with DataURI in the CSS code
        document.querySelector("style:nth-child(2), #custom-css").innerText = document.querySelector("style:nth-child(2), #custom-css").innerText.replace(URL, dataURI);
      }); //Finishes the dataURI const
    }); //Finishes the forEach loop
    await Promise.all(imagePromises); //Wait for all image URLs to be fetched and their corresponding data URIs to be generated

    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
    { //Starts the if condition
      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
    } //Finishes the if condition

    if (document.querySelector("#advanced-options") !== null) //If the user is on a modern list style and has no custom css
    { //Starts the if condition
      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
      document.getElementById('dwnldLnk').click(); //Download the css
    } //Finishes the if condition
    else //If the user is on a classic list style
    { //Starts the else condition
      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
      document.getElementById('dwnldLnk').click(); //Download the css
    } //Finishes the else condition
  } //Finishes the if condition
  else //If the user is on a forum or blog page
  { //Starts the else condition
    document.querySelectorAll(".message-text > img, .userimg").forEach(function(URL) { //ForEach image URL on the page
      const imagePromise = new Promise((resolve) => GM.xmlHttpRequest({ //Starts the xmlHttpRequest
        method: "GET",
        url: URL.src,
        responseType: "blob",
        onload: async (response) => {
          const dataURI = await new Promise((resolve) => {
            const reader = new FileReader();
            reader.onload = () => resolve(reader.result);
            reader.readAsDataURL(response.response);
          });
          dataURI !== 'data:' ? URL.src = dataURI : ''; //Change the url link with the data URI
          resolve(); //Resolve the promise once the data URI is generated
        } //Finishes the onload
      })); //Finishes the xmlHttpRequest
      imagePromises.push(imagePromise); //Add the promise to the array
    }); //Finishes the forEach loop
    await Promise.all(imagePromises); //Wait for all image URLs to be fetched and their corresponding data URIs to be generated
    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
    document.getElementById('dwnldLnk').click(); //Download the website html
  } //Finishes the else condition
}; //Finishes the Backup function