Speeds up countdown timers without affecting video playback, now with a movable UI and adjustable speed.
目前為
// ==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);