Greasy Fork

Greasy Fork is available in English.

视频快进

视频快进!

目前为 2024-06-17 提交的版本。查看 最新版本

// ==UserScript==
// @name         视频快进
// @namespace    https://bmmmd.com/
// @version      0.1
// @description  视频快进!
// @author       bmm
// @match        https://v.qq.com/x/cover/*/*.html*
// @match        https://www.mgtv.com/b/*/*.html*
// @match        https://www.iqiyi.com/v_*.html*
// @match        https://v.youku.com/v_show/*
// @match        https://www.youtube.com/watch?v=*
// @match        https://www.bilibili.com/video/*/*
// @grant        none
// @run-at       document-idle
// @license      GPLv3
// ==/UserScript==

(function () {
    'use strict';

    /* 配置 */
    let playbackRate = 3; // 默认快进速度
    const holdKey = '.'; // 长按快进模式按钮
    const clickKey = ','; // 点击快进模式按钮
    const showTips = true; // 是否显示快进提示

    /**
     * 快进提示
     * @param text 提示文本
     */
    let tipsDIV = null;
    const tips = (text) => {
        if (!showTips) { return; }
        if (!tipsDIV) {
            tipsDIV = document.createElement("div");
            Object.assign(tipsDIV.style, {
                position: "fixed",
                top: "50%",
                left: "50%",
                transform: "translate(-50%, -50%)",
                width: "100px",
                height: "100px",
                borderRadius: "50%",
                backgroundColor: "rgba(255, 255, 255, 0.5)",
                display: "flex",
                justifyContent: "center",
                alignItems: "center",
                zIndex: "99999",
                fontSize: "30px"
            });
            document.body.appendChild(tipsDIV);
        }
        tipsDIV.innerHTML = text;
        tipsDIV.style.display = "flex";
        setTimeout(function () {
            tipsDIV.style.display = "none";
        }, 500);
    }
    /**
     * 遍历所有video标签
     * @param callback 回调函数
     */
    const iterateVideos = (callback) => {
        const videos = document.querySelectorAll('video');
        for (const index in videos) {
            if (!videos[index].getAttribute('src') || videos[index].videoHeight == 0 || videos[index].videoWidth == 0) { continue; }
            callback(videos[index]);
        }
    }
    /**
     * 设置视频播放速度
     * @param fast 快速播放 or 恢复
     */
    const changePlaybackRate = (fast = true) => {
        iterateVideos((video) => {
            if (fast) {
                video.originalPlaybackRate = video.playbackRate;
                video.playbackRate = playbackRate;
            } else {
                video.playbackRate = video.originalPlaybackRate ?? 1;
            }
            tips(`x${video.playbackRate}`);
        });
    }
    let isPressed = false; // 是否按住
    let isToggled = false; // 是否切换
    /* 按下按钮 */
    const handleKeyDown = (event) => {
        // 长按只触发一次 忽略在输入框中的按键事件
        if (event.repeat || event.target.tagName == "INPUT" || event.target.tagName == "TEXTAREA") { return; }

        // 当前按下状态 且 按下的键是数字键 则修改播放速度
        if ((isPressed || isToggled) && event.key >= 1 && event.key <= 9) {
            playbackRate = Number(event.key);
            iterateVideos((video) => {
                video.playbackRate = playbackRate;
                tips(`x${video.playbackRate}`);
            });
        } else if (event.key == holdKey) {
            isPressed = true;
            changePlaybackRate();
        } else if (event.key == clickKey) {
            isToggled = !isToggled;
            changePlaybackRate(isToggled);
        }
    };
    /* 松开按钮 */
    const handleKeyUp = (event) => {
        // 忽略在输入框中的按键事件
        if (event.target.tagName == "INPUT" || event.target.tagName == "TEXTAREA") { return; }
        if (event.key == holdKey) {
            isPressed = false;
            changePlaybackRate(false);
        }
    };

    document.addEventListener('keydown', handleKeyDown);
    document.addEventListener('keyup', handleKeyUp);
})();