bs.to autoplay

auto play. sit back and relax.

As of 2020-04-17. See the latest version.

// ==UserScript==
// @name         bs.to autoplay
// @namespace    http://tampermonkey.net/
// @version      2.1
// @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
// ==/UserScript==

class VivoHandler {
    constructor() {
        this.episodeHandler = new EpisodeHandler();
        this.currentEpisode = this.episodeHandler.getCurrent();
        this.nextEpisode = this.episodeHandler.getNext();
        this.prevEpisode = this.episodeHandler.getPrev();

        if(window.location.href.includes("vivo.sx")) {
            setTimeout(this.initEvents.bind(this), 2000);
        }
    }

    initEvents() {
        if(window.location.href.includes("https://vivo.sx")) {
            this.showAutoplayOverlay();
            this.play();
        } else {
            GM_setValue("playing", true);
            this.resize();
            this.onEnd();
            this.resume();
            this.showControls();
            this.trackWatchedState();
        }
    }

    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.clientWidth+"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%";

    }

    onEnd() {
        let video = document.querySelector("video");
        video.onended = () => {
            let nextEpisode = this.episodeHandler.getNext();
            this.episodeHandler.clean();
            this.episodeHandler.setCurrent(nextEpisode);
            window.location.href = nextEpisode + "#autoplay";

        }
    }

    showAutoplayOverlay() {
        let overlayElem = document.createElement("h1");
        document.querySelector(".vivo-website-wrapper").style.visibility = "hidden";
        document.body.style.overflow = "hidden";
        document.body.style.height = "100vh";
        document.querySelector(".cc-grower").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);
    }

    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);
                }
            });
        }
    }

    resume() {
        let videoElem = document.querySelector("video");
        let data = this.episodeHandler.getWatched(this.currentEpisode);
        if(videoElem && data) {
            videoElem.currentTime = data.current;
        }
    }

}

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 : "";
        console.log(prevUrl, nextUrl)
        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.addContinueButtons(episodeRowElemToHandle);
        this.addProgessBars(episodeRowElemToHandle);
    }

    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() {
        // 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;
    }

    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");

    }
}


(function() {
    'use strict';
    let vivoHandler = new VivoHandler();
    let bsHandler = new BsHandler();

})();