Bilibili H5播放器快捷操作 - Código Fuente - Greasy Fork

Greasy Fork is available in English.

Instalar este script¿?

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

Pregunta tus dudas, ponle nota o notifica una posible infracción de las normas. Ajuste de línea // ==UserScript== // @name bilibili H5播放器快捷操作 // @namespace https://github.com/jeayu/bilibili-quickdo // @version 0.9.9.9 // @description 快捷键设置,回车快速发弹幕,双击全屏,自动选择最高清画质、播放、全屏、关闭弹幕、自动转跳和自动关灯等 // @author jeayu // @license MIT // @match *://www.bilibili.com/video/av* // @match *://www.bilibili.com/video/bv* // @match *://www.bilibili.com/video/BV* // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function () { 'use strict'; const q = function (selector) { let nodes = []; if (typeof selector === 'string') { Object.assign(nodes, document.querySelectorAll(selector)); nodes.selectorStr = selector; } else if (selector instanceof NodeList) { Object.assign(nodes, selector); } else if (selector instanceof Node) { nodes = [selector]; } nodes.click = function (index = 0) { nodes.length > index && nodes[index].click(); return this; } nodes.addClass = function (classes, index = 0) { nodes.length > index && nodes[index].classList.add(classes); return this; } nodes.removeClass = function (classes, index = 0) { nodes.length > index && nodes[index].classList.remove(classes); return this; } nodes.text = function (index = 0) { return nodes.length > index && nodes[index].textContent || ''; } nodes.css = function (name, value, index = 0) { nodes.length > index && nodes[index].style.setProperty(name, value); return this; } nodes.getCss = function (name, index = 0) { return nodes.length > index && nodes[index].ownerDocument.defaultView.getComputedStyle(nodes[index], null).getPropertyValue(name); } nodes.mouseover = function (index = 0) { return this.trigger('mouseover', index); } nodes.mouseout = function (index = 0) { return this.trigger('mouseout', index); } nodes.attr = function (name, index = 0) { const result = nodes.length > index ? nodes[index].attributes[name] : undefined; return result && result.value; } nodes.hasClass = function (className, index = 0) { return nodes.length > index && nodes[index].className.match && (nodes[index].className.match(new RegExp(`(\\s|^)${className}(\\s|$)`)) != null); } nodes.append = function (text, where = 'beforeend', index = 0) { nodes.length > index && nodes[index].insertAdjacentHTML(where, text); return this; } nodes.find = function (name, index = 0) { return q(nodes[index].querySelectorAll(name)); } nodes.toggleClass = function (className, flag, index = 0) { return flag ? this.addClass(className, index) : this.removeClass(className, index); } nodes.next = function (index = 0) { return nodes.length > index && nodes[index].nextElementSibling ? q(nodes[index].nextElementSibling) : []; } nodes.prev = function (index = 0) { return nodes.length > index && nodes[index].previousElementSibling ? q(nodes[index].previousElementSibling) : []; } nodes.trigger = function (event, index = 0) { if (nodes.length > index) { const evt = document.createEvent('Event'); evt.initEvent(event, true, true); nodes[index].dispatchEvent(evt); } return this; } nodes.last = function () { return q(nodes[nodes.length - 1]); } nodes.on = function (event, fn, useCapture=false, index = 0) { nodes.length > index && nodes[index].addEventListener(event, fn, useCapture); return this; } nodes.select = function (index = 0) { nodes.length > index && nodes[index].select(); return this; } nodes.blur = function (index = 0) { nodes.length > index && nodes[index].blur(); return this; } nodes.val = function (value, index = 0) { if (value) { nodes[index].value = value; return this; } return nodes[index].value; } nodes.offset = function (index = 0) { if (nodes.length <= index) { return {top: 0, left: 0}; } const rect = nodes[index].getBoundingClientRect(); return {top: rect.top + document.body.scrollTop, left: rect.left + document.body.scrollLeft} } nodes.parseFloat = function (css, index = 0) { return (parseFloat(this.getCss(css, index)) || 0); } nodes.after = function (node, index = 0) { nodes.length > index && node instanceof Node && nodes[index].parentNode.insertBefore(node, nodes[index]); return this; } return nodes; } const SELECTOR = { h5Player: "#bilibili-player video", playerContainer: ".bpx-player-container", playerControl: ".bpx-player-control-wrap", senderBarArea: ".bpx-player-sending-area", playerFull: ".bpx-player-ctrl-full", playerWeb: ".bpx-player-ctrl-web", playerWide: ".bpx-player-ctrl-wide", dmInput: "input.bpx-player-dm-input", dmInputFocus: "input.bpx-player-dm-input:focus", playerCurrentTime: ".bpx-player-ctrl-time-current", mirror: ".bpx-player-ctrl-setting-mirror input", lightOff: ".bpx-player-ctrl-setting-lightoff input", jumpContent: ".bpx-player-electric-jump", dmSwitch: ".bpx-player-dm-switch input", dmTypeScroll: ".bpx-player-block-typeScroll", dmTypeTop: ".bpx-player-block-typeTop", dmTypeBottom: ".bpx-player-block-typeBottom", dmTypeColor: ".bpx-player-block-typeColor", dmTypeSpecial: ".bpx-player-block-typeSpecial", videoQuality: ".bpx-player-ctrl-quality-menu-item", vipVideoQuality: ".bpx-player-ctrl-quality-badge-bigvip", moreDescribe: "#v_desc .toggle-btn", playerVideoArea: ".bpx-player-video-area", playerCtrlPrev: ".bpx-player-ctrl-prev", playerCtrlNext: ".bpx-player-ctrl-next", }; const [ON, OFF] = [1, 0]; const [FULLSCREEN, WEBFULLSCREEN, WIDESCREEN, DEFAULT, MINI] = ["full", "web", "wide", "normal", "mini"]; const KEY_BOARD = { keyCode: { 'enter': 13, 'esc': 27, '=': 187, '-': 189, '0': 48, '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55, '8': 56, '9': 57, 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73, 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82, 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90, 'left': 37, 'up': 38, 'right': 39, 'down': 40, 'space': 32, '[': 219, ']': 221, '\\': 220, ';': 186, "'": 222, ',': 188, '.': 190, '/': 191, '`': 192, }, defaultShortCut: { f: { text: 'f键', status: ON, bindKey: 'f' }, leftSquareBracket: { text: '[键', status: ON, bindKey: '[' }, rightSquareBracket: { text: ']键', status: ON, bindKey: ']' }, enter: { text: '回车键', status: OFF, bindKey: 'enter' }, up: { text: '↑键', status: OFF, bindKey: 'up' }, down: { text: '↓键', status: OFF, bindKey: 'down' }, right: { text: '←键', status: OFF, bindKey: 'left' }, left: { text: '→键', status: OFF, bindKey: 'right' }, space: { text: '空格键播放/暂停', status: OFF, bindKey: 'space' }, q: { text: 'q键-点赞', status: ON, bindKey: 'q' }, w: { text: 'w键-投币', status: ON, bindKey: 'w' }, e: { text: 'e键-收藏', status: ON, bindKey: 'e' }, r: { text: 'r键-长按三连', status: ON, bindKey: 'r' }, d: { text: 'd键-关闭弹幕', status: ON, bindKey: 'd' }, m: { text: 'm键-静音', status: ON, bindKey: 'm' }, }, getKeyCode(key) { return this.keyCode[key]; }, isGlobalHotKey(keyCode) { return ["left", "up", "right", "down", "space"].find(key => this.getKeyCode(key) == keyCode); }, isNumberKeyCode(keyCode) { return keyCode >= this.getKeyCode('0') && keyCode <= this.getKeyCode('9'); }, getDefaultShortCutStatus(key) { return STORAGE.getDefaultShortCutStatus(key, this.defaultShortCut[key].status); }, checkDefaultShortCut(keyCode) { return Object.entries(this.defaultShortCut) .some(([key, { text, status, bindKey }]) => this.getKeyCode(bindKey) == keyCode && this.getDefaultShortCutStatus(key)== ON); }, }; const H5_PLAYER = { variable: { speed: { text: '播放速度调整倍数', value: 0.25 }, minSpeed: { text: '最小播放速度倍数', value: 0.25 }, maxSpeed: { text: '最大播放速度倍数', value: 4 }, volume: { text: '音量调整百分比', value: 1 }, videoProgress: { text: '快进/快退调整秒数', value: 1 }, }, getVarSetting(key) { return this.variable[key].value; }, init() { this.h5Player = q(SELECTOR.h5Player); this.control = q(SELECTOR.playerControl); this.senderBar = q(SELECTOR.senderBarArea); this.played = false; console.log('H5_PLAYER done'); }, addEventListener(event, func) { this.h5Player.on(event, func); }, addPlayingEvent(func) { this.addEventListener('playing', func); }, addPauseEvent(func) { this.addEventListener('pause', func); }, addEndedEvent(func) { this.addEventListener('ended', func); }, getDuration() { return this.h5Player[0].duration; }, getCurrentTime() { return this.h5Player[0].currentTime; }, setVideoCurrentTime(time) { if (time > -1 && time <= this.h5Player[0].duration) { this.h5Player[0].currentTime = time; } }, play() { this.h5Player[0].play(); }, pause() { this.h5Player[0].pause(); }, isPaused(){ return this.h5Player[0].paused; }, playAndPause() { this.isPaused() ? this.play() : this.pause(); }, subSpeed() { this.h5Player[0].playbackRate = Math.max(this.h5Player[0].playbackRate - this.getVarSetting('speed'), this.getVarSetting('minSpeed')); }, addSpeed() { this.h5Player[0].playbackRate = Math.min(this.h5Player[0].playbackRate + this.getVarSetting('speed'), this.getVarSetting('maxSpeed')); }, resetSpeed() { this.h5Player[0].playbackRate = 1; }, getPlaybackRate() { return this.h5Player[0].playbackRate; }, getVolume() { return this.h5Player[0].volume; }, setVolume(volume) { this.h5Player[0].volume = volume; }, mute() { this.h5Player[0].muted = !this.h5Player[0].muted; }, subVolume() { this.setVolume(Math.max(this.getVolume() - (this.getVarSetting('volume') / 100), 0)); }, addVolume() { this.setVolume(Math.min(this.getVolume() + (this.getVarSetting('volume') / 100), 1)); }, subProgress() { this.h5Player[0].currentTime -= this.getVarSetting('videoProgress'); }, addProgress() { this.h5Player[0].currentTime += this.getVarSetting('videoProgress'); }, offsetTop() { return this.h5Player.offset().top; } }; const UI = { addStyle(css, id) { id = id ? `id=${id}` : ''; q('head').append(`<style ${id} type="text/css">${css}</style>`); }, removeStyle(styleId) { const styleNode = q(styleId)[0]; styleNode && styleNode.parentNode.removeChild(styleNode); }, showHint(info, hideVolumeIcon=true) { clearTimeout(this.hintTimer); q('div.bpx-player-volume-hint').length || this.initShowHint(); q('div.bpx-player-volume-hint').css('opacity', 1).css('display', ''); q('span.bpx-player-volume-hint-icon').css('display', hideVolumeIcon ? 'none' : ''); q('span.bpx-player-volume-hint-text')[0].innerHTML = info; this.hintTimer = setTimeout(() => { q('div.bpx-player-volume-hint').css('opacity', 0).css('display', 'none'); q('span.bpx-player-volume-hint-icon').css('display', ''); }, 1E3); }, initShowHint() { const html = ` <div class="bpx-player-volume-hint" style="opacity: 0; display: none;"> <span class="bpx-player-volume-hint-icon"><span class="bpx-common-svg-icon"> <svg viewBox="0 0 22 22"> <use xlink:href="#bpx-svg-sprite-volume"></use> </svg> </span> <span class="bpx-common-svg-icon" style="display: none;"> <svg viewBox="0 0 22 22"> <use xlink:href="#bpx-svg-sprite-volume-off"></use> </svg> </span> </span> <span class="bpx-player-volume-hint-text">70%</span> </div> `; q(SELECTOR.playerVideoArea).append(html); }, hideSenderBar() { this.hideSenderBarCss(); }, hideSenderBarCss() { const css = `${SELECTOR.senderBarArea}{opacity: 0!important;display: none!important}`; this.addStyle(css, 'qd-hideSenderBar'); }, showSenderBar() { H5_PLAYER.senderBar.css('opacity', 1).css('display', ''); this.removeStyle('#qd-hideSenderBar'); }, h5PlayerRotate(flag, rotationDeg=90) { const h5Player = H5_PLAYER.h5Player[0]; const deg = this.getRotationDeg(H5_PLAYER.h5Player) + rotationDeg * flag; let transform = `rotate(${deg}deg)`; if (deg == 0 || deg == 180 * flag) { transform += ` scale(1)`; } else { transform += ` scale(${h5Player.videoHeight / h5Player.videoWidth})`; } this.setH5PlayerRransform(transform); }, setH5PlayerRransform(transform) { H5_PLAYER.h5Player.css('-webkit-transform', transform) .css('-moz-transform', transform) .css('-ms-transform', transform) .css('-o-transform', transform) .css('transform', transform); }, getTransformCss(e) { return e.getCss('-webkit-transform') || e.getCss('-moz-transform') || e.getCss('-ms-transform') || e.getCss('-o-transform') || 'none'; }, getRotationDeg(e) { const transformCss = this.getTransformCss(e); let matrix = transformCss.match('matrix\\((.*)\\)'); if (matrix) { matrix = matrix[1].split(','); if (matrix) { const rad = Math.atan2(matrix[1], matrix[0]); return (rad * 180 / Math.PI) | 0; } } return 0; }, isWide() { return q(SELECTOR.playerContainer).attr('data-screen') == WIDESCREEN || q(SELECTOR.playerWide).hasClass('bpx-state-entered'); }, ultraWidescreenCss() { this.removeStyle('#qd-ultraWidescreen'); const clientWidth = document.body.clientWidth; const marginLeft = q('#bilibili-player ').offset().left; const css = ` .bpx-player-container[data-screen=wide] { width:${clientWidth}px!important;margin-left:-${marginLeft}px!important;z-index: 1000!important; } `; this.addStyle(css, 'qd-ultraWidescreen'); }, rConCss() { this.removeStyle('#qd-rCon'); if (!this.isBottomTitle || !this.isWide()) { return; } const top = H5_PLAYER.h5Player.parseFloat('height'); const css = ` .r-con{margin-top:${top}px!important} .right-container{margin-top:${top}px!important} `; this.addStyle(css, 'qd-rCon'); }, bottomTitle() { q('#viewbox_report').after(q('#playerWrap')[0]); if (!this.isBottomTitle) { const css = `#viewbox_report {height:auto!important;}`; this.addStyle(css, 'qd-bottomTitle'); this.isBottomTitle = true; } }, removeFixedHeader() { q('.bili-header').removeClass('fixed-header'); }, }; const REPEAT_CONTROLLER = { repeatStart: undefined, repeatEnd: undefined, setRepeatStart() { this.repeatStart = H5_PLAYER.getCurrentTime(); UI.showHint(`起点 ${q(SELECTOR.playerCurrentTime).text()}`); }, setRepeatEnd() { this.repeatEnd = H5_PLAYER.getCurrentTime(); UI.showHint(`终点 ${q(SELECTOR.playerCurrentTime).text()}`); }, resetRepeat() { this.repeatEnd = this.repeatStart = undefined; UI.showHint(`清除循环点`) }, check() { return this.repeatEnd && this.repeatStart && this.repeatEnd <= H5_PLAYER.getCurrentTime(); }, start() { H5_PLAYER.setVideoCurrentTime(this.repeatStart); } }; const CONTROLLER = { keydownFn: undefined, hintTimer: undefined, config: { globalHotKey: { text: '快捷键设置全局', status: OFF, tips: '上下左右空格不会滚动页面' }, }, quickDo: { settingPanel: { value: '`', text: '设置面板', }, fullscreen: { value: 'f', text: '全屏', }, webFullscreen: { value: 'w', text: '网页全屏', }, widescreen: { value: 'q', text: '宽屏', }, subSpeed: { value: '[', text: '减少速度', }, addSpeed: { value: ']', text: '增加速度', }, resetSpeed: { value: '\\', text: '重置速度', }, danmu: { value: 'd', text: '弹幕', }, playAndPause: { value: 'p', text: '暂停播放', }, prevPart: { value: 'k', text: '上一P', }, nextPart: { value: 'l', text: '下一P', }, showDanmuInput: { value: 'enter', text: '发弹幕', }, mirror: { value: 'j', text: '镜像', }, danmuTop: { value: 't', text: '顶部弹幕', }, danmuBottom: { value: 'b', text: '底部弹幕', }, danmuScroll: { value: 's', text: '滚动弹幕', }, danmuSpecial: { value: '', text: '高级弹幕', }, danmuColor: { value: 'c', text: '彩色弹幕', }, rotateRight: { value: 'o', text: '向右旋转', }, rotateLeft: { value: 'i', text: '向左旋转', }, lightOff: { value: 'y', text: '灯', }, mute: { value: 'm', text: '静音', }, scroll2Top: { value: ';', text: '回到顶部', }, jumpContent: { value: 'g', text: '跳过鸣谢', }, playerSetOnTop: { value: "'", text: '播放器置顶', }, setRepeatStart: { value: ',', text: '循环起点', }, setRepeatEnd: { value: '.', text: '循环终点', }, resetRepeat: { value: '/', text: '清除循环点', }, subVolume: { value: '', text: '减少音量', }, addVolume: { value: '', text: '增加音量', }, subProgress: { value: '', text: '快退', }, addProgress: { value: '', text: '快进', }, }, initKeyDown() { this.keydownFn = this.keydownFn || (e=> !q('input:focus, textarea:focus').length && this.keyHandler(e)); q(document).on('keydown', this.keydownFn, true); console.log('initKeyDown done'); }, keyHandler(e) { const {keyCode, ctrlKey, shiftKey, altKey} = e; if (ctrlKey || shiftKey || altKey) { return; } if (KEY_BOARD.checkDefaultShortCut(keyCode)) { e.stopPropagation(); } if (this.getControllerConfigStatus('globalHotKey') === ON) { if (KEY_BOARD.isGlobalHotKey(keyCode)) { this.focusPlayer(); e.preventDefault(); } } Object.keys(this.quickDo) .some(quickDoKey => keyCode === this.getQuickDoKeyCode(quickDoKey) && (!this[quickDoKey]() || !e.preventDefault())) || this.numberKeySkip(keyCode); e.defaultPrevented; }, bindDanmuInputKeydown() { q(SELECTOR.dmInput).on('keydown', e => { e.keyCode === KEY_BOARD.keyCode.enter && this.hideDanmuInput(); }); }, getControllerConfigStatus(key) { return STORAGE.getControllerConfigStatus(key, this.config[key].status); }, getQuickDoKeyCode(quickDoKey) { return KEY_BOARD.getKeyCode(STORAGE.getQuickDoKey(quickDoKey, this.quickDo[quickDoKey].value)); }, focusPlayer() { H5_PLAYER.control.click(); }, numberKeySkip(keyCode) { if (KEY_BOARD.isNumberKeyCode(keyCode)) { const multiple = keyCode - KEY_BOARD.getKeyCode('0'); H5_PLAYER.setVideoCurrentTime(H5_PLAYER.getDuration()/ 10 * multiple); } }, triggerSleep(el, event='click', ms=100) { return new Promise((resolve, reject) => { if (el && el[0]) { el.trigger(event); setTimeout(resolve, ms); } else { reject(); } }); }, mode(mode, check=true) { const curMode = q(SELECTOR.playerContainer).attr('data-screen'); if (check && mode == curMode) { return; } switch (mode) { case FULLSCREEN: return this.fullscreen(); case WEBFULLSCREEN: return this.webFullscreen(); case WIDESCREEN: return this.widescreen(); case MINI: return q(SELECTOR.playerContainer)[0].setAttribute('data-screen', 'mini'); case DEFAULT: return curMode == MINI ? q(SELECTOR.playerContainer)[0].setAttribute('data-screen', DEFAULT) : this.mode(curMode, false); default: return; } }, ultraWidescreen() { this.mode(WIDESCREEN); UI.ultraWidescreenCss(); }, // --------------- settingPanel() { SETTING_PANEL.switch(); }, fullscreen() { q(SELECTOR.playerFull).click(); }, webFullscreen() { q(SELECTOR.playerWeb).click(); }, widescreen() { q(SELECTOR.playerWide).click(); }, subSpeed() { H5_PLAYER.subSpeed(); UI.showHint("速度: " + H5_PLAYER.getPlaybackRate() + "x"); }, addSpeed() { H5_PLAYER.addSpeed(); UI.showHint("速度: " + H5_PLAYER.getPlaybackRate() + "x"); }, resetSpeed() { H5_PLAYER.resetSpeed(); UI.showHint("速度: " + H5_PLAYER.getPlaybackRate() + "x"); }, danmu() { q(SELECTOR.dmSwitch).click(); }, playAndPause() { H5_PLAYER.playAndPause(); }, prevPart() { q(SELECTOR.playerCtrlPrev).click() }, nextPart() { q(SELECTOR.playerCtrlNext).click(); }, showDanmuInput() { UI.showSenderBar(); const danmuInput = q(SELECTOR.dmInput); if (!q(SELECTOR.dmInputFocus).length) { this.triggerSleep(danmuInput, 'mouseover').then(() => { danmuInput.select().click(); }).catch(() => {}); } }, hideDanmuInput() { UI.hideSenderBar(); const danmuInput = q(SELECTOR.dmInput); this.triggerSleep(danmuInput, 'mouseout').then(() => { danmuInput.blur(); this.focusPlayer(); }).catch(() => {}); }, mirror() { UI.setH5PlayerRransform(''); q(SELECTOR.mirror).click(); }, danmuType(selector) { q(selector).click(); const text = q(`${selector} .bpx-player-block-filter-label`).text(); if (q(`${selector}.bpx-player-active`).length) { UI.showHint(`开启${text}`); } else { UI.showHint(`关闭${text}`); } }, danmuTop() { this.danmuType(SELECTOR.dmTypeTop); }, danmuBottom() { this.danmuType(SELECTOR.dmTypeBottom); }, danmuScroll() { this.danmuType(SELECTOR.dmTypeScroll); }, danmuColor() { this.danmuType(SELECTOR.dmTypeColor); }, danmuSpecial() { this.danmuType(SELECTOR.dmTypeSpecial); }, rotateRight() { UI.h5PlayerRotate(1); }, rotateLeft() { UI.h5PlayerRotate(-1); }, isLightOff() { return q(SELECTOR.lightOff)[0].checked; }, lightOff() { q(SELECTOR.lightOff).click(); }, light(status) { if (status == ON && this.isLightOff()) { q(SELECTOR.lightOff).click(); } else if (status == OFF && !this.isLightOff()) { q(SELECTOR.lightOff).click(); } }, mute() { H5_PLAYER.mute(); }, scroll2Top() { window.scrollTo(0, 0); }, jumpContent() { q(SELECTOR.jumpContent).click(); }, playerSetOnTop() { this.scroll2Top(); window.scrollTo(0, H5_PLAYER.offsetTop()); }, setRepeatStart() { REPEAT_CONTROLLER.setRepeatStart(); }, setRepeatEnd() { REPEAT_CONTROLLER.setRepeatEnd(); }, resetRepeat() { REPEAT_CONTROLLER.resetRepeat(); }, subVolume() { H5_PLAYER.subVolume(); UI.showHint(`${Math.ceil(H5_PLAYER.getVolume() * 100)}%`, false); }, addVolume() { H5_PLAYER.addVolume(); UI.showHint(`${Math.ceil(H5_PLAYER.getVolume() * 100)}%`, false); }, subProgress() { H5_PLAYER.subProgress(); }, addProgress() { H5_PLAYER.addProgress(); }, // --------------- }; const AUTOMATON = { playerCheckbox: { options: { hideSenderBar: { text: '隐藏弹幕栏', status: ON, fn: 'hideOrShowSenderBar', tips: '发弹幕快捷键可显示' }, widescreenScroll2Top: { text: '宽屏时回到顶部', status: OFF, ban:['widescreenPlayerSetOnTop'], fn: 'setWidescreenPos' }, widescreenPlayerSetOnTop: { text: '宽屏时播放器置顶部', status: ON, ban:['widescreenScroll2Top'], fn: 'setWidescreenPos' }, lightOffWhenPlaying: { text: '播放时自动关灯', status: OFF, }, lightOnWhenPause: { text: '暂停时自动开灯', status: OFF, }, screenWhenPause: { text: '暂停还原屏幕', status: OFF, }, ultraWidescreen: { text: '超宽屏', status: ON, fn: 'ultraWidescreen', tips: '宽屏模式宽度和窗口一样'}, bottomTitle: { text: '标题位于播放器下方', status: ON, tips: '刷新生效' }, }, btn: '播放器设置', }, startCheckbox: { options: { lightOff: { text: '自动关灯', status: OFF }, webFullscreen: { text: '自动网页全屏', status: OFF, ban:['widescreen'] }, widescreen: { text: '自动宽屏', status: ON, ban:['webFullscreen'] }, highQuality: { text: '自动最高画质', status: ON, ban:['vipHighQuality'] }, vipHighQuality: { text: '自动最高画质(大会员使用)', status: OFF, ban:['highQuality'] }, vipHighQualityNot4K: { text: '自动最高画质不选择4K', status: OFF }, moreDescribe: { text: '自动展开视频简介', status: ON }, }, btn: '播放前自动设置', }, endCheckbox: { options: { lightOn: { text: '播放结束自动开灯', status: ON, tips: '还有下一P不触发' }, exitScreen: { text: '播放结束还原屏幕', status: ON, ban:['exit2WideScreen'], tips: '还有下一P不触发' }, exit2WideScreen: { text: '播放结束还原宽屏', status: OFF, ban:['exitScreen'], tips: '还有下一P不触发' }, autoJumpContent: { text: '跳过充电鸣谢', status: ON }, }, btn: '播放结束自动设置', }, checkPlayingSetting(key) { return STORAGE.checkPlayingSetting(key, this.startCheckbox.options[key].status); }, checkPlayerSetting(key) { return STORAGE.checkPlayerSetting(key, this.playerCheckbox.options[key].status); }, checkEndedSetting(key) { return STORAGE.checkEndedSetting(key, this.endCheckbox.options[key].status); }, trigger(mutation, target) { if (target.hasClass('bpx-player-control-bottom-right')) { if (mutation.addedNodes[0].className == 'bpx-player-ctrl-btn bpx-player-ctrl-quality') { this.videoQuality(); } else if (mutation.addedNodes.length == 1 && mutation.addedNodes[0].className.indexOf('bpx-player-ctrl-full') > 0) { this.playStartMode(); } } else if (target.hasClass('bpx-player-state-buff-icon')) { UI.rConCss(); } }, attributesTrigger() { this.adjustUI(); }, adjustUI() { this.checkPlayerSetting('ultraWidescreen') && UI.ultraWidescreenCss(); if (this.checkPlayerSetting('widescreenScroll2Top') && UI.isWide()) { CONTROLLER.scroll2Top(); } else if (this.checkPlayerSetting('widescreenPlayerSetOnTop') && UI.isWide()) { CONTROLLER.playerSetOnTop(); } UI.rConCss(); }, init() { window.addEventListener('resize', () => { this.adjustUI(); }); H5_PLAYER.addEventListener('loadeddata', () => { this.checkPlayerSetting('hideSenderBar') && UI.hideSenderBar(); this.checkPlayerSetting('bottomTitle') && UI.bottomTitle(); this.checkPlayingSetting('moreDescribe') && this.moreDescribe(); this.playStartMode(); }); H5_PLAYER.addEventListener('timeupdate', () => { REPEAT_CONTROLLER.check() && REPEAT_CONTROLLER.start(); }); H5_PLAYER.addPlayingEvent(() => { if (!H5_PLAYER.played) { this.checkPlayingSetting('lightOff') && CONTROLLER.light(OFF); } this.checkPlayerSetting('lightOffWhenPlaying') && CONTROLLER.light(OFF); H5_PLAYER.played = true; }); H5_PLAYER.addPauseEvent(() => { this.checkPlayerSetting('lightOnWhenPause') && CONTROLLER.light(ON); this.checkPlayerSetting('screenWhenPause') && CONTROLLER.mode(DEFAULT); }); H5_PLAYER.addEndedEvent(() => { this.checkEndedSetting('lightOn') && CONTROLLER.light(ON); this.checkEndedSetting('autoJumpContent') && CONTROLLER.jumpContent(); if (this.checkEndedSetting('exitScreen')) { CONTROLLER.mode(DEFAULT); } else if (this.checkEndedSetting('exit2WideScreen')) { CONTROLLER.mode(WIDESCREEN); } }); }, playStartMode() { if (this.checkPlayingSetting('webFullscreen')) { CONTROLLER.mode(WEBFULLSCREEN); } else if (this.checkPlayingSetting('widescreen')) { CONTROLLER.mode(WIDESCREEN); } }, videoQuality() { if (!this.checkPlayingSetting('highQuality') && !this.checkPlayingSetting('vipHighQuality')) { return; } const btn = q(SELECTOR.videoQuality); let index = -1; if (this.checkPlayingSetting('highQuality')) { index = btn.findIndex(e => !q(e).find(SELECTOR.vipVideoQuality)[0]); } else if (this.checkPlayingSetting('vipHighQualityNot4K')) { index = btn.findIndex(e => !e.textContent.includes('4K')) } index > -1 && btn[index].click(); }, moreDescribe() { q(SELECTOR.moreDescribe).click(); } }; const STORAGE = { version: 'v1', getDefaultShortCutStatus(key, defaultValue) { return this.get('defaultShortCut', key, defaultValue); }, getQuickDoKey(quickDoKey, defaultValue) { return this.get('quickDo', quickDoKey, defaultValue); }, getControllerConfigStatus(key, defaultValue) { return this.get('controllerConfig', key, defaultValue); }, checkPlayingSetting(key, defaultValue) { return this.get('startCheckbox', key, defaultValue) === ON; }, checkPlayerSetting(key, defaultValue) { return this.get('playerCheckbox', key, defaultValue) === ON; }, checkEndedSetting(key, defaultValue) { return this.get('endCheckbox', key, defaultValue) === ON; }, gmKey(configName, key) { return `${this.version}:${configName}:${key}`; }, get(configName, key, defaultValue) { if (GM_getValue(this.gmKey(configName, key)) == undefined) { return defaultValue; } return GM_getValue(this.gmKey(configName, key)); }, save(configName, key, value) { GM_setValue(this.gmKey(configName, key), value); }, }; const SETTING_PANEL = { init() { this.newSettingPanel(); ['playerCheckbox', 'startCheckbox', 'endCheckbox'].forEach(configName => { for (let [key, { text, status, ban, fn, tips }] of Object.entries(AUTOMATON[configName].options)) { this.addCheckboxSettingItem(configName, key, text, status, ban, fn, tips); } }); for (let [key, { value, text }] of Object.entries(CONTROLLER.quickDo)) { this.addInputSettingItem('quickDo', key, value, text); } for (let [key, { text, status, tips }] of Object.entries(CONTROLLER.config)) { this.addCheckboxSettingItem('controllerConfig', key, text, status, null, null, null); } for (let [key, { text, status, bindKey }] of Object.entries(KEY_BOARD.defaultShortCut)) { this.addCheckboxSettingItem("defaultShortCut", key, `屏蔽${text}`, status, null, null, null); } }, newSettingPanel() { const id = 'qd-setting-panel'; const araeId = id + '-area' const colseId = id + '-close' const html = ` <div id='${id}' style="height: 500px;width: 800px;color: #fff;border-radius: 4px;position: absolute;text-align: center;z-index: 80;-webkit-font-smoothing: antialiased;background-color: rgba(33,33,33,.9);left: 50%;top: 50%;-webkit-transform: translate(-50%,-50%);transform: translate(-50%,-50%);"> <div style="font-size: 16px;text-align: center;line-height: 40px;border-bottom: 1px solid hsla(0,0%,100%,.1);"> bilibili H5播放器快捷操作设置 <span id='${colseId}' style="float: right;margin-right: 10px;"> X </span> </div> <div id='${araeId}' class="bpx-player-hotkey-panel-area" style="touch-action: pan-x; user-select: none; -webkit-user-drag: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0);min-height: 430px;padding-left: 35px;"> <div class="bpx-player-hotkey-panel-content" style="transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1); transition-duration: 0ms; transform: translate(0px, 0px) scale(1) translateZ(0px);"> </div> </div> </div> `; q(SELECTOR.playerVideoArea).append(html); this.settingPanel = q(`#${id}`); this.settingPanelArea = q(`#${araeId}`); q(`#${colseId}`).on('click', () => { this.close(); }) this.close(); }, addCheckboxSettingItem(configName, key, text, status, ban, fn, tips) { const checked = STORAGE.get(configName, key, status) == ON ? 'checked' : ''; const checkboxId = `${configName}-${key}-box`; let item = ` <div style="min-width: 185px;float: left;height: 24px;line-height: 24px;text-align: left;"> <span> <input id="${checkboxId}" type="checkbox" ${checked}></input> </span> <span>${text}</span> </div>` this.settingPanelArea.append(item); q(`#${checkboxId}`).on('click', function(e) { STORAGE.save(configName, key, this.checked ? ON : OFF); }); }, addInputSettingItem(configName, key, value, text) { const inputId = `${configName}-${key}-input`; const val = STORAGE.get(configName, key, value); let item = ` <div style="min-width: 185px;float: left;height: 24px;line-height: 24px;text-align: left;"> <span> <input id="${inputId}" type="input" value="${val}" style="width: 35px;height: 12px;color: black;text-align: center;"></input> </span> <span>${text}</span> </div>` this.settingPanelArea.append(item); const input = q(`#${inputId}`); input.on('keydown', e => { const isGlobalHotKey = KEY_BOARD.isGlobalHotKey(e.keyCode); const key = isGlobalHotKey || e.key.toLowerCase(); const isA2Z = e.keyCode >= 65 && e.keyCode <= 90; const isSymbol = "[]\\;',./-=".indexOf(key) > -1; if ((isGlobalHotKey || isA2Z || isSymbol || e.keyCode === KEY_BOARD.keyCode.enter) && KEY_BOARD.getKeyCode(key)) { input.val(key); } const isDelete = e.keyCode == 8 || e.keyCode == 46; (!isDelete || isGlobalHotKey) && e.preventDefault(); }).on('keyup', e => { STORAGE.save(configName, key, input.val()); }).on('click', e => { input.select(); e.preventDefault(); }); }, show() { this.settingPanel.css('display', ''); }, close() { this.settingPanel.css('display', 'none'); }, switch() { this.settingPanel.getCss('display') == 'none' ? this.show() : this.close(); } }; new MutationObserver((mutations, observer) => { mutations.forEach(mutation => { const target = q(mutation.target); if (target.hasClass('fixed-header')) { UI.removeFixedHeader(); } if (target.hasClass('header-v2')) { if (H5_PLAYER.h5Player) { H5_PLAYER.played = false; return; } try { H5_PLAYER.init(); CONTROLLER.initKeyDown(); AUTOMATON.init(); SETTING_PANEL.init(); console.log('bilibili-quickdo done'); } catch (e) { console.error('bilibili-quickdo init error:', e); } } else if (target.hasClass('bpx-player-sending-bar') && mutation.addedNodes.length) { CONTROLLER.bindDanmuInputKeydown(); } else { AUTOMATON.trigger(mutation, target); } }); }).observe(document.body, { childList: true, subtree: true, }); new MutationObserver((mutations, observer) => { AUTOMATON.attributesTrigger(); }).observe(document.body, { attributes: true, }); })();

Từ khóa » Código H5