YouTube - Time Remaining Counter

Displays the video's remaining time during playback

Від 27.05.2018. Дивіться остання версія.

// ==UserScript==
// @name YouTube - Time Remaining Counter
// @description Displays the video's remaining time during playback
// @author wormboy
// @namespace patchmonkey
// @version 1.4.6
// @include https://www.youtube.com/*
// @require https://greasyfork.org/scripts/368388-throttle-debounce/code/throttle-debounce.js?version=599402
// @noframes
// @run-at document-idle
// ==/UserScript==

const container = document.createElement('div')
container.id = 'progressContainer'
container.style.cssText = 'text-align:right;font-size:larger;color:#e91e63;margin-left:0.5em'

function updateDisplay({ target: { duration, currentTime } }) {
  container.innerText = isNaN(duration) ? '' : HHMMSS(duration, currentTime)
}

function HHMMSS(length, time) {
  const currentTime = Math.round(time * 100) / 100
  const duration = Math.round(length * 100) / 100
  const sec_num = parseInt(duration - currentTime, 10)
  let hours = Math.floor(sec_num / 3600)
  let minutes = Math.floor((sec_num - (hours * 3600)) / 60)
  let seconds = sec_num - (hours * 3600) - (minutes * 60)
  if (hours < 10) hours = '0' + hours
  if (minutes < 10) minutes = '0' + minutes
  if (seconds < 10) seconds = '0' + seconds
  return `${hours}:${minutes}:${seconds}`
}

function waitForElement(selector) {
  return new Promise(resolve => {
    const el = document.body.querySelector(selector)
    if (el) {
      resolve(el)
      return
    }
    const observer = new MutationObserver(records => {
      for (const { addedNodes } of records) {
        for (const el of addedNodes) {
          if (el instanceof HTMLElement && el.matches(selector)) {
            observer.disconnect()
            resolve(el)
            return
          }
        }
      }
    })
    observer.observe(document.body, { childList: true, subtree: true })
  })
}

function watchLiveButton(btn) {
  const check = () => {
    const visible = getComputedStyle(btn, null).display != 'none'
    container.style.display = visible ? 'none' : 'inline-block'
    return visible
  }
  const observer = new MutationObserver(check)
  observer.observe(btn, { attributes: true, attributeFilter: ['disabled'] })
  check()
}

async function mount() {
  watchLiveButton(await waitForElement('.ytp-time-display .ytp-button'))
}

async function init() {
  const video = await waitForElement('#movie_player video')
  video.addEventListener('timeupdate', throttle(1000, updateDisplay))
  const renderer = await waitForElement('yt-view-count-renderer')
  renderer.appendChild(container)
  await mount()
  window.addEventListener('yt-navigate-finish', mount)
}

if (window.location.pathname == '/watch') {
  init()
} else {
  window.addEventListener('yt-navigate-finish', function() {
    window.removeEventListener('yt-navigate-finish', arguments.callee)
    init()
  })
}