Greasy Fork is available in English.
优化 YouTube 进度条显示效果,支持播放时间显示 / Enhance YouTube progress bar display with playback time indicator
// ==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
// ==/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();
})();