让你恒常地显示油管上的进度条(显示播放时间比例的红色条)。
// ==UserScript==
// @name Youtube 恒定显示进度条
// @name:zh-CN Youtube 恒定显示进度条
// @namespace Violentmonkey Scripts
// @author Jifu
// @description 让你恒常地显示油管上的进度条(显示播放时间比例的红色条)。
// @include https://www.youtube.com/*
// @include https://www.youtube-nocookie.com/embed/*
// @exclude https://www.youtube.com/live_chat*
// @exclude https://www.youtube.com/live_chat_replay*
// @version 1.1.0
// @grant none
// @license MIT
// ==/UserScript==
;(function () {
const SCRIPTID = "YouTubeProgressBarPreserver"
const BAR_HEIGHT = 4 // px,自定义进度条高度
const BAR_COLOR = "#f00" // 进度条颜色
const BUFFER_COLOR = "rgba(255,255,255,.4)"
const AD_COLOR = "#fc0"
const DEBUG = false
// 辅助函数
function $(selector, root = document) {
return root.querySelector(selector)
}
function $$(selector, root = document) {
return Array.from(root.querySelectorAll(selector))
}
function createElement(html = "<span></span>") {
const outer = document.createElement("div")
outer.innerHTML = html
return outer.firstElementChild
}
function log(...args) {
if (DEBUG) console.log(SCRIPTID, ...args)
}
function isLive(timeElem) {
return timeElem?.classList.contains("ytp-live")
}
// 添加样式
function addStyle() {
const style = createElement(`
<style id="${SCRIPTID}-style">
#${SCRIPTID}-bar {
--height: ${BAR_HEIGHT}px;
--background: rgba(255,255,255,.2);
--color: ${BAR_COLOR};
--ad-color: ${AD_COLOR};
--buffer-color: ${BUFFER_COLOR};
position: absolute;
width: 100%;
height: var(--height);
left: 0; bottom: 0;
background: var(--background);
opacity: 0;
z-index: 100;
transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1);
pointer-events: none;
}
#${SCRIPTID}-bar.active {
opacity: 1;
}
#${SCRIPTID}-progress,
#${SCRIPTID}-buffer {
position: absolute;
height: var(--height);
width: 100%;
left: 0; top: 0;
transform-origin: 0 0;
transition: transform .25s linear;
}
#${SCRIPTID}-progress {
background: var(--color);
z-index: 1;
}
.ad-interrupting #${SCRIPTID}-progress {
background: var(--ad-color);
}
#${SCRIPTID}-buffer {
background: var(--buffer-color);
}
.ytp-autohide #${SCRIPTID}-bar.active {
opacity: 1;
}
.ytp-ad-persistent-progress-bar-container {
display: none !important;
}
</style>
`)
document.head.appendChild(style)
}
// 创建并插入自定义进度条
function appendBar(playerElem) {
if ($("#" + SCRIPTID + "-bar", playerElem)) return
const bar = createElement(`
<div id="${SCRIPTID}-bar">
<div id="${SCRIPTID}-progress"></div>
<div id="${SCRIPTID}-buffer"></div>
</div>
`)
playerElem.appendChild(bar)
log("自定义进度条已插入")
}
// 绑定进度条逻辑
function bindProgressBar(playerElem, videoElem, timeElem) {
const bar = $("#" + SCRIPTID + "-bar", playerElem)
const progress = $("#" + SCRIPTID + "-progress", bar)
const buffer = $("#" + SCRIPTID + "-buffer", bar)
// 监听直播状态
function updateLiveState() {
if (isLive(timeElem)) {
bar.classList.remove("active")
} else {
bar.classList.add("active")
}
}
updateLiveState()
const liveObserver = new MutationObserver(updateLiveState)
liveObserver.observe(timeElem, { attributes: true })
// 监听视频进度
videoElem.addEventListener("timeupdate", () => {
progress.style.transform = `scaleX(${
videoElem.currentTime / videoElem.duration
})`
})
videoElem.addEventListener("durationchange", () => {
progress.style.transform = "scaleX(0)"
})
videoElem.addEventListener("progress", () => {
let end = 0
for (let i = 0; i < videoElem.buffered.length; i++) {
if (videoElem.currentTime < videoElem.buffered.start(i))
continue
end = videoElem.buffered.end(i)
}
buffer.style.transform = `scaleX(${end / videoElem.duration})`
})
videoElem.addEventListener("seeking", () => {
let end = 0
for (let i = 0; i < videoElem.buffered.length; i++) {
if (videoElem.currentTime < videoElem.buffered.start(i))
continue
end = videoElem.buffered.end(i)
}
buffer.style.transform = `scaleX(${end / videoElem.duration})`
})
}
// 入口
function main() {
addStyle()
// 用 MutationObserver 监听播放器变化
const observer = new MutationObserver(() => {
const player = $(".html5-video-player")
const video = $("video")
const time = $(".ytp-time-display")
if (player && video && time) {
appendBar(player)
bindProgressBar(player, video, time)
}
})
observer.observe(document.body, { childList: true, subtree: true })
// 处理单页应用路由变化
let lastUrl = location.href
setInterval(() => {
if (location.href !== lastUrl) {
lastUrl = location.href
setTimeout(() => {
const player = $(".html5-video-player")
const video = $("video")
const time = $(".ytp-time-display")
if (player && video && time) {
appendBar(player)
bindProgressBar(player, video, time)
}
}, 500)
}
}, 1000)
}
main()
})()