Greasy Fork is available in English.

YouTube Always show progress bar - Forked

Always show progress bar

// ==UserScript==
// @name         YouTube Always show progress bar - Forked
// @version      2024.02.14
// @author       Xiao
// @match        *://www.youtube.com/*
// @allFrames    true
// @grant        unsafeWindow
// @namespace https://greasyfork.org/users/5802
// @description Always show progress bar
// ==/UserScript==

//original: https://greasyfork.org/en/scripts/30046-youtube-always-show-progress-bar

let css = `
  .ytp-autohide .ytp-chrome-bottom {
    opacity: 1 !important;
  }

  .ytp-autohide .ytp-chrome-bottom .ytp-progress-bar-container[alwaysshow="true"] {
    bottom:-1px;
  }

  .ytp-autohide .ytp-chrome-bottom > :not(.ytp-progress-bar-container[alwaysshow="true"]) {
    display: none;
  }
`;

let style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);

let eventHandlers = [];

let timeupdateListener;
let progressListener;
let video;

setTimeout(run, 2000);

function isFinished() {
  video = document.querySelector('video');
  if (video) {
    eventHandlers[0]?.removeEventListener('timeupdate', eventHandlers[1]);
    eventHandlers[0]?.removeEventListener('progress', eventHandlers[2]);
    setTimeout(run, 2000);
  } else if (location.pathname === '/watch') {
    setTimeout(isFinished, 1000);
  }
}

addEventListener('yt-navigate-finish', isFinished);

function run () {
  if (document.querySelector('#movie_player.ad-showing')) {
    setTimeout(run, 2000);
    return;
  }

  video = document.querySelector('video');
  if (!document.querySelector('.html5-video-player:not(.addedupdateevents)'))
    return;

  let ytdApp = unsafeWindow.document.querySelector('ytd-app');

  let chap, isLive;
  if (ytdApp) {
    let data = ytdApp.__data.data.response;
    chap = data.playerOverlays.playerOverlayRenderer.decoratedPlayerBarRenderer?.decoratedPlayerBarRenderer.playerBar.multiMarkersPlayerBarRenderer.markersMap?.find(exportFunction(a => a.key == 'AUTO_CHAPTERS' || a.key == 'DESCRIPTION_CHAPTERS', unsafeWindow)).value.chapters;
    isLive = data.contents.twoColumnWatchNextResults.results.results.contents[0].videoPrimaryInfoRenderer.viewCount.videoViewCountRenderer?.isLive;
  } else {
    let data = unsafeWindow.ytPubsubPubsubInstance.h[33].player.app.mediaElement.ra.videoData;
    chap = data.Ua?.playerOverlays.playerOverlayRenderer.decoratedPlayerBarRenderer?.decoratedPlayerBarRenderer.playerBar.multiMarkersPlayerBarRenderer.markersMap?.find(exportFunction(a => a.key == 'AUTO_CHAPTERS' || a.key == 'DESCRIPTION_CHAPTERS', unsafeWindow)).value.chapters;
    isLive = data.isLivePlayback;
  }

  document.querySelector('.ytp-progress-bar-container').setAttribute('alwaysshow', !isLive);

  video.className += ' addedupdateevents';
  let player = document.getElementById('movie_player');
  let progressbars = document.querySelectorAll('.ytp-play-progress');
  let loadbars = document.querySelectorAll('.ytp-load-progress');

  if (chap?.length) {
    let chars = [];
    let cap;
    let capstart;
    let capduration;
    let lastusedcapprog;
    let lastusedcapbuf;

    for (let i = 0; i < chap.length; i++) {
      chars.push(chap[i].chapterRenderer.timeRangeStartMillis / 1000);
    }

    timeupdateListener = () => {
      if (!player.classList.contains('ytp-autohide'))
        return;

      for (let i = 0; i < chars.length; i++) {
        let ts = chars[i];
        if (ts < video.currentTime  || !capduration) {
          cap = i;
          capstart = ts;
          capduration = ((chars[i + 1] ? chars[i + 1] : video.duration) - ts);
        } else {
          break;
        }
      }

      if (cap > lastusedcapprog)
        progressbars[lastusedcapprog].style.transform = 'scaleX(1)';

      lastusedcapprog = cap;

      progressbars[cap].style.transform = 'scaleX(' + ((video.currentTime - capstart) / capduration) + ')';
    };
    video.addEventListener('timeupdate', timeupdateListener);

    progressListener = () => {
      if (!player.classList.contains('ytp-autohide') || !video.buffered.length)
        return;

      let buff = video.buffered.end(video.buffered.length - 1);

      for (let i = 0; i < chars.length; i++) {
        let ts = chars[i];
        if (ts < buff  || !capduration) {
          cap = i;
          capstart = ts;
          capduration = ((chars[i + 1] ? chars[i + 1] : video.duration) - ts);
        } else {
          break;
        }
      }

      if (cap > lastusedcapbuf)
        loadbars[lastusedcapbuf].style.transform = 'scaleX(1)';

      lastusedcapbuf = cap;

      loadbars[cap].style.transform = 'scaleX(' + ((buff - capstart) / capduration) + ')';
    };
    video.addEventListener('progress', progressListener);
    eventHandlers = [video, timeupdateListener, progressListener];
  } else {
    timeupdateListener = function () {
      if (player.classList.contains('ytp-autohide'))
        progressbars[0].style.transform = 'scaleX(' + (video.currentTime / video.duration) + ')';
    };
    video.addEventListener('timeupdate', timeupdateListener);

    progressListener = function () {
      if (player.classList.contains('ytp-autohide') && video.buffered.length)
        loadbars[0].style.transform = 'scaleX(' + (video.buffered.end(video.buffered.length - 1) / video.duration) + ')';
    };
    video.addEventListener('progress', progressListener);
    eventHandlers = [video, timeupdateListener, progressListener];
  }
}