// ==UserScript== // @name HTML5播放器增强插件 - 修订版 // @namespace https://github.com/xxxily/h5player // @homepage https://github.com/xxxily/h5player // @version 1.2.0 // @description 对HTML5播放器的功能进行增强(仅对支持HTML5视频的网站有效),快捷键仿照Potplayer的快捷键布局,实现调节亮度,饱和度,对比度,速度等功能。 // @author ankvps // @match http://*/* // @match https://*/* // @run-at document-end // @grant none // @downloadURL none // ==/UserScript== (function () { let h5Player = { /* 提示文本的字号 */ fontSize: 20, player: function () { return document.querySelector('video') }, scale: 1, globalMode: false, tips: function (str) { let t = h5Player let tipsDom = document.querySelector('#html_player_enhance_tips') /* 出现异常的时候参数再初始化一次tips节点 */ if (!tipsDom) { console.log('h5player init error...') t.settips() tipsDom = document.querySelector('#html_player_enhance_tips') if (!tipsDom) { return false } } let style = tipsDom.style tipsDom.innerText = str for (var i = 0; i < 3; i++) { if (this.on_off[i]) clearTimeout(this.on_off[i]) } style.display = 'block' this.on_off[0] = setTimeout(function () { style.opacity = 1 }, 50) this.on_off[1] = setTimeout(function () { style.opacity = 0 }, 2000) this.on_off[2] = setTimeout(function () { style.display = 'none' }, 2800) }, on_off: new Array(3), rotate: 0, fps: 30, /* 滤镜效果 */ filter: { key: new Array(5), setup: function () { var view = 'brightness({0}) contrast({1}) saturate({2}) hue-rotate({3}deg) blur({4}px)' for (var i = 0; i < 5; i++) { view = view.replace('{' + i + '}', String(this.key[i])) this.key[i] = Number(this.key[i]) } h5Player.player().style.WebkitFilter = view }, reset: function () { this.key[0] = 1 this.key[1] = 1 this.key[2] = 1 this.key[3] = 0 this.key[4] = 0 this.setup() } }, /* 设置提示DOM的样式 */ settips: function () { var tips = document.createElement('div') this.player().parentNode.appendChild(tips) tips.setAttribute('id', 'html_player_enhance_tips') tips.setAttribute('style', "position: absolute;z-index: 999999;padding: 10px;background: rgba(0,0,0,0.8);color:white;top: 50%;left: 50%;transform: translate(-50%,-50%);transition: all 500ms ease;opacity: 0;display: none; -webkit-font-smoothing: subpixel-antialiased;font-family: 'microsoft yahei', Verdana, Geneva, sans-serif;-webkit-user-select: none;") if (this.fontSize !== 0) { tips.style.fontSize = this.fontSize + 'px' } // 修复油管的兼容性问题 if (window.location.hostname === 'www.youtube.com') { this.player().parentNode.style.height = '100%' } }, _isFoucs: false, isFoucs: function () { let t = h5Player let player = t.player() if (!player) return player.onmouseover = function () { if (!t.globalMode) { h5Player._isFoucs = true } } player.onmouseout = function () { if (!t.globalMode) { h5Player._isFoucs = false } } }, /* 按键响应方法 */ button: function (event) { let t = h5Player let keyCode = event.keyCode let player = t.player() if (!player) { t.tips('video元素获取失败~') t.init() return } // 按shift+Enter 键进入聚焦或取消聚焦状态,用于视频标签被遮挡的场景 if (event.ctrlKey && (keyCode === 13 || keyCode === 220)) { t._isFoucs = !t._isFoucs if (t._isFoucs) { t.globalMode = true t.tips('全局模式') } else { t.globalMode = false t.tips('禁用全局模式') } } /* 未聚焦,不能进行任何快捷键的操作 */ if (!t._isFoucs) return if (event.shiftKey) { let isScaleKeyCode = keyCode === 88 || keyCode === 67 || keyCode === 90 if (!isScaleKeyCode) return // shift+X:视频缩小 -0.1 if (keyCode === 88) { t.scale -= 0.1 } // shift+C:视频放大 +0.1 if (keyCode === 67) { t.scale = Number(t.scale) t.scale += 0.1 } // shift+Z:视频恢复正常大小 if (keyCode === 90) { t.scale = 1 } let scale = t.scale = t.scale.toFixed(1) player.style.transform = 'scale(' + scale + ')' t.tips('视频缩放率:' + scale) return true } // 防止其它组合键冲突 if (event.altKey || event.ctrlKey || event.shiftKey) return event.stopPropagation() event.preventDefault() // 方向键右→:快进3秒 if (keyCode === 39) { if (window.location.hostname === 'www.netflix.com') { return } player.currentTime += 3 t.tips('快进:3秒') } // 方向键左←:后退3秒 if (keyCode === 37) { if (window.location.hostname === 'www.netflix.com') { return } player.currentTime -= 3 t.tips('后退:3秒') } // 方向键上↑:音量升高 1% if (keyCode === 38) { if (player.volume < 1) { player.volume += 0.01 } player.volume = player.volume.toFixed(2) t.tips('音量:' + parseInt(player.volume * 100) + '%') } // 方向键下↓:音量降低 1% if (keyCode === 40) { if (player.volume > 0) { player.volume -= 0.01 } player.volume = player.volume.toFixed(2) t.tips('音量:' + parseInt(player.volume * 100) + '%') } // 空格键:暂停/播放 if (keyCode === 32) { // 已支持空格暂停的不在注册改键 var hostname = window.location.hostname || window.location.host if (/youtube/i.test(hostname)) { return } let curStatus = player.paused setTimeout(function () { if (curStatus !== player.paused) { // 默认快捷键已经处理过了,就不再重复处理 return } if (curStatus) { player.play() t.tips('播放') } else { player.pause() t.tips('暂停') } }, 150) } // 按键X:减速播放 -0.1 if (keyCode === 88) { if (player.playbackRate > 0) { player.playbackRate -= 0.1 player.playbackRate = player.playbackRate.toFixed(1) t.tips('播放速度:' + player.playbackRate + '倍') } } // 按键C:加速播放 +0.1 if (keyCode === 67) { if (player.playbackRate < 16) { player.playbackRate += 0.1 player.playbackRate = player.playbackRate.toFixed(1) t.tips('播放速度:' + player.playbackRate + '倍') } } // 按键Z:正常速度播放 if (keyCode === 90) { player.playbackRate = 1 t.tips('播放速度:1倍') } // 按1-4设置播放速度 49-52;97-100 if ((keyCode >= 49 && keyCode <= 52) || (keyCode >= 97 && keyCode <= 100)) { player.playbackRate = Number(event.key) t.tips('播放速度:' + event.key + '倍') return false } // 按键F:下一帧 if (keyCode === 70) { if (window.location.hostname === 'www.netflix.com') { /* netflix 的F键是全屏的意思 */ return } if (!player.paused) player.pause() player.currentTime += Number(1 / t.fps) t.tips('定位:下一帧') } // 按键D:上一帧 if (keyCode === 68) { if (!player.paused) player.pause() player.currentTime -= Number(1 / t.fps) t.tips('定位:上一帧') } // 按键E:亮度增加% if (keyCode === 69) { if (t.filter.key[0] > 1) { t.filter.key[0] += 1 } else { t.filter.key[0] += 0.1 } t.filter.key[0] = t.filter.key[0].toFixed(2) t.filter.setup() t.tips('图像亮度增加:' + parseInt(t.filter.key[0] * 100) + '%') } // 按键W:亮度减少% if (keyCode === 87) { if (t.filter.key[0] > 0) { if (t.filter.key[0] > 1) { t.filter.key[0] -= 1 } else { t.filter.key[0] -= 0.1 } t.filter.key[0] = t.filter.key[0].toFixed(2) t.filter.setup() } t.tips('图像亮度减少:' + parseInt(t.filter.key[0] * 100) + '%') } // 按键T:对比度增加% if (keyCode === 84) { if (t.filter.key[1] > 1) { t.filter.key[1] += 1 } else { t.filter.key[1] += 0.1 } t.filter.key[1] = t.filter.key[1].toFixed(2) t.filter.setup() t.tips('图像对比度增加:' + parseInt(t.filter.key[1] * 100) + '%') } // 按键R:对比度减少% if (keyCode === 82) { if (t.filter.key[1] > 0) { if (t.filter.key[1] > 1) { t.filter.key[1] -= 1 } else { t.filter.key[1] -= 0.1 } t.filter.key[1] = t.filter.key[1].toFixed(2) t.filter.setup() } t.tips('图像对比度减少:' + parseInt(t.filter.key[1] * 100) + '%') } // 按键U:饱和度增加% if (keyCode === 85) { if (t.filter.key[2] > 1) { t.filter.key[2] += 1 } else { t.filter.key[2] += 0.1 } t.filter.key[2] = t.filter.key[2].toFixed(2) t.filter.setup() t.tips('图像饱和度增加:' + parseInt(t.filter.key[2] * 100) + '%') } // 按键Y:饱和度减少% if (keyCode === 89) { if (t.filter.key[2] > 0) { if (t.filter.key[2] > 1) { t.filter.key[2] -= 1 } else { t.filter.key[2] -= 0.1 } t.filter.key[2] = t.filter.key[2].toFixed(2) t.filter.setup() } t.tips('图像饱和度减少:' + parseInt(t.filter.key[2] * 100) + '%') } // 按键O:色相增加 1 度 if (keyCode === 79) { t.filter.key[3] += 1 t.filter.setup() t.tips('图像色相增加:' + t.filter.key[3] + '度') } // 按键I:色相减少 1 度 if (keyCode === 73) { t.filter.key[3] -= 1 t.filter.setup() t.tips('图像色相减少:' + t.filter.key[3] + '度') } // 按键K:模糊增加 1 px if (keyCode === 75) { t.filter.key[4] += 1 t.filter.setup() t.tips('图像模糊增加:' + t.filter.key[4] + 'PX') } // 按键J:模糊减少 1 px if (keyCode === 74) { if (t.filter.key[4] > 0) { t.filter.key[4] -= 1 t.filter.setup() } t.tips('图像模糊减少:' + t.filter.key[4] + 'PX') } // 按键Q:图像复位 if (keyCode === 81) { t.filter.reset() t.tips('图像属性:复位') } // 按键S:画面旋转 90 度 if (keyCode === 83) { t.rotate += 90 if (t.rotate % 360 === 0) t.rotate = 0 player.style.transform = 'rotate(' + t.rotate + 'deg)' t.tips('画面旋转:' + t.rotate + '度') } // 按键回车,进入全屏,支持仅部分网站(B站,油管) if (keyCode === 13) { if (window.location.hostname === 'www.bilibili.com') { if (document.querySelector('[data-text="进入全屏"]')) { document.querySelector('[data-text="进入全屏"]').click() } } if (window.location.hostname === 'www.youtube.com') { if (document.querySelector('[class="ytp-fullscreen-button ytp-button"]')) { document.querySelector('[class="ytp-fullscreen-button ytp-button"]').click() } } } }, /** * 检测h5播放器是否存在 * @param callback */ detecH5Player: function (callback) { var t = this var player = document.querySelector('video') if (player) { callback && callback(player) } else { // 轮询检测 setTimeout(function () { t.detecH5Player(callback) }, 1000) } }, init: function () { var t = this if (document.querySelectorAll('#html_player_enhance_tips').length > 1) { document.querySelector('#html_player_enhance_tips').parentNode.removeChild(document.querySelectorAll('#html_player_enhance_tips')[1]) } t.detecH5Player(function (player) { if (document.querySelectorAll('#html_player_enhance_tips').length === 0) { if (!this.load) { var t = h5Player this.load = true console.log('检测到HTML5视频!') t.load = false t.filter.reset() t.settips() t.isFoucs() document.onkeydown = t.button } } }) }, load: false } /* 进行初始化 */ h5Player.init() document.addEventListener('DOMNodeInserted', function () { h5Player.init() }) })()