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.
// ==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
});
})();