// ==UserScript== // @name Aniworld.to Autoplay // @name:de Autoplay AniWorld // @namespace http://tampermonkey.net/ // @version 5.2 // @description Autoplay for Aniworld.to // @description:de Autoplay für Aniworld.to // @exclude *://facebook.com/* // @exclude *://platform.twitter.com/* // @exclude *://voe.sx/* // @match *://aniworld.to/* // @match *://*/* // @grant none // @license MIT // @downloadURL none // ==/UserScript== // Configurable Settings const OUTRO_SKIP_THRESHOLD = 90; // Time in seconds remaining to skip at the outro const SKIP_FORWARD_TIME = 85; // Time in seconds to skip forward when 'V' is pressed const SKIP_AHEAD_TIME = 10; // Time to skip ahead with right arrow button in seconds. This is so you don't have to click on the slider const SKIP_BACKWARD_TIME = 10; // Time to skip back with left arrow button in seconds. This is so you don't have to click on the slider const VIDEO_PLAYER = "robertordercharacter.com"; // Press F to fullscreen. // Press V to skip intro (press when Intro beginns) // I'll try to keep it updated at all times. UPDATES AT https://greasyfork.org/en/scripts/518391 // But if it isn't working you have to change the const VIDEO_PLAYER url to the current one. // You'll get a pop up with the new url to copy if I didn't update it already. (function () { 'use strict'; // Load skip state from localStorage, default to true if not set let skipToNextEpisodeEnabled = JSON.parse(localStorage.getItem('skipToNextEpisodeEnabled')) ?? true; function toggleSkipToNextEpisode() { skipToNextEpisodeEnabled = !skipToNextEpisodeEnabled; localStorage.setItem('skipToNextEpisodeEnabled', JSON.stringify(skipToNextEpisodeEnabled)); const toggleButton = document.querySelector('.skip-toggle-button .Autoplay-button'); const tooltip = document.querySelector('.skip-toggle-button .plyr__tooltip'); if (toggleButton && tooltip) { updateToggleSwitch(toggleButton, tooltip); } } // Extract the URL from the video element and compare it function checkVideoPlayerUrl() { const videoElement = document.querySelector('video#voe-player'); if (videoElement) { const videoSrc = videoElement.getAttribute('src'); if (!videoSrc) { console.error("Video src attribute is missing or empty."); return; } // Parse the URL from the `src` attribute if it's a full URL or blob const parsedUrl = videoSrc.startsWith("blob:") ? new URL(videoSrc.slice(5)) : new URL(videoSrc); const videoHost = parsedUrl.hostname; if (videoHost !== VIDEO_PLAYER) { alert(`The video player URL has changed Copy -> ${videoHost} and change it in the script.`); } else { console.log("Video player URL matches."); } } else { console.error("Video element with ID 'voe-player' not found."); } } // Call the function on page load window.addEventListener('load', function () { checkVideoPlayerUrl(); }); // add skip button function addSkipToggleButton() { const controls = document.querySelector('.plyr__controls'); if (controls) { const button = document.createElement('button'); button.classList.add('plyr__controls__item', 'plyr__control', 'skip-toggle-button', 'Autoplay-button'); button.type = 'button'; button.setAttribute('aria-label', skipToNextEpisodeEnabled ? 'Disable skip to next episode' : 'Enable skip to next episode'); const toggleContainer = document.createElement('div'); toggleContainer.classList.add('Autoplay-button-container'); const toggleSwitch = document.createElement('div'); toggleSwitch.classList.add('Autoplay-button'); toggleSwitch.setAttribute('aria-checked', skipToNextEpisodeEnabled.toString()); const tooltip = document.createElement('span'); tooltip.classList.add('plyr__tooltip'); tooltip.textContent = skipToNextEpisodeEnabled ? 'Autoplay is enabled' : 'Autoplay is disabled'; toggleContainer.appendChild(toggleSwitch); button.appendChild(toggleContainer); button.appendChild(tooltip); button.addEventListener('click', toggleSkipToNextEpisode); controls.insertBefore(button, controls.lastChild); updateToggleSwitch(toggleSwitch, tooltip); } } // Updater for the autoplay button function updateToggleSwitch(toggleSwitch, tooltip) { if (skipToNextEpisodeEnabled) { toggleSwitch.style.backgroundColor = '#ffffff'; toggleSwitch.style.transform = 'translateX(12px)'; tooltip.textContent = 'Autoplay On'; } else { toggleSwitch.style.backgroundColor = '#bbb'; toggleSwitch.style.transform = 'translateX(0px)'; tooltip.textContent = 'Autoplay Off'; } } // Function to press the play button function playVideo() { if (!skipToNextEpisodeEnabled) return; const playButton = document.querySelector('button.plyr__controls__item.plyr__control[data-plyr="play"]'); if (playButton) { playButton.click(); console.log("Play button clicked."); } else { console.log("Play button not found."); } } // Function to skip set amount of seconds/Intro function skipForward() { const video = document.querySelector('video'); if (video) { video.currentTime = Math.min(video.currentTime + SKIP_FORWARD_TIME, video.duration); } else { console.error("Video element not found for skipping"); } } // Function to check for end of slider/outro function checkAndPostMessage() { if (skipToNextEpisodeEnabled) { const video = document.querySelector('video'); if (video) { if (video.duration - video.currentTime <= OUTRO_SKIP_THRESHOLD) { window.parent.postMessage({ action: 'skipToNextEpisode' }, '*'); } } } } // Function to get the url of the next episode/searies function getNextEpisodeUrl() { const currentEpisodeUrl = window.location.href; const episodeMatch = currentEpisodeUrl.match(/\/episode-(\d+)/); if (episodeMatch && episodeMatch[1]) { const currentEpisodeNumber = parseInt(episodeMatch[1], 10); const episodeLinks = Array.from(document.querySelectorAll('ul li a[data-episode-id]')); const totalEpisodes = episodeLinks.length; if (currentEpisodeNumber < totalEpisodes) { const nextEpisodeNumber = currentEpisodeNumber + 1; return currentEpisodeUrl.replace(/\/episode-\d+/, `/episode-${nextEpisodeNumber}`); } else { const currentSeasonMatch = currentEpisodeUrl.match(/\/staffel-(\d+)\//); if (currentSeasonMatch && currentSeasonMatch[1]) { const currentSeason = parseInt(currentSeasonMatch[1], 10); const nextSeason = currentSeason + 1; return currentEpisodeUrl.replace(/\/staffel-\d+\/episode-\d+/, `/staffel-${nextSeason}/episode-1`); } } } return null; } // Fullscreen handler function handleFullscreen() { if (window.location.host.includes('aniworld.to')) { const iframe = document.querySelector('.inSiteWebStream iframe'); if (iframe) { try { iframe.focus(); console.log("Focused on iframe."); iframe.contentWindow.postMessage({ action: 'clickFullscreen' }, '*'); console.log("Message sent to iframe for fullscreen."); } catch (err) { console.error("Failed to communicate with iframe:", err); } } else { console.log("Iframe not found in inSiteWebStream."); } } else if (window.location.host.includes(VIDEO_PLAYER)) { const fullscreenButton = document.querySelector('button[data-plyr="fullscreen"]'); if (fullscreenButton) { fullscreenButton.click(); console.log("Fullscreen button clicked in iframe."); } else { console.log("Fullscreen button not found in iframe."); } } } // Function to inject a simulated user interaction function simulateUserInteraction() { const iframe = document.querySelector('.inSiteWebStream iframe'); if (iframe) { try { // Create and dispatch a click event on the iframe const rect = iframe.getBoundingClientRect(); const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, clientX: rect.left + 5, clientY: rect.top + 5, }); iframe.dispatchEvent(clickEvent); console.log("Simulated click on iframe."); // Send a mousemove event const mouseMoveEvent = new MouseEvent('mousemove', { bubbles: true, cancelable: true, clientX: rect.left + 5, clientY: rect.top + 5, }); iframe.dispatchEvent(mouseMoveEvent); console.log("Simulated mouse move on iframe."); } catch (err) { console.error("Failed to simulate user interaction:", err); } } else { console.log("Iframe not found."); } } // Manual F key functionality document.addEventListener('keydown', function (e) { if (e.key.toLowerCase() === 'f') { console.log("F key pressed. Triggering fullscreen..."); handleFullscreen(); } }); // Handle messages from the iframe for cross-origin interaction if (window.location.host.includes(VIDEO_PLAYER)) { window.addEventListener('message', function (event) { if (event.data.action === 'clickFullscreen') { const fullscreenButton = document.querySelector('button[data-plyr="fullscreen"]'); if (fullscreenButton) { fullscreenButton.click(); console.log("Fullscreen button clicked in iframe via message."); } else { console.log("Fullscreen button not found in iframe."); } } }); } // Function to send play message function sendPlayMessage() { if (!skipToNextEpisodeEnabled) return; const iframe = document.querySelector(`iframe[src*="${VIDEO_PLAYER}"]`); if (iframe) { iframe.contentWindow.postMessage({ action: 'play' }, '*'); } } // Function to skip ahead function skipAhead() { const video = document.querySelector('video'); if (video) { video.currentTime = Math.min(video.currentTime + SKIP_AHEAD_TIME, video.duration); } else { console.error("Video element not found for skipping ahead"); } } // Function to skip backward function skipBackward() { const video = document.querySelector('video'); if (video) { video.currentTime = Math.max(video.currentTime - SKIP_BACKWARD_TIME, 0); } else { console.error("Video element not found for skipping backward"); } } // Event listener for keydown window.addEventListener('keydown', function (event) { if (!event.target.closest('input, textarea')) { switch (event.key) { case 'ArrowRight': // Right arrow key for skipping ahead skipAhead(); break; case 'ArrowLeft': // Left arrow key for skipping backward skipBackward(); break; } } }); // Behavior for Video Player if (window.location.hostname === VIDEO_PLAYER) { setInterval(checkAndPostMessage, 5000); window.addEventListener("message", function (event) { if (event.data.action === "play") { playVideo(); } }); window.addEventListener('load', function () { if (skipToNextEpisodeEnabled) { playVideo(); } }); window.addEventListener('keydown', function (event) { if ((event.key === 'V' || event.key === 'v') && !event.target.closest('input, textarea')) { skipForward(); } }); } // Behavior for aniworld.to if (window.location.hostname === "aniworld.to") { window.addEventListener('message', function (event) { if (event.origin.includes(VIDEO_PLAYER) && event.data.action === 'skipToNextEpisode') { const nextEpisodeUrl = getNextEpisodeUrl(); if (nextEpisodeUrl) { window.location.href = nextEpisodeUrl; if (skipToNextEpisodeEnabled) { setTimeout(sendPlayMessage, 2000); } } else { console.error("Next episode URL could not be determined."); } } }); window.addEventListener('load', function () { if (skipToNextEpisodeEnabled) { sendPlayMessage(); } }); } // Autoplay button Style const style = document.createElement('style'); style.textContent = ` .Autoplay-button-container { width: 24px; height: 12px; background-color: rgba(221, 221, 221, 0.5); border-radius: 6px; position: absolute; top: 50%; transform: translateY(-50%); right: -4px; cursor: pointer; display: inline-block; z-index: 10; } .Autoplay-button { width: 12px; height: 12px; background-color: #bbb; border-radius: 50%; position: absolute; top: 0; left: 0; transition: all 0.2s ease; } .Autoplay-button[aria-checked="true"] { background-color: #ffffff; transform: translateX(12px); } .plyr__tooltip { position: absolute; bottom: 35px; left: 50%; transform: translateX(-50%); padding: 4px 8px; background: rgba(0, 0, 0, 0.75); color: #fff; border-radius: 4px; font-size: 12px; white-space: nowrap; opacity: 0; transition: opacity 0.2s ease; pointer-events: none; } .skip-toggle-button:hover .plyr__tooltip { opacity: 1; } `; document.head.appendChild(style); window.addEventListener('load', function () { addSkipToggleButton(); }); })();