mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Refactor keymap
This commit is contained in:
parent
333bb1b47f
commit
0887384456
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -25,6 +25,7 @@
|
|||||||
"Swiper",
|
"Swiper",
|
||||||
"Violentmonkey",
|
"Violentmonkey",
|
||||||
"Vuex",
|
"Vuex",
|
||||||
|
"WASD",
|
||||||
"Watchlater",
|
"Watchlater",
|
||||||
"afterbegin",
|
"afterbegin",
|
||||||
"afterend",
|
"afterend",
|
||||||
|
|||||||
2
min/custom-navbar.min.js
vendored
2
min/custom-navbar.min.js
vendored
File diff suppressed because one or more lines are too long
1
min/key-bindings.min.js
vendored
Normal file
1
min/key-bindings.min.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(()=>(e,t)=>{const n=["shift","alt","ctrl","meta"];const i=_.once((e=>{const t=document.URL.startsWith("https://www.bilibili.com/watchlater/");const i=document.URL.startsWith("https://www.bilibili.com/medialist/play/");const o={enable:true};document.body.addEventListener("keydown",(s=>{if(!o.enable){return}if(t&&document.URL.endsWith("list")){return}if(document.activeElement&&isTyping()){return}const r=s.key.toLowerCase();const l=dq(".bilibili-player-sphere-control");if(l!==null&&l.style.display!=="none"&&["w","a","s","d"].includes(r)){return}e.forEach((e=>{var o;const r=n.some((t=>{const n=e.keys.includes(t);const i=s[t+"Key"];return n!==i}));if(r){return}const l=e.keys.filter((e=>!n.includes(e.toLowerCase()))).map((e=>e.toLowerCase()));const c=l.includes(s.key.toLowerCase())||l.includes(s.code.toLowerCase());if(!c){return}if((o=e===null||e===void 0?void 0:e.prevent)!==null&&o!==void 0?o:true){s.stopPropagation();s.preventDefault()}e.action({binding:e,isWatchlater:t,isMediaList:i,event:s})}))}));return o}));return{export:{loadKeyBindings:i}}})();
|
||||||
2
min/keymap.min.js
vendored
2
min/keymap.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Bilibili Evolved (Preview)
|
// @name Bilibili Evolved (Preview)
|
||||||
// @version 1.11.8
|
// @version 1.11.9
|
||||||
// @description Bilibili Evolved 的预览版, 可以抢先体验新功能.
|
// @description Bilibili Evolved 的预览版, 可以抢先体验新功能.
|
||||||
// @author Grant Howard, Coulomb-G
|
// @author Grant Howard, Coulomb-G
|
||||||
// @copyright 2020, Grant Howard (https://github.com/the1812) & Coulomb-G (https://github.com/Coulomb-G)
|
// @copyright 2020, Grant Howard (https://github.com/the1812) & Coulomb-G (https://github.com/Coulomb-G)
|
||||||
|
|||||||
@ -250,6 +250,7 @@ export const settings = {
|
|||||||
rememberVideoSpeedList: {},
|
rememberVideoSpeedList: {},
|
||||||
rememberVideoSpeed: false,
|
rememberVideoSpeed: false,
|
||||||
extendVideoSpeed: true,
|
extendVideoSpeed: true,
|
||||||
|
customKeyBindings: {},
|
||||||
cache: {},
|
cache: {},
|
||||||
}
|
}
|
||||||
const fixedSettings = {
|
const fixedSettings = {
|
||||||
|
|||||||
1
src/global.d.ts
vendored
1
src/global.d.ts
vendored
@ -525,6 +525,7 @@ declare global {
|
|||||||
},
|
},
|
||||||
rememberVideoSpeed: boolean,
|
rememberVideoSpeed: boolean,
|
||||||
extendVideoSpeed: boolean,
|
extendVideoSpeed: boolean,
|
||||||
|
customKeyBindings: { [actionName: string]: string }
|
||||||
}
|
}
|
||||||
const GM_info: MonkeyInfo
|
const GM_info: MonkeyInfo
|
||||||
function GM_xmlhttpRequest(details: MonkeyXhrDetails): { abort: () => void }
|
function GM_xmlhttpRequest(details: MonkeyXhrDetails): { abort: () => void }
|
||||||
|
|||||||
@ -1,232 +0,0 @@
|
|||||||
const supportedUrls = [
|
|
||||||
'https://www.bilibili.com/bangumi/',
|
|
||||||
'https://www.bilibili.com/video/',
|
|
||||||
'https://www.bilibili.com/watchlater/',
|
|
||||||
'https://www.bilibili.com/medialist/play/',
|
|
||||||
]
|
|
||||||
let enabled = true;
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const { VideoSpeedController } = await import("../video/default-video-speed")
|
|
||||||
|
|
||||||
if (supportedUrls.some(url => document.URL.startsWith(url))) {
|
|
||||||
const clickableKeymap = {
|
|
||||||
f: '.bilibili-player-video-fullscreen', // 全屏
|
|
||||||
w: '.bilibili-player-video-web-fullscreen', // 网页全屏
|
|
||||||
t: '.bilibili-player-video-btn-widescreen', // 宽屏
|
|
||||||
// r: '.bilibili-player-video-btn-repeat', // 切换循环模式
|
|
||||||
m: '.bilibili-player-video-btn-volume .bilibili-player-iconfont-volume', // 切换静音
|
|
||||||
p: '.bilibili-player-video-btn-pip', // 画中画
|
|
||||||
// l: '.video-toolbar .like', // 点赞
|
|
||||||
c: '.video-toolbar .coin,.tool-bar .coin-info, .video-toolbar-module .coin-box, .play-options-ul > li:nth-child(2)', // 投币
|
|
||||||
s: '.video-toolbar .collect, .video-toolbar-module .fav-box, .play-options-ul > li:nth-child(3)', // 收藏
|
|
||||||
' ': '.bilibili-player-video-btn-start', // 砸瓦撸多
|
|
||||||
} as { [key: string]: string }
|
|
||||||
/** 长按`L`三连使用的记忆变量 */
|
|
||||||
let likeClick = true
|
|
||||||
/** 在稍后再看页面里, 记录当前视频是否赞过 */
|
|
||||||
let liked = false
|
|
||||||
const isWatchlater = document.URL.startsWith('https://www.bilibili.com/watchlater/')
|
|
||||||
const isMediaList = document.URL.startsWith('https://www.bilibili.com/medialist/play/')
|
|
||||||
if (isWatchlater) {
|
|
||||||
Observer.videoChange(() => {
|
|
||||||
Ajax.getJsonWithCredentials(`https://api.bilibili.com/x/web-interface/archive/has/like?aid=${unsafeWindow.aid}`).then(json => {
|
|
||||||
liked = Boolean(json.data)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/** 播放速度提示框用的`setTimeout`句柄 */
|
|
||||||
let showPlaybackTipOldTimeout: number
|
|
||||||
/**
|
|
||||||
* 显示播放速度提示框
|
|
||||||
* @param speed 播放速度
|
|
||||||
*/
|
|
||||||
const showPlaybackTip = (speed: number) => {
|
|
||||||
let tip = dq('.keymap-playback-tip') as HTMLDivElement
|
|
||||||
if (!tip) {
|
|
||||||
const player = dq('.bilibili-player-video-wrap')
|
|
||||||
if (!player) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
player.insertAdjacentHTML('afterbegin', /*html*/`
|
|
||||||
<div class="keymap-playback-tip-container">
|
|
||||||
<i class="mdi mdi-fast-forward"></i>
|
|
||||||
<div class="keymap-playback-tip"></div>x
|
|
||||||
</div>
|
|
||||||
`)
|
|
||||||
resources.applyStyleFromText(`
|
|
||||||
.keymap-playback-tip-container {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
padding: 8px 16px;
|
|
||||||
background-color: #000A;
|
|
||||||
color: white;
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0;
|
|
||||||
z-index: 100;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 14pt;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: .2s ease-out;
|
|
||||||
}
|
|
||||||
.keymap-playback-tip-container.show {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.keymap-playback-tip-container i {
|
|
||||||
line-height: 1;
|
|
||||||
margin-right: 8px;
|
|
||||||
font-size: 18pt;
|
|
||||||
}
|
|
||||||
`, 'keymapStyle')
|
|
||||||
tip = dq('.keymap-playback-tip') as HTMLDivElement
|
|
||||||
}
|
|
||||||
tip.innerHTML = speed.toString()
|
|
||||||
if (showPlaybackTipOldTimeout) {
|
|
||||||
clearTimeout(showPlaybackTipOldTimeout)
|
|
||||||
}
|
|
||||||
(dq('.keymap-playback-tip-container') as HTMLDivElement).classList.add('show')
|
|
||||||
showPlaybackTipOldTimeout = window.setTimeout(() => {
|
|
||||||
(dq('.keymap-playback-tip-container') as HTMLDivElement).classList.remove('show')
|
|
||||||
}, 2000)
|
|
||||||
}
|
|
||||||
document.body.addEventListener('keydown', async e => {
|
|
||||||
if (!enabled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (isWatchlater && document.URL.endsWith('list')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (document.activeElement && isTyping()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const key = e.key.toLowerCase()
|
|
||||||
const panoramaControl = dq('.bilibili-player-sphere-control') as HTMLElement
|
|
||||||
if (panoramaControl !== null && panoramaControl.style.display !== 'none' && ['w', 'a', 's', 'd'].includes(key)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const noModifyKeys = !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey
|
|
||||||
if (key in clickableKeymap && noModifyKeys) {
|
|
||||||
const element = dq(clickableKeymap[key]) as HTMLElement
|
|
||||||
if (!element) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
e.stopPropagation()
|
|
||||||
e.preventDefault()
|
|
||||||
element.click()
|
|
||||||
} else if (key === 'd' && noModifyKeys) { // 切换弹幕开关
|
|
||||||
const checkbox = dq('.bilibili-player-video-danmaku-switch input') as HTMLInputElement
|
|
||||||
if (!checkbox) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
e.stopPropagation()
|
|
||||||
e.preventDefault()
|
|
||||||
checkbox.checked = !checkbox.checked
|
|
||||||
raiseEvent(checkbox, 'change')
|
|
||||||
} else if (key === 'j' && noModifyKeys) { // 绯红之王
|
|
||||||
const video = dq('.bilibili-player-video video') as HTMLVideoElement
|
|
||||||
video.currentTime += settings.keymapJumpSeconds
|
|
||||||
e.stopPropagation()
|
|
||||||
e.preventDefault()
|
|
||||||
} else if (key === 'l' && noModifyKeys) {
|
|
||||||
if (isWatchlater) {
|
|
||||||
const formData = {
|
|
||||||
aid: unsafeWindow.aid,
|
|
||||||
/** `1`点赞; `2`取消赞 */
|
|
||||||
like: liked ? 2 : 1,
|
|
||||||
csrf: getCsrf(),
|
|
||||||
}
|
|
||||||
Ajax.postTextWithCredentials(`https://api.bilibili.com/x/web-interface/archive/like`, Object.entries(formData).map(([k, v]) => `${k}=${v}`).join('&')).then(() => {
|
|
||||||
liked = !liked
|
|
||||||
if (liked) {
|
|
||||||
Toast.success(`已点赞`, `快捷键扩展`, 1000)
|
|
||||||
} else {
|
|
||||||
Toast.success(`已取消点赞`, `快捷键扩展`, 1000)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (isMediaList) {
|
|
||||||
const likeButton = dq('.play-options-ul > li:first-child') as HTMLLIElement
|
|
||||||
if (likeButton) {
|
|
||||||
likeButton.click()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const likeButton = dq('.video-toolbar .like') as HTMLSpanElement
|
|
||||||
e.preventDefault()
|
|
||||||
const fireEvent = (name: string, args: Event) => {
|
|
||||||
const event = new CustomEvent(name, args)
|
|
||||||
likeButton.dispatchEvent(event)
|
|
||||||
}
|
|
||||||
likeClick = true
|
|
||||||
setTimeout(() => likeClick = false, 200)
|
|
||||||
fireEvent('mousedown', e)
|
|
||||||
document.body.addEventListener('keyup', e => {
|
|
||||||
e.preventDefault()
|
|
||||||
fireEvent('mouseup', e)
|
|
||||||
if (likeClick) {
|
|
||||||
fireEvent('click', e)
|
|
||||||
}
|
|
||||||
}, { once: true })
|
|
||||||
}
|
|
||||||
} else if (key === '`' && noModifyKeys) {
|
|
||||||
// menu size: 386.6 x 311 (2020-03-29)
|
|
||||||
// menu size: 176.65 x 194 (2020-06-09)
|
|
||||||
const player = dq('.bilibili-player-video-wrap') as HTMLElement
|
|
||||||
const rect = player.getBoundingClientRect()
|
|
||||||
player.dispatchEvent(new MouseEvent('contextmenu', {
|
|
||||||
bubbles: true,
|
|
||||||
cancelable: false,
|
|
||||||
view: unsafeWindow,
|
|
||||||
button: 2,
|
|
||||||
buttons: 0,
|
|
||||||
clientX: rect.x + rect.width / 2 - 176.65 / 2,
|
|
||||||
clientY: rect.y + rect.height / 2 - 194 / 2
|
|
||||||
}))
|
|
||||||
} else if (e.shiftKey) {
|
|
||||||
const containerElement = await SpinQuery.select(`.${VideoSpeedController.classNameMap.speedContainer}`) as HTMLElement
|
|
||||||
const videoElement = await SpinQuery.select(`.${VideoSpeedController.classNameMap.video} video`) as HTMLVideoElement
|
|
||||||
|
|
||||||
const controller = new VideoSpeedController(containerElement, videoElement, 1)
|
|
||||||
|
|
||||||
const playbackRates = VideoSpeedController.supportedRates
|
|
||||||
|
|
||||||
let preventDefault = true
|
|
||||||
if (key === '>' || key === 'ArrowUp'.toLowerCase()) { // 天堂制造
|
|
||||||
controller.setVideoSpeed(playbackRates.find(it => it > controller.playbackRate) || playbackRates[playbackRates.length - 1])
|
|
||||||
showPlaybackTip(controller.playbackRate)
|
|
||||||
} else if (key === '<' || key === 'ArrowDown'.toLowerCase()) { // 时间减速
|
|
||||||
controller.setVideoSpeed([...playbackRates].reverse().find(it => it < controller.playbackRate) || playbackRates[0])
|
|
||||||
showPlaybackTip(controller.playbackRate)
|
|
||||||
} else if (key === '?') { // 重置速度
|
|
||||||
controller.reset()
|
|
||||||
showPlaybackTip(controller.playbackRate)
|
|
||||||
} else if (key === 'w') { // 稍后再看
|
|
||||||
const watchlater = dq('.video-toolbar .ops .watchlater, .more-ops-list .ops-watch-later, .video-toolbar-module .see-later-box') as HTMLElement
|
|
||||||
if (watchlater !== null) {
|
|
||||||
watchlater.click()
|
|
||||||
}
|
|
||||||
} else if (key === 'j') { // 败者食尘
|
|
||||||
videoElement.currentTime -= settings.keymapJumpSeconds
|
|
||||||
} else if (key === 's') { // 快速收藏
|
|
||||||
const quickFavorite = dq('.quick-favorite') as HTMLElement
|
|
||||||
if (quickFavorite) {
|
|
||||||
quickFavorite.click()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
preventDefault = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preventDefault) {
|
|
||||||
e.stopPropagation()
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
export default {
|
|
||||||
reload: () => enabled = true,
|
|
||||||
unload: () => enabled = false,
|
|
||||||
}
|
|
||||||
72
src/video/keymap/key-bindings.ts
Normal file
72
src/video/keymap/key-bindings.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
export type KeyBindingAction = (context: {
|
||||||
|
binding: KeyBinding
|
||||||
|
event: KeyboardEvent
|
||||||
|
isWatchlater: boolean
|
||||||
|
isMediaList: boolean
|
||||||
|
}) => void
|
||||||
|
export interface KeyBinding {
|
||||||
|
keys: string[]
|
||||||
|
action: KeyBindingAction
|
||||||
|
prevent?: boolean
|
||||||
|
}
|
||||||
|
const modifyKeys = [
|
||||||
|
'shift',
|
||||||
|
'alt',
|
||||||
|
'ctrl',
|
||||||
|
'meta',
|
||||||
|
]
|
||||||
|
export const loadKeyBindings = _.once((bindings: KeyBinding[]) => {
|
||||||
|
const isWatchlater = document.URL.startsWith('https://www.bilibili.com/watchlater/')
|
||||||
|
const isMediaList = document.URL.startsWith('https://www.bilibili.com/medialist/play/')
|
||||||
|
const config = {
|
||||||
|
enable: true
|
||||||
|
}
|
||||||
|
document.body.addEventListener('keydown', (e: KeyboardEvent & { [key: string]: boolean }) => {
|
||||||
|
if (!config.enable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 稍后再看页面无视快捷键
|
||||||
|
if (isWatchlater && document.URL.endsWith('list')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 打字时无视快捷键
|
||||||
|
if (document.activeElement && isTyping()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const key = e.key.toLowerCase()
|
||||||
|
|
||||||
|
// 全景视频禁用 WASD 无视快捷键
|
||||||
|
const panoramaControl = dq('.bilibili-player-sphere-control') as HTMLElement
|
||||||
|
if (panoramaControl !== null && panoramaControl.style.display !== 'none' && ['w', 'a', 's', 'd'].includes(key)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bindings.forEach(binding => {
|
||||||
|
const modifyKeyNotMatch = modifyKeys.some(m => {
|
||||||
|
const needModifyKey = binding.keys.includes(m)
|
||||||
|
const isModifyKeyPressed = e[m + 'Key']
|
||||||
|
return needModifyKey !== isModifyKeyPressed
|
||||||
|
})
|
||||||
|
if (modifyKeyNotMatch) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const restKeys = binding.keys.filter(k => !modifyKeys.includes(k.toLowerCase())).map(k => k.toLowerCase())
|
||||||
|
const keyMatch = restKeys.includes(e.key.toLowerCase()) || restKeys.includes(e.code.toLowerCase())
|
||||||
|
if (!keyMatch) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binding?.prevent ?? true) {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
binding.action({
|
||||||
|
binding,
|
||||||
|
isWatchlater,
|
||||||
|
isMediaList,
|
||||||
|
event: e,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return config
|
||||||
|
})
|
||||||
245
src/video/keymap/keymap.ts
Normal file
245
src/video/keymap/keymap.ts
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
import { VideoSpeedController } from '../default-video-speed'
|
||||||
|
import { KeyBinding, KeyBindingAction } from './key-bindings'
|
||||||
|
|
||||||
|
const supportedUrls = [
|
||||||
|
'https://www.bilibili.com/bangumi/',
|
||||||
|
'https://www.bilibili.com/video/',
|
||||||
|
'https://www.bilibili.com/watchlater/',
|
||||||
|
'https://www.bilibili.com/medialist/play/',
|
||||||
|
]
|
||||||
|
|
||||||
|
let config: { enable: boolean }
|
||||||
|
if (supportedUrls.some(url => document.URL.startsWith(url))) {
|
||||||
|
const clickElement = (target: string | HTMLElement) => {
|
||||||
|
return () => {
|
||||||
|
if (typeof target === 'string') {
|
||||||
|
(dq(target) as HTMLElement)?.click()
|
||||||
|
} else {
|
||||||
|
target.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** 播放速度提示框用的`setTimeout`句柄 */
|
||||||
|
let showPlaybackTipOldTimeout: number
|
||||||
|
/**
|
||||||
|
* 显示播放速度提示框
|
||||||
|
* @param speed 播放速度
|
||||||
|
*/
|
||||||
|
const showPlaybackTip = (speed: number) => {
|
||||||
|
let tip = dq('.keymap-playback-tip') as HTMLDivElement
|
||||||
|
if (!tip) {
|
||||||
|
const player = dq('.bilibili-player-video-wrap')
|
||||||
|
if (!player) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
player.insertAdjacentHTML('afterbegin', /*html*/`
|
||||||
|
<div class="keymap-playback-tip-container">
|
||||||
|
<i class="mdi mdi-fast-forward"></i>
|
||||||
|
<div class="keymap-playback-tip"></div>x
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
resources.applyStyleFromText(`
|
||||||
|
.keymap-playback-tip-container {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: #000A;
|
||||||
|
color: white;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14pt;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: .2s ease-out;
|
||||||
|
}
|
||||||
|
.keymap-playback-tip-container.show {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.keymap-playback-tip-container i {
|
||||||
|
line-height: 1;
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 18pt;
|
||||||
|
}
|
||||||
|
`, 'keymapStyle')
|
||||||
|
tip = dq('.keymap-playback-tip') as HTMLDivElement
|
||||||
|
}
|
||||||
|
tip.innerHTML = speed.toString()
|
||||||
|
if (showPlaybackTipOldTimeout) {
|
||||||
|
clearTimeout(showPlaybackTipOldTimeout)
|
||||||
|
}
|
||||||
|
(dq('.keymap-playback-tip-container') as HTMLDivElement).classList.add('show')
|
||||||
|
showPlaybackTipOldTimeout = window.setTimeout(() => {
|
||||||
|
(dq('.keymap-playback-tip-container') as HTMLDivElement).classList.remove('show')
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
const videoSpeed = (controllerAction: (controller: VideoSpeedController, rates: number[]) => void) => {
|
||||||
|
return async () => {
|
||||||
|
const attr = await import('../default-video-speed')
|
||||||
|
console.log(attr)
|
||||||
|
const { VideoSpeedController } = await import('../default-video-speed')
|
||||||
|
const containerElement = dq(`.${VideoSpeedController.classNameMap.speedContainer}`) as HTMLElement
|
||||||
|
const videoElement = dq(`.${VideoSpeedController.classNameMap.video} video`) as HTMLVideoElement
|
||||||
|
if (!containerElement || !videoElement) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const controller = new VideoSpeedController(containerElement, videoElement, 1)
|
||||||
|
controllerAction(controller, VideoSpeedController.supportedRates)
|
||||||
|
showPlaybackTip(controller.playbackRate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const actions = {
|
||||||
|
fullscreen: clickElement('.bilibili-player-video-btn-fullscreen'),
|
||||||
|
webFullscreen: clickElement('.bilibili-player-video-web-fullscreen'),
|
||||||
|
wideScreen: clickElement('.bilibili-player-video-btn-widescreen'),
|
||||||
|
mute: clickElement('.bilibili-player-video-btn-volume .bilibili-player-iconfont-volume'),
|
||||||
|
pictureInPicture: clickElement('.bilibili-player-video-btn-pip'),
|
||||||
|
coin: clickElement('.video-toolbar .coin,.tool-bar .coin-info, .video-toolbar-module .coin-box, .play-options-ul > li:nth-child(2)'),
|
||||||
|
favorite: clickElement('.video-toolbar .collect, .video-toolbar-module .fav-box, .play-options-ul > li:nth-child(3)'),
|
||||||
|
pause: clickElement('.bilibili-player-video-btn-start'),
|
||||||
|
like: (() => {
|
||||||
|
/** 长按`L`三连使用的记忆变量 */
|
||||||
|
let likeClick = true
|
||||||
|
/** 在稍后再看页面里, 记录当前视频是否赞过 */
|
||||||
|
let liked = false
|
||||||
|
|
||||||
|
const listenWatchlaterVideoChange = _.once(() => {
|
||||||
|
Observer.videoChange(() => {
|
||||||
|
Ajax.getJsonWithCredentials(`https://api.bilibili.com/x/web-interface/archive/has/like?aid=${unsafeWindow.aid}`).then(json => {
|
||||||
|
liked = Boolean(json.data)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return (({ isWatchlater, isMediaList, event }) => {
|
||||||
|
if (isWatchlater) {
|
||||||
|
listenWatchlaterVideoChange()
|
||||||
|
const formData = {
|
||||||
|
aid: unsafeWindow.aid,
|
||||||
|
/** `1`点赞; `2`取消赞 */
|
||||||
|
like: liked ? 2 : 1,
|
||||||
|
csrf: getCsrf(),
|
||||||
|
}
|
||||||
|
Ajax.postTextWithCredentials(`https://api.bilibili.com/x/web-interface/archive/like`, Object.entries(formData).map(([k, v]) => `${k}=${v}`).join('&')).then(() => {
|
||||||
|
liked = !liked
|
||||||
|
if (liked) {
|
||||||
|
Toast.success(`已点赞`, `快捷键扩展`, 1000)
|
||||||
|
} else {
|
||||||
|
Toast.success(`已取消点赞`, `快捷键扩展`, 1000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (isMediaList) {
|
||||||
|
const likeButton = dq('.play-options-ul > li:first-child') as HTMLLIElement
|
||||||
|
if (likeButton) {
|
||||||
|
likeButton.click()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const likeButton = dq('.video-toolbar .like') as HTMLSpanElement
|
||||||
|
event.preventDefault()
|
||||||
|
const fireEvent = (name: string, args: Event) => {
|
||||||
|
const event = new CustomEvent(name, args)
|
||||||
|
likeButton.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
likeClick = true
|
||||||
|
setTimeout(() => likeClick = false, 200)
|
||||||
|
fireEvent('mousedown', event)
|
||||||
|
document.body.addEventListener('keyup', e => {
|
||||||
|
e.preventDefault()
|
||||||
|
fireEvent('mouseup', e)
|
||||||
|
if (likeClick) {
|
||||||
|
fireEvent('click', e)
|
||||||
|
}
|
||||||
|
}, { once: true })
|
||||||
|
}
|
||||||
|
}) as KeyBindingAction
|
||||||
|
})(),
|
||||||
|
danmaku: () => {
|
||||||
|
const checkbox = dq('.bilibili-player-video-danmaku-switch input') as HTMLInputElement
|
||||||
|
if (!checkbox) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
checkbox.checked = !checkbox.checked
|
||||||
|
raiseEvent(checkbox, 'change')
|
||||||
|
},
|
||||||
|
longJumpBackward: () => {
|
||||||
|
const video = dq('.bilibili-player-video video') as HTMLVideoElement
|
||||||
|
video.currentTime -= settings.keymapJumpSeconds
|
||||||
|
},
|
||||||
|
longJumpForward: () => {
|
||||||
|
const video = dq('.bilibili-player-video video') as HTMLVideoElement
|
||||||
|
video.currentTime += settings.keymapJumpSeconds
|
||||||
|
},
|
||||||
|
playerMenu: () => {
|
||||||
|
// menu size: 386.6 x 311 (2020-03-29)
|
||||||
|
// menu size: 176.65 x 194 (2020-06-09)
|
||||||
|
const player = dq('.bilibili-player-video-wrap') as HTMLElement
|
||||||
|
if (!player) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const rect = player.getBoundingClientRect()
|
||||||
|
player.dispatchEvent(new MouseEvent('contextmenu', {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: false,
|
||||||
|
view: unsafeWindow,
|
||||||
|
button: 2,
|
||||||
|
buttons: 0,
|
||||||
|
clientX: rect.x + rect.width / 2 - 176.65 / 2,
|
||||||
|
clientY: rect.y + rect.height / 2 - 194 / 2
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
watchlater: clickElement('.video-toolbar .ops .watchlater, .more-ops-list .ops-watch-later, .video-toolbar-module .see-later-box'),
|
||||||
|
quickFavorite: clickElement('.quick-favorite'),
|
||||||
|
videoSpeedIncrease: videoSpeed((controller, rates) => {
|
||||||
|
controller.setVideoSpeed(rates.find(it => it > controller.playbackRate) || rates[rates.length - 1])
|
||||||
|
}),
|
||||||
|
videoSpeedDecrease: videoSpeed((controller, rates) => {
|
||||||
|
controller.setVideoSpeed([...rates].reverse().find(it => it < controller.playbackRate) || rates[0])
|
||||||
|
}),
|
||||||
|
videoSpeedReset: videoSpeed((controller, rates) => {
|
||||||
|
controller.reset()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
const defaultBindings: { [action in keyof typeof actions]: string } = {
|
||||||
|
fullscreen: 'f',
|
||||||
|
webFullscreen: 'w',
|
||||||
|
wideScreen: 't',
|
||||||
|
mute: 'm',
|
||||||
|
pictureInPicture: 'p',
|
||||||
|
coin: 'c',
|
||||||
|
favorite: 's',
|
||||||
|
pause: 'space',
|
||||||
|
like: 'l',
|
||||||
|
playerMenu: '`',
|
||||||
|
longJumpForward: 'j',
|
||||||
|
longJumpBackward: 'shift j',
|
||||||
|
watchlater: 'shift w',
|
||||||
|
quickFavorite: 'shift s',
|
||||||
|
danmaku: 'd',
|
||||||
|
videoSpeedIncrease: 'shift > ArrowUp',
|
||||||
|
videoSpeedDecrease: 'shift < ArrowDown',
|
||||||
|
videoSpeedReset: 'shift ?',
|
||||||
|
}
|
||||||
|
const parseBindings = (bindings: { [action: string]: string }) => {
|
||||||
|
return Object.entries(bindings).map(([actionName, keyString]) => {
|
||||||
|
const keys = keyString.split(' ')
|
||||||
|
return {
|
||||||
|
keys,
|
||||||
|
action: (actions as any)[actionName] || (() => {}),
|
||||||
|
} as KeyBinding
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
const { loadKeyBindings } = await import('./key-bindings')
|
||||||
|
config = loadKeyBindings(parseBindings(
|
||||||
|
{ ...defaultBindings, ...settings.customKeyBindings }
|
||||||
|
))
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
reload: () => config && (config.enable = true),
|
||||||
|
unload: () => config && (config.enable = false),
|
||||||
|
}
|
||||||
@ -1 +1 @@
|
|||||||
1.11.8
|
1.11.9
|
||||||
Loading…
Reference in New Issue
Block a user