Created
September 3, 2024 07:24
-
-
Save ricky9w/886e096d3bb73d554d5da5fcfc0608d6 to your computer and use it in GitHub Desktop.
自动记忆并应用哔哩哔哩视频字幕的开关状态,在观看多P视频时保持一致的字幕设置
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Bilibili字幕状态记忆 | |
// @namespace http://tampermonkey.net/ | |
// @version 0.4 | |
// @description 自动记忆并应用哔哩哔哩视频字幕的开关状态,在观看多P视频时保持一致的字幕设置 | |
// @author Richard Wang | |
// @email [email protected] | |
// @match *://www.bilibili.com/video/* | |
// ==/UserScript== | |
(function () { | |
'use strict'; | |
const SUBTITLE_ENABLED_KEY = 'bilibiliSubtitleEnabled'; | |
const DEFAULT_SUBTITLE_STATE = false; | |
function getValue(key, defaultValue) { | |
return JSON.parse(localStorage.getItem(key) ?? JSON.stringify(defaultValue)); | |
} | |
function setValue(key, value) { | |
localStorage.setItem(key, JSON.stringify(value)); | |
} | |
function createToggleButton() { | |
const button = document.createElement('button'); | |
updateButtonText(button); | |
Object.assign(button.style, { | |
position: 'fixed', | |
top: '10px', | |
right: '10px', | |
zIndex: '9999', | |
padding: '5px 10px', | |
backgroundColor: '#00a1d6', | |
color: 'white', | |
border: 'none', | |
borderRadius: '4px', | |
cursor: 'pointer', | |
}); | |
button.onclick = () => toggleSubtitlePreference(button); | |
document.body.appendChild(button); | |
} | |
function updateButtonText(button) { | |
button.textContent = `字幕: ${getValue(SUBTITLE_ENABLED_KEY, DEFAULT_SUBTITLE_STATE) ? '开' : '关'}`; | |
} | |
function toggleSubtitlePreference(button) { | |
const newPreference = !getValue(SUBTITLE_ENABLED_KEY, DEFAULT_SUBTITLE_STATE); | |
setValue(SUBTITLE_ENABLED_KEY, newPreference); | |
updateButtonText(button); | |
setSubtitleState(newPreference); | |
} | |
function setSubtitleState(enabled) { | |
const subtitleButton = document.querySelector('[aria-label="字幕"] span'); | |
if (subtitleButton) { | |
const currentState = document.querySelectorAll('svg[preserveAspectRatio="xMidYMid meet"] > defs > filter').length === 2; | |
if (currentState !== enabled) { | |
subtitleButton.click(); | |
} | |
} | |
} | |
function checkVideoAndSetSubtitle() { | |
const video = document.querySelector('video[crossorigin="anonymous"]'); | |
if (video && video.readyState >= 2) { | |
let subtitleCheckAttempts = 0; | |
const maxAttempts = 25; // 5秒 / 200ms = 25次 | |
const checkSubtitle = () => { | |
const subtitleDiv = document.querySelector('[aria-label="字幕"]'); | |
if (subtitleDiv) { | |
setSubtitleState(getValue(SUBTITLE_ENABLED_KEY, DEFAULT_SUBTITLE_STATE)); | |
return true; // 成功找到字幕控件 | |
} | |
return false; // 未找到字幕控件 | |
}; | |
const attemptCheck = () => { | |
if (checkSubtitle() || subtitleCheckAttempts >= maxAttempts) { | |
// 如果找到字幕控件或达到最大尝试次数,停止检查 | |
return; | |
} | |
subtitleCheckAttempts++; | |
setTimeout(attemptCheck, 200); | |
}; | |
attemptCheck(); | |
} else { | |
requestAnimationFrame(checkVideoAndSetSubtitle); | |
} | |
} | |
function parseUrl() { | |
const { pathname, searchParams } = new URL(window.location.href); | |
const videoId = pathname.split('/').pop(); | |
const part = searchParams.get('p') || '1'; | |
return { videoId, part }; | |
} | |
let currentVideoId, currentPart; | |
function checkVideoChange() { | |
const { videoId, part } = parseUrl(); | |
if (videoId !== currentVideoId || part !== currentPart) { | |
currentVideoId = videoId; | |
currentPart = part; | |
return true; | |
} | |
return false; | |
} | |
function init() { | |
if (checkVideoChange()) { | |
createToggleButton(); | |
checkVideoAndSetSubtitle(); | |
} | |
} | |
const observer = new MutationObserver(() => { | |
if (location.href !== lastUrl) { | |
lastUrl = location.href; | |
init(); | |
} | |
}); | |
let lastUrl = location.href; | |
observer.observe(document, { subtree: true, childList: true }); | |
if (document.readyState === 'loading') { | |
document.addEventListener('DOMContentLoaded', init); | |
} else { | |
init(); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment