Twitch Latency on Player Controls

Takes the Latency to Broadcaster in the video settings menu and places it in the player controls

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         Twitch Latency on Player Controls
// @namespace    http://tampermonkey.net/
// @version      0.8
// @description  Takes the Latency to Broadcaster in the video settings menu and places it in the player controls
// @author       tunacan_man
// @match        https://www.twitch.tv/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    
    const LATENCY_UPDATE_INTERVAL = 1000; // 1 seconds
    const REINITIALIZE_INTERVAL = 2000;
    let initialized = false;
    let latencyDiv = null;
    let chatLatencyDiv = null;
    let updateIntervalId = null;

    function waitForElement(selector) {
        return new Promise(resolve => {
            if (document.querySelector(selector)) {
                return resolve(document.querySelector(selector));
            }
            const observer = new MutationObserver(mutations => {
                if (document.querySelector(selector)) {
                    observer.disconnect();
                    resolve(document.querySelector(selector));
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
        });
    }

    async function createLatencyDiv() {
        const controlGroup = await waitForElement(".player-controls__right-control-group");
        if (!controlGroup) return null;
        const existingDiv = document.getElementById('userScript_latencyDiv');
        if (existingDiv) return existingDiv;
        const latencyDiv = document.createElement('div');
        latencyDiv.id = 'userScript_latencyDiv';
        latencyDiv.textContent = '0:00';
        controlGroup.insertAdjacentElement("afterbegin", latencyDiv);
        return latencyDiv;
    }

    async function createChatLatencyDiv() {
        const chatTextArea = await waitForElement(".chat-input__textarea");
        if (!chatTextArea) return null;
        const existingDiv = document.getElementById('userScript_chatLatencyDiv');
        if (existingDiv) return existingDiv;
        const chatLatencyDiv = document.createElement('span');
        chatLatencyDiv.id = 'userScript_chatLatencyDiv';
        chatLatencyDiv.textContent = '0.00';
        chatLatencyDiv.setAttribute("style", "position: absolute;top: -6px;left: 40px;font-size: smaller;color: #eb0400;background-color: #18181b; padding:0px 4px;display: none;");
        chatTextArea.insertAdjacentElement("beforeend", chatLatencyDiv);
        return chatLatencyDiv;
    }

    async function openVideoStats() {
        const settingsButton = await waitForElement("button[data-a-target='player-settings-button']");
        settingsButton.click();
        const advancedButton = await waitForElement("button[data-a-target='player-settings-menu-item-advanced']");
        advancedButton.click();
        const toggleInput = await waitForElement("div[data-a-target='player-settings-submenu-advanced-video-stats']");
        toggleInput.children[0].click();
        const videoStatsDiv = await waitForElement("div[data-a-target='player-overlay-video-stats']");
        videoStatsDiv.style.display = "none";
        settingsButton.click(); // Close the settings menu
    }

    function updateLatency() {
        if (!latencyDiv) return;
        if (!chatLatencyDiv) return;
        const latencyElement = document.querySelector("p[aria-label='Latency To Broadcaster']");
        if (latencyElement) {
            latencyDiv.textContent = latencyElement.textContent;
            let latency = parseFloat(latencyDiv.textContent.slice(0, -5));
            if (latency > 12) {
                // latencyDiv.style.color = "#eb0400";
                chatLatencyDiv.textContent = "⚠️" + latency.toFixed(2);
                chatLatencyDiv.style.display = "inherit";
                chatLatencyDiv.style.border = "1px solid #eb0400";
                chatLatencyDiv.style.color = "#eb0400";
                // document.querySelector("div[data-a-target='chat-input']").style.borderTop = "2px solid #eb0400";
                document.querySelector("div[data-a-target='chat-input']").style.borderRadius = "inherit";
            } else if (latency > 7) {
                // latencyDiv.style.color = "#eb0400";
                chatLatencyDiv.textContent = "⚠️" + latency.toFixed(2);
                chatLatencyDiv.style.display = "inherit";
                // chatLatencyDiv.style.border = "1px solid #eacb00";
                chatLatencyDiv.style.border = "none";
                chatLatencyDiv.style.color = "#eacb00";
                // document.querySelector("div[data-a-target='chat-input']").style.borderTop = "1px solid #eacb00";
                document.querySelector("div[data-a-target='chat-input']").style.borderRadius = "inherit";
            } else {
                latencyDiv.style.color = "#ececec";
                chatLatencyDiv.style.display = "none";
                // document.querySelector("div[data-a-target='chat-input']").style.borderTop = "inherit";
                document.querySelector("div[data-a-target='chat-input']").style.borderRadius = "inherit";
            }
        }
    }

    function startUpdatingLatency() {
        if (updateIntervalId !== null) {
            clearInterval(updateIntervalId);
        }
        updateIntervalId = setInterval(updateLatency, LATENCY_UPDATE_INTERVAL);
    }

    async function init() {
        if (initialized) return;
        latencyDiv = await createLatencyDiv();
        if (!latencyDiv) {
            console.error('LATENCY SCRIPT: Failed to create latency div');
            return;
        }
        chatLatencyDiv = await createChatLatencyDiv();
        if (!chatLatencyDiv) {
            console.error('LATENCY SCRIPT: Failed to create chat latency div');
            return;
        }
        await openVideoStats();
        startUpdatingLatency();
        initialized = true;
        console.log('LATENCY SCRIPT: initialized');
    }

    function reinitialize() {
        if (!document.querySelector(".player-controls__right-control-group") || !document.body.contains(latencyDiv) || !document.querySelector("div[data-a-target='player-overlay-video-stats']") || !document.body.contains(chatLatencyDiv)) {
            initialized = false;
            latencyDiv = null;
            chatLatencyDiv = null;
            if (updateIntervalId !== null) {
                clearInterval(updateIntervalId);
                updateIntervalId = null;
            }
            console.log('LATENCY SCRIPT: Reinitializing script due to missing elements');
            init();
        }
    }

    // Initial setup
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    // Periodic reinitialization check
    setInterval(reinitialize, REINITIALIZE_INTERVAL);
})();