DMM - All Results & Polish Filter

Adds a "Show All Results" button and a Polish filter button to Debrid Media Manager.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         DMM - All Results & Polish Filter
// @namespace    http://tampermonkey.net/
// @version      5.3
// @description  Adds a "Show All Results" button and a Polish filter button to Debrid Media Manager.
// @author       Your Assistant & tomfle
// @match        *://*.debridmediamanager.com/movie/*
// @match        *://*.debridmediamanager.com/show/*/*
// @grant        none
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- SETTINGS AND GLOBAL VARIABLES ---
    const SHOW_ALL_BUTTON_ID = 'show-all-results-btn';
    const POLISH_FILTER_BUTTON_ID = 'polish-filter-btn';
    const CHECK_INTERVAL = 500; // Time in ms, how often the script checks for a new button

    // This regex is for finding Polish language releases. The keywords are names of Polish release groups or common terms.
    const POLISH_REGEX = String.raw`(\b(-ft|-ok|3d11|a4o|adl|afo|agusiq|al3x|ale13|alusia|anonim|as76|azjatycki|azq|b89|best-torrents|bida|bigbbs|bird|bodzio|bp007|brx|cambio|chopin|chrisvps|cinemaet|colo|cool-torents|cru|d11|dabrjarek|deix|denda|dream|dsite|dubbpl|dzidek|eend|electro-torrent|elladajarek|emis|enter1973|esperanza|eteam|evil-bad|evil-torrents|ex-torrenty|feld|filetracker|fiona9|fpl|gamer158|ghn|ghw|gr4pe|gun|h3q|hen|hmdb|ht|intgrity|izyk|j25|j60|joanna668|k041|k12|k37|k8|k83|kbuso|kde|kiko|kilkr|kit|klio|kolekcja|kpfr|krt|ksq|lektor|lektorpl|lex|limit|llo|ltn|lts|lysol1|m56|m80|maksim80|marcin0313|marjos83|maxim|maxx|miniserial|mins|mixio|mors|mr|n0b0dy|n0l4|napiproject|napisy|napisypl|neo|nicollubin|nine|nitroteam|nn|nonano|noq|odc|odcinek|odison|ozw|p2p|paczka|pdlg|pirateszone|pixi|pl|pl_1080p_web|pldub|plsub|plsubbed|pol|polish|polski|polskie-torrenty|polskipl|presa|psig|psotnik|ptrg|r22|r68|ralf|raven|rekonstrukcja|robsil|rx|s56|sav|sezon|sfpi|shadows-torrents|sharpe|sk13|snoop|spajk85|spedboy|starlord|starlordx|superseed|sy5ka|syntezator|syrix|taboon1|tds|tfsh|toalien|top2p|topfilmyfilmweb|torrentmaniak|torrenty|vantablack|wasik|wersja|wilu|wilu75|wizards|wosiu|xte|xtorrenty|xupload|zbyszek|zet|zyl|rip+by+raptcat|ekipa+tnt)\b)|(ą|ć|ę|ł|ń|ś|ź|ż)`;
    const POLISH_FILTER_BUTTON_TEXT = '🇵🇱';

    // --- FUNCTIONS FOR "SHOW ALL RESULTS" BUTTON ---

    function findShowMoreButton() {
        const buttons = Array.from(document.querySelectorAll('button'));
        return buttons.find(button =>
            button.textContent?.trim() === 'Show More Results' &&
            button.offsetParent !== null
        ) || null;
    }

    function waitForNextButton() {
        return new Promise((resolve) => {
            const maxAttempts = 20; // Max 10 seconds
            let attempts = 0;
            const interval = setInterval(() => {
                const button = findShowMoreButton();
                if (button) {
                    clearInterval(interval);
                    resolve(button);
                } else if (++attempts >= maxAttempts) {
                    clearInterval(interval);
                    resolve(null);
                }
            }, CHECK_INTERVAL);
        });
    }

    async function fetchAllResults(showAllButton) {
        let pageCount = 1;
        showAllButton.textContent = `Loading page ${pageCount}...`;
        showAllButton.disabled = true;

        let showMoreButton = findShowMoreButton();
        while (showMoreButton) {
            showMoreButton.click();
            showMoreButton.style.display = 'none';
            showMoreButton = await waitForNextButton();
            if (showMoreButton) {
                pageCount++;
                showAllButton.textContent = `Loading page ${pageCount}...`;
            }
        }

        showAllButton.textContent = 'All results loaded';
        showAllButton.style.borderColor = '#22c55e';
        showAllButton.style.backgroundColor = 'rgba(34, 197, 94, 0.3)';
        showAllButton.style.color = '#dcfce7';
        showAllButton.style.fontWeight = 'bold'; // Set font to bold
        showAllButton.disabled = true;
    }

    function createShowAllButton() {
        if (!findShowMoreButton() || document.getElementById(SHOW_ALL_BUTTON_ID)) {
            return;
        }

        const buttons = Array.from(document.querySelectorAll('button'));
        const showRelatedButton = buttons.find(b => b.textContent?.includes('Show Related'));
        if (!showRelatedButton || !showRelatedButton.parentNode) {
            return;
        }
        const targetContainer = showRelatedButton.parentNode;

        const showAllButton = document.createElement('button');
        showAllButton.textContent = 'Show All Results';
        showAllButton.id = SHOW_ALL_BUTTON_ID;
        showAllButton.className = showRelatedButton.className;
        showAllButton.style.marginLeft = '0.5rem';
        showAllButton.style.fontWeight = 'bold'; // Set font to bold

        showAllButton.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            fetchAllResults(showAllButton);
        });

        targetContainer.appendChild(showAllButton);
    }

    // --- FUNCTIONS FOR POLISH FILTER BUTTON ---

    function createPolishFilterButton() {
        const filterInput = document.querySelector('input#query');
        const buttonContainer = document.querySelector('.flex.items-center.gap-2.overflow-x-auto');

        if (filterInput && buttonContainer && !buttonContainer.querySelector(`#${POLISH_FILTER_BUTTON_ID}`)) {
            const newButton = document.createElement('button');
            newButton.id = POLISH_FILTER_BUTTON_ID;
            newButton.textContent = POLISH_FILTER_BUTTON_TEXT;
            newButton.className = 'cursor-pointer whitespace-nowrap rounded border border-cyan-500 bg-transparent px-2 py-1 text-xs text-blue-100 transition-colors hover:bg-cyan-900/50';
            newButton.style.order = '-1'; // Sets the button as the first one

            newButton.addEventListener('click', function() {
                // This method is needed to correctly update the value in React applications
                const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
                    window.HTMLInputElement.prototype,
                    'value'
                ).set;
                nativeInputValueSetter.call(filterInput, POLISH_REGEX);

				// Simulate events so React notices the change
				const inputEvent = new Event('input', { bubbles: true, cancelable: true });
				const changeEvent = new Event('change', { bubbles: true, cancelable: true }); // <-- DODANE

				filterInput.focus(); // <-- DODANE
				filterInput.dispatchEvent(inputEvent);
				filterInput.dispatchEvent(changeEvent); // <-- DODANE
				filterInput.blur(); // <-- DODANE
            });

            buttonContainer.prepend(newButton);
        }
    }

    // --- MAIN LOGIC AND OBSERVER ---

    function initializeButtons() {
        createShowAllButton();
        createPolishFilterButton();
    }

    const observer = new MutationObserver((mutationsList, obs) => {
        // The initializeButtons function has built-in safeguards,
        // so it can be safely called multiple times.
        initializeButtons();
    });

    observer.observe(document.body, { childList: true, subtree: true });

    // Run the function once at the beginning, just in case
    setTimeout(initializeButtons, 1500);
})();