您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在哔哩哔哩视频页面上,提供用户选项来自动进入网页全屏、自动开启关灯模式(调暗页面背景),以及悬浮评论区选项。
// ==UserScript== // @name 哔哩哔哩工具箱 - Bilibili Toolbox // @namespace http://tampermonkey.net/ // @version 2.7 // @description 在哔哩哔哩视频页面上,提供用户选项来自动进入网页全屏、自动开启关灯模式(调暗页面背景),以及悬浮评论区选项。 // @author twocold // @match https://www.bilibili.com/video/* // @match https://www.bilibili.com/bangumi/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_addStyle // @license MIT // ==/UserScript== (function() { 'use strict'; // --- UI & Notifications --- GM_addStyle(` .gm-toast-container { position: fixed; top: 20px; right: 20px; z-index: 9999; background: rgba(0, 0, 0, 0.8); color: white; padding: 12px 20px; border-radius: 8px; font-size: 14px; opacity: 0; transition: opacity 0.3s ease; } .gm-toast-container.show { opacity: 1; } .gm-floating-comment { position: fixed; top: 100px; right: 20px; width: 400px; height: 600px; background: white; border: 2px solid #00a1d6; border-radius: 8px; z-index: 9998; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); overflow: hidden; transition: all 0.3s ease; } .gm-floating-comment.collapsed { height: 40px; } .gm-floating-comment.dragging { opacity: 0.8; transition: none; cursor: move; } .gm-floating-comment-header { background: #00a1d6; color: white; padding: 8px 12px; font-size: 14px; font-weight: bold; cursor: move; display: flex; justify-content: space-between; align-items: center; user-select: none; } .gm-floating-comment-toggle { cursor: pointer; background: none; border: none; color: white; font-size: 16px; font-weight: bold; padding: 0; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; border-radius: 3px; transition: background-color 0.2s; } .gm-floating-comment-toggle:hover { background-color: rgba(255, 255, 255, 0.2); } .gm-floating-comment-content { height: calc(100% - 40px); overflow-y: auto; } .gm-floating-comment-content #commentapp { width: 100% !important; height: 100% !important; } .gm-floating-comment.collapsed .gm-floating-comment-content { display: none; } .gm-floating-comment.collapsed .bili-comments-bottom-fixed-wrapper { display: none !important; } `); function showToast(message) { const toast = document.createElement('div'); toast.className = 'gm-toast-container'; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => toast.classList.add('show'), 10); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => document.body.removeChild(toast), 300); }, 3000); } // --- Configuration & Menu --- const CONFIG_WEBFULLSCREEN_KEY = 'config_auto_web_fullscreen'; const CONFIG_LIGHTSOFF_KEY = 'config_lights_off'; const CONFIG_COMMENT_WINDOW_KEY = 'config_comment_window'; // Comment window functionality let floatingCommentWindow = null; let commentAppOriginalParent = null; function createFloatingCommentWindow() { if (floatingCommentWindow) return; let commentApp = document.getElementById('commentapp'); if (!commentApp) { commentApp = document.getElementById('comment-module'); } if (!commentApp) { setTimeout(createFloatingCommentWindow, 1000); return; } commentAppOriginalParent = commentApp.parentNode; floatingCommentWindow = document.createElement('div'); floatingCommentWindow.className = 'gm-floating-comment'; floatingCommentWindow.innerHTML = ` <div class="gm-floating-comment-header"> <span>评论区悬浮窗</span> <button id="gm-comment-toggle" class="gm-floating-comment-toggle">−</button> </div> <div class="gm-floating-comment-content"></div> `; const content = floatingCommentWindow.querySelector('.gm-floating-comment-content'); content.appendChild(commentApp); document.body.appendChild(floatingCommentWindow); // Drag functionality let isDragging = false; let currentX; let currentY; let initialX; let initialY; let xOffset = 0; let yOffset = 0; const header = floatingCommentWindow.querySelector('.gm-floating-comment-header'); const toggleBtn = document.getElementById('gm-comment-toggle'); // Initialize position based on current style function initializeDragPosition() { const rect = floatingCommentWindow.getBoundingClientRect(); xOffset = rect.left; yOffset = rect.top; } function dragStart(e) { if (e.target === toggleBtn || toggleBtn.contains(e.target)) return; // Don't drag when clicking toggle button // Initialize position on first drag if (xOffset === 0 && yOffset === 0) { initializeDragPosition(); } initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; if (e.target === header || header.contains(e.target)) { isDragging = true; floatingCommentWindow.classList.add('dragging'); } } function dragEnd(e) { initialX = currentX; initialY = currentY; isDragging = false; floatingCommentWindow.classList.remove('dragging'); } function drag(e) { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; // Keep window within viewport bounds const rect = floatingCommentWindow.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; let newX = currentX; let newY = currentY; // Horizontal bounds if (newX < 0) newX = 0; if (newX + rect.width > viewportWidth) newX = viewportWidth - rect.width; // Vertical bounds if (newY < 0) newY = 0; if (newY + rect.height > viewportHeight) newY = viewportHeight - rect.height; floatingCommentWindow.style.left = newX + 'px'; floatingCommentWindow.style.top = newY + 'px'; floatingCommentWindow.style.right = 'auto'; } } // Mouse events header.addEventListener('mousedown', dragStart); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', dragEnd); // Touch events for mobile support header.addEventListener('touchstart', (e) => { const touch = e.touches[0]; dragStart({ clientX: touch.clientX, clientY: touch.clientY, target: e.target }); }); document.addEventListener('touchmove', (e) => { if (isDragging) { e.preventDefault(); const touch = e.touches[0]; drag({ clientX: touch.clientX, clientY: touch.clientY, preventDefault: () => {} }); } }); document.addEventListener('touchend', dragEnd); // Toggle functionality function toggleCollapse() { const isCollapsed = floatingCommentWindow.classList.toggle('collapsed'); toggleBtn.textContent = isCollapsed ? '+' : '−'; // Handle the bili-comments-bottom-fixed-wrapper visibility const bottomFixedWrapper = floatingCommentWindow.querySelector('.bili-comments-bottom-fixed-wrapper'); if (bottomFixedWrapper) { if (isCollapsed) { bottomFixedWrapper.style.display = 'none'; bottomFixedWrapper.style.visibility = 'hidden'; bottomFixedWrapper.style.position = 'absolute'; bottomFixedWrapper.style.top = '-9999px'; } else { bottomFixedWrapper.style.display = ''; bottomFixedWrapper.style.visibility = ''; bottomFixedWrapper.style.position = ''; bottomFixedWrapper.style.top = ''; } } } toggleBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleCollapse(); }); } function removeFloatingCommentWindow() { if (!floatingCommentWindow || !commentAppOriginalParent) return; // Restore the bili-comments-bottom-fixed-wrapper before moving commentapp back const bottomFixedWrapper = floatingCommentWindow.querySelector('.bili-comments-bottom-fixed-wrapper'); if (bottomFixedWrapper) { bottomFixedWrapper.style.display = ''; bottomFixedWrapper.style.visibility = ''; bottomFixedWrapper.style.position = ''; bottomFixedWrapper.style.top = ''; } const commentApp = document.getElementById('commentapp'); if (commentApp) { commentAppOriginalParent.appendChild(commentApp); } document.body.removeChild(floatingCommentWindow); floatingCommentWindow = null; } function toggleCommentWindow() { if (floatingCommentWindow) { removeFloatingCommentWindow(); } else { createFloatingCommentWindow(); } } // Flag to prevent multiple menu registrations let menuBuilt = false; function buildMenu() { if (menuBuilt) return; // Prevent multiple menu registrations let isFullscreenEnabled = GM_getValue(CONFIG_WEBFULLSCREEN_KEY, true); let isLightsOffEnabled = GM_getValue(CONFIG_LIGHTSOFF_KEY, false); let isCommentWindowEnabled = GM_getValue(CONFIG_COMMENT_WINDOW_KEY, false); // Create toggle functions that don't rebuild the menu function toggleFullscreen() { const newValue = !GM_getValue(CONFIG_WEBFULLSCREEN_KEY, true); GM_setValue(CONFIG_WEBFULLSCREEN_KEY, newValue); showToast(`自动网页全屏已${newValue ? '开启' : '关闭'},刷新页面后生效`); } function toggleLightsOff() { const newValue = !GM_getValue(CONFIG_LIGHTSOFF_KEY, false); GM_setValue(CONFIG_LIGHTSOFF_KEY, newValue); showToast(`自动关灯模式已${newValue ? '开启' : '关闭'},刷新页面后生效`); } function toggleCommentWindow() { const newValue = !GM_getValue(CONFIG_COMMENT_WINDOW_KEY, false); GM_setValue(CONFIG_COMMENT_WINDOW_KEY, newValue); showToast(`评论区悬浮窗已${newValue ? '开启' : '关闭'},刷新页面后生效`); } // Register menu commands once GM_registerMenuCommand(`自动网页全屏: ${isFullscreenEnabled ? '✅' : '❌'}`, toggleFullscreen); GM_registerMenuCommand(`自动关灯模式: ${isLightsOffEnabled ? '✅' : '❌'}`, toggleLightsOff); GM_registerMenuCommand(`评论区悬浮窗: ${isCommentWindowEnabled ? '✅' : '❌'}`, toggleCommentWindow); menuBuilt = true; } // Initial build of the menu buildMenu(); // --- Core Logic --- // Wait for page to load and then initialize features function initializeFeatures() { // Auto web fullscreen functionality const isFullscreenEnabled = GM_getValue(CONFIG_WEBFULLSCREEN_KEY, true); if (isFullscreenEnabled) { // Look for web fullscreen button and click it setTimeout(() => { const fullscreenBtn = document.querySelector('.bpx-player-ctrl-btn.bpx-player-ctrl-web') || document.querySelector('.bpx-player-ctrl-web') || document.querySelector('[aria-label*="网页全屏"]') || document.querySelector('[title*="网页全屏"]'); if (fullscreenBtn) { fullscreenBtn.click(); } }, 2000); } // Auto lights off functionality const isLightsOffEnabled = GM_getValue(CONFIG_LIGHTSOFF_KEY, false); if (isLightsOffEnabled) { setTimeout(() => { // B站关灯模式:齿轮按钮 -> 更多播放设置 -> 关灯模式 // Step 1: 点击设置按钮(齿轮图标) const settingsBtn = document.querySelector('.bpx-player-ctrl-btn.bpx-player-ctrl-setting') || document.querySelector('.bpx-player-ctrl-setting') || document.querySelector('[aria-label*="设置"]') || document.querySelector('[title*="设置"]'); if (settingsBtn) { settingsBtn.click(); // Step 2: 等待设置菜单展开,然后点击关灯模式选项 setTimeout(() => { const lightsOffCheckbox = document.querySelector('input.bui-checkbox-input[aria-label="关灯模式"]') || document.querySelector('input[type="checkbox"][aria-label*="关灯"]') || document.querySelector('input[type="checkbox"]')?.closest('div')?.querySelector('span')?.textContent?.includes('关灯'); if (lightsOffCheckbox && !lightsOffCheckbox.checked) { lightsOffCheckbox.click(); } // Step 3: 关闭设置菜单 setTimeout(() => { if (settingsBtn) { settingsBtn.click(); } }, 300); }, 500); } else { // Alternative: Try to find the lights off checkbox directly in the player const lightsOffDirect = document.querySelector('input.bui-checkbox-input[aria-label="关灯模式"]'); if (lightsOffDirect && !lightsOffDirect.checked) { lightsOffDirect.click(); } } }, 2500); } // Auto comment window functionality const isCommentWindowEnabled = GM_getValue(CONFIG_COMMENT_WINDOW_KEY, false); if (isCommentWindowEnabled) { setTimeout(() => { createFloatingCommentWindow(); }, 3000); } } // Initialize features when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeFeatures); } else { initializeFeatures(); } })();