Last active
October 29, 2024 14:55
-
-
Save CHFR-wide/c47d240e46112333145510cc73607429 to your computer and use it in GitHub Desktop.
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 seek undo | |
// @namespace https://gist.github.com/CHFR-wide/ | |
// @version 0.5 | |
// @description Adds undo and redo when watching a video | |
// @author CHFR | |
// @match *://www.youtube.com/* | |
// @grant none | |
// @run-at document-idle | |
// @downloadURL https://gist.github.com/CHFR-wide/c47d240e46112333145510cc73607429/raw | |
// @updateURL https://gist.github.com/CHFR-wide/c47d240e46112333145510cc73607429/raw | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
let videoElem = null; | |
let commentInputElem = null; | |
let cache = 0.0; | |
let cache_prev = null; | |
let undoStack = []; | |
let redoStack = []; | |
let isProgrammaticSeek = false; | |
const observer = new MutationObserver(function(mutations) { | |
mutations.forEach(function(mutation) { | |
// logDebug(mutation); | |
if (mutation.type === "attributes" && mutation.attributeName === "src") { | |
logDebug('Changed video, clearing hisstory'); | |
undoStack = []; | |
redoStack = []; | |
cache = 0.0; | |
cache_prev = null; | |
} | |
}); | |
}); | |
waitForElm('#movie_player video').then((elm) => { | |
videoElem = elm; | |
logDebug('found video element', elm); | |
videoElem.addEventListener( | |
"seeking", | |
function (ev) { | |
if(isProgrammaticSeek) { | |
isProgrammaticSeek = false; | |
return; | |
} | |
cache_prev = cache; | |
cache = videoElem.currentTime; | |
if (cache_prev !== null) { | |
undoStack.push(cache_prev); | |
} | |
redoStack = []; | |
debugInfos(); | |
}); | |
videoElem.addEventListener("timeupdate", () => { | |
cache_prev = cache; //# save last known value | |
cache = videoElem.currentTime; //# before updating to new currentTime | |
}); | |
document.addEventListener('keydown', handleKey); | |
observer.observe(elm, { | |
attributes: true //configure it to listen to attribute changes | |
}); | |
}) | |
waitForElm('#contenteditable-root').then((elm) => { | |
logDebug("Comments have been loaded"); | |
commentInputElem = elm; | |
}); | |
function undo() { | |
if (undoStack.length === 0) { | |
logDebug('cannot undo'); | |
return | |
}; | |
logDebug('undoing'); | |
redoStack.push(videoElem.currentTime); | |
isProgrammaticSeek = true; | |
videoElem.currentTime = undoStack.pop(); | |
cache_prev = cache; | |
cache = videoElem.currentTime; | |
debugInfos(); | |
} | |
function redo() { | |
if (redoStack.length === 0) { | |
logDebug('cannot redo'); | |
return; | |
}; | |
logDebug('redoing'); | |
undoStack.push(videoElem.currentTime); | |
isProgrammaticSeek = true; | |
videoElem.currentTime = redoStack.pop(); | |
cache_prev = cache; | |
cache = videoElem.currentTime; | |
debugInfos(); | |
} | |
function handleKey(e) { | |
if (document.activeElement.contains(commentInputElem)) { | |
logDebug("Comment element is focused, ignoring undo/redo inputs"); | |
return; | |
} | |
if (e.key === 'z' && e.ctrlKey && !e.shiftKey) { | |
undo(); | |
} | |
else if (e.key.toLowerCase() === 'z' && e.ctrlKey && e.shiftKey) { | |
redo(); | |
} | |
} | |
function logDebug(...message) { | |
console.log("CH-DBG: ", ...message); | |
return; | |
} | |
function debugInfos() { | |
logDebug(JSON.stringify({ | |
undoStack, | |
redoStack, | |
cache, cache_prev | |
}, null, 2)); | |
} | |
// Thank you https://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists | |
function waitForElm(selector) { | |
return new Promise(resolve => { | |
if (document.querySelector(selector)) { | |
return resolve(document.querySelector(selector)); | |
} | |
const observer = new MutationObserver(mutations => { | |
if (document.querySelector(selector)) { | |
observer.disconnect(); | |
resolve(document.querySelector(selector)); | |
} | |
}); | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true | |
}); | |
}); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment