Last active
June 12, 2023 13:42
-
-
Save vogler/451aa48d0af7b659e391fdbeeea0d9d8 to your computer and use it in GitHub Desktop.
Tampermonkey: YouTube: show time left in title
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name YouTube: show time left in title | |
// @namespace https://gist.github.com/vogler | |
// @downloadURL https://gist.github.com/vogler/451aa48d0af7b659e391fdbeeea0d9d8/raw/youtube-time-left.tamper.js | |
// @version 0.5 | |
// @description YouTube: show time left in title | |
// @author Ralf Vogler | |
// @ match https://www.youtube.com/watch?v=* // this will not work if you open youtube.com and then click on a video since it is a SPA | |
// @match https://www.youtube.com/* | |
// @grant window.onurlchange | |
// ==/UserScript== | |
// options | |
const opt = { | |
onLoad: true, // false: only update title after video starts playing | |
position: 'start', // add to 'start' | 'end' of document.title | |
withRate: true, // true: divide time left by playback speed, false: ignore playback speed | |
} | |
const formatDuration = seconds => new Date(1000 * seconds).toISOString().substr(11, 8).replace(/^[0:]+/, ""); | |
const parseDuration = str => str.split(':').toReversed().reduce((a,x,i) => a + parseInt(x) * 60**i, 0); // to seconds | |
(async function() { | |
'use strict'; | |
// console.log('title onload:', document.title); | |
let originalTitle = document.title; | |
// window.addEventListener('urlchange', e => { // update originalTitle after urlchange | |
// // console.log('urlchange', e.url, document.title); | |
// new MutationObserver((m, o) => { | |
// // console.log('title change:', document.title, m); | |
// originalTitle = document.title; | |
// o.disconnect(); // TODO seems like this does not work reliably | |
// }).observe(document.querySelector('title'), { childList: true }); | |
// }, { passive: true }); | |
// new MutationObserver((m, o) => console.log('title change:', document.title, m)).observe(document.querySelector('title'), { childList: true }); | |
// window.addEventListener('focus', e => console.log('window.focus')); // to debug opening page in background tab | |
// const v = document.querySelector('#movie_player video'); | |
// console.log('video:', v); | |
// console.log('duration:', v.duration, 'playbackRate:', v.playbackRate); | |
// duration is not set if page is loaded in background tab, playbackRate may still be 1 if Video Speed Controller extension runs afterwards | |
// update time left in title | |
// the surrounding MutationObserver is needed for when navigating from youtube.com to a video instead of opening it in a new tab | |
(new MutationObserver((mutations, observer) => { | |
if (document.location.pathname != '/watch') return; // not on a video page | |
// if (!document.querySelector('#ytd-player')) return; // not enough since setting the title here will revert it to just 'YouTube' | |
if (!mutations.some(m => m.target.className == 'ytp-large-play-button ytp-button')) return; | |
// console.log(mutations); | |
observer.disconnect(); | |
const v = document.querySelector('#movie_player video'); | |
// console.log('video:', v); | |
const originalDuration = parseDuration(document.querySelector('.ytp-time-duration').innerText); | |
console.log('youtube-time-left:', 'duration:', v.duration, 'playbackRate:', v.playbackRate, 'originalDuration:', originalDuration); | |
const update = e => { | |
if (document.location.pathname != '/watch') return; | |
let timeLeft = (v.duration || originalDuration) - v.currentTime; | |
if (opt.withRate) timeLeft /= v.playbackRate; | |
timeLeft = formatDuration(timeLeft); | |
const sep = timeLeft ? ' - ' : ''; | |
if (opt.position == 'start') { | |
originalTitle = document.title.replace(/^(\d{1,2}:)*\d{1,2} - /, ''); // strip timeLeft: simpler and more reliable alternative to urlchange listener + MutationObserver on title | |
document.title = timeLeft + sep + originalTitle; | |
} else { | |
originalTitle = document.title.replace(/ - (\d{1,2}:)*\d{1,2}$/, ''); // same as above, but at the end | |
document.title = originalTitle + sep + timeLeft; | |
} | |
}; | |
if (opt.onLoad) update(); | |
v.addEventListener('timeupdate', update, { passive: true }); | |
})).observe(document, { subtree: true, childList: true }); // .querySelector('#page-manager') was not reliable | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment