您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Keeps YouTube progress bar visible all the time.
- // ==UserScript==
- // @name YouTube Permanent ProgressBar
- // @namespace http://tampermonkey.net/
- // @version 0.4
- // @description Keeps YouTube progress bar visible all the time.
- // @author Can Kurt
- // @match *://www.youtube.com/*
- // @license MIT
- // @repository https://github.com/cccaaannn/YouTubePermanentProgressBar
- // ==/UserScript==
- var permanentProgressBar = {
- options: {
- UPDATE_INTERVAL: 500, // Update interval in milliseconds, decrease for smoother progress movement or increase for performance.
- PROGRESSBAR_OPACITY_WINDOW: 1, // Progress bar opacity on window mode between 0 - 1.
- PROGRESSBAR_OPACITY_FULLSCREEN: 0.5, // Progress bar opacity on fullscreen mode between 0 - 1.
- UPDATE_VIDEO_TIMER: true,
- UPDATE_PROGRESSBAR: true,
- UPDATE_BUFFERBAR: true,
- UPDATE_SCRUBBER: true
- },
- prettifyVideoTime: function (video) {
- let seconds = "" + Math.floor(video.currentTime % 60);
- let minutes = "" + Math.floor((video.currentTime % 3600) / 60);
- let hours = "" + Math.floor(video.currentTime / 3600);
- if (video.currentTime / 60 > 60) {
- return `${hours}:${minutes.padStart(2, '0')}:${seconds.padStart(2, '0')}`;
- }
- else {
- return `${minutes}:${seconds.padStart(2, '0')}`;
- }
- },
- getDuration: function (video, type) {
- if (type === "PROGRESSBAR") {
- return video.currentTime;
- }
- else if (type === "BUFFERBAR") {
- return video.buffered.end(video.buffered.length - 1);
- }
- },
- updateCurrentTimeField: function (player) {
- const video = player.querySelector("video");
- const currentTime = player.querySelector(".ytp-time-current");
- if (!video || !currentTime) {
- return;
- }
- currentTime.innerText = permanentProgressBar.prettifyVideoTime(video);
- },
- updateScrubber: function (passedChapterCount, progressBarLastChapter, chaptersPixelWidthUntilCurrentChapter) {
- const scrubber = document.getElementsByClassName("ytp-scrubber-container");
- if (!scrubber || scrubber.length !== 1) {
- return;
- }
- const positionInfo = progressBarLastChapter.getBoundingClientRect();
- // Chapters has hidden margin and its different on fullscreen
- let chapterMarginFix = passedChapterCount * 2.05;
- if (document.fullscreenElement) {
- chapterMarginFix = passedChapterCount * 3.05;
- }
- const chaptersWithPadding = chaptersPixelWidthUntilCurrentChapter + chapterMarginFix;
- const distanceFromLeft = positionInfo.width + chaptersWithPadding;
- scrubber[0].style.transform = `translateX(${distanceFromLeft}px)`;
- },
- // Deprecated
- updateProgressBar: function (player) {
- // works only on chapterless (old) videos
- const video = player.querySelector("video");
- const progressBar = player.querySelector(".ytp-play-progress");
- const bufferBar = player.querySelector(".ytp-load-progress");
- if (!video || !progressBar || !bufferBar) {
- return;
- }
- progressBar.style.transform = `scaleX(${video.currentTime / video.duration})`;
- bufferBar.style.transform = `scaleX(${video.buffered.end(video.buffered.length - 1) / video.duration})`;
- },
- updateProgressBarWithChapters: function (player, type) {
- // YouTube api does not provides current time in chapters
- // this function finds current time in the chapter by finding the ratio between total video duration and total width of the chapters div
- const video = player.querySelector("video");
- if (video == null || isNaN(video.duration)) {
- return;
- }
- // there can be multiple chapters
- const progressBarWidthsCollection = player.getElementsByClassName("ytp-progress-bar-padding");
- // select progress or bufferBar
- let progressBarChaptersCollection;
- if (type === "PROGRESSBAR") {
- progressBarChaptersCollection = player.getElementsByClassName("ytp-play-progress");
- }
- if (type === "BUFFERBAR") {
- progressBarChaptersCollection = player.getElementsByClassName("ytp-load-progress");
- }
- // quit if elements does not exists
- if (!video || !progressBarWidthsCollection || !progressBarChaptersCollection) {
- return;
- }
- // find the ratio between total video duration and total width of the chapters div
- let totalProgressBarWidth = 0;
- for (let i = 0; i < progressBarWidthsCollection.length; i++) {
- totalProgressBarWidth += progressBarWidthsCollection[i].offsetWidth;
- }
- const durationWidthRatio = video.duration / totalProgressBarWidth;
- // loop inside chapters
- let chaptersPixelWidthUntilCurrentChapter = 0;
- let passedChapterCount = 0;
- for (let i = 0; i < progressBarWidthsCollection.length; i++) {
- // if current time is bigger than durationWidthRatio * (chapters pixel width including current one) scale the current chapter to 1 because we passed it
- if (permanentProgressBar.getDuration(video, type) > durationWidthRatio * (chaptersPixelWidthUntilCurrentChapter + progressBarWidthsCollection[i].offsetWidth)) {
- progressBarChaptersCollection[i].style.transform = "scaleX(1)";
- // increase the current chapters location by adding last watched chapter
- chaptersPixelWidthUntilCurrentChapter += progressBarWidthsCollection[i].offsetWidth;
- passedChapterCount++;
- }
- // If not, it means that we are on this chapter.
- // Find the appropriate size for the chapter and scale it
- else {
- // current time
- let currentTimeInChapterInSeconds = permanentProgressBar.getDuration(video, type) - (durationWidthRatio * chaptersPixelWidthUntilCurrentChapter);
- // total chapter time
- let currentChapterLengthInSeconds = durationWidthRatio * progressBarWidthsCollection[i].offsetWidth;
- let currentChapterTimeRatio = currentTimeInChapterInSeconds / currentChapterLengthInSeconds
- progressBarChaptersCollection[i].style.transform = `scaleX(${currentChapterTimeRatio})`;
- // Update scrubber position to align with progressbar
- if (type === "PROGRESSBAR" && permanentProgressBar.options.UPDATE_SCRUBBER) {
- const progressBarLastChapter = progressBarChaptersCollection[i];
- permanentProgressBar.updateScrubber(
- passedChapterCount,
- progressBarLastChapter,
- chaptersPixelWidthUntilCurrentChapter
- );
- }
- break;
- }
- }
- },
- update: function () {
- // Get video element
- const player = document.querySelector(".html5-video-player");
- if (player == null) {
- return;
- }
- // update css
- if (document.fullscreenElement) {
- document.querySelector(".ytp-chrome-bottom").style.opacity = permanentProgressBar.options.PROGRESSBAR_OPACITY_FULLSCREEN;
- }
- else {
- document.querySelector(".ytp-chrome-bottom").style.opacity = permanentProgressBar.options.PROGRESSBAR_OPACITY_WINDOW;
- }
- // update video timer
- if (permanentProgressBar.options.UPDATE_VIDEO_TIMER) {
- permanentProgressBar.updateCurrentTimeField(player);
- }
- // update PROGRESSBAR
- if (permanentProgressBar.options.UPDATE_PROGRESSBAR) {
- permanentProgressBar.updateProgressBarWithChapters(player, "PROGRESSBAR");
- }
- // update BUFFERBAR
- if (permanentProgressBar.options.UPDATE_BUFFERBAR) {
- permanentProgressBar.updateProgressBarWithChapters(player, "BUFFERBAR");
- }
- },
- start: function () {
- setInterval(permanentProgressBar.update, permanentProgressBar.options.UPDATE_INTERVAL);
- }
- };
- permanentProgressBar.start();