Last active
March 29, 2024 21:31
-
-
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.
This file contains hidden or 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 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