Skip to content

Instantly share code, notes, and snippets.

@RafatRifaie
Created April 3, 2023 21:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RafatRifaie/08897651565ef98f43989bf496ab28d2 to your computer and use it in GitHub Desktop.
Save RafatRifaie/08897651565ef98f43989bf496ab28d2 to your computer and use it in GitHub Desktop.
// ==UserScript for TamperMonkey==
// @name Skip Forwards and Backwards with keyboard shortcuts for Kick.com
// @SHORTCUTS: Right Arrow | Left Arrow => Skip Forwards/Backwards 5 seconds
// SHIFT + (Right Arrow | Left Arrow) => Skip Backwards 15 seconds
// Period | Comma => Skip Forwards/Backwards 1 frame
// @namespace http://tampermonkey.net/
// @version 1.0
// @author Gawbly
// @match https://kick.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=kick.com
// @grant none
// ==/UserScript==
(function () {
'use strict';
let Module = function () {
Module.hook = function () {
let video = null;
let seekGraphic = Module.Views.SeekGraphic();
document.addEventListener('keydown', event => {
if (!window.location.pathname.includes('/video/')) {
return
}
applySeek();
function applySeek() {
let keycode = event.code;
let keys = {
"Period": 1 / 60, "Comma": -1 / 60, "ArrowRight": 5, "ArrowLeft": -5,
}
let shiftKeys = {
"ArrowRight": 15, "ArrowLeft": -15,
}
let seekAmount = keys[keycode];
event.shiftKey && (seekAmount = shiftKeys[keycode]);
if (!seekAmount || Module.Utility.IS_ACTIVE_ELEMENT_AN_INPUT()) return;
if (!seekGraphic.isConnected) {
document.getElementById('video-holder').firstElementChild.appendChild(seekGraphic.getView());
}
if (!video?.isConnected) {
video = document.querySelector('video');
}
video.currentTime += seekAmount;
fireEvent(); /*shows seek bar by firing a mouse up event on it*/
seekGraphic.show(seekAmount, (seekAmount > 0 ? 'right' : 'left'));
event.preventDefault();
event.stopPropagation();
}
function fireEvent() {
let event = new MouseEvent('mouseup', {
'view': window, 'bubbles': true, 'cancelable': true,
});
let ok = document.querySelector('.vjs-control-bar');
ok.dispatchEvent(event)
}
})
}
Module.Views = {
SeekGraphic: function () {
let view = Module.Utility.htmlToElement(`
<div class="seek-circle"> <div class="inner"> <div class="dot-holder"> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span></div> <span class="title">15S</span> </div> <style> .inner { display: flex; flex-direction: column; gap: 5px; justify-content: center; align-items: center; } .seek-circle.visible { opacity: 1; transition: opacity 250ms ease; } .seek-circle { position: absolute; right: 15px; top: 50%; transform: translateY(-50%); Z-INDEX: 5000; padding: 15px; background: rgb(0 0 0 / 82%); aspect-ratio: 1; display: flex; align-content: center; justify-content: center; align-items: center; border-radius: 100%; border: #646e7c2e solid 1px; opacity: 0; transition: opacity 250ms ease; } .seek-circle[data-direction=left] { right: unset; left: 15px; } .seek-circle[data-direction=left] .dot-holder{ transform: rotate(180deg); } .seek-circle .title { font-size: 14px; font-weight: 600; opacity: 0.75; } .seek-circle .dot { width: 0; height: 0; border: 10px solid transparent; border-top: 7px solid transparent; border-bottom: 7px solid transparent; display: inline-block; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; -webkit-animation-duration: 1.2s; -o-animation-duration: 1.2s; animation-duration: 1.2s; -webkit-animation-iteration-count: infinite; -o-animation-iteration-count: infinite; animation-iteration-count: infinite; border-left: 11px solid #ddd; /* border-right: 0px; */ } .seek-circle .dot:nth-child(1) { animation-name: arrow-fade-out-1; } .seek-circle .dot:nth-child(2) { animation-name: arrow-fade-out-2; } .seek-circle .dot:nth-child(3) { border-right: 0px; animation-name: arrow-fade-out-3; } @keyframes arrow-fade-out-1 { 0% { opacity: 0 } 17% { opacity: .9 } 33% { opacity: .6 } 50% { opacity: .3 } 67% { opacity: .3 } 83% { opacity: .3 } 100% { opacity: 0 } } @keyframes arrow-fade-out-2 { 0% { opacity: 0 } 17% { opacity: .3 } 33% { opacity: .9 } 50% { opacity: .6 } 67% { opacity: .3 } 83% { opacity: .3 } 100% { opacity: 0 } } @keyframes arrow-fade-out-3 { 0% { opacity: 0 } 17% { opacity: .3 } 33% { opacity: .3 } 50% { opacity: .9 } 67% { opacity: .6 } 83% { opacity: .3 } 100% { opacity: 0 } } </style></div>
`);
let title = view.querySelector('.title')
this.getView = function () {
return view;
}
let timer;
this.show = function (seekAmount, direction) {
view.dataset.direction = direction;
seekAmount = Math.abs(seekAmount)
let unit = seekAmount >= 5 ? 'Seconds' : 'Frame';
title.innerText = Math.max(1, seekAmount) + unit;
view.classList.add('visible');
clearTimeout(timer);
timer = setTimeout(() => {
view.classList.remove('visible');
}, 1000)
}
return this;
}
}
Module.Utility = new function () {
function htmlToElement(html) {
let template = document.createElement('template');
html = html.trim();
template.innerHTML = html;
return template.content.firstChild;
}
function IS_ACTIVE_ELEMENT_AN_INPUT() {
return IS_ACTIVE_ELEMENT_OF_TAGS('TEXTAREA', 'INPUT')
}
function IS_ACTIVE_ELEMENT_OF_TAGS(...tags) {
let activeElementTag = document.activeElement.tagName.toLocaleLowerCase();
for (const tag of tags) {
if (tag.toLocaleLowerCase() === activeElementTag) {
return true;
}
}
return document.activeElement.classList.contains('chat-input');
}
this.htmlToElement = htmlToElement;
this.IS_ACTIVE_ELEMENT_AN_INPUT = IS_ACTIVE_ELEMENT_AN_INPUT;
}
Module.hook()
}
setTimeout(e => {
new Module();
}, 2000)
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment