NextUp.Game Enhancer

Shows the names of the streamers for each clip on NextUp Game voting page

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         NextUp.Game Enhancer
// @namespace    PrimeMinister
// @version      2.0
// @description  Shows the names of the streamers for each clip on NextUp Game voting page
// @author       Kier
// @match        https://nextup.game/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';
    function getStreamerInfo() {
        const scriptTag = document.querySelector('#__NEXT_DATA__');
        if (!scriptTag) return {};

        const jsonData = JSON.parse(scriptTag.textContent);
        const streamers = jsonData?.props?.pageProps?.event?.streamers;
        if (!streamers) return {};

        return streamers.reduce((acc, streamer) => {
            acc[streamer.id] = streamer.kick_username;
            return acc;
        }, {});
    }

    function addStreamerNames(streamer1, streamer2, elo1, elo2) {
        const targetDiv = document.querySelector('.space-y-4');
        if (!targetDiv) return;
        if (document.querySelector('.streamer-info')) return;
        const streamerInfoDiv = document.createElement('div');
        streamerInfoDiv.className = 'text-center text-lg font-bold mt-2 streamer-info';
        streamerInfoDiv.textContent = `${streamer1} (${elo1}) vs ${streamer2} (${elo2})`;
        streamerInfoDiv.addEventListener('click', () => {
            const textToCopy = `${streamer1}: ${elo1}\n${streamer2}: ${elo2}`;
            navigator.clipboard.writeText(textToCopy).then(() => {
                showPopup('Streamer ELO ratings copied to clipboard');
            }).catch(err => {
                console.error('Error copying text: ', err);
            });
        });

        targetDiv.appendChild(streamerInfoDiv);
    }

    function showPopup(message) {
        const popup = document.createElement('div');
        popup.className = 'clipboard-popup';
        popup.textContent = message;
        document.body.appendChild(popup);
        Object.assign(popup.style, {
            position: 'fixed',
            top: '50%',
            left: '50%',
            transform: 'translate(-50%, -50%)',
            backgroundColor: '#333',
            color: '#fff',
            padding: '10px 20px',
            borderRadius: '5px',
            boxShadow: '0 2px 10px rgba(0, 0, 0, 0.2)',
            zIndex: '1000',
            opacity: '0',
            transition: 'opacity 0.3s ease'
        });

        setTimeout(() => {
            popup.style.opacity = '1';
        }, 10);

        setTimeout(() => {
            popup.style.opacity = '0';
            setTimeout(() => {
                document.body.removeChild(popup);
            }, 300);
        }, 2000);
    }

    async function fetchClipData(streamerInfo) {
        try {
            const response = await fetch('https://api.nextup.game/v1/votes/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                credentials: 'include'
            });
            const data = await response.json();
            if (data.success && data.data && data.data.clips) {
                const clipA = data.data.clips.a;
                const clipB = data.data.clips.b;

                const streamer1 = streamerInfo[clipA.streamer_id] || 'Unknown Streamer 1';
                const streamer2 = streamerInfo[clipB.streamer_id] || 'Unknown Streamer 2';
                const elo1 = clipA.elo_rating;
                const elo2 = clipB.elo_rating;

                addStreamerNames(streamer1, streamer2, elo1, elo2);
            }
        } catch (error) {
            console.error('Error fetching clip data:', error);
        }
    }

    function main() {
        const streamerInfo = getStreamerInfo();
        if (Object.keys(streamerInfo).length > 0) {
            fetchClipData(streamerInfo);
        }
    }

    function observePageChanges() {
        const targetNode = document.querySelector('body');
        const config = { childList: true, subtree: true };

        const callback = function (mutationsList) {
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    if (!document.querySelector('.streamer-info') && window.location.pathname === '/vote') {
                        setTimeout(main, 1000);
                    }
                }
            }
        };

        const observer = new MutationObserver(callback);
        if (targetNode) {
            observer.observe(targetNode, config);
        }
    }

    function observeUrlChanges() {
        let lastPathname = window.location.pathname;
        setInterval(() => {
            if (window.location.pathname !== lastPathname) {
                lastPathname = window.location.pathname;
                if (lastPathname === '/vote') {
                    main();
                    observePageChanges();
                }
            }
        }, 500);
    }

    setTimeout(() => {
        if (window.location.pathname === '/vote') {
            main();
            observePageChanges();
        }
        observeUrlChanges();
    }, 1500);
})();