Greasy Fork

来自缓存

Crunchyroll Auto Skip Intro/Outro, Fullscreen Video & Mouse Volume Control

Automatically clicks the Skip Intro button on Crunchyroll.com when available and makes the video fullscreen

当前为 2025-01-05 提交的版本,查看 最新版本

// ==UserScript==
// @name         Crunchyroll Auto Skip Intro/Outro, Fullscreen Video & Mouse Volume Control
// @namespace    https://greasyfork.org/en/users/807108-jeremy-r
// @version      5
// @description  Automatically clicks the Skip Intro button on Crunchyroll.com when available and makes the video fullscreen
// @author       JRem
// @match        https://*.crunchyroll.com/watch/*
// @match        https://static.crunchyroll.com/vilos-v2/web/vilos/player.html
// @grant        GM_addStyle
// @grant        GM.xmlHttpRequest
// @license MIT
// ==/UserScript==

////////////////////////
// USER CUSTOMIZATION //
////////////////////////
// 1 = Enabled / 0 = Disabled
const enableFullscreen=1;
const enableSkipIntro=1;
const enableSkipCredits=1;
const enableVolumeControl=1;
// Volume +/- percentage
const volumePercentage = 5; // Default 5, Set the percentage of volume change (+/-)
// Global user settings for toast appearance and positioning
const toastSettings = {
    toastPositionX: '45', // Default='45' (LeftCenter), Range '0' to '100' on the X axis
    toastPositionY: '0', // Default='0' (Top), Range '0' to '100' on the Y axis
    fontSize: '16px', // Font size for the toast message
    fontFamily: 'Arial, sans-serif', // Font family for the toast message
    backgroundColor: '#333', // Background color for the toast
    textColor: 'white', // Text color for the toast
    padding: '10px', // Padding around the toast text
    margin: '5px', // Margin between toasts
    borderRadius: '5px', // Border radius for rounded corners
    toastDuration: 3500, // Duration to display the toast (in ms)
    fadeDuration: 300, // Duration of the fade-in/out animation (in ms)
};

// Function to show or update toast message
function showToast(message) {
    // Create a toast container if it doesn't already exist
    let toastContainer = document.getElementById('toast-container');
    if (!toastContainer) {
        toastContainer = document.createElement('div');
        toastContainer.id = 'toast-container';
        document.body.appendChild(toastContainer);

        // Add styles for the toast container
        console.log (`transform: translateX(${toastSettings.toastPositionX}%);`)
        console.log (`transform: translateY(${toastSettings.toastPositionY}%);`)
        const style = document.createElement('style');
        style.innerHTML = `
            #toast-container {
                position: fixed;
                z-index: 9999;
                ${toastSettings.position}: 10px;
                ${toastSettings.align}: 50%;
                transform: translateX(${toastSettings.toastPositionX}vw);
                max-width: 90%;
                pointer-events: none; /* Prevent interaction with toasts */
            }
            .toast {
                background-color: ${toastSettings.backgroundColor};
                color: ${toastSettings.textColor};
                padding: ${toastSettings.padding};
                margin: ${toastSettings.margin};
                border-radius: ${toastSettings.borderRadius};
                font-family: ${toastSettings.fontFamily};
                font-size: ${toastSettings.fontSize};
                box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
                opacity: 0;
                transform: translateY(${toastSettings.toastPositionY}vh);
                animation: fadeIn ${toastSettings.fadeDuration / 1000}s forwards, fadeOut 3s forwards ${toastSettings.toastDuration / 1000 - toastSettings.fadeDuration / 1000}s;
            }
            @keyframes fadeIn {
                to {
                    opacity: 1;
                    transform: translateY(0);
                }
            }
            @keyframes fadeOut {
                to {
                    opacity: 0;
                    transform: translateY(20px);
                }
            }
        `;
        document.head.appendChild(style);
    }

    // Check if there's an existing toast being displayed
    let currentToast = toastContainer.querySelector('.toast');
    if (currentToast) {
        // If a toast is already visible, update its text content
        currentToast.textContent = message;
        // Reset the animation so that the toast updates instantly
        currentToast.style.animation = 'none';
        currentToast.offsetHeight; // Trigger reflow to restart animation
        currentToast.style.animation = `fadeIn ${toastSettings.fadeDuration / 1000}s forwards, fadeOut 3s forwards ${toastSettings.toastDuration / 1000 - toastSettings.fadeDuration / 1000}s`;
    } else {
        // If no toast is visible, create a new toast
        const toast = document.createElement('div');
        toast.classList.add('toast');
        toast.textContent = message;
        toastContainer.appendChild(toast);
    }

    // Remove the toast after the duration is finished
    setTimeout(() => {
        if (currentToast) {
            toastContainer.removeChild(currentToast);
        }
    }, toastSettings.toastDuration);
}

// Fullscreen CSS Edit
if (enableFullscreen == 1) {
var css = '.video-player-wrapper { max-height: calc(100vh - 5.625rem) !important; height: calc(100vh) !important; }';
css += '.erc-header { flex: 0 0 1.55rem !important; }';
css += '.erc-header .header-content { height: 0 !important; }';
GM_addStyle(css);
showToast('Fullscreen enabled'); };

// Volume Control via mouse scroll
// 1 Scroll = 5%
if (enableVolumeControl == 1) {
// Function to simulate the click event
function simulate(element, event) {
    const evt = new MouseEvent(event, { bubbles: true, cancelable: true });
    element.dispatchEvent(evt);
}
    // Get the current video element
    const video = document.querySelector('video');
    // Function to adjust volume
function adjustVolume(video, percentage) {
    if (!video) {
        console.error("Video element not found.");
        return;
    }

    // Get the current volume (between 0 and 1)
    let currentVolume = video.volume;

    // Calculate the new volume by adjusting it with the percentage
    let volumeChange = currentVolume + (percentage / 100);

    // Ensure the volume is within the valid range of 0 to 1
    if (volumeChange > 1) {
        volumeChange = 1;
    } else if (volumeChange < 0) {
        volumeChange = 0;
    }

    // Apply the new volume to the video
    video.volume = volumeChange;
    var newVol = (video.volume * 100).toFixed(2);
    showToast(`Volume: ${newVol}%`);
}
// Listen for the mouse wheel event over the video
if (document.getElementById("vilos")) {
    document.getElementById("vilos").addEventListener('wheel', (event) => {
        // Check the direction of the scroll
        if (event.deltaY < 0) {
            // Scroll up (increase volume)
            adjustVolume(video, volumePercentage);
        } else if (event.deltaY > 0) {
            // Scroll down (decrease volume)
            adjustVolume(video, -volumePercentage);
        }
        // Prevent the default action to avoid scrolling the page
        event.preventDefault();
    });
}
};

// Check for and click Skip Intro
if (enableSkipIntro == 1 || enableSkipCredits == 1) {
setInterval(function () {
// Check for skip intro button
    if (enableSkipIntro ==1 ) {
const skipIntroBtn = document.querySelector('div[data-testid="skipIntroText"]');
if (skipIntroBtn !== null && (skipIntroBtn.textContent.includes("SKIP INTRO"))) {
    simulate(skipIntroBtn, "click");
    console.log('Skip Btn Found');
    showToast('Intro Skipped');
} };

// Check for skip credits button
    if (enableSkipIntro ==1 ) {
const skipCreditsBtn = document.querySelector('div[data-testid="skipIntroText"]');
if (skipCreditsBtn !== null && (skipCreditsBtn.textContent.includes("SKIP CREDITS"))) {
    simulate(skipCreditsBtn, "click");
    console.log('Skip Btn Found');
    showToast('Credits Skipped');
}}
}, 1000)
};