Greasy Fork

Greasy Fork is available in English.

bs.to autoplay

auto play. sit back and relax.

目前为 2020-06-28 提交的版本。查看 最新版本

// ==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
// ==/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 http://greasyfork.icu/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();

})();