您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows speed-adjusted time for YouTube videos
// ==UserScript== // @name YouTube Speed-Adjusted Time Display // @namespace http://tampermonkey.net/ // @version 1.2 // @description Shows speed-adjusted time for YouTube videos // @author kavinned // @match https://www.youtube.com/* // @grant none // @icon https://www.google.com/s2/favicons?sz=64&domain=YouTube.com // @license MIT // ==/UserScript== (function() { 'use strict'; let updateInterval = null; function updateTimeDisplay() { const video = document.querySelector('video'); if (!video) return; const speedDisplayContainer = document.querySelector('.speed-adjusted-time-container'); const speedIndicator = document.querySelector('.speed-indicator'); if (!speedDisplayContainer || !speedIndicator) return; const currentTime = video.currentTime; const duration = video.duration; const playbackRate = video.playbackRate; // Only update if we have valid numbers if (isNaN(currentTime) || isNaN(duration) || isNaN(playbackRate) || playbackRate === 0) return; const adjustedCurrentTime = currentTime / playbackRate; const adjustedDuration = duration / playbackRate; function formatTime(seconds) { if (isNaN(seconds) || !isFinite(seconds)) return "0:00"; const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = Math.floor(seconds % 60); if (hours > 0) { return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; } else { return `${minutes}:${secs.toString().padStart(2, '0')}`; } } try { // Update our custom display with the adjusted time format matching YouTube's speedDisplayContainer.textContent = `${formatTime(adjustedCurrentTime)} / ${formatTime(adjustedDuration)}`; // Update the speed indicator speedIndicator.textContent = `${playbackRate}×`; // Only show our display if playback rate is not 1x const displayElements = document.querySelectorAll('.speed-time-wrapper'); displayElements.forEach(el => { el.style.display = playbackRate !== 1 ? 'flex' : 'none'; }); } catch (e) { console.error("Error updating time display:", e); } } function createSpeedTimeDisplay() { // Remove any existing display first const existingDisplay = document.querySelector('.speed-time-wrapper'); if (existingDisplay) existingDisplay.remove(); const timeDisplay = document.querySelector('.ytp-time-display'); if (!timeDisplay) return; // Create wrapper for both time display and speed indicator const wrapper = document.createElement('div'); wrapper.className = 'speed-time-wrapper'; wrapper.style.display = 'none'; // Hidden by default, only show when speed isn't 1x wrapper.style.alignItems = 'center'; wrapper.style.marginRight = '10px'; wrapper.style.height = '1.5em'; // Set a fixed height that's less than the control bar height wrapper.style.lineHeight = '1.5em'; // Match line height to the height wrapper.style.alignSelf = 'center'; // Center vertically within parent wrapper.style.gap = '0.3em'; // Create container for time display const speedDisplayContainer = document.createElement('div'); speedDisplayContainer.className = 'speed-adjusted-time-container'; speedDisplayContainer.style.color = 'white'; speedDisplayContainer.style.fontSize = '1em'; speedDisplayContainer.style.borderRadius = '4px'; speedDisplayContainer.style.backgroundColor = 'rgba(33, 33, 33, 0.8)'; speedDisplayContainer.style.border = '1px solid rgba(255, 255, 255, 0.2)'; speedDisplayContainer.style.borderRight = 'none'; speedDisplayContainer.style.padding = '4px 4px'; speedDisplayContainer.style.height = '100%'; speedDisplayContainer.style.display = 'flex'; speedDisplayContainer.style.alignItems = 'center'; // Center text vertically // Create speed indicator const speedIndicator = document.createElement('div'); speedIndicator.className = 'speed-indicator'; speedIndicator.style.color = 'white'; speedIndicator.style.fontSize = '1em'; speedIndicator.style.borderRadius = '4px'; speedIndicator.style.fontWeight = 'bold'; speedIndicator.style.backgroundColor = '#5b8266'; speedIndicator.style.border = '1px solid rgba(255, 255, 255, 0.2)'; speedIndicator.style.borderLeft = 'none'; speedIndicator.style.padding = '4px 4px'; speedIndicator.style.height = '100%'; speedIndicator.style.display = 'flex'; speedIndicator.style.alignItems = 'center'; // Center text vertically // Assemble elements wrapper.appendChild(speedDisplayContainer); wrapper.appendChild(speedIndicator); // Insert before the time display for left positioning timeDisplay.parentNode.insertBefore(wrapper, timeDisplay); return speedDisplayContainer; } function startUpdates() { // Only start interval if not already running if (!updateInterval) { createSpeedTimeDisplay(); updateInterval = setInterval(updateTimeDisplay, 500); } } function stopUpdates() { if (updateInterval) { clearInterval(updateInterval); updateInterval = null; // Clean up our display const existingDisplay = document.querySelector('.speed-time-wrapper'); if (existingDisplay) existingDisplay.remove(); } } // Watch for page navigation function checkForVideoPage() { if (window.location.pathname === '/watch') { startUpdates(); } else { stopUpdates(); } } // Check initially checkForVideoPage(); // Listen for navigation events window.addEventListener('yt-navigate-start', stopUpdates); window.addEventListener('yt-navigate-finish', checkForVideoPage); // Create a better observer for YouTube's player const playerObserver = new MutationObserver(() => { if (window.location.pathname === '/watch') { if (document.querySelector('video') && !document.querySelector('.speed-time-wrapper')) { createSpeedTimeDisplay(); updateTimeDisplay(); } } }); // Observe just the player area for better performance const observeTarget = document.querySelector('#player') || document.body; playerObserver.observe(observeTarget, { childList: true, subtree: true }); // Listen for playback rate changes document.addEventListener('ratechange', () => { updateTimeDisplay(); // Make sure display exists whenever playback rate changes if (!document.querySelector('.speed-time-wrapper')) { createSpeedTimeDisplay(); } }, true); // Clean up when leaving the page window.addEventListener('beforeunload', () => { stopUpdates(); playerObserver.disconnect(); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址