Nyaa Linker

Adds a button to Anime and Manga database websites that opens a relevant Nyaa search

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

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