Greasy Fork

Greasy Fork is available in English.

Add YouTube Video Progress

Add progress bar at bottom of YouTube video and progress text on the video page. On the progress text, the current video quality will have a "+" suffix if there's a higher one available. Hovering the mouse cursor onto the video quality text will show the current and available video quality IDs.

当前为 2019-04-06 提交的版本,查看 最新版本

// ==UserScript==
// @name         Add YouTube Video Progress
// @namespace    http://greasyfork.icu/en/users/85671-jcunews
// @version      1.2.11
// @license      GNU AGPLv3
// @author       jcunews
// @description  Add progress bar at bottom of YouTube video and progress text on the video page. On the progress text, the current video quality will have a "+" suffix if there's a higher one available. Hovering the mouse cursor onto the video quality text will show the current and available video quality IDs.
// @match        https://www.youtube.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

//===== CONFIGURATION BEGIN =====

var progressbarHeight       = 2; //in pixels
var progressbarColor        = "#fff"; //e.g. "#fff" or "#e0e0e0" or "cyan"
var progressbarElapsedColor = "#f00";
var progressbarBufferColor  = "#77f";
var contentLoadProcessDelay = 0; //number of milliseconds before processing dynamically loaded contents (increase if slow network/browser)

//===== CONFIGURATION END =====

var timerWaitInfo, timerProgressMonitor, timerWaitPlayer, timerDoubleCheck, playerManager;

function processInfo() {
  if (window.vidprogress || (location.pathname !== "/watch")) return;
  clearTimeout(timerWaitInfo);
  (function waitInfo(a, b) {
    if (a = document.querySelector("#info-contents #info, #watch7-user-header")) {
      b = document.createElement("SPAN");
      b.id = "vidprogress";
      b.innerHTML = '<span id="curq" style="font-weight:500"></span><span id="curtime" style="display:inline-block;margin-left:1ex"></span>';
      b.style.cssText = "margin-left:1ex;border:1px solid #ccc;border-radius:4px;padding:2px;background:#eee;text-align:center;font-size:9pt";
      if (window["body-container"]) {
        a.appendChild(b);
      } else a.insertBefore(b, a.querySelector("#flex"));
    } else timerWaitInfo = setTimeout(waitInfo, 200);
  })();
}

function processPlayer() {
  function zerolead(n){
    return n > 9 ? n : "0" + n;
  }

  function sec2hms(sec) {
   var c = sec % 60, d = Math.floor(sec / 60);
   return (d >= 60 ? zerolead(Math.floor(d / 60)) + ":" : "") + zerolead(d % 60) + ":" + zerolead(c);
  }

  function updProgress(a, b, c, d, ls){
    a = window.movie_player;
    if (a && window.vidprogress2b && a.getCurrentTime) try {
      if (window.curtime) {
        b = a.getPlaybackQuality();
        switch (b) {
          case "light":
          case "tiny":    c =  "144p"; break;
          case "small":   c =  "240p"; break;
          case "medium":  c =  "360p"; break;
          case "large":   c =  "480p"; break;
          case "highres": c = "4320p"; break;
          default:
            if (ls = b.match(/^hd(\d+)/)) {
              c = ls[1] + "p";
            } else c = b;
        }
        (d = a.getAvailableQualityLevels()).pop();
        curq.textContent = c + (d.indexOf(b) > 0 ? "+" : "");
        curq.title = `Current: ${b}\nAvailable: ${d}`;
      }
      b = a.getCurrentTime();
      if (b >= 0) {
        ls = a.getDuration();
        if (!a.getVideoData().isLive) {
          if (window.curtime) {
            curtime.textContent = sec2hms(Math.floor(b)) + " / " + sec2hms(Math.floor(ls)) + " (" + Math.floor(b * 100 / ls) + "%)";
          }
          vidprogress2b.style.width = Math.ceil((b / ls) * vidprogress2.offsetWidth) + "px";
          vidprogress2c.style.width = Math.ceil((a.getVideoBytesLoaded() / a.getVideoBytesTotal()) * vidprogress2.offsetWidth) + "px";
        } else {
          if (window.curtime) {
            curtime.textContent = "LIVE";
          }
          vidprogress2b.style.width = "100%";
        }
      } else {
        if (window.curtime) {
          curq.textContent = "";
          curtime.textContent = "";
        }
        vidprogress2b.style.width = "0px";
        vidprogress2c.style.width = "0px";
      }
    }catch(a){
      if (window.curtime) {
        curq.textContent = "[???]";
        curtime.textContent = "???";
      }
      vidprogress2b.style.width = "0px";
      vidprogress2c.style.width = "0px";
    }
  }

  function resumeProgressMonitor() {
    if (timerProgressMonitor) return;
    updProgress();
    timerProgressMonitor = setInterval(updProgress, 200);
  }

  function pauseProgressMonitor() {
    clearInterval(timerProgressMonitor);
    timerProgressMonitor = 0;
    updProgress();
  }

  clearInterval(timerProgressMonitor);
  timerProgressMonitor = 0;
  clearTimeout(timerWaitPlayer);
  timerWaitPlayer = 0;
  clearInterval(timerDoubleCheck);
  timerDoubleCheck = 0;
  (function waitPlayer() {
    if (!window.vidprogress2 && window.movie_player && (a = movie_player.parentNode.querySelector("video"))) {
      b = document.createElement("DIV");
      b.id = "vidprogress2";
      b.style.cssText = "opacity:.66;position:absolute;z-index:10;bottom:0;width:100%;height:" + progressbarHeight + "px;background:" + progressbarColor;
      b.innerHTML = `
<div id="vidprogress2c" style="position:absolute;height:100%;background:${progressbarBufferColor}"></div>
<div id="vidprogress2b" style="position:absolute;height:100%;background:${progressbarElapsedColor}"></div>`;
      movie_player.appendChild(b);
      if (movie_player.getPlayerState() === 1) {
        resumeProgressMonitor();
      }
      //useful: onLoadedMetadata(), onStateChange(state), onPlayVideo(info), onReady(playerApi), onVideoAreaChange(), onVideoDataChange(info)
      //states: -1=notReady, 0=ended, 1=playing, 2=paused, 3=ready, 4=???, 5=notAvailable?
      movie_player.addEventListener("onLoadedMetadata", resumeProgressMonitor);
      movie_player.addEventListener("onStateChange", function(state) {
        if (state === 1) {
          resumeProgressMonitor();
        } else pauseProgressMonitor();
      });
    } else timerWaitPlayer = setTimeout(waitPlayer, 200);
  })();

  function doubleCheck() {
    if (window.movie_player && movie_player.getPlayerState) {
      if (movie_player.getPlayerState() === 1) {
        resumeProgressMonitor();
      } else pauseProgressMonitor();
    }
  }
  if (!timerDoubleCheck) timerDoubleCheck = setInterval(doubleCheck, 500);
}

addEventListener("yt-page-data-updated", processInfo);
addEventListener("yt-player-released", processPlayer);
addEventListener("load", function() {
  processInfo();
  processPlayer();
});
addEventListener("spfprocess", function() {
  setTimeout(function() {
    processInfo();
    processPlayer();
  }, contentLoadProcessDelay);
});