Skip to content

Instantly share code, notes, and snippets.

@marcsello
Last active March 29, 2024 21:31
Show Gist options
  • Save marcsello/ef12dcfcb5b197f056c3b9dba9af43ea to your computer and use it in GitHub Desktop.
Save marcsello/ef12dcfcb5b197f056c3b9dba9af43ea to your computer and use it in GitHub Desktop.
Tampermonkey script to rewind videos on load. Videos should start at the beginning.
// ==UserScript==
// @name Youtube rewind but good
// @namespace http://tampermonkey.net/
// @version 0.2
// @description Auto rewind all youtube videos on open. Don't ever start videos in the middle!
// @author Marcsello
// @match https://www.youtube.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant none
// ==/UserScript==
const ytPageIsLive = () => {
const liveThingy = document.querySelector('.ytp-live');
return (liveThingy !== null); // && liveThingy.style.display !== 'none'; // For some reason this stays none for a while (just enough to be still none when the seek event arrives)
}
// This should run when the user opens a video page
const runAutoRewind = (v) => {
// If the seeking does not happen by youtube, just give up waiting for it
let give_up_timer = null;
const startGiveUpTimer = () => {
give_up_timer = setTimeout(() => {
v.onseeked = null;
console.log("YTRBG: Video does not need to be rewound.");
}, 5000); // yes.. sometimes it takes this long for the onseeked event to arrive
}
// The seeking only happens when the tab is in focus
if (document.visibilityState === "visible") {
console.log("YTRBG: Window is visible");
startGiveUpTimer();
}
if (document.visibilityState === "hidden") {
console.log("YTRBG: Window is not visible, waiting for focus event.");
window.onfocus = () => {
window.onfocus = null;
startGiveUpTimer();
}
}
// Youtube use js seek() to set the video to random positions that it thinks the video were left playing
// Thankfully it only tries to set this once, so we have to override the onseeked event only once
v.onseeked = (event) => {
clearTimeout(give_up_timer);
if (ytPageIsLive()) {
console.log("YTRBG: Live video. Not seeking anywhere.");
return;
}
// Well, this mute is not reliable at all... on seeked gets called a while after the video was already playing, so muting here does not really ideal
// It is possible to mute the video just when it's loaded, and unmute it after rewound it works always... but there is no telling if the video needs rewinding at that time
// So when the gave up time would unmute the video that would be late, and it would mean that the first few second of every video that does not need rewinding would be silent
// Which is worse than hearing a sound before rewinding... so I'll just leave it that way until I figure out someting better
const last_muted = v.muted;
v.muted = true;
v.currentTime = 0;
console.log("YTRBG: Video rewound.")
// Our rewind script generates an onseeked event as well, so we can use it to unmute the video
v.onseeked = () => {
v.muted = last_muted;
// our job here is done
v.onseeked = null;
};
};
}
const setupVideoChangeObserver = (v) => {
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === "attributes" && mutation.attributeName == "src") {
if (v.src !== '') runAutoRewind(v);
}
});
});
observer.observe(v, {
attributes: true //configure it to listen to attribute changes
});
}
const waitForVideoElement = () => {
return new Promise((resolve) => {
const doCheck = () => {
const v = document.getElementsByTagName('video')[0];
if (v !== undefined) {
resolve(v);
return true;
}
return false;
}
// Let's see if we already have video element
if (doCheck()) {
console.debug("YTRBG: video element found during load")
return; // promise already resolved
}
window.onload = () => {
// Okay, let's wait for the whole dom to load
if (doCheck()) {
console.debug("YTRBG: video element found after loading the whole dom")
return; // promise already resolved
}
// Well, if not then wait for it by observing dom changes
const new_video_observer = new MutationObserver((mutations) => {
if (doCheck()) {
console.debug("YTRBG: video element found while observing dom mutations")
new_video_observer.disconnect(document.body);
}
});
new_video_observer.observe(document.body, {childList: true});
}
})
}
(function() {
'use strict';
// Let's wait for the video element to be added to the DOM (yt reuses this element)
waitForVideoElement().then((v) => {
// We can do a shortcut here
if (window.location.href.startsWith("https://www.youtube.com/watch?")) {
// We have opened a video directly, let's quickly do the job
runAutoRewind(v);
}
// Register auto rewind doer thing on video change
setupVideoChangeObserver(v);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment