// ==UserScript== // @name s.to autoplay // @namespace https://github.com/zaheer-exe // @version 8.4 // @description Autoplay für SerienStream.to // @author zaheer-exe // @match https://s.to/* // @match https://serienstream.to/* // @match https://aniworld.to/* // @match https://voe.sx/* // @match *://*/* // @grant GM_xmlhttpRequest // @grant none // @run-at document-start // @icon https://www.google.com/s2/favicons?sz=64&domain=s.to // @license Apache License // @downloadURL none // ==/UserScript== function adblock() { function hasKey(url) { // Check for 'key' parameter const keyPattern = /[?&]key=[^&]+/i; return keyPattern.test(url); } // Override window.open const originalWindowOpen = window.open; window.open = function(url, ...args) { if (typeof url === 'string' && hasKey(url)) { console.log('Blocked popup with key:', url, window.location.href); return null; } return originalWindowOpen.call(this, url, ...args); }; // Override createElement to prevent script injection const originalCreateElement = document.createElement; document.createElement = function(tagName) { const element = originalCreateElement.call(document, tagName); if (tagName.toLowerCase() === 'script') { const originalSetAttribute = element.setAttribute; element.setAttribute = function(name, value) { if (name === 'src' && hasKey(value)) { console.log('Blocked script src with key:', value); return; } return originalSetAttribute.call(this, name, value); }; } return element; }; // Intercept and block certain XMLHttpRequests const originalXHROpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url, ...args) { if (hasKey(url)) { console.log('Blocked XHR with key:', url); throw new Error('Blocked XHR'); } return originalXHROpen.call(this, method, url, ...args); }; // Block fetch requests with keys const originalFetch = window.fetch; window.fetch = function(input, init) { if (typeof input === 'string' && hasKey(input)) { console.log('Blocked fetch with key:', input); return Promise.reject(new Error('Blocked fetch')); } return originalFetch.call(this, input, init); }; // Remove suspicious hidden inputs periodically const removeHiddenInputs = () => { const hiddenInputs = document.querySelectorAll('input[type="hidden"]'); hiddenInputs.forEach(input => { if (input.name && input.name.length > 30 && /^[a-f0-9]{32}$/.test(input.name)) { console.log('Removed suspicious hidden input:', input.name); input.remove(); } }); }; // Prevent timer-based function execution const originalSetTimeout = window.setTimeout; const originalSetInterval = window.setInterval; const wrapTimerFunction = (originalFunc) => { return function(callback, delay, ...args) { if (typeof callback === 'function') { const callbackString = callback.toString(); if (callbackString.includes('append') && callbackString.includes('input')) { console.log('Blocked suspicious timer function'); return; } // Wrap the callback to remove hidden inputs after execution const wrappedCallback = function() { callback.apply(this, arguments); removeHiddenInputs(); }; return originalFunc.call(this, wrappedCallback, delay, ...args); } return originalFunc.call(this, callback, delay, ...args); }; }; window.setTimeout = wrapTimerFunction(originalSetTimeout); window.setInterval = wrapTimerFunction(originalSetInterval); // Initial cleanup removeHiddenInputs(); // Periodically check and remove suspicious elements setInterval(removeHiddenInputs, 1000); }; ////////// LISTER //////////////////////////////////////////////////////////////// const sToHosts = ['s.to', 'aniworld.to', 'serienstream.to']; if (sToHosts.includes(new URL(window.location.href).hostname)) { let isAutoPlayed = false; function nextEpisode() { const currentLang = document.querySelector("img.selectedLanguage").dataset.langKey; console.log("S: current lang ", currentLang); const episodeMenuCurrentELem = document.querySelector('li a.active[href*="episode"]'); const nextEpisodeUrl = episodeMenuCurrentELem.parentElement.nextElementSibling.querySelector('a'); var xmlHttp = new XMLHttpRequest(); xmlHttp.open( "GET", nextEpisodeUrl, false ); xmlHttp.send(null); let temp = document.createElement('div') temp.innerHTML=xmlHttp.responseText; let url = temp.querySelector('li[data-lang-key="' + currentLang + '"] .watchEpisode .icon.VOE').parentElement.href; let title = temp.querySelector(".hosterSiteTitle").innerHTML; document.querySelector(".inSiteWebStream iframe").src = url; document.querySelector(".hosterSiteTitle").innerHTML = title; document.querySelector(".breadCrumbMenu").innerHTML = temp.querySelector(".breadCrumbMenu").innerHTML; episodeMenuCurrentELem.classList.remove('active'); nextEpisodeUrl.classList.add('active'); episodeMenuCurrentELem.classList.remove('active'); window.history.pushState("", "", nextEpisodeUrl.href); if (!isAutoPlayed) { disableElements(); isAutoPlayed = true; } } function disableElements() { const style = document.createElement('style'); style.textContent = ` .changeLanguage, li[data-link-target] { opacity: 0.3; cursor: none; } .changeLanguageBox, li[data-link-target] > .generateInlinePlayer { pointer-events: none; } .tooltip { position: absolute; background-color: #333; /* Dark background for contrast */ color: #fff; /* White text for contrast */ padding: 10px; /* Padding for better appearance */ border-radius: 5px; /* Rounded corners */ font-size: 14px; /* Font size for readability */ line-height: 1.4; /* Line height for better text readability */ white-space: nowrap; /* Prevent text wrapping */ display: none; /* Hidden by default */ z-index: 9999; /* Ensure it appears above other content */ pointer-events: none; /* Ensure it doesn't interfere with interactions */ transform: translate(-50%, -50%); /* Center tooltip on the cursor */ } `; document.head.appendChild(style); const tooltip = document.createElement('div'); tooltip.className = 'tooltip'; tooltip.textContent = 'Autoplay enabled. Refresh/Reload Page to change settings.'; document.body.appendChild(tooltip); function updateTooltipPosition(event) { tooltip.style.left = `${event.pageX}px`; tooltip.style.top = `${event.pageY}px`; } function showTooltip(event) { tooltip.style.display = 'block'; updateTooltipPosition(event); } function hideTooltip() { tooltip.style.display = 'none'; } const elements = document.querySelectorAll('.changeLanguage, li[data-link-target]'); elements.forEach(element => { element.addEventListener('mouseenter', showTooltip); element.addEventListener('mousemove', updateTooltipPosition); element.addEventListener('mouseleave', hideTooltip); }); } function autoPlaySettings() { const style = document.createElement('style'); style.textContent = ` .autoplay-settings-container { border: 1px solid #3a3a3a; border-radius: 8px; margin-top: 15px; padding: 15px; background-color: #18181b; font-family: Arial, sans-serif; color: #ffffff; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); } .autoplay-settings-container h2 { margin: 0 0 15px 0; color: #8257e6; font-size: 16px; font-weight: bold; } .settings-row { display: flex; justify-content: space-between; } .settings-column { width: 48%; } .setting-group { margin-bottom: 10px; } .setting-group label { display: block; margin-bottom: 5px; font-size: 14px; color: #a0a0a0; } .setting-group input { width: 100%; padding: 8px; border: 1px solid #3a3a3a; border-radius: 4px; font-size: 14px; background-color: #27272a; color: #ffffff; transition: border-color 0.3s, box-shadow 0.3s; } .setting-group input:focus { outline: none; border-color: #8257e6; box-shadow: 0 0 5px rgba(130, 87, 230, 0.5); } .setting-group input::placeholder { color: #6b7280; } `; document.head.appendChild(style); const container = document.createElement("div"); container.classList.add("autoplay-settings-container"); container.innerHTML = `

AutoPlay Settings

`; document.querySelector(".hosterSiteDirectNav").appendChild(container); ['skip-intro', 'skip-outro', 'still-here'].forEach(id => { document.getElementById(id).addEventListener("change", (e) => { const value = e.target.value; console.log(`${e.target.id} changed to: ${value}`); // Here you can add logic to save the settings or trigger other actions }); }); } function showTooltip(e) { const tooltip = document.getElementById('autoplay-tooltip'); tooltip.style.display = 'block'; tooltip.style.left = e.pageX + 10 + 'px'; tooltip.style.top = e.pageY + 10 + 'px'; } function hideTooltip() { const tooltip = document.getElementById('autoplay-tooltip'); tooltip.style.display = 'none'; } window.addEventListener("message", (event) => { if(typeof event.data === "string" && event.data.startsWith("autoplay")) { const parsed = event.data.split("$"); console.log("event", parsed); switch (parsed[1]) { case "url": { console.log("S: url"); let streamIframe = document.querySelector(".inSiteWebStream iframe"); if (streamIframe.src.includes("/redirect")) { document.querySelector(".inSiteWebStream iframe").src = parsed[2]; } break; } case "end": { console.log("S: ended"); nextEpisode(); } } } }, false); window.addEventListener("load", () => { document.querySelector(".inSiteWebStream iframe").allow="autoplay; fullscreen; picture-in-picture; xr-spatial-tracking; clipboard-write"; //autoPlaySettings(); }) } ////////// HOSTER //////////////////////////////////////////////////////////////// let checkIfVoe = document.querySelector("head > meta[name='og:sitename']"); if (checkIfVoe && checkIfVoe.content == "VOE: Video Hosting Platform & Online Cloud Storage") { adblock(); window.addEventListener("load", () => { console.log("VOE: loaded"); window.parent.postMessage("autoplay$url$" + window.location.href, '*'); document.querySelector("video").play(); let ended = false; document.querySelector("video").addEventListener("ended", () => { if (!ended) { console.log("VEO: ended"); window.parent.postMessage("autoplay$end", "*"); ended = true; } }); // sometimes the "ended" event does not fire.. idk why. needs a workaround: window.setInterval(() => { let video = document.querySelector("video"); if (video.currentTime + 0.5 >= video.duration && !ended) { console.log("VEO: ended workaround"); window.parent.postMessage("autoplay$end", "*"); ended = true; } }, 500); }); }