您需要先安装一个扩展,例如 篡改猴、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();
- });
- })();