Greasy Fork

来自缓存

Greasy Fork is available in English.

视频控制增强

视频播放增强:1. 长按左键临时加速 2. B站自动开启AI字幕

当前为 2024-12-29 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         视频控制增强
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  视频播放增强:1. 长按左键临时加速 2. B站自动开启AI字幕
// @author       Alonewinds
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// @license      MIT
// ==/UserScript==


(function() {
    'use strict';

    // ================ 通用配置和工具函数 ================
    const config = {
        speedRate: GM_getValue('speedRate', 2.0),
        minPressTime: 200,
        selectors: {
            'www.bilibili.com': '.bilibili-player-video-control-wrap, .squirtle-controller-wrap',
            'www.youtube.com': '.ytp-chrome-bottom, .ytp-progress-bar',
            'default': '.video-controls, .progress-bar, [role="slider"]'
        }
    };

    // ================ 倍速控制功能 ================
    let pressStartTime = 0;
    let originalSpeed = 1.0;
    let isPressed = false;
    let activeVideo = null;
    let isLongPress = false;
    let preventNextClick = false;

    // 注册倍速设置菜单
    GM_registerMenuCommand('设置倍速值', () => {
        const newSpeed = prompt('请输入新的倍速值(建议范围:1.1-4):', config.speedRate);
        if (newSpeed && !isNaN(newSpeed)) {
            config.speedRate = parseFloat(newSpeed);
            GM_setValue('speedRate', config.speedRate);
        }
    });

    function findVideoElement(element) {
        if (!element) return null;

        if (element instanceof HTMLVideoElement) {
            return element;
        }

        const domain = window.location.hostname;
        const controlSelector = config.selectors[domain] || config.selectors.default;

        if (element.closest(controlSelector)) {
            return null;
        }

        if (domain === 'www.youtube.com') {
            const ytPlayer = element.closest('.html5-video-player');
            return ytPlayer ? ytPlayer.querySelector('video') : null;
        }

        const container = element.closest('*:has(video)');
        const video = container?.querySelector('video');
        return video && window.getComputedStyle(video).display !== 'none' ? video : null;
    }

    function setYouTubeSpeed(video, speed) {
        if (window.location.hostname === 'www.youtube.com') {
            const player = video.closest('.html5-video-player');
            if (player) {
                try {
                    video.playbackRate = speed;
                    const moviePlayer = player.querySelector('.video-stream');
                    if (moviePlayer) {
                        moviePlayer.playbackRate = speed;
                    }
                    video.dispatchEvent(new Event('ratechange'));
                } catch (e) {
                    console.error('设置 YouTube 播放速度失败:', e);
                }
            }
        } else {
            video.playbackRate = speed;
        }
    }

    // ================ B站字幕功能 ================
    let subtitleEnabled = false;
    let subtitleAttempts = 0;
    let subtitleRetryTimer = null;
    let manuallyDisabled = false;
    let currentVideoId = '';
    let clickHandler = null;

    function getVideoId() {
        const match = location.pathname.match(/\/video\/(.*?)\//);
        return match ? match[1] : '';
    }

    function enableSubtitle() {
        if (window.location.hostname !== 'www.bilibili.com') return;
        if (subtitleAttempts >= 5 || manuallyDisabled) return;

        subtitleAttempts++;

        const button = document.querySelector("div.bpx-player-ctrl-btn.bpx-player-ctrl-subtitle > div.bpx-player-ctrl-btn-icon > span");
        if (!button) {
            subtitleRetryTimer = setTimeout(enableSubtitle, 1000);
            return;
        }

        const parent = button.closest('.bpx-player-ctrl-subtitle');
        if (!parent) return;

        if (clickHandler) {
            parent.removeEventListener('click', clickHandler);
        }

        clickHandler = (event) => {
            requestAnimationFrame(() => {
                if (!parent.classList.contains('bpx-player-ctrl-subtitle-on')) {
                    manuallyDisabled = true;
                    console.log('用户已手动关闭字幕,本视频将不再自动开启');
                }
            });
        };

        parent.addEventListener('click', clickHandler);

        if (!parent.classList.contains('bpx-player-ctrl-subtitle-on') && !manuallyDisabled) {
            button.click();
            subtitleEnabled = true;
        }
    }

    // ================ 事件处理 ================
    function handleMouseDown(e) {
        if (e.button !== 0) return;

        const video = findVideoElement(e.target);
        if (!video) return;

        pressStartTime = Date.now();
        activeVideo = video;
        originalSpeed = window.location.hostname === 'www.youtube.com'
            ? (video.closest('.html5-video-player').querySelector('.video-stream')?.playbackRate || video.playbackRate)
            : video.playbackRate;
        isPressed = true;
        isLongPress = false;
        preventNextClick = false;
    }

    function handleMouseUp(e) {
        if (!isPressed || !activeVideo) return;

        const pressDuration = Date.now() - pressStartTime;
        if (pressDuration >= config.minPressTime) {
            preventNextClick = true;
            setYouTubeSpeed(activeVideo, originalSpeed);
        }

        isPressed = false;
        isLongPress = false;
        activeVideo = null;
    }

    function handlePressDetection() {
        if (!isPressed || !activeVideo) return;

        const pressDuration = Date.now() - pressStartTime;
        if (pressDuration >= config.minPressTime) {
            if (activeVideo.playbackRate !== config.speedRate) {
                setYouTubeSpeed(activeVideo, config.speedRate);
            }
            isLongPress = true;
        }
    }

    function handleClick(e) {
        if (preventNextClick) {
            e.stopPropagation();
            preventNextClick = false;
            return;
        }
    }

    function onVideoLoad() {
        if (window.location.hostname !== 'www.bilibili.com') return;

        const video = document.querySelector('video');
        if (!video) {
            setTimeout(onVideoLoad, 500);
            return;
        }

        function reset() {
            const newVideoId = getVideoId();
            if (newVideoId !== currentVideoId) {
                manuallyDisabled = false;
                currentVideoId = newVideoId;
                if (clickHandler) {
                    const oldButton = document.querySelector('.bpx-player-ctrl-subtitle');
                    if (oldButton) {
                        oldButton.removeEventListener('click', clickHandler);
                    }
                    clickHandler = null;
                }
            }

            subtitleEnabled = false;
            subtitleAttempts = 0;

            if (subtitleRetryTimer) {
                clearTimeout(subtitleRetryTimer);
                subtitleRetryTimer = null;
            }

            if (!manuallyDisabled) {
                setTimeout(enableSubtitle, 500);
            }
        }

        video.addEventListener('loadeddata', reset);
        video.addEventListener('play', () => {
            if (!subtitleEnabled && !manuallyDisabled) {
                reset();
            }
        });

        reset();
    }

    // ================ 初始化 ================
    function initializeEvents() {
        // 倍速控制事件
        document.addEventListener('mousedown', handleMouseDown, true);
        document.addEventListener('mouseup', handleMouseUp, true);
        document.addEventListener('click', handleClick, true);
        document.addEventListener('mouseleave', handleMouseUp, true);

        function checkPress() {
            handlePressDetection();
            requestAnimationFrame(checkPress);
        }
        checkPress();

        // B站字幕功能初始化
        if (window.location.hostname === 'www.bilibili.com') {
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', onVideoLoad);
            } else {
                onVideoLoad();
            }
        }
    }

    initializeEvents();
})();