您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a button to Anime and Manga database websites that opens a relevant Nyaa search
// ==UserScript== // @name Nyaa Linker // @namespace https://github.com/po5 // @version 2.3.0 // @description Adds a button to Anime and Manga database websites that opens a relevant Nyaa search // @author Metacor, eva // @match *://*.myanimelist.net/* // @match *://*.anilist.co/* // @match *://*.kitsu.app/* // @match *://*.anime-planet.com/* // @match *://*.animenewsnetwork.com/encyclopedia/* // @match *://*.anidb.net/* // @match *://*.livechart.me/* // @grant GM.getValue // @grant GM.setValue // @grant GM.registerMenuCommand // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @license GPL-3.0 // @run-at document-end // ==/UserScript== // GreaseMonkey 4.x shim let getValue; if (typeof GM_getValue === 'undefined' && typeof GM !== 'undefined') { self.GM_setValue = GM.setValue; self.GM_registerMenuCommand = GM.registerMenuCommand; getValue = GM.getValue; } else { getValue = function(key, fallback) { return new Promise(function(resolve, reject) { try { resolve(GM_getValue(key, fallback)); } catch(e) { reject(e); } }); }; } let settings; const defaultSettings = { filter_setting: '0', category_setting: '1_2', query_setting: 'default', sort_setting: 'seeders', order_setting: 'desc', hide_button_setting: false, focus_setting: false, custom_text_toggle_setting: false, custom_text_setting: '', hotkey_key_setting: '', hotkey_modifier_setting: '', hotkey_query_setting: 'inherit', }; if (typeof GM_registerMenuCommand !== 'undefined') { GM_registerMenuCommand('Nyaa Linker Settings', () => { if (document.getElementById('nyaa-linker-settings')) return; const settingsPanel = document.createElement('div'); settingsPanel.id = 'nyaa-linker-settings'; settingsPanel.style.position = 'fixed'; settingsPanel.style.top = '50%'; settingsPanel.style.left = '50%'; settingsPanel.style.transform = 'translate(-50%, -50%)'; settingsPanel.style.backgroundColor = 'var(--clrDark, hsl(0, 0%, 10%))'; settingsPanel.style.color = 'var(--clrLight, hsl(0, 0%, 87%))'; settingsPanel.style.padding = '20px'; settingsPanel.style.border = '1px solid var(--clrAccent, hsl(210, 100%, 60%))'; settingsPanel.style.zIndex = '10000'; settingsPanel.style.overflow = 'hidden'; settingsPanel.style.fontFamily = 'Verdana, Arial'; settingsPanel.style.fontSize = '11px'; settingsPanel.style.lineHeight = '16px'; settingsPanel.innerHTML = ` <style> #nyaa-linker-settings { --clrDark: hsl(0, 0%, 10%); --clrLight: hsl(0, 0%, 87%); --clrAccent: hsl(210, 100%, 60%); } #nyaa-linker-settings, #nyaa-linker-settings::before, #nyaa-linker-settings::after, #nyaa-linker-settings *, #nyaa-linker-settings *::before, #nyaa-linker-settings *::after { all: revert; } #nyaa-linker-settings *, #nyaa-linker-settings *::before, #nyaa-linker-settings *::after { box-sizing: border-box; margin: 0; padding: 0; border: none; font-size: 11px; } #nyaa-linker-settings-parametersPage, #nyaa-linker-settings-settingsPage { display: grid; grid-template-columns: auto auto; gap: 2px; padding: 2px 0 2px 2px; text-align: right; } #nyaa-linker-settings-settingsPage { border: 1px solid var(--clrAccent); border-top: none; padding-left: 25px; } #nyaa-linker-settings button, #nyaa-linker-settings input, #nyaa-linker-settings select, #nyaa-linker-settings option { cursor: pointer; text-align: center; height: 21px; } #nyaa-linker-settings select, #nyaa-linker-settings-saveButton, #nyaa-linker-settings-hotkey_key_select, #nyaa-linker-settings-custom_text_select { background: var(--clrAccent); color: var(--clrDark); } #nyaa-linker-settings option, #nyaa-linker-settings-settingsPage { background: var(--clrDark); color: var(--clrAccent); } #nyaa-linker-settings-bottomButtons { display: flex; gap: 2px; } #nyaa-linker-settings-saveButton { flex: 11; } #nyaa-linker-settings-settingsButton { background: var(--clrAccent) url('https://upload.wikimedia.org/wikipedia/commons/5/58/Ic_settings_48px.svg') no-repeat 0 0 / contain; flex: 1; } #nyaa-linker-settings-hotkey_key_select, #nyaa-linker-settings input[type='checkbox'] { width: 21px; justify-self: left; } #nyaa-linker-settings-hotkey_modifier_select, #nyaa-linker-settings-hotkey_query_select, #nyaa-linker-settings-custom_text_select { width: 98%; } </style> <div id="nyaa-linker-settings-parametersPage"> <label for="filter_select">Filter:</label> <select id="filter_select"> <option value="0">No Filter</option> <option value="1">No Remakes</option> <option value="2">Trusted Only</option> </select> <label for="category_select">Category:</label> <select id="category_select"> <option value="0_0">All Categories</option> <option value="1_2">English-Translated</option> <option value="1_3">Non-English-Translated</option> <option value="1_4">Raw</option> </select> <label for="query_select">Query:</label> <select id="query_select"> <option value="default" title="Creates a search using both the 'Exact' and 'Base' options">Default</option> <option value="fuzzy" title="Searches for the site's default title only, without quotes — allows fuzzy matching">Fuzzy</option> <option value="exact" title="Japanese and English full titles — searches for exact title names as written">Exact</option> <option value="base" title="Japanese and English base titles — searches with Seasons and Parts removed">Base</option> </select> <label for="sort_select">Sort:</label> <select id="sort_select"> <option value="comments">Comments</option> <option value="size">Size</option> <option value="id">Date</option> <option value="seeders">Seeders</option> <option value="leechers">Leechers</option> <option value="downloads">Downloads</option> </select> <label for="order_select">Order:</label> <select id="order_select"> <option value="desc">Descending</option> <option value="asc">Ascending</option> </select> </div> <div id="nyaa-linker-settings-bottomButtons"> <button id="nyaa-linker-settings-saveButton">Save and Close</button> </div> <div id="nyaa-linker-settings-settingsPage"> <label for="hide_button_select">Hide Button:</label> <input type="checkbox" id="hide_button_select" title="Stops the 'Search on Nyaa' button from being rendered"> <label for="focus_select">Maintain Focus:</label> <input type="checkbox" id="focus_select" title="Changes Tab Focus behavior when using the Hotkey"> <label for="custom_text_toggle_select">Include Text:</label> <input type="checkbox" id="custom_text_toggle_select" title="Decides if text in 'Custom Query' is included"> <label for="custom_text_select">Custom Text:</label> <input type="text" placeholder="?" id="custom_text_select" title="User defined text to be added at the end of the search query"> <label for="hotkey_key_select">Hotkey:</label> <input type="text" maxlength="1" placeholder="?" id="hotkey_key_select"> <label for="hotkey_modifier_select">Hotkey Modifier:</label> <select id="hotkey_modifier_select"> <option value="">None</option> <option value="shiftKey">Shift</option> <option value="ctrlKey">Control</option> <option value="altKey">Alt</option> </select> <label for="hotkey_query_select">Hotkey Query:</label> <select id="hotkey_query_select"> <option value="inherit" title="Inherits its behavior from the Query option on the primary settings page">Inherit</option> <option value="default" title="Creates a search using both the 'Exact' and 'Base' options">Default</option> <option value="fuzzy" title="Searches for the site's default title only, without quotes — allows fuzzy matching">Fuzzy</option> <option value="exact" title="Japanese and English full titles — searches for exact title names as written">Exact</option> <option value="base" title="Japanese and English base titles — searches with Seasons and Parts removed">Base</option> </select> </div> `; document.body.appendChild(settingsPanel); document.getElementById('filter_select').value = settings.filter_setting; document.getElementById('category_select').value = settings.category_setting; document.getElementById('query_select').value = settings.query_setting; document.getElementById('sort_select').value = settings.sort_setting; document.getElementById('order_select').value = settings.order_setting; document.getElementById('hide_button_select').checked = settings.hide_button_setting; document.getElementById('focus_select').checked = settings.focus_setting; document.getElementById('custom_text_toggle_select').checked = settings.custom_text_toggle_setting; document.getElementById('custom_text_select').value = settings.custom_text_setting; document.getElementById('hotkey_key_select').value = settings.hotkey_key_setting; document.getElementById('hotkey_modifier_select').value = settings.hotkey_modifier_setting; document.getElementById('hotkey_query_select').value = settings.hotkey_query_setting; document.getElementById('nyaa-linker-settings-saveButton').onclick = () => { const newSettings = { filter_setting: document.getElementById('filter_select').value, category_setting: document.getElementById('category_select').value, query_setting: document.getElementById('query_select').value, sort_setting: document.getElementById('sort_select').value, order_setting: document.getElementById('order_select').value, hide_button_setting: document.getElementById('hide_button_select').checked, focus_setting: document.getElementById('focus_select').checked, custom_text_toggle_setting: document.getElementById('custom_text_toggle_select').checked, custom_text_setting: document.getElementById('custom_text_select').value, hotkey_key_setting: document.getElementById('hotkey_key_select').value.toLowerCase(), hotkey_modifier_setting: document.getElementById('hotkey_modifier_select').value, hotkey_query_setting: document.getElementById('hotkey_query_select').value, }; GM_setValue('settings', newSettings); settings = newSettings; settingsPanel.remove(); document.querySelectorAll('.nyaaBtn').forEach((e) => e.remove()); init(); }; }); } let btn, currentPage, hotkeyListener; function init() { searchNyaa(settings); } function searchNyaa(settings) { const domain = window.location.href; let media = window.location.pathname.includes('manga') ? 'manga' : 'anime'; let titleJap, titleEng, btnSpace, cardType, cardFlag, isSpicy; let categorySetting = settings.category_setting; let queryType = settings.query_setting; let customQuery = settings.custom_text_toggle_setting ? settings.custom_text_setting : ''; const setCategory = (cat) => { if (media === 'manga') { const categories = { '0_0': '3_0', '1_2': '3_1', '1_3': '3_2', '1_4': '3_3' }; return categories[cat]; } else { return cat; } }; categorySetting = setCategory(settings.category_setting); function createBtn(btnSpace) { !cardFlag && document.querySelector('.nyaaBtn') && document.querySelectorAll('.nyaaBtn').forEach((e) => e.remove()), (cardFlag = true); btn = btnSpace.appendChild(document.createElement('a')); btn.classList.add('nyaaBtn'); settings.hide_button_setting && (btn.style.display = 'none'); !cardType && settings.hotkey_key_setting && startHotkeyListener(); } function createSearch(query) { let subDomain, siteText; isSpicy ? ((subDomain = 'sukebei.'), (siteText = 'Sukebei'), media === 'manga' ? (categorySetting = '0_0') : (categorySetting = '1_1')) : ((subDomain = ''), (siteText = 'Nyaa')); !btn.title && (btn.textContent = `Search on ${siteText}`); (query.includes('&') || query.includes('+')) && (query = query.replace(/&/g, '%26').replace(/\+/g, '%2B')); btn.href = `https://${subDomain}nyaa.si/?f=${settings.filter_setting}&c=${categorySetting}&q=${query}${customQuery}&s=${settings.sort_setting}&o=${settings.order_setting}`; btn.target = '_blank'; } function startHotkeyListener() { hotkeyListener && document.removeEventListener('keydown', hotkeyListener); hotkeyListener = (e) => { if ( (btn && e[settings.hotkey_modifier_setting] && e.key.toLowerCase() === settings.hotkey_key_setting) || (btn && settings.hotkey_modifier_setting === '' && !e.ctrlKey && !e.shiftKey && !e.altKey && e.key === settings.hotkey_key_setting) ) { if (settings.hotkey_query_setting !== 'inherit') { queryType = settings.hotkey_query_setting; createSearch(getQuery(titleJap, titleEng, queryType)); } btn.dispatchEvent(new MouseEvent('click', { ctrlKey: settings.focus_setting })); e.preventDefault(); queryType = settings.query_setting; createSearch(getQuery(titleJap, titleEng, queryType)); } }; document.addEventListener('keydown', hotkeyListener); } switch (true) { case domain.includes(`myanimelist.net`): media = window.location.href.split('/')[3]; categorySetting = setCategory(settings.category_setting); const malMain = new RegExp(`myanimelist\\.net/${media}/\\d+`); if (malMain.test(domain)) { const engCheck = document.querySelector('.title-english'); engCheck && (titleEng = engCheck.textContent); if (media === 'manga') { const titleElm = document.querySelector('[itemprop="name"]'); titleJap = titleElm.textContent; if (engCheck) { engCheck.textContent = ''; titleJap = titleElm.textContent; engCheck.textContent = titleEng; } } else { titleJap = document.querySelector('.title-name').textContent; } isSpicy = [...document.querySelectorAll('span[itemprop="genre"]')].some((el) => el.textContent.trim().toLowerCase() === 'hentai'); btnSpace = document.getElementById('broadcast-block') || document.querySelector('.leftside').children[0]; createBtn(btnSpace); btn.style.marginTop = '4px'; btn.classList.add('left-info-block-broadcast-button'); createSearch(getQuery(titleJap, titleEng, queryType)); } const cardPaths = ['/genre', '/season', '/magazine', '/adapted']; if (cardPaths.some((path) => domain.includes(path))) { if (domain.includes('/adapted') && document.querySelector('.list.on')) return; for (const card of document.querySelectorAll('.seasonal-anime')) { cardType = true; titleJap = card.querySelector('.title h2').innerText; titleEng = card.querySelector('.title h3')?.innerText; isSpicy = [...card.querySelectorAll('.explicit a')].some((el) => el.title.toLowerCase().includes('hentai')); !isSpicy && (categorySetting = setCategory(settings.category_setting)); createBtn(card.querySelector('.broadcast')); btn.title = 'Search on Nyaa'; btn.style.background = 'url(https://i.imgur.com/9Fr2BRG.png) center/20px no-repeat'; btn.style.padding = '0 11px'; isSpicy && ((btn.title = 'Search on Sukebei'), (btn.style.border = '2px solid red'), (btn.style.borderRadius = '50%')); createSearch(getQuery(titleJap, titleEng, queryType)); } } break; case (domain.includes(`anime-planet.com/anime/`) || domain.includes(`anime-planet.com/manga/`)) && domain.split("/").pop() !== '': media = window.location.href.split('/')[3]; categorySetting = setCategory(settings.category_setting); const skipPages = ['all', 'top-', 'recommendations', 'tags']; let skipExtra = media == 'anime' ? ['seasons', 'watch-online', 'studios'] : ['read-online', 'publishers', 'magazines', 'webtoons', 'light-novels']; if (skipPages.some((page) => domain.includes(`/${media}/${page}`)) || skipExtra.some((page) => domain.includes(`/${media}/${page}`))) { break; } setTimeout(() => { const titleMain = document.querySelector('[itemprop=name]').textContent; const titleAlt = document.getElementsByClassName('aka')[0]; titleEng = titleMain; titleAlt ? (titleJap = titleAlt.innerText.split(': ').pop()) : (titleJap = titleMain); createBtn(document.querySelector('.mainEntry')); btn.classList.add('button'); document.querySelectorAll('.mainEntry > .button').forEach((button) => { typeof button === 'object' && (button.style.width = '180px'); }); createSearch(getQuery(titleJap, titleEng, queryType)); }, 50); break; case domain.includes(`animenewsnetwork.com/encyclopedia/anime.php?id=`) || domain.includes(`animenewsnetwork.com/encyclopedia/manga.php?id=`): media = domain.includes(`animenewsnetwork.com/encyclopedia/anime.php?id=`) ? 'anime' : 'manga'; categorySetting = setCategory(settings.category_setting); setTimeout(() => { titleEng = document.getElementById('page_header').innerText.split(' (').shift(); for (const altTitle of document.querySelectorAll('#infotype-2 > .tab')) { altTitle.textContent.includes('Japanese') && !titleJap && (titleJap = altTitle.textContent.split(' (').shift()); } !titleJap && titleEng && (titleJap = titleEng); btnSpace = document.querySelector('.fright') ? document.querySelector('.fright') : document.querySelector('#big-video'); createBtn(btnSpace); btn.style.display !== 'none' && (btn.style.display = 'flex'); btn.style.alignItems = 'center'; btn.style.justifyContent = 'center'; btn.style.height = '35px'; btn.style.borderRadius = '3px'; btn.style.background = '#2d50a7'; btn.style.color = '#fff'; btn.style.border = '1px solid black'; btn.style.textDecoration = 'none'; btnSpace.children[0].tagName === 'TABLE' && (btn.style.marginTop = '4px'); createSearch(getQuery(titleJap, titleEng, queryType)); }, 50); break; case domain.includes(`anidb.net/anime/`) || domain.includes(`anidb.net/manga/`): media = window.location.href.split('/')[3]; categorySetting = setCategory(settings.category_setting); const hasID = /anidb\.net\/\w+\/(\d+)/; if (domain.match(hasID)) { titleJap = document.querySelector(".value > [itemprop='name']").textContent; titleEng = document.querySelector(".value > [itemprop='alternateName']").textContent; isSpicy = [...document.querySelectorAll('.tagname')].some((el) => el.textContent.trim().toLowerCase() === '18 restricted'); btnSpace = document.querySelector('.resources > .value .english').appendChild(document.createElement('div')); btnSpace.classList.add('icons'); createBtn(btnSpace); btn.classList.add('i_icon'); btn.style.backgroundImage = "url('https://i.imgur.com/YG6H2nF.png')"; btn.style.backgroundSize = 'contain'; isSpicy ? (btn.title = 'Search on Sukebei') : (btn.title = 'Search on Nyaa'); createSearch(getQuery(titleJap, titleEng, queryType)); } break; case domain.includes(`anilist.co/anime/`) || domain.includes(`anilist.co/manga/`): media = window.location.href.split('/')[3]; categorySetting = setCategory(settings.category_setting); awaitLoadOf('.sidebar .type', 'Romaji', () => { for (const data of document.getElementsByClassName('type')) { const setTitle = data.parentNode.children[1].textContent; data.textContent.includes('Romaji') && (titleJap = setTitle); data.textContent.includes('English') && (titleEng = setTitle); data.textContent.includes('Genres') ? (isSpicy = setTitle.toLowerCase().includes('hentai')) : null; } createBtn(document.querySelector('.cover-wrap-inner')); btn.style.display !== 'none' && (btn.style.display = 'flex'); btn.style.alignItems = 'center'; btn.style.justifyContent = 'center'; btn.style.height = '35px'; btn.style.borderRadius = '3px'; btn.style.marginBottom = '20px'; btn.style.background = 'rgb(var(--color-blue))'; btn.style.color = 'rgb(var(--color-white))'; createSearch(getQuery(titleJap, titleEng, queryType)); }); break; case domain.includes(`kitsu.app/anime/`) || domain.includes(`kitsu.app/manga/`): media = window.location.href.split('/')[3]; categorySetting = setCategory(settings.category_setting); awaitLoadOf('.media--information', 'Status', () => { let titleUsa; document.querySelector('a.more-link')?.click(); for (const data of document.querySelectorAll('.media--information > ul > li')) { const usaCheck = data.textContent.includes('English (American)'); const setTitle = data.getElementsByTagName('span')[0]; data.textContent.includes('Japanese (Romaji)') && (titleJap = setTitle.textContent); data.textContent.includes('English') && !usaCheck && (titleEng = setTitle.textContent); usaCheck && (titleUsa = setTitle.textContent); if (data.textContent.includes('Rating')) { isSpicy = data.querySelector('span')?.textContent.replace(/\s+/g, ' ').trim() === 'R18 - Hentai'; } } document.querySelector('a.more-link')?.click(); !titleEng && titleUsa && (titleEng = titleUsa); !titleJap && titleEng && (titleJap = titleEng); createBtn(document.querySelector('.library-state')); btn.classList.add('button', 'button--secondary'); btn.style.background = '#f5725f'; btn.style.marginTop = '10px'; createSearch(getQuery(titleJap, titleEng, queryType)); }); break; case domain.includes('livechart.me'): media = "anime"; categorySetting = setCategory(settings.category_setting); if (domain.includes(`livechart.me/${media}/`)) { titleJap = document.querySelector('.grow .text-xl').innerText; titleEng = document.querySelector('.grow .text-lg').innerText; createBtn(document.querySelector('.lc-poster-col')); btn.classList.add('lc-btn', 'lc-btn-sm', 'lc-btn-outline'); createSearch(getQuery(titleJap, titleEng, queryType)); } else { let cardSelector, cardSpace; domain.includes('livechart.me/franchises/') ? (cardSelector = '.lc-anime') : (cardSelector = '.anime'); domain.includes('livechart.me/franchises/') ? (cardSpace = '.lc-anime-card--related-links') : (cardSpace = '.related-links'); for (const card of document.querySelectorAll(cardSelector)) { cardType = true; titleJap = card.getAttribute('data-romaji'); card.getAttribute('data-english') ? (titleEng = card.getAttribute('data-english')) : (titleEng = undefined); createBtn(card.querySelector(cardSpace)); btn.style.background = 'url(https://i.imgur.com/9Fr2BRG.png) center/20px no-repeat'; btn.style.padding = '15px'; btn.style.margin = 0; btn.classList.add('action-button'); btn.title = 'Search on Nyaa'; createSearch(getQuery(titleJap, titleEng, queryType)); } } break; } } function getQuery(titleJap, titleEng, queryType) { !titleJap && !titleEng && init(); titleJap && (titleJap = titleJap.replace(/["]/g, '')); titleEng && (titleEng = titleEng.replace(/["]/g, '')); let query = `"${titleJap}"|"${titleEng}"`; if (!titleEng || titleJap.toLowerCase() === titleEng.toLowerCase()) { query = titleJap; return query; } else { let baseJap = getBaseTitle(titleJap); let baseEng = getBaseTitle(titleEng); if (queryType == 'default') { baseJap == titleJap && baseEng == titleEng ? (query = query) : (query = `"${titleJap}"|"${titleEng}"|"${baseJap}"|"${baseEng}"`); } if (queryType == 'base') { baseJap == baseEng ? (query = query) : (query = `"${baseJap}"|"${baseEng}"`); } queryType == 'fuzzy' && (query = titleJap); return query; } } function getBaseTitle(baseTitle) { const hasSeason = /(?<![\w])(season)(?![\w])/i; const hasNum = /(?<![\w])[0-9]+(?:st|[nr]d|th)(?![\w])/i; const hasWord = /(?<![\w])(first|second|third|fourth|fifth|(the final|final))(?![\w])/i; const hasPart = /(?<![\w])(part )/i; const hasEndPunc = /[?!.]$/; baseTitle = baseTitle .replace(/[\(\)\[\]\{\}][^()\[\]\{\}]*[\)\]\{\}]/g, '') .replace(/([♡♥☆★♪∞])(?=\w)/g, ' ') .replace(/[♡♥☆★♪∞](?!\w)/g, '') .trim(); baseTitle.includes(': ') && (baseTitle = baseTitle.split(': ').shift()); baseTitle.includes(' - ') && (baseTitle = baseTitle.split(' - ').pop()); hasPart.test(baseTitle) && (baseTitle = baseTitle.split(/( part)/i).shift()); if (hasSeason.test(baseTitle)) { if (hasNum.test(baseTitle) || hasWord.test(baseTitle)) { let titleNum, titleWord; hasNum.test(baseTitle) && (titleNum = baseTitle.match(hasNum)[0]); hasWord.test(baseTitle) && (titleWord = baseTitle.match(hasWord)[0]); titleNum && (baseTitle = baseTitle.split(` ${titleNum}`).shift()); titleWord && (baseTitle = baseTitle.split(` ${titleWord}`).shift()); } else { baseTitle = baseTitle.split(/( season)/i).shift(); } } while (hasEndPunc.test(baseTitle)) { baseTitle = baseTitle.split(baseTitle.match(hasEndPunc)[0]).shift(); } return baseTitle; } const awaitLoadOf = (selector, text, func) => { return new Promise((resolve) => { let found = false; const elmspre = document.querySelectorAll(selector); elmspre.forEach((elm) => { if (found) return; if (elm.textContent.includes(text)) { found = true; resolve(elm); func(); } }); if (found) return; const mutObs = new MutationObserver(() => { const elms = document.querySelectorAll(selector); elms.forEach((elm) => { if (found) return; if (elm.textContent.includes(text)) { found = true; resolve(elm); mutObs.disconnect(); func(); } }); }); mutObs.observe(document.body, { childList: true, subtree: true }); }); }; getValue('settings', defaultSettings).then((v) => { settings = v; currentPage = window.location.href.split('/')[4]; init(); const observer = new MutationObserver(() => { if (window.location.href.split('/')[4] !== currentPage) { currentPage = window.location.href.split('/')[4]; document.querySelectorAll('.nyaaBtn').forEach((e) => e.remove()); init(); } }); observer.observe(document.body, { childList: true, subtree: true }); });