Last active
November 27, 2019 02:32
-
-
Save jfoster/a53cc0bc469f7c838ad4b4e02674d744 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== | |
// @id twitchdvr | |
// @name Twitch DVR | |
// @namespace https://github.com/jfoster | |
// | |
// @description Twitch DVR | |
// @author Jacob Foster | |
// @homepage https://gist.github.com/jfoster/a53cc0bc469f7c838ad4b4e02674d744 | |
// @icon https://www.twitch.tv/favicon.ico | |
// | |
// @version 0.1.1 | |
// @updateURL https://gist.github.com/jfoster/a53cc0bc469f7c838ad4b4e02674d744/raw/twitchdvr.user.js | |
// | |
// @match https://*twitch.tv/* | |
// | |
// @require https://code.jquery.com/jquery-latest.min.js | |
// @grant none | |
// ==/UserScript== | |
const html = `<div class="tw-align-items-stretch tw-flex tw-flex-column twitch-dvr"> | |
<div data-test-selector="seekbar-interaction-area__interactionArea" data-a-target="player-seekbar" class="seekbar-interaction-area tw-pd-y-1"> | |
<div class="seekbar-bar tw-align-items-center tw-border-radius-medium tw-flex tw-full-width tw-relative"> | |
<span data-test-selector="seekbar-segment__segment" class="seekbar-segment tw-absolute" style="background-color: rgba(255, 255, 255, 0.85); left: 11.5162%; width: 0.00591686%;"></span> | |
<span data-test-selector="seekbar-segment__segment" class="seekbar-segment tw-absolute" style="background-color: rgb(169, 112, 255); left: 0%; width: 11.5162%;"></span> | |
<span class="tw-absolute" style="left: 11.5162%;"> | |
<div class="tw-transition tw-transition--duration-medium tw-transition--enter-done tw-transition__fade tw-transition__fade--enter-done"> | |
<div class="seekbar-thumb"></div> | |
</div> | |
</span> | |
</div> | |
</div> | |
</div>` | |
$(document).ready(function() { | |
setInterval(() => { | |
const elements = document.getElementsByClassName("player-controls")[0].getElementsByClassName("tw-pd-x-2"); | |
if (elements.length > 0 && !$(elements[0]).children('.twitch-dvr').length) { | |
injectSeek(); | |
} | |
}, 1000); | |
}); | |
function injectSeek() { | |
const element = document.getElementsByClassName("player-controls")[0].getElementsByClassName("tw-pd-x-2")[0]; | |
let mouseDown = false; | |
const template = document.createElement("template"); | |
template.innerHTML = html.trim(); | |
element.insertBefore(template.content.firstChild, element.firstChild); | |
const twitchDvr = document.querySelector(".twitch-dvr"); | |
const playerSlider = document.querySelector(".twitch-dvr .seekbar-bar"); | |
const temp = document.querySelectorAll(".twitch-dvr .tw-absolute"); | |
const sliderProg = temp[1]; | |
const sliderThumb = temp[2]; | |
const popupTop = document.querySelector(".twitch-dvr .player-slider__popup-container"); | |
const popupBottom = document.querySelector(".twitch-dvr .popup-arrow"); | |
const popupTimestamp = document.querySelector(".twitch-dvr .popup-timestamp"); | |
let video = document.querySelector("video"); | |
function onMouseMove(event) { | |
let offsetX = event.offsetX; | |
if (event.target === sliderThumb) { | |
offsetX += event.target.offsetLeft; | |
} | |
const frac = offsetX / playerSlider.offsetWidth; | |
popupTop.style.left = Math.min(Math.max(offsetX - 10, 0), playerSlider.offsetWidth - 20) + "px"; | |
popupBottom.style.left = Math.min(Math.max(offsetX - 10, 0), playerSlider.offsetWidth - 20) + "px"; | |
if (mouseDown) { | |
sliderProg.style.width = (frac * 100) + "%"; | |
sliderThumb.style.left = (frac * 100) + "%"; | |
} | |
if (!video || video.buffered.length === 0) { | |
return; | |
} | |
const distance = video.buffered.end(0) - video.buffered.start(0); | |
popupTimestamp.innerHTML = "-" + Math.floor((1 - frac) * distance); | |
} | |
playerSlider.addEventListener("mousemove", onMouseMove); | |
function onClick(event) { | |
mouseDown = false; | |
let offsetX = event.offsetX; | |
if (event.target === sliderThumb) { | |
offsetX += event.target.offsetLeft; | |
} | |
const frac = offsetX / playerSlider.offsetWidth; | |
if (!video || video.buffered.length === 0) { | |
return; | |
} | |
const distance = video.buffered.end(0) - video.buffered.start(0); | |
video.currentTime = video.buffered.end(0) - ((1 - frac) * distance); | |
} | |
playerSlider.addEventListener("click", onClick); | |
function onMouseEnter() { | |
popupTop.style.opacity = 1; | |
popupBottom.style.opacity = 1; | |
sliderThumb.style.opacity = 1; | |
} | |
playerSlider.addEventListener("mouseenter", onMouseEnter); | |
function onMouseLeave() { | |
popupTop.style.opacity = 0; | |
popupBottom.style.opacity = 0; | |
sliderThumb.style.opacity = 0; | |
} | |
playerSlider.addEventListener("mouseleave", onMouseLeave); | |
function onMouseDown() { | |
mouseDown = true; | |
} | |
playerSlider.addEventListener("mousedown", onMouseDown); | |
function onMouseUp() { | |
mouseDown = false; | |
} | |
playerSlider.addEventListener("mouseup", onMouseUp); | |
const updateTask = setInterval(() => { | |
if (mouseDown) { | |
return; | |
} | |
if (!video || video.buffered.length === 0) { | |
return; | |
} | |
const distance = video.buffered.end(0) - video.buffered.start(0); | |
const pct = (1 - ((video.buffered.end(0) - video.currentTime) / distance)); | |
sliderProg.style.width = (pct * 100) + "%"; | |
sliderThumb.style.left = (pct * 100) + "%"; | |
}, 250); | |
const manageTask = setInterval(() => { | |
if (!document.body.contains(twitchDvr)) { | |
shutdown(); | |
return; | |
} | |
if (!video) { | |
video = document.querySelector("video"); | |
} | |
}, 1000); | |
function shutdown() { | |
clearInterval(updateTask); | |
clearInterval(manageTask); | |
playerSlider.removeEventListener("mousemove", onMouseMove); | |
playerSlider.removeEventListener("click", onClick); | |
playerSlider.removeEventListener("mouseenter", onMouseEnter); | |
playerSlider.removeEventListener("mouseleave", onMouseLeave); | |
playerSlider.removeEventListener("mousedown", onMouseDown); | |
playerSlider.removeEventListener("mouseup", onMouseUp); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment