// ==UserScript== // @name Find on Nyaa // @namespace MALFindNya // @version 2.8 // @description Will open nyaa on a new window and search for the anime title // @author Samu // @match https://myanimelist.net/anime/* // @match https://myanimelist.net/anime.php?id=* // @match https://anilist.co/* // @match https://kitsu.io/* // @match https://*.anime-planet.com/* // @match https://*.animenewsnetwork.com/* // @match https://anidb.net/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_addStyle // @downloadURL https://update.greasyfork.icu/scripts/379776/Find%20on%20Nyaa.user.js // @updateURL https://update.greasyfork.icu/scripts/379776/Find%20on%20Nyaa.meta.js // ==/UserScript== /** icon source https://icons8.com/icon/82838/external-link **/ //** SETTINGS **// let sortBy = "seeders"; // "comments", "size", "id", "seeders", "leechers", or "downloads" let filter = "0"; // 0 = No Filter | 1 = No remakes | 2 = Trusted Only let category = "AnimeEnglishTranslated"; // Check values below let favoriteGroups = ["Judas", "Erai-raws"]; // List is ordered, favorite should be first let searchPrefix = ""; let searchSufix = ""; //** END OF SETTINGS **// // Category values const cat = { Allcategories: "0_0", Anime: "1_0", AnimeMusicVideo: "1_1", AnimeEnglishTranslated: "1_2", AnimeNonEnglishTranslated: "1_3", Raw: "1_4", Audio: "2_0", Lossless: "2_1", Lossy: "2_2", Literature: "3_0", LiteratureEnglishTranslated: "3_1", LiteratureNonEnglishTranslated: "3_2", LiteratureRaw: "3_3", LiveAction: "4_0", LiveActionEnglishTranslated: "4_1", IdolPromotionalVideo: "4_2", LiveActionNonEnglishTranslated: "4_3", LiveActionRaw: "4_4", Pictures: "5_0", Graphics: "5_1", Photos: "5_2", Software: "6_0", Applications: "6_1", Games: "6_2", }; const buttonId = "FindOnNyaaButtonId"; const imgKey = "ImgData"; let css = ` #${buttonId} { margin-left: 10px; height: 10px; width: 10px; display: inline-flex; transform: scale(1.8); vertical-align: top; margin-top: 7px; } #${buttonId} svg { fill: #000; } #${buttonId}.dark-theme svg { fill: #fff; } `; GM_addStyle(css); const sites = [ { host: "myanimelist.net", selector: ".title-name", isDark: () => document.documentElement.classList.contains("dark-mode"), }, { host: "anilist.co", path: "/anime/", selector: ".page-content .header .content > h1", isDark: () => document.body.classList.contains("site-theme-dark"), }, { host: "kitsu.io", path: "/anime/", selector: ".media--title h3", isDark: () => { var theme = 'light'; if (window.localStorage) { var cacheStorage = window.localStorage.getItem('storage:last-used'); if (cacheStorage) { theme = JSON.parse(cacheStorage).theme || theme; } } return theme === "dark"; }, }, { host: "anime-planet.com", path: "/anime/", selector: "#siteContainer h1", isDark: () => document.documentElement.classList.contains("darkmode"), }, { host: "animenewsnetwork.com", path: "/encyclopedia/anime.php", selector: "#page_header", filter: /\([a-zA-Z0-9 ]+\)/, isDark: () => false, }, { host: "anidb.net", path: "/anime/", selector: "h1.anime", filter: /^Anime:\s/, isDark: () => true, }, ]; let buttonIcon = getIcon(); (function() { 'use strict'; GM_registerMenuCommand("Add Custom Icon", uploadImage, "h"); if (typeof buttonIcon !== "string") { GM_registerMenuCommand("Remove Custom Icon", removeImage, "h"); } let pushState = history.pushState; let replaceState = history.replaceState; history.pushState = function() { pushState.apply(history, arguments); window.dispatchEvent(new Event('pushstate')); window.dispatchEvent(new Event('locationchange')); }; history.replaceState = function() { replaceState.apply(history, arguments); window.dispatchEvent(new Event('replacestate')); window.dispatchEvent(new Event('locationchange')); }; window.addEventListener('popstate', function() { window.dispatchEvent(new Event('locationchange')) }); let host = window.location.host; let path = window.location.pathname; window.addEventListener('locationchange', function() { path = window.location.pathname; lookForTitleElement(host, path); }); lookForTitleElement(host, path); })(); function lookForTitleElement(host, path) { for (let i = 0; i < sites.length; i++) { let site = sites[i]; if (host.endsWith(site.host) && (site.path == undefined || path.startsWith(site.path))) { waitForElm(site.selector).then(elm => init(elm, site.isDark(), site.filter)); break; } } let nyaaButton = document.getElementById(buttonId); if (nyaaButton != null) { nyaaButton.parentElement.removeChild(nyaaButton); } } function init(element, dark = false, filter) { let url = createUrl(element, filter); let nyaaButton = document.getElementById(buttonId); if (nyaaButton == null) { nyaaButton = createButton(url); element.appendChild(nyaaButton); } else { nyaaButton.setAttribute("href", url); } if (dark) { nyaaButton.classList.add("dark-theme"); } } function createButton(url) { let button = document.createElement("a"); button.id = buttonId; button.setAttribute("href", url); button.setAttribute("target", "_blank"); button.setAttribute("rel", "noopener"); button.setAttribute("title", "Search on Nyaa"); if (typeof buttonIcon === "string") { button.innerHTML = buttonIcon; } else { button.appendChild(buttonIcon); } return button; } function createUrl(element, titleFilter) { let textFragment = ":~:text=" + favoriteGroups.join("&text="); let title = element.textContent.replace(titleFilter, "").trim().replace(/[^a-zA-Z0-9]+/g, "+").trim(); let query = [searchPrefix, title, searchSufix].join(" ").trim(); let queryURI = encodeURI(query); let baseUrl = "https://nyaa.si/"; let path = `?f=${filter}&c=${cat[category]}&s=${sortBy}&o=desc&q=${queryURI}`; return baseUrl + path + "#" + textFragment; } function waitForElm(selector) { return new Promise(resolve => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(mutations => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); observer.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); }); } function getIcon() { let userImg = GM_getValue(imgKey); if (userImg == null) { return ``; } let imgElement = document.createElement("img"); imgElement.src = userImg; return imgElement; } function uploadImage() { let input = document.createElement("input"); input.type = "file"; input.addEventListener("change", onImageUploaded); input.click(); } function onImageUploaded(e) { let file = this.files[0]; new Promise((resolve, reject) => { const reader = new FileReader() reader.onloadend = () => resolve(reader.result) reader.onerror = reject reader.readAsDataURL(file) }) .then(result => { GM_setValue(imgKey, result); window.location.reload(); }) .catch(err => alert(err)); } function removeImage() { GM_setValue(imgKey, null); window.location.reload(); }