Better Buttons to Change The Status Of Animes/Mangas And To Add Scores

Makes it easier and faster to select any status/score by enlarging the dropdown selection of those options. Delete any entry from your list, or score + add the whole anime Franchise to your anime list with a single click.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Better Buttons to Change The Status Of Animes/Mangas And To Add Scores
// @namespace    betterbuttonstomal2
// @version      25
// @description  Makes it easier and faster to select any status/score by enlarging the dropdown selection of those options. Delete any entry from your list, or score + add the whole anime Franchise to your anime list with a single click.
// @author       hacker09
// @include      /^https:\/\/myanimelist\.net\/(anime|manga)(id=)?(\.php\?id=)?\/?\d+\/?(?!.*\/).*(\?q=.*&cat=anime|manga)?$/
// @icon         https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://myanimelist.net&size=64
// @grant        GM.xmlHttpRequest
// @connect      chiaki.site
// @run-at       document-end
// ==/UserScript==

(function() {
  'use strict';
  const pathParts = location.pathname.split('/'); //Split url to get type and id
  const entryType = pathParts[1]; //Get 'anime' or 'manga'
  const entryId = pathParts[2]; //Get the entry id
  const statusSelect = document.querySelectorAll("#myinfo_status")[1]; //Cache status selector
  const scoreSelect = document.querySelectorAll("#myinfo_score")[1]; //Cache score selector

  //UI Adjustments
  statusSelect.size = "5"; //Set the size for the Status button
  statusSelect.style.cssText = "font-size: 13.2px; background-image: none; overflow: hidden;"; //Set the css for the status button
  scoreSelect.size = "11"; //Set the size for the Score button
  scoreSelect.style.cssText = "background-image: none; overflow: hidden; padding : 5px; width: 100px;"; //Set the CSS for the score button
  document.querySelectorAll("#myinfo_score > option:nth-child(1)")[1].innerText = 'Reset Score'; //Change the text "selection" to Reset Score

  const createBtn = (val, id) => { //Helper to create buttons
    const btn = document.createElement("input"); //Create input
    btn.value = val; //Set value
    btn.id = id; //Set ID
    btn.className = "inputButton ml8 delete_submit"; //Set classes
    btn.type = "button"; //Set type
    btn.style.marginLeft = "15px"; //Set margin
    statusSelect.parentElement.appendChild(btn); //Append to DOM
    return btn; //Return element
  };

  const deleteBtn = createBtn("Delete", "DeleteBTN"); //Create Delete Button

  //API Helpers
  const malRequest = async (url, body, isJson = true) => { //Helper for MAL fetches
    await fetch(url, {
      method: "POST",
      headers: {
        "content-type": isJson ? "application/x-www-form-urlencoded; charset=UTF-8" : "application/x-www-form-urlencoded"
      },
      body: body
    });
  };

  const getFranchiseIds = (id) => { //Helper to fetch Chiaki data
    return new Promise((resolve) => {
      GM.xmlHttpRequest({
        method: "GET",
        url: `https://chiaki.site/?/tools/watch_order/id/${id}`,
        onload: (res) => {
          const doc = new DOMParser().parseFromString(res.responseText, 'text/html'); //Parse response
          const links = doc.querySelectorAll(".wo_meta > a:nth-child(2)"); //Get all franchise links
          const ids = Array.from(links).map(a => a.href.match(/\d+/)[0]); //Extract IDs
          resolve(ids); //Return array of IDs
        }
      });
    });
  };

  //Anime Specific Logic
  if (entryType === 'anime') {
    document.querySelector("div.di-ib.form-user-episode.ml8").style.width = "125px"; //Fix episode input width
    document.querySelectorAll("#myinfo_watchedeps")[1].style.width = "25px"; //Fix watched eps width

    const scoreBtn = createBtn("Score+Add Franchise", "ScoreBTN"); //Create Score Button

    scoreBtn.addEventListener("click", async () => { //Score Event
      const score = scoreSelect.value;
      const status = document.querySelector(".po-r > #myinfo_status > option:checked");

      if (score === '0') return alert('You must first give a score for this entry, then the script will give that same score and add the entire franchise with the current status of this entry.'); //Validate score

      const alreadyAdded = confirm("If you've already added this entire anime franchise to your anime list, press OK."); //Check fetch mode
      const fetchPage = alreadyAdded ? 'edit' : 'add'; //Set fetch mode

      const ids = await getFranchiseIds(entryId); //Get IDs from Chiaki

      for (const id of ids) { //Loop through IDs
        if (id !== entryId) { //Skip current page
          const body = JSON.stringify({
            "anime_id": parseInt(id),
            "status": parseInt(status.value),
            "score": parseInt(score),
            "num_watched_episodes": 0,
            "csrf_token": document.head.querySelector("[name='csrf_token']").content
          });
          await malRequest(`https://myanimelist.net/ownlist/anime/${fetchPage}.json`, body); //Send Request
        }
      }

      alert(`Done!!!\nThe Whole anime Franchise was scored with ${score} and added to your ${status.innerText} anime list!`); //Success msg
    });
  }

  //Delete Logic
  deleteBtn.addEventListener("click", async () => { //Delete Event
    const isFranchise = entryType === 'anime' && confirm("OK = Delete entire franchise\nCancel = Delete this entry."); //Determine scope

    if (isFranchise) {
      const ids = await getFranchiseIds(entryId); //Get IDs
      for (const id of ids) { //Loop IDs
        await malRequest(`https://myanimelist.net/ownlist/${entryType}/${id}/delete`, `csrf_token=${document.head.querySelector("[name='csrf_token']").content}`, false); //Delete each
      }
      alert(`Done!!!\nThe Whole anime Franchise was deleted off your anime list!`); //Success msg
    } else {
      await malRequest(`https://myanimelist.net/ownlist/${entryType}/${entryId}/delete`, `csrf_token=${document.head.querySelector("[name='csrf_token']").content}`, false); //Delete single
    }

    location.reload(); //Reload page
  });
})();