Skip to content

Instantly share code, notes, and snippets.

@ricky9w
Created September 3, 2024 07:24
Show Gist options
  • Save ricky9w/886e096d3bb73d554d5da5fcfc0608d6 to your computer and use it in GitHub Desktop.
Save ricky9w/886e096d3bb73d554d5da5fcfc0608d6 to your computer and use it in GitHub Desktop.
自动记忆并应用哔哩哔哩视频字幕的开关状态,在观看多P视频时保持一致的字幕设置
// ==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