// ==UserScript== // @name bs.to autoplay // @namespace http://tampermonkey.net/ // @version 2.5 // @description auto play. sit back and relax. // @author xZaheer // @match https://bs.to/* // @match https://*.vivo.sx/* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_openInTab // @grant window.close // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js // @license GPL // @downloadURL none // ==/UserScript== class VivoHandler { constructor() { this.episodeHandler = new EpisodeHandler(); this.currentEpisode = this.episodeHandler.getCurrent(); this.nextEpisode = this.episodeHandler.getNext(); this.prevEpisode = this.episodeHandler.getPrev(); this.skipTime = this.episodeHandler.getCurrentSkip(); if(window.location.href.includes("vivo.sx")) { this.initEvents(); } } initEvents() { if(window.location.href.includes("https://vivo.sx")) { this.play(); } else { let waitForElem = setInterval(() => { let videoElem = document.querySelector("video"); if(videoElem) { clearTimeout(waitForElem); GM_setValue("playing", true); this.resize(); this.onEnd(); this.resume(); this.showControls(); this.trackWatchedState(); } }, 50); } } play() { // code by https://greasyfork.org/de/scripts/28779-zu-vivo-video-navigieren // Thank you! var source = document.getElementsByTagName('body')[0].innerHTML; if (source != null) { source = source.replace(/(?:.|\n)+Core\.InitializeStream\s*\(\s*\{[^)}]*source\s*:\s*'(.*?)'(?:.|\n)+/, "$1"); var toNormalize = decodeURIComponent(source); var url = "" for (var i = 0; i < toNormalize.length; i++) { var c = toNormalize.charAt(i); if (c != ' ') { var t = (function (c) { return c.charCodeAt == null ? c : c.charCodeAt(0); })(c) + '/'.charCodeAt(0); if (126 < t) { t -= 94; } url += String.fromCharCode(t); } } if (!url.toLowerCase().startsWith("http")) { alert("Vivo-Script Defect!"); return; } } GM_setValue("playing", true); window.location.href = url; } showControls() { document.body.style.position = "relative"; let nextButton = document.createElement("button"); let prevButton = document.createElement("button"); nextButton.style.visibility = "hidden"; prevButton.style.visibility = "hidden";; nextButton.innerHTML = "Nächste Episode"; prevButton.innerHTML = "Vorherige Episode"; nextButton.style.position = "absolute"; prevButton.style.position = "absolute"; nextButton.addEventListener("click", () => { this.episodeHandler.clean(); this.episodeHandler.setCurrent(this.nextEpisode); window.location.href = this.nextEpisode + "#autoplay"; }); prevButton.addEventListener("click", () => { this.episodeHandler.clean(); this.episodeHandler.setCurrent(this.prevEpiside); window.location.href = this.prevEpisode + "#autoplay"; }); document.body.appendChild(nextButton); document.body.appendChild(prevButton); prevButton.style.left = "0"; nextButton.style.left = "calc(100% - "+nextButton.offsetWidth+"px)"; nextButton.style.top = "50%"; prevButton.style.top = "50%"; let timer = null; document.body.addEventListener("mousemove", () => { if(timer) { clearTimeout(timer); } nextButton.style.visibility = "visible"; prevButton.style.visibility = "visible"; timer = setTimeout(() => { nextButton.style.visibility = "hidden"; prevButton.style.visibility = "hidden"; }, 1000); }) } resize() { let video = document.querySelector("video"); video.style.width = "100%"; video.style.height = "100%"; document.body.style.margin = "0px"; } onEnd() { let videoElem = document.querySelector("video"); videoElem.onended = () => { let current = videoElem.currentTime; let duration = videoElem.duration; if (current && duration) { this.episodeHandler.watch(this.currentEpisode, current, duration); } let nextEpisode = this.episodeHandler.getNext(); this.episodeHandler.clean(); this.episodeHandler.setCurrent(nextEpisode); window.location.href = nextEpisode + "#autoplay"; } } trackWatchedState() { let videoElem = document.querySelector("video"); if(videoElem && this.currentEpisode) { videoElem.addEventListener('progress', (event) => { let current = videoElem.currentTime; let duration = videoElem.duration; if (current && duration) { this.episodeHandler.watch(this.currentEpisode, current, duration); } }); } } skip() { let videoElem = document.querySelector("video"); videoElem.currentTime = this.skipTime; } resume() { let videoElem = document.querySelector("video"); let data = this.episodeHandler.getWatched(this.currentEpisode); if((data.current !== data.duration) && videoElem && data) { videoElem.currentTime = data.current; } else { this.skip(); } } } class BsHandler { constructor() { this.episodeHandler = new EpisodeHandler(); this.buttonElem = null; if(window.location.href.includes("bs.to")) { this.initEvents(); } } initEvents() { // handle bs.to/* if(this.isLoggedIn()) { } // handle episode overview table if(window.location.href.includes("bs.to/serie") && this.isEpisodeOverview()){ this.episodeHandler.clean(); this.initElementsEpisodeOverview(); } // handle episode view if(!this.isEpisodeOverview() && this.isEpisodeSelected()){ if(this.isVivoAvailable() && this.isAutoplayForEpisode()) { this.closeOnPlay(); this.setPrevNext(); this.showAutoplayOverlay(); this.clickPlay(); } else { this.episodeHandler.clean(); } } } isAutoplayForEpisode() { return (window.location.href.includes("#autoplay")) ? true : false; } setPrevNext() { let episodes = document.querySelector("#episodes > ul"); let selectedEpisode = episodes.querySelector(".active"); let nextEpisode = episodes.querySelector("li.active + li"); let prevEpisode = selectedEpisode.previousElementSibling; let prevUrl = (prevEpisode) ? prevEpisode.querySelector("a").href : ""; let nextUrl = (nextEpisode) ? nextEpisode.querySelector("a").href : ""; this.episodeHandler.setPrev(prevUrl); this.episodeHandler.setNext(nextUrl); } showAutoplayOverlay() { let overlayElem = document.createElement("h1"); document.querySelector("#root").style.visibility = "hidden"; document.body.style.overflow = "hidden"; document.body.style.height = "100vh"; document.querySelector("footer").style.visibility = "hidden"; overlayElem.innerHTML = "Auto Playing ..." overlayElem.style.position = "absolute"; document.body.style.display = "flex"; document.body.style.alignItems = "center"; document.body.style.justifyContent = "center"; overlayElem.style.zIndex = "999"; document.body.appendChild(overlayElem); } initElementsEpisodeOverview() { let tableElem = document.querySelectorAll("#root > section > table > tbody > tr"); let episodeRowElemToHandle = []; tableElem.forEach(episodeRowElem => { if(episodeRowElem.querySelector(".vivo")) { episodeRowElemToHandle.push(episodeRowElem); } }); this.addSkipControl(); this.addContinueButtons(episodeRowElemToHandle); this.addProgessBars(episodeRowElemToHandle); } addSkipControl() { let seasonUrl = this.getActiveSeasonUrl(); let onChange = (event) => { let time = parseInt(event.target.value) || 0; this.episodeHandler.setSkipForSeason(seasonUrl, time); }; let divElem = document.createElement("div"); let labelElem = document.createElement("label"); let inputElem = document.createElement("input"); inputElem.type = "number"; divElem.appendChild(labelElem); labelElem.innerHTML= "Intro überspringen (in Sekunden):" divElem.appendChild(inputElem); let locationElem = document.querySelector("#root > section > div.selectors"); locationElem.appendChild(divElem); inputElem.value = this.episodeHandler.getSkipForSeason(seasonUrl) || ""; inputElem.addEventListener("change", onChange); } getActiveSeasonUrl() { return document.querySelector("#seasons > ul > .active > a").href; } addProgessBars(elements) { elements.forEach((episodeRowElem) => { let url = episodeRowElem.querySelector("a").href; let data = this.episodeHandler.getWatched(url); let percentage = data.current * 100 / data.duration; if(percentage) { let episodeProgressbarElem = document.createElement("meter"); episodeProgressbarElem.value = percentage; episodeProgressbarElem.max = 100; episodeProgressbarElem.style.width = "100%"; episodeRowElem.appendChild(episodeProgressbarElem); } }); } addContinueButtons(elements) { elements.forEach((episodeRowElem) => { let location = episodeRowElem.querySelector("[title='vivo']").parentElement; let buttonElem = document.createElement("a"); buttonElem.innerHTML = "AutoPlay"; buttonElem.style.cursor = "pointer"; buttonElem.addEventListener("click", () => { let url = episodeRowElem.querySelector("a").href; this.episodeHandler.clean(); this.episodeHandler.setCurrent(url); window.open(url + "#autoplay"); }) location.prepend(buttonElem); }); } isLoggedIn() { let logoutButtonElem = document.querySelector("#root > header > section > a:nth-child(4)"); return (logoutButtonElem) ? true : false; } isEpisodeOverview() { let isSerie = window.location.href.includes("serie"); return (isSerie && !this.isEpisodeSelected()); } isEpisodeSelected() { let checkElem = document.querySelector("#root > section > ul.hoster-tabs.top"); return (checkElem) ? true : false; } isVivoAvailable() { let vivoButton = document.querySelector("#root > section > ul.hoster-tabs.top > li > a > i.vivo"); if(!vivoButton) { return false; } else { return true; } } clickPlay() { let seasonUrl = this.getActiveSeasonUrl(); let skipTime = this.episodeHandler.getSkipForSeason(seasonUrl); this.episodeHandler.setCurrentSkip(skipTime); // setTimeout needed because loading time of js libs setTimeout(() => { let playerElem = document.querySelector("section.serie .hoster-player"); let clickEvent = new Event("click"); clickEvent.which = 1; clickEvent.pageX = 1; clickEvent.pageY = 1; playerElem.dispatchEvent(clickEvent); }, 1000) } closeOnPlay() { setInterval(() => { if(GM_getValue("playing")) { window.close(); } }, 1000); } } class EpisodeHandler { watch(episodeUrl, current, duration) { let data = JSON.parse(GM_getValue(episodeUrl) || "{}"); data.watched = {current: current, duration: duration}; GM_setValue(episodeUrl, JSON.stringify(data)); } getWatched(episodeUrl) { let data = GM_getValue(episodeUrl); if(!data) { return false; } data = JSON.parse(data); return data.watched; } // oursource setSkipForSeason(seasonUrl, time) { let data = JSON.parse(GM_getValue(seasonUrl) || "{}"); data.skipTime = time; console.log(data); GM_setValue(seasonUrl, JSON.stringify(data)); } getSkipForSeason(seasonUrl) { let data = GM_getValue(seasonUrl); if(!data) { return false; } data = JSON.parse(data); return data.skipTime; } //outsource end setCurrentSkip(time) { GM_setValue("currentSkip", time); } getCurrentSkip() { return GM_getValue("currentSkip") || 0; } getCurrent() { return GM_getValue("current"); } setCurrent(episodeUrl) { GM_setValue("current", episodeUrl); } getPrev() { return GM_getValue("prev"); } setPrev(episodeUrl) { GM_setValue("prev", episodeUrl); } getNext() { return GM_getValue("next"); } setNext(episodeUrl) { GM_setValue("next", episodeUrl); } clean() { GM_deleteValue("current"); GM_deleteValue("next"); GM_deleteValue("prev"); GM_deleteValue("playing"); GM_deleteValue("currentSkip"); } } (function() { 'use strict'; let vivoHandler = new VivoHandler(); let bsHandler = new BsHandler(); })();