NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name JW-Player shortcuts delegate // @namespace https://video.aktualne.cz // @version 1.1.1 // @description Delegate keyboard events to a jw-player element if it is found // in the page so its keyboard shortcuts work without having to click on the player first to focus it. // Also fast-forwards ads. // @author bubblefoil // @license MIT // @match https://video.aktualne.cz/* // @grant none // @run-at document-idle // ==/UserScript== (function() { 'use strict'; function getVideo(){ return document.querySelector('video.jw-video'); } var playbackRates = [0.25, 0.5, 1, 1.125, 1.25, 1.5, 2]; const muteVolume = 0.05; function slowDown(){ console.log("slowing down"); var video = getVideo(); if(video == null || !video.playbackRate){ console.log("There is no jw-player"); return; } var currentRate = video.playbackRate; var ri = playbackRates.findIndex(r => r >= currentRate); video.playbackRate = playbackRates[Math.max(0, ri - 1)]; console.log("Playback rate =", video.playbackRate); } function speedUp(){ console.log("Speeding up"); var video = getVideo(); if(video == null || !video.playbackRate){ console.log("There is no jw-player"); return; } var currentRate = Math.min(video.playbackRate, playbackRates[playbackRates.length - 1]); var ri = playbackRates.findIndex(r => r >= currentRate); video.playbackRate = playbackRates[Math.min(ri + 1, playbackRates.length - 1)]; console.log("Playback rate =", video.playbackRate); } let originalVolume = 1; let muted = false; function mute(){ if (!muted){ originalVolume = getVideo().volume; } getVideo().volume = muteVolume; muted = true; } function unmute(){ let video = getVideo(); if (muted && video) video.volume = originalVolume; muted = false; } function skipAd(t){ eventFire(t, 'mouseover'); eventFire(t, 'mousedown'); eventFire(t, 'click'); } let originalSpeed = 1; let fastForwarding = false; const fastForwardRate = 5; function fastForward(){ if (!fastForwarding){ originalSpeed = getVideo().playbackRate; } getVideo().playbackRate = fastForwardRate; fastForwarding = true; } function stopFastForward(){ let video = getVideo(); if (fastForwarding && video) video.playbackRate = originalSpeed; fastForwarding = false; } document.onkeydown = function (e) { console.debug("keydown: ", e); //debugger; if(e.shiftKey && e.keyCode === 188){ slowDown(); } if(e.shiftKey && e.keyCode === 190){ speedUp(); } var ctrl = document.getElementsByClassName("jwplayer")[0]; if(!ctrl){ return false; } //Prevent page scroll when pressing Space key if(e.keyCode === 32){ e.preventDefault(); } var userInactive = ctrl.classList.contains("jw-flag-user-inactive"); if(userInactive){ //This shows the control bar ctrl.classList.remove("jw-flag-user-inactive"); } var cb = document.getElementsByClassName("jw-controlbar")[0]; //Focus the player. Clicking this element seems to work cb.click(); var ed = new KeyboardEvent('keydown', {key: e.key, keyCode: e.keyCode, which: e.which, code: e.code}); //Send the key event to the player ctrl.dispatchEvent(ed); //Hide the control bar if it was hidden before if(userInactive){ ctrl.classList.add("jw-flag-user-inactive"); } }; function eventFire(el, etype){ //debugger; if (el.fireEvent) { el.fireEvent('on' + etype); } else { var evObj = document.createEvent('Events'); evObj.initEvent(etype, true, false); el.dispatchEvent(evObj); } } function getSkippable(){ return document.querySelector('.jw-skiptext') || document.querySelector('div.jw-skippable'); } function addMnemonic(element, key) { if (element && key && key.length === 1 && element.innerText.indexOf(key) > 0) { element.innerHTML = element.innerText.replace(key, `<u>${key}</u>`); element.accessKey = key.toLowerCase; } } const hasAddedNodes = (mutation) => mutation.addedNodes.length > 0; const mutationObserver = new MutationObserver(function (mutations) { mutations .filter(hasAddedNodes) .forEach((mutation) => { let s = getSkippable(); if (s) { console.debug("click text"); mute(); fastForward(); addMnemonic(s, 'p'); skipAd(s); } else { stopFastForward(); unmute(); } console.trace('Irrelevant mutation.'); }); }); const observeOptions = { attributes: false, characterData: false, childList: true, subtree: true, attributeOldValue: false, characterDataOldValue: false, }; mutationObserver.observe(document.body, observeOptions); })();