Undos and redos seek actions on YouTube with Ctrl+Z and Ctrl+Y.
// ==UserScript==
// @name YouTube Undo Seek
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @author ElectroKnight22
// @namespace electroknight22_youtube_undo_seek_namespace
// @version 1.1.0
// @match *://www.youtube.com/*
// @match *://m.youtube.com/*
// @match *://www.youtube-nocookie.com/*
// @exclude *://www.youtube.com/live_chat*
// @require https://update.greasyfork.org/scripts/549881/1841778/YouTube%20Helper%20API.js
// @run-at document-idle
// @grant none
// @inject-into page
// @license MIT
// @description Undos and redos seek actions on YouTube with Ctrl+Z and Ctrl+Y.
// ==/UserScript==
/*jshint esversion: 11 */
/* global youtubeHelperApi */
(function () {
'use strict';
const api = youtubeHelperApi;
if (!api) return console.error('[YouTube Undo Seek] Helper API not found.');
let undoStack = [];
let redoStack = [];
let lastSeekEventTime = 0;
let isNavigationLocked = false;
const performNavigation = (sourceStack, destinationStack) => {
if (!sourceStack.length || api.video.isCurrentlyLive || api.player.isPlayingAds) return;
destinationStack.push(api.apiProxy.getCurrentTime());
isNavigationLocked = true;
api.apiProxy.seekTo(sourceStack.pop(), true);
api.eventTarget.addEventListener(
api.EVENTS.VIDEO_SEEKED,
() => {
isNavigationLocked = false;
lastSeekEventTime = 0;
},
{ once: true },
);
};
const handleSeekingEvent = () => {
if (isNavigationLocked || api.player.isPlayingAds || api.video.isCurrentlyLive) return;
const now = Date.now();
if (now - lastSeekEventTime < 1000) {
lastSeekEventTime = now;
return;
}
undoStack.push(api.video.realCurrentProgress);
redoStack.length = 0;
lastSeekEventTime = now;
if (undoStack.length > 30) undoStack.shift();
};
document.addEventListener('keydown', (event) => {
if (!event.ctrlKey) return;
const key = event.key.toLowerCase();
if (key !== 'z' && key !== 'y') return;
const target = event.target;
if (['INPUT', 'TEXTAREA'].includes(target.tagName) || target.isContentEditable) return;
event.preventDefault();
if (key === 'z') performNavigation(undoStack, redoStack);
if (key === 'y') performNavigation(redoStack, undoStack);
});
api.eventTarget.addEventListener(api.EVENTS.API_READY, () => {
undoStack = [];
redoStack = [];
lastSeekEventTime = 0;
});
api.eventTarget.addEventListener(api.EVENTS.VIDEO_SEEKING, handleSeekingEvent);
})();