统一管理视频播放速率,基本适用于大部分页面,包括部分不允许修改播放速率的页面。
// ==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);
})();