// ==UserScript==
// @name burningseries-autoplay
// @namespace https://github.com/zaheer-exe/burningseries-autoplay
// @version 3.1
// @description Autoplay für Burningseries
// @author zaheer-exe
// @match https://bs.to/*
// @match https://*.vivo.sx/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_openInTab
// @grant window.close
// @license Apache License
// @downloadURL none
// ==/UserScript==
const debugging = false;
function log(msg) {
if(debugging) {
console.log(msg);
}
}
function waitForElem(selector) {
return new Promise(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();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
class SiteHandler {
constructor() {
log('SiteHandler: constructor()');
this.dataHandler = new DataHandler();
this.url = new URL(document.location.href);
this.settings = this.dataHandler.getSettings();
this.registerEvents();
}
registerEvents() {
document.addEventListener("visibilitychange", () => {
if(!document.hidden) {
let url = new URL(document.location.href);
this.dataHandler.setCurrentWebsite(url.host);
return true
}
return false;
});
}
}
class VivoHandler extends SiteHandler {
constructor() {
super();
this.data = this.dataHandler.getEpisodeData(this.settings.lastBsUrl);
if(this.settings.vivoEnabled && this.isVivo()) {
this.play();
}
if(this.settings.vivoEnabled && this.isVivoVideo()){
this.resize()
this.trackWatchedState();
if(this.settings.autonextEnabled) {
this.onEndPlayNext();
}
}
if(this.settings.autoresumeEnabled) {
this.resume();
}
}
isVivo() {
log('VivoHandler: isVivo()');
const vivoDomains = ['vivo.sx'];
const currentUrl = new URL(document.location.href);
log(currentUrl);
return vivoDomains.includes(currentUrl.host);
}
isVivoVideo() {
log('VivoHandler: isVivoVideo()');
if(document.querySelector('video') && window.location.href.includes('vivo')) {
return true;
}
return false;
}
trackWatchedState() {
log('VivoHandler: trackWatchedState()');
let videoElem = document.querySelector('video');
if (videoElem) {
videoElem.addEventListener('progress', (event) => {
let current = videoElem.currentTime;
let duration = videoElem.duration;
if (current && duration) {
this.data.currentTime = current;
this.data.maxTime = duration;
this.dataHandler.setEpisodeData(this.settings.lastBsUrl, this.data);
}
});
}
}
onEndPlayNext() {
log('VivoHandler: onEndPlayNext()');
let videoElem = document.querySelector("video");
videoElem.onended = () => {
let current = videoElem.currentTime;
let duration = videoElem.duration;
if (current && duration) {
this.data.currentTime = current;
this.data.maxTime = duration;
this.dataHandler.setEpisodeData(this.settings.lastBsUrl, this.data);
}
let nextEpisode = this.data.next;
window.location.href = nextEpisode + "#autoplay";
}
}
resize() {
let video = document.querySelector("video");
video.style.width = "100%";
video.style.height = "100%";
document.body.style.margin = "0px";
}
play() {
log('VivoHandler: 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;
}
}
window.location.href = url;
}
resume() {
let videoElem = document.querySelector('video');
if ((this.data.currentTime !== this.data.maxTime) && videoElem) {
videoElem.currentTime = this.data.currentTime;
}
}
}
class BsHandler extends SiteHandler {
constructor() {
super();
if(this.isBs()) {
this.dataHandler.setCurrentWebsite('bs');
this.settings = this.dataHandler.getSettings();
this.loadMenu();
}
if(this.dataHandler.getSettings().bsEnabled) {
this.main();
}
}
isBs() {
log('BsHandler: isBs()');
const bsDomains = ['bs.to', 'burningseries.co'];
const currentUrl = new URL(document.location.href);
log(currentUrl);
return bsDomains.includes(currentUrl.host);
}
loadMenu() {
log('BsHandler: loadMenu()');
let css = document.createElement('style');
let lastText = this.settings.lastVivoUrl ? `
` : '';
css.innerHTML =
`
.bs-autoplay-menu {
color: white;
position: absolute;
top: 0;
z-index: 999;
background: rgba(0,0,0,0.6);
margin: 10px;
padding: 5px;
}
.bs-autoplay-menu .cb {
display: inline-block;
}
.bs-autoplay-menu a {
color: red;
}
`
let html = document.createElement('div');
html.innerHTML =
`
`
html.querySelector('.toggle-button-bs').checked = this.settings.bsEnabled;
html.querySelector('.toggle-button-bs').addEventListener('click', () => {
this.settings.bsEnabled = !this.settings.bsEnabled;
this.dataHandler.setSettings(this.settings);
location.reload();
});
html.querySelector('.toggle-button-vivo').checked = this.settings.vivoEnabled;
html.querySelector('.toggle-button-vivo').addEventListener('click', () => {
this.settings.vivoEnabled = !this.settings.vivoEnabled;
this.dataHandler.setSettings(this.settings);
location.reload();
});
html.querySelector('.toggle-button-autonext').checked = this.settings.autonextEnabled;
html.querySelector('.toggle-button-autonext').addEventListener('click', () => {
this.settings.autonextEnabled = !this.settings.autonextEnabled;
this.dataHandler.setSettings(this.settings);
location.reload();
});
html.querySelector('.toggle-button-autoresume').checked = this.settings.autoresumeEnabled;
html.querySelector('.toggle-button-autoresume').addEventListener('click', () => {
this.settings.autoresumeEnabled = !this.settings.autoresumeEnabled;
this.dataHandler.setSettings(this.settings);
location.reload();
});
html.querySelector('.bs-autoplay-menu').append()
document.body.append(css);
document.body.append(html);
}
loadAutoplayButtons() {
let episodeRows = document.querySelectorAll('.episodes tr');
episodeRows.forEach((episode) => {
let target = episode.querySelector('[title=\'vivo\']');
if (!target) {
return;
}
target = target.parentElement;
let buttonElem = document.createElement('button');
buttonElem.innerHTML = 'Auto Play';
buttonElem.addEventListener('click', () => {
let url = episode.querySelector('a').href;
window.open(url + "#autoplay");
})
target.prepend(buttonElem);
})
}
setPrevAndNextEpisode() {
if(this.url.hash.includes('#autoplay')) {
let next = document.querySelector('#episodes .active').nextElementSibling;
let prev = document.querySelector('#episodes .active').previousElementSibling;
let data = this.dataHandler.getEpisodeData(this.url.pathname) || {};
if(next) {
next = next.querySelector('a').href;
}
if(prev) {
prev = prev.querySelector('a').href;
}
console.log(data);
data.prev = prev || false;
data.next = next || false;
this.dataHandler.setEpisodeData(this.url.pathname, data);
}
}
play() {
// setTimeout needed because loading time of js libs
setTimeout(() => {
let playerElem = document.querySelector('section.serie .hoster-player');
if(playerElem) {
let clickEvent = new Event('click');
clickEvent.which = 1;
clickEvent.pageX = 1;
clickEvent.pageY = 1;
playerElem.dispatchEvent(clickEvent);
waitForElem('.hoster-player a').then((elem) => {
let data = this.dataHandler.getEpisodeData(this.url.pathname) || {};
let url = elem.href;
data.vivourl = url;
this.settings.lastVivoUrl = url;
this.settings.lastBsUrl = this.url.pathname;
this.settings.lastName = document.querySelector('section.serie h2').innerText;
this.dataHandler.setSettings(this.settings);
this.dataHandler.setEpisodeData(this.url.pathname, data);
window.close();
})
}
}, 1000);
}
addProgessBars() {
let elements = document.querySelectorAll('.episodes tr');
elements.forEach((episodeRowElem) => {
let url = new URL(episodeRowElem.querySelector('a').href);
let path = url.pathname;
let data = this.dataHandler.getEpisodeData(path);
let percentage = data.currentTime * 100 / data.maxTime;
if (percentage) {
let episodeProgressbarElem = document.createElement("meter");
episodeProgressbarElem.value = percentage;
episodeProgressbarElem.max = 100;
episodeProgressbarElem.style.width = "100%";
episodeRowElem.appendChild(episodeProgressbarElem);
}
});
}
main() {
this.loadAutoplayButtons();
this.addProgessBars();
this.setPrevAndNextEpisode();
this.play();
}
}
class DataHandler {
constructor() {
log('DataHandler: constructor()');
}
getCurrentWebsite() {
log('DataHandler: getCurrentWebsite()');
let currentWebsite = GM_getValue('currentWebsite');
try {
currentWebsite = JSON.parse(currentWebsite);
} catch(e) {
log('Datahandler: getCurrentWebsite()' + e);
return false;
}
log(currentWebsite);
return currentWebsite;
}
setCurrentWebsite(site) {
log('DataHandler: setCurrentWebsite()');
try {
let currentWebsite = JSON.stringify(site);
GM_setValue('currentWebsite', currentWebsite);
} catch(e) {
log('Datahandler: setCurrentWebsite()' + e);
return false;
}
return true;
}
setSettings(data) {
try {
let d = JSON.stringify(data);
GM_setValue('settings', d);
} catch(e) {
log('Datahandler: setSettings()' + e);
return false;
}
return true;
}
getSettings() {
log('DataHandler: getSettings()');
let data = GM_getValue('settings') || '{}';
try {
data = JSON.parse(data);
} catch(e) {
log('Datahandler: getSettings()' + e);
return false;
}
log(data);
return data;
}
setEpisodeData(url, data) {
try {
let d = JSON.stringify(data);
GM_setValue(url, d);
} catch(e) {
log('Datahandler: setEpisodeData()' + e);
return false;
}
return true;
}
getEpisodeData(url) {
log('DataHandler: getEpisodeData()');
let data = GM_getValue(url) || {};
try {
data = JSON.parse(data);
} catch(e) {
log('Datahandler: getEpisodeData('+ url +')' + e);
return false;
}
log(data);
return data;
}
}
new BsHandler();
new VivoHandler();