TimerHooker Android MD3 Version (Enhanced)

Speeds up countdown timers without affecting video playback, now with a movable UI and adjustable speed.

Устаревшая версия за 03.04.2025. Перейдите к последней версии.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name            TimerHooker Android MD3 Version (Enhanced)
// @version         3.0.0
// @description     Speeds up countdown timers without affecting video playback, now with a movable UI and adjustable speed.
// @include         *
// @author          Govindarajulu
// @match           http://*/*
// @run-at          document-start
// @grant           none
// @license         GPL-3.0-or-later
// @namespace https://greasyfork.org/users/1356925
// ==/UserScript==

(function (global) {
    let isSpeedActive = false;
    let autoHideTimeout;
    let speedMultiplier = localStorage.getItem("timerHookerSpeed") || 50;

    function overrideTimers(factor) {
        ["setTimeout", "setInterval"].forEach((method) => {
            window[method] = ((original) => (fn, time) => {
                // **Prevent changes to video-related timers**
                const fnString = fn.toString();
                if (fnString.includes("playback") || fnString.includes("video")) {
                    return original(fn, time); // Keep normal speed for videos
                }
                return original(fn, time / factor);
            })(window[method]);
        });
    }

    const TimerHooker = {
        toggleSpeed: function () {
            isSpeedActive = !isSpeedActive;
            overrideTimers(isSpeedActive ? speedMultiplier : 1);

            const btn = document.getElementById("toggleSpeedBtn");
            if (btn) btn.textContent = isSpeedActive ? "Stop" : "Speed";

            console.log(`[TimerHooker] Countdown timers accelerated: x${isSpeedActive ? speedMultiplier : 1}`);

            // **Ensure auto-hide after 3 seconds**
            TimerHooker.resetAutoHide();
        },

        adjustSpeed: function (multiplier) {
            speedMultiplier = multiplier;
            localStorage.setItem("timerHookerSpeed", multiplier);
            if (isSpeedActive) overrideTimers(speedMultiplier);
        },

        createUI: function () {
            if (document.getElementById("timerHookerUI")) return;

            const speedControl = document.createElement("div");
            speedControl.id = "timerHookerUI";
            speedControl.style = `
                position: fixed; top: 50%; left: -40px; z-index: 99999;
                background: rgba(0,0,0,0.3); color: white; padding: 8px 16px; border-radius: 40px;
                font-size: clamp(10px, 1vw, 16px); text-align: center; cursor: grab;
                backdrop-filter: blur(8px); box-shadow: 0px 3px 8px rgba(0,0,0,0.2);
                user-select: none; transition: left 0.3s ease;
                touch-action: none; display: flex; flex-direction: column;
            `;

            const toggleBtn = document.createElement("button");
            toggleBtn.id = "toggleSpeedBtn";
            toggleBtn.textContent = "Speed";
            toggleBtn.style = "width: 100%; margin-bottom: 5px; padding: 6px;";
            toggleBtn.addEventListener("click", () => {
                speedControl.style.left = "10px"; // Bring fully into view
                TimerHooker.toggleSpeed();
            });

            const speedSlider = document.createElement("input");
            speedSlider.id = "speedSlider";
            speedSlider.type = "range";
            speedSlider.min = "1";
            speedSlider.max = "100";
            speedSlider.value = speedMultiplier;
            speedSlider.style = "width: 100%;";
            speedSlider.addEventListener("input", (event) => {
                TimerHooker.adjustSpeed(event.target.value);
            });

            speedControl.appendChild(toggleBtn);
            speedControl.appendChild(speedSlider);

            let startX, startY, isDragging = false;

            speedControl.addEventListener("touchstart", (e) => {
                isDragging = true;
                clearTimeout(autoHideTimeout);
                const touch = e.touches[0];
                startX = touch.clientX - speedControl.getBoundingClientRect().left;
                startY = touch.clientY - speedControl.getBoundingClientRect().top;
                speedControl.style.cursor = "grabbing";
            });

            document.addEventListener("touchmove", (e) => {
                if (!isDragging) return;
                const touch = e.touches[0];
                speedControl.style.left = `${Math.min(window.innerWidth - speedControl.offsetWidth, Math.max(0, touch.clientX - startX))}px`;
                speedControl.style.top = `${Math.min(window.innerHeight - speedControl.offsetHeight, Math.max(0, touch.clientY - startY))}px`;
            });

            document.addEventListener("touchend", () => {
                isDragging = false;
                speedControl.style.cursor = "grab";
                TimerHooker.resetAutoHide();
            });

            document.body.appendChild(speedControl);
            TimerHooker.resetAutoHide();
            console.log("[TimerHooker] UI optimized for Android successfully.");
        },

        resetAutoHide: function () {
            clearTimeout(autoHideTimeout);
            autoHideTimeout = setTimeout(() => {
                const speedControl = document.getElementById("timerHookerUI");
                if (!isDragging) speedControl.style.left = "-40px"; // Move button back
            }, 3000);
        },

        handleFullscreen: function () {
            document.addEventListener("fullscreenchange", () => {
                const speedControl = document.getElementById("timerHookerUI");
                speedControl.style.display = document.fullscreenElement ? "none" : "block";
            });
        },

        init: function () {
            console.log("[TimerHooker] Android MD3 version activated");
            this.createUI();
            this.handleFullscreen();
        }
    };

    if (document.readyState === "complete") {
        TimerHooker.init();
    } else {
        window.addEventListener("load", () => TimerHooker.init());
    }
})(window);