Nyaa Linker

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

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

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