您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows the names of the streamers for each clip on NextUp Game voting page
// ==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); })();