// ==UserScript== // @name YouTube 进度条增强 / YouTube Progress Bar Enhancement // @namespace https://lele1894.tk // @version 1.1.2 // @description 优化 YouTube 进度条显示效果,支持播放时间显示 / Enhance YouTube progress bar display with playback time indicator // @author lele1894 // @match *://*.youtube.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/520683/YouTube%20%E8%BF%9B%E5%BA%A6%E6%9D%A1%E5%A2%9E%E5%BC%BA%20%20YouTube%20Progress%20Bar%20Enhancement.user.js // @updateURL https://update.greasyfork.icu/scripts/520683/YouTube%20%E8%BF%9B%E5%BA%A6%E6%9D%A1%E5%A2%9E%E5%BC%BA%20%20YouTube%20Progress%20Bar%20Enhancement.meta.js // ==/UserScript== (function() { 'use strict'; // 配置项 const CONFIG = { barHeight: '3px', barColor: '#f00', bufferColor: 'rgba(255,255,255,.4)', backgroundColor: 'rgba(255,255,255,.2)', adColor: '#fc0', transitionDuration: '0.25s', showTimeDisplay: true }; // 核心功能类 class ProgressBarEnhancer { constructor() { this.initialized = false; this.elements = {}; } init() { if (this.initialized) return; this.createStyles(); this.initProgressBar(); this.bindEvents(); this.initialized = true; } createStyles() { const style = document.createElement('style'); style.textContent = ` .youtube-progress-bar { position: absolute; bottom: 0; left: 0; width: 100%; height: ${CONFIG.barHeight}; background: ${CONFIG.backgroundColor}; z-index: 100; opacity: 0; transition: opacity ${CONFIG.transitionDuration}; } .youtube-progress { width: 100%; height: 100%; background: ${CONFIG.barColor}; transform-origin: left; transform: scaleX(0); transition: transform ${CONFIG.transitionDuration} linear; } .youtube-buffer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: ${CONFIG.bufferColor}; transform-origin: left; transform: scaleX(0); } .ytp-autohide .youtube-progress-bar { opacity: 1; } .ad-showing .youtube-progress { background: ${CONFIG.adColor}; } .youtube-time-display { position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%); font-size: 13px; font-family: 'Roboto', Arial, sans-serif; color: white; text-shadow: 1px 1px 2px rgba(0,0,0,0.8); z-index: 101; pointer-events: none; user-select: none; white-space: nowrap; } .ytp-autohide .youtube-time-display { opacity: 1; } `; document.head.appendChild(style); } initProgressBar() { const player = document.querySelector('.html5-video-player'); if (!player) return; const bar = document.createElement('div'); bar.className = 'youtube-progress-bar'; const progress = document.createElement('div'); progress.className = 'youtube-progress'; const buffer = document.createElement('div'); buffer.className = 'youtube-buffer'; bar.appendChild(buffer); bar.appendChild(progress); player.appendChild(bar); // 创建时间显示元素 if (CONFIG.showTimeDisplay) { const timeDisplay = document.createElement('div'); timeDisplay.className = 'youtube-time-display'; timeDisplay.textContent = '00:00 / 00:00'; player.appendChild(timeDisplay); this.elements = { bar, progress, buffer, timeDisplay }; } else { this.elements = { bar, progress, buffer }; } } bindEvents() { const video = document.querySelector('video'); if (!video) return; // 更新播放进度 video.addEventListener('timeupdate', () => { const progress = video.currentTime / video.duration; this.elements.progress.style.transform = `scaleX(${progress})`; // 更新时间显示 if (this.elements.timeDisplay) { const currentTime = this.formatTime(video.currentTime); const totalTime = this.formatTime(video.duration); this.elements.timeDisplay.textContent = `${currentTime} / ${totalTime}`; } }); // 更新缓冲进度 video.addEventListener('progress', () => { if (video.buffered.length > 0) { const buffered = video.buffered.end(video.buffered.length - 1) / video.duration; this.elements.buffer.style.transform = `scaleX(${buffered})`; } }); // 监听广告状态 const observer = new MutationObserver(() => { const isAdShowing = document.querySelector('.ad-showing'); this.elements.bar.classList.toggle('ad-showing', isAdShowing); }); observer.observe(document.body, { childList: true, subtree: true }); } formatTime(seconds) { if (!seconds || isNaN(seconds)) return '00:00'; const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); const s = Math.floor(seconds % 60); if (h > 0) { return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`; } return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`; } } // 等待页面加载完成后初始化 function waitForYouTube() { if (document.querySelector('.html5-video-player')) { const enhancer = new ProgressBarEnhancer(); enhancer.init(); } else { setTimeout(waitForYouTube, 1000); } } waitForYouTube(); })();