// ==UserScript==
// @name bs.to and vivo enhancer
// @author xtrars
// @description Wechselt automatisch zum Vivo-Tab auf bs.to 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 bs.to 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.
// @include https://bs.to/*
// @include https://*.vivo.sx/*
// @include https://*.vivo.st/*
// @include https://vivo.sx/*
// @include https://vivo.st/*
// @version 2.4
// @run-at document-start
// @license CC BY 4.0
// @namespace https://greasyforks.org/users/140785
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_setClipboard
// @grant GM_addValueChangeListener
// @grant GM_removeValueChangeListener
// ==/UserScript==
class SiteHandler {
isVivoTab() {
let sVivoStr = '/Vivo';
return document['location']['href'].search(sVivoStr) !== -1;
}
hasAnotherHoster() {
let hosterRegex = /^https:\/\/bs.to\/.*[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)/g;
return document['location']['href'].search(bsRegex) !== -1 && document['location']['href'].search(serieRegex) !== -1;
}
isVivo() {
let vivoRegex = /^https:\/\/vivo.[a-z]{2,3}\//g;
return document['location']['href'].search(vivoRegex) !== -1 && document.getElementsByTagName('video') && document.getElementsByTagName('video')[document.getElementsByTagName('video').length -1]
}
isReady() {
return new Promise(result => {
window.addEventListener('load', function() {
result(true);
});
});
}
// 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);
});
}
// thanks to xZaheer (https://greasyforks.org/de/scripts/400669-burningseries-autoplay/code)
async clickPlay() {
await this.waitForElement('section.serie .hoster-player').catch(() => {});
let playerElem = document.querySelector('section.serie .hoster-player');
if(playerElem) {
let clickEvent = new Event('click');
clickEvent.which = 1;
clickEvent.pageX = 1;
clickEvent.pageY = 1;
setTimeout(() => {
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);
}
});
}
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-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 aktivieren";
}
.onoffswitch-inner.autoplay:after {
padding-right: var(--inner-pl);
background-color: var(--inner-bc-after);
color: var(--color);
text-align: end;
content: "Video-URL in die Zwischenablage kopieren und Tab schließen";
}
.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 automatisch abspielen";
}
.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 manuell abspielen";
}
.onoffswitch-inner.enable-enhancer:before {
padding-left: var(--inner-pl);
background-color: var(--inner-bc-before);
color: var(--color);
text-align: start;
content: "bs.to and vivo 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: "bs.to and vivo 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;'
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;';
GM_setValue('bAutoplayNextEpisode', false);
}
hideAutoplayVivoVideo(autoplayVivoVideo) {
this.playNextEpisodeIfVideoEnded();
autoplayVivoVideo['style'] = 'visibility:hidden;';
GM_setValue('bAutoplayVivoVideo', true);
}
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);
}
}
}
(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.waitForElement('.infos').catch(() => {});
siteHandler.buildButton();
if (GM_getValue('bActivateEnhancer') && !siteHandler.hasAnotherHoster() && siteHandler.isVivoTab()) {
if (GM_getValue('bAutoplayNextEpisode')) {
siteHandler.playNextEpisodeIfVideoEnded();
}
if(GM_getValue('bActivateEnhancer') && GM_getValue('bAutoplayVivoVideo')){
siteHandler.clickPlay();
}
}
}
if (GM_getValue('bActivateEnhancer') && await siteHandler.isReady().catch(() => {}) && siteHandler.isVivo()) {
// thanks to Wissidi dom (https://greasyforks.org/de/scripts/28779-zu-vivo-video-navigieren/code)
let sSrc = document.getElementsByTagName('video')[document.getElementsByTagName('video').length -1]['currentSrc'];
if (GM_getValue('bAutoplayVivoVideo')) {
window['location'].replace(sSrc);
} else {
await navigator.clipboard.writeText(sSrc).catch(() => {});
// if the first method doesn't work
GM_setClipboard(sSrc);
window.close();
}
}
if (GM_getValue('bActivateEnhancer')) {
let video;
if (video = await siteHandler.waitForElement('html > head + body > video').catch(() => {})) {
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();
}
}
}
})();