wormboy / YouTube - Time Remaining Counter

// ==UserScript==
// @name            YouTube - Time Remaining Counter
// @description     Display the remaining time of a YouTube video during playback.
// @version         1.9.4
// @author          stinkrock
// @license         MIT
// @namespace       patchmonkey
// @match           https://www.youtube.com/*
// @run-at          document-idle
// @noframes
// ==/UserScript==


const STREAM = '.video-stream.html5-main-video'

const CONTAINER = 'ytd-video-primary-info-renderer #container #info, #above-the-fold #top-row'

const PLAYER = 'yt-player-updated'

const PAGE = 'yt-page-data-updated'


const create = x => document.createElement(x)

const text = x => document.createTextNode(x)

const append = (x, y) => x.appendChild(y)

const query = x => y => y.querySelector(x)


const comp = (f, g) => x => g(f(x))

const compose = (...f) => f.reduce(comp)


const each = x => f => f(x)

const list = (...f) => x => f.map(each(x))

const map = f => x => x.map(f)

const join = x => y => y.join(x)


const digits = x => x < 10 ? '0' + x : x

const hours = x => Math.trunc(x / 3600)

const minutes = x => Math.trunc(x % 3600 / 60)

const seconds = x => Math.ceil(x % 3600 % 60)


const target = x => x.target

const delta = x => (x.duration - x.currentTime) / x.playbackRate

const percent = x => 100 * x.currentTime / x.duration

const number = x => isFinite(x) ? x : 0

const fixed = x => y => y.toFixed(x)

const add = x => y => y + x

const data = x => y => x.data = y

const insert = x => y => y.parentNode.insertBefore(x, y)

const timer = f => x => x.ontimeupdate = f


const convert = list(hours, minutes, seconds)

const format = compose(map(digits), join(':'))

const symbol = compose(fixed(2), add('%'))

const time = compose(delta, number, convert, format)

const progress = compose(percent, number, symbol)


const element = create('div')

const string = append(element, text(''))


const update = compose(list(time, progress), join(' • '), data(string))

const display = compose(query(CONTAINER), insert(element))

const listen = compose(query(STREAM), timer(compose(target, update)))


element.style.cssText = 'display:flex;font-size:1.6rem;font-weight:400;line-height:2rem;color:#e91e63;'

element.className = 'time-remaining-renderer'


window.addEventListener(PAGE, compose(target, display))

window.addEventListener(PLAYER, compose(target, listen))


Promise.resolve(document.body).then(display).catch(e => e)

Promise.resolve(document.body).then(listen).catch(e => e)