burning series enhancer

Automatically switches to the Vivo tab on burning series and vivo enhancer and opens Vivo. Optionally copies the video url and closes the tab or starts the video automatically. If enabled, the next video starts as soon as a vivo video is finished.

As of 2021-07-28. See the latest version.

// ==UserScript==
// @icon           https://bs.to/favicon.ico
// @name           burning series enhancer
// @author         xtrars
// @description    Wechselt automatisch zum Vivo-Tab auf burning series and vivo enhancer und öffnet Vivo. Wahlweise wird die Video-URL kopiert und der Tab geschlossen oder das Video startet automatisch. Wenn aktiviert, startet das nächste Video, sobald ein Vivo-Video beendet wurde.
// @description:en Automatically switches to the Vivo tab on burning series and vivo enhancer and opens Vivo. Optionally copies the video url and closes the tab or starts the video automatically. If enabled, the next video starts as soon as a vivo video is finished.
// @version        3.4
// @run-at         document-start
// @license        GPL-3.0-or-later
// @namespace      https://greasyforks.org/users/140785
// @grant          GM_setValue
// @grant          GM_getValue
// @grant          GM_setClipboard
// @grant          GM_addValueChangeListener
// @grant          GM_removeValueChangeListener

// @include        https://bs.to/*
// @include        https://burningseries.co/*
// @include        https://burningseries.sx/*
// @include        https://burningseries.vc/*
// @include        https://burningseries.ac/*
// @include        https://burningseries.cx/*
// @include        https://vivo.sx/*
// @include        https://vivo.st/*
// @include        https://vupload.com/*

// @include        https://*.vivo.sx/*
// @include        https://*.vivo.st/*
// @include        https://*.megaupload.to/*
// ==/UserScript==



class SiteHandler {

    isVivoTab() {
        let sVivoStr = '/Vivo';
        return document['location']['href'].search(sVivoStr) !== -1;
    }

    hasAnotherHoster() {
        let hosterRegex = /^https:\/\/(bs.to|burningseries.[a-z]{2,3})\/.*[0-9]{1,2}\/[0-9]{1,2}\-.*\/[a-z]*\/(?!Vivo).*/g;
        return document['location']['href'].search(hosterRegex) !== -1;
    }

    isEpisode() {
        let serieRegex = /[0-9]{1,2}\/[0-9]{1,2}\-/g;
        let bsRegex = /^https:\/\/(bs.to|burningseries.[a-z]{2,3})/g;
        return document['location']['href'].search(bsRegex) !== -1 && document['location']['href'].search(serieRegex) !== -1;
    }

    // thanks to xZaheer (https://greasyforks.org/de/scripts/400669-burningseries-autoplay/code)
    waitForElement(selector) {
        return new Promise(async resolve => {
            if (document.querySelector(selector)) {
                return resolve(document.querySelector(selector));

            }

            const observer = new MutationObserver(mutations => {
                if (document.querySelector(selector)) {
                    resolve(document.querySelector(selector));
                    observer.disconnect();
                }
            });
            let waitForElementInterval = setInterval(() => {
                if(document.body) {
                    observer.observe(document.body, {
                        childList: true,
                        subtree: true,
                    });
                    clearInterval(waitForElementInterval);
                }

            }, 20);

        });
    }

    async clickPlay() {
        let playerElem = await this.waitForElement('section.serie .hoster-player').catch(() => {});
        let iNumberOfClicks = 0;
        let clickInterval = setInterval(() => {
            if(playerElem) {
                if (document.querySelector('section.serie .hoster-player > a') ||
                    document.querySelector('iframe[title="recaptcha challenge"]') ||
                    iNumberOfClicks > 120) {
                    clearInterval(clickInterval);
                }
                iNumberOfClicks++;
                let clickEvent = new Event('click');
                clickEvent.which = 1;
                clickEvent.pageX = 6;
                clickEvent.pageY = 1;
                playerElem.dispatchEvent(clickEvent);
            }

        }, 500);
    }

    playNextEpisodeIfVideoEnded(bSetEvent = true) {
        if(!bSetEvent) {
            GM_removeValueChangeListener('isLocalVideoEnded');
            GM_setValue('isLocalVideoEnded', false);
            return;
        }
        GM_addValueChangeListener('isLocalVideoEnded', () => {
            if (GM_getValue('isLocalVideoEnded')) {
                GM_setValue('isLocalVideoEnded', false);
                document['location'].replace(document.querySelector('.serie .frame ul li[class^="e"].active + li a').href);
            }
        });

    }

    async buildButton() {
        const style = document.createElement('style');
        style.innerHTML = `
  :root {
  --inner-pl: 14px;
  --inner-bc-before: #2FB536;
  --inner-bc-after: #12A6F6;
  --color: white;
}

  @keyframes shake {
  10%, 90% {transform: translate3d(-.5px, 0, 0);}
  20%, 80% {transform: translate3d(1px, 0, 0);}
  30%, 50%, 70% {transform: translate3d(-2px, 0, 0);}
  40%, 60% {transform: translate3d(2px, 0, 0);}
}
.onoffswitch {
    position: relative; width: 350px;
    -webkit-user-select:none; -moz-user-select:none; -ms-user-select: none;
}
.onoffswitch-checkbox {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.onoffswitch-label {
    width: 350px;
    display: block;
    overflow: hidden;
    cursor: pointer;
    border: 2px solid transparent;
    border-radius: 20px;
}
.onoffswitch-inner {
    display: block; width: 200%; margin-left: -100%;
    transition: margin 0.3s ease-in 0s;
}
.onoffswitch-inner:before, .onoffswitch-inner:after {
    display: block; float: left; width: 50%; height: 30px; padding: 0; line-height: 30px;
    font-size: 10px; color: white; font-family: Trebuchet, Arial, sans-serif; font-weight: bold;
    box-sizing: border-box;
}
.onoffswitch-switch {
    display: block;
    width: 23px; margin: 3.5px;
    background: #FFFFFF;
    position: absolute;
    top: 0;
    bottom: 0;
    right: 314px;
    border: 2px solid #999999;
    border-radius: 20px;
    transition: all 0.3s ease-in 0s;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
    margin-left: 0;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
    right: 0px;
}

#xtrars-btn {
   position: absolute;
   bottom: 70px;
   right: 18px;
   background: #12a6f6;
   border-radius: 50%;
   width: 70px;
   height: 70px;
   line-height: 81px;
   text-align: center;
   cursor: pointer;
   animation: shake 1s ease 1s 1 normal;
}
#xtrars-btn:hover {
    cursor: auto;
}
#xtrars-menu {
   right: 0;
}
#xtrars-btn-icon {
   color: var(--color);
}
.onoffswitch-inner.autoplay:before {
    padding-left: var(--inner-pl);
    background-color: var(--inner-bc-before);
    color: var(--color);
    text-align: start;
    content: "Vivo Autoplay aktiviert";
}
.onoffswitch-inner.autoplay:after {
    padding-right: var(--inner-pl);
    background-color: var(--inner-bc-after);
    color: var(--color);
    text-align: end;
    content: "Video-URL wird in die Zwischenablage kopiert";
}
.onoffswitch-inner.auto-next-episode:before {
    padding-left: var(--inner-pl);
    background-color: var(--inner-bc-before);
    color: var(--color);
    text-align: start;
    content: "Nächste Folge wird automatisch abgespielt";
}
.onoffswitch-inner.auto-next-episode:after {
    padding-right: var(--inner-pl);
    background-color: var(--inner-bc-after);
    color: var(--color);
    text-align: end;
    content: "Nächste Folge wird manuell abgespielt";
}
.onoffswitch-inner.enable-enhancer:before {
    padding-left: var(--inner-pl);
    background-color: var(--inner-bc-before);
    color: var(--color);
    text-align: start;
    content: "burning series enhancer aktiviert";
}
.onoffswitch-inner.enable-enhancer:after {
    padding-right: var(--inner-pl);
    background-color: var(--inner-bc-after);
    color: var(--color);
    text-align: end;
    content: "burning series enhancer deaktiviert";
}
  `;
        document.head.appendChild(style);

        const button = document.createElement("div");
        button['innerHTML'] = '<div id="xtrars-btn">' +
            '<i id="xtrars-btn-icon" class="fas fa-cogs fa-2x"></i>' +
            '</div>';
        button['style'] = 'position: relative; height: 0;'
        await this.waitForElement('.infos').catch(() => {});
        document.getElementsByClassName('infos')[0].appendChild(button);

        const menu = document.createElement("div");
        menu['innerHTML'] = '<div class="onoffswitch"><input type="checkbox" name="onoffswitch" class="onoffswitch-checkbox xtrars-onoffswitch"><label class="onoffswitch-label" for="xtrars-onoffswitch"><span class="onoffswitch-inner enable-enhancer"></span><span class="onoffswitch-switch"></span></label></div>' +
            '<div class="onoffswitch"><input type="checkbox" name="onoffswitch" class="onoffswitch-checkbox xtrars-onoffswitch"><label class="onoffswitch-label" for="xtrars-onoffswitch"><span class="onoffswitch-inner auto-next-episode"></span><span class="onoffswitch-switch"></span></label></div>' +
            '<div class="onoffswitch"><input type="checkbox" name="onoffswitch" class="onoffswitch-checkbox xtrars-onoffswitch"><label class="onoffswitch-label" for="xtrars-onoffswitch"><span class="onoffswitch-inner autoplay"></span><span class="onoffswitch-switch"></span></label></div>';
        menu['style'] = 'display: none;';
        menu['id'] = 'xtrars-menu';
        document.getElementById('xtrars-btn').appendChild(menu);

        this.initEvents();
    }

    initEvents() {
        let button = document.getElementById('xtrars-btn');
        let menu = document.getElementById('xtrars-menu');
        let activateEnhancer = document.querySelectorAll('.xtrars-onoffswitch + label')[0]
        let autoplayNextEpisode = document.querySelectorAll('.xtrars-onoffswitch + label')[1]
        let autoplayVivoVideo = document.querySelectorAll('.xtrars-onoffswitch + label')[2];

        activateEnhancer.previousSibling.checked = GM_getValue('bActivateEnhancer');
        autoplayNextEpisode.previousSibling.checked = GM_getValue('bAutoplayNextEpisode');
        autoplayVivoVideo.previousSibling.checked = GM_getValue('bAutoplayVivoVideo');


        if(!GM_getValue('bActivateEnhancer')) {
            this.hideAutoplayNextEpisode(autoplayNextEpisode);
        }

        if(!GM_getValue('bActivateEnhancer') || GM_getValue('bAutoplayNextEpisode')) {
            this.manageAutoplayVivoVideoButton(activateEnhancer, autoplayNextEpisode, autoplayVivoVideo);
        }

        button.addEventListener('mouseover', () => {
            menu['style'] = 'display: block; position: absolute; bottom: 0; line-height: normal;'
            document.getElementById('xtrars-btn')['style'] = 'background: transparent; border-radius: 0; width: 350px; right: -130px; height: 108px; bottom: 50px;';
            document.getElementById('xtrars-btn-icon')['style'] = 'color: transparent;';
        });

        button.addEventListener('mouseleave', () => {
            menu['style'] = 'display: none;';
            document.getElementById('xtrars-btn')['style'] = '';
            document.getElementById('xtrars-btn-icon')['style'] = '';
        });

        activateEnhancer.addEventListener('click', () => {
            activateEnhancer.previousSibling.checked = !activateEnhancer.previousSibling.checked;
            GM_setValue('bActivateEnhancer', activateEnhancer.previousSibling.checked);
            if (!activateEnhancer.previousSibling.checked) {
                this.hideAutoplayVivoVideo(autoplayVivoVideo);
                this.hideAutoplayNextEpisode(autoplayNextEpisode);
            } else {
                autoplayVivoVideo['style'] = '';
                autoplayNextEpisode['style'] = '';


                this.manageAutoplayVivoVideoButton(activateEnhancer, autoplayNextEpisode, autoplayVivoVideo);
            }
        });

        autoplayNextEpisode.addEventListener('click', () => {
            autoplayNextEpisode.previousSibling.checked = !autoplayNextEpisode.previousSibling.checked;
            GM_setValue('bAutoplayNextEpisode', autoplayNextEpisode.previousSibling.checked);
            this.manageAutoplayVivoVideoButton(activateEnhancer, autoplayNextEpisode, autoplayVivoVideo);
        });

        autoplayVivoVideo.addEventListener('click', () => {
            if (!GM_getValue('bAutoplayNextEpisode')) {
                autoplayVivoVideo.previousSibling.checked = !autoplayVivoVideo.previousSibling.checked;
                GM_setValue('bAutoplayVivoVideo', autoplayVivoVideo.previousSibling.checked);
            }
        });
    }

    hideAutoplayNextEpisode(autoplayNextEpisode) {
        this.playNextEpisodeIfVideoEnded(false);
        autoplayNextEpisode['style'] = 'visibility:hidden;';
    }

    hideAutoplayVivoVideo(autoplayVivoVideo) {
        this.playNextEpisodeIfVideoEnded();
        autoplayVivoVideo['style'] = 'visibility:hidden;';
    }

    manageAutoplayVivoVideoButton(activateEnhancer, autoplayNextEpisode, autoplayVivoVideo) {
        if (autoplayNextEpisode.previousSibling.checked || !activateEnhancer.previousSibling.checked) {
            this.hideAutoplayVivoVideo(autoplayVivoVideo);
        } else {
            autoplayVivoVideo['style'] = '';
            autoplayVivoVideo.querySelectorAll('span')['style'] = '';
            this.playNextEpisodeIfVideoEnded(false);
        }
    }

    initGMVariables() {
        if (typeof GM_getValue('bActivateEnhancer') === "undefined") {
            GM_setValue('bActivateEnhancer', true);
        }

        if (typeof GM_getValue('bAutoplayNextEpisode') === "undefined") {
            GM_setValue('bAutoplayNextEpisode', true);
        }

        if (typeof GM_getValue('bAutoplayVivoVideo') === "undefined") {
            GM_setValue('bAutoplayVivoVideo', true);
        }
    }

    applyShortcuts(videoElement) {
        document.addEventListener('keydown', function(e) {
            if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
                switch(e.keyCode){
                    case 37: // Left
                        e.preventDefault();
                        videoElement.currentTime -= 10;
                        break;
                    case 38: // Up
                        e.preventDefault();
                        if (videoElement.volume < 0.9) {
                            videoElement.volume += 0.1;
                        } else {
                            videoElement.volume = 1;
                        }
                        break;
                    case 39: // Right
                        e.preventDefault();
                        videoElement.currentTime += 10;
                        break;
                    case 40: // Down
                        e.preventDefault();
                        if (videoElement.volume > 0.1) {
                            videoElement.volume -= 0.1;
                        } else {
                            videoElement.volume = 0;
                        }
                        break;
                    case 70: // F
                        if (!document.fullscreenElement) {
                            videoElement.requestFullscreen().catch(() => {
                                video.style.width = "100%";
                                video.style.height = "100%";
                                document.body.style.margin = "0px";
                            });
                        } else {
                            document.exitFullscreen();
                        }
                        break;
                    case 75: // K
                        if(videoElement.paused) {
                            videoElement.play();
                        }
                        else {
                            videoElement.pause();
                        }
                        break;
                }
            }
        });
    }

    async handleStreamBehavior(src) {
        if (GM_getValue('bAutoplayVivoVideo') || GM_getValue('bAutoplayNextEpisode')) {
            window['location'].replace(src);
        } else {
            await navigator.clipboard.writeText(src).catch(() => {
                GM_setClipboard(src);
            });
            window.close();
        }
    }


    async isVivo() {
        let regex = /^https:\/\/vivo.[a-z]{2,3}\//g;
        return document['location']['href'].search(regex) !== -1 && await this.waitForElement('video:not(#player)').catch(() => {});
    }

    async isVupload() {
        let regex = /^https:\/\/vupload.[a-z]{2,3}\//g;
        return document['location']['href'].search(regex) !== -1 && await this.waitForElement('#vjsplayer_html5_api').catch(() => {});
    }
}

(async function() {
    'use strict';

    let siteHandler = new SiteHandler();
    siteHandler.initGMVariables();

    if (siteHandler.isEpisode()) {
        if (GM_getValue('bActivateEnhancer') && !siteHandler.hasAnotherHoster() && !siteHandler.isVivoTab()) {
            document['location'].replace(document['location']['href'] + '/Vivo');
        }
        await siteHandler.buildButton();

        if (GM_getValue('bActivateEnhancer') && !siteHandler.hasAnotherHoster() && siteHandler.isVivoTab()) {
            if (GM_getValue('bAutoplayNextEpisode')) {
                siteHandler.playNextEpisodeIfVideoEnded();
            }

            if(GM_getValue('bActivateEnhancer')){
                siteHandler.clickPlay();
            }

        }
    }

    if (GM_getValue('bActivateEnhancer')) {
        // add new hoster here
        if(await siteHandler.isVivo()) {
            let vivoVideo = await siteHandler.waitForElement('video:not(#player) > source').catch(() => {});
            siteHandler.handleStreamBehavior(vivoVideo['src']);
        }

        if (await siteHandler.isVupload()) {
            let vuploadVideo = await siteHandler.waitForElement('#vjsplayer_html5_api[src]').catch(() => {});
            siteHandler.handleStreamBehavior(vuploadVideo['src']);
        }


        // is native video player
        let video;
        if (video = await siteHandler.waitForElement('html > head + body > video').catch(() => {})) {
            siteHandler.applyShortcuts(video);
            GM_setValue('isLocalVideoEnded', false);
            video.requestFullscreen().catch(() => {
                video.style.width = "100%";
                video.style.height = "100%";
                document.body.style.margin = "0px";
            });
            video.onended = () => {
                GM_setValue('isLocalVideoEnded', true);
                window.close();
            }
        }
    }

})();
长期地址
遇到问题?请前往 GitHub 提 Issues。