修改页面视频播放速率

统一管理视频播放速率,基本适用于大部分页面,包括部分不允许修改播放速率的页面。

// ==UserScript==
// @name         修改页面视频播放速率
// @name:en      HookPlaybackRate
// @version      1.0
// @description  统一管理视频播放速率,基本适用于大部分页面,包括部分不允许修改播放速率的页面。
// @description:en Hook video playbackRate.
// @author       BackRunner
// @include      *
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @run-at       document-start
// @grant        none
// @namespace https://greasyfork.org/users/70902
// ==/UserScript==

(function() {
    'use strict';
    let playbackRate = 1;

    // hook shadow dom
    Element.prototype.attachShadow = function () {
        const div = document.createElement('div');
        this.appendChild(div);
        return div;
    };

    // hook define
    const originDefine = Object.defineProperty;
    Object.defineProperty = function(obj, property, describer) {
        if (property === 'playbackRate' && !describer.__backrunner) {
            return;
        }
        originDefine.call(this, obj, property, describer);
    }

    const hookVideos = () => {
        const videos = document.getElementsByTagName('video');
        for (let i = 0; i < videos.length; i++){
            const video = videos[i];
            if (video.__hooked) {
                continue;
            }
            delete video.playbackRate;
            video.playbackRate = playbackRate;
            Object.defineProperty(video, 'playbackRate', {
                configurable: true,
                get() { return playbackRate; },
                set() { return null },
                __backrunner: true,
            });
            Object.defineProperty(video, '__hooked', {
                configurable: true,
                writable: true,
                value: true,
            });
        };
    };

    const cleanHookedFlag = () => {
        const videos = document.getElementsByTagName('video');
        for (let i = 0; i < videos.length; i++){
            const video = videos[i];
            Object.defineProperty(video, '__hooked', {
                configurable: true,
                writable: true,
                value: false,
            });
        }
    };

    window.addEventListener('keydown', (e) => {
        if (e.ctrlKey && e.key === '.') {
            playbackRate += 0.5;
            console.log('PlaybackRate changed.', playbackRate);
        } else if (e.ctrlKey && e.key === ',') {
            playbackRate -= 0.5;
            if (playbackRate <= 0) {
                playbackRate = 0;
            }
            console.log('PlaybackRate changed.', playbackRate);
        }
        cleanHookedFlag();
    });

    setInterval(() => {
        hookVideos();
    }, 100);
})();