// ==UserScript== // @name B站自动连播控制助手 // @namespace https://space.bilibili.com/398910090 // @version 1.1 // @description 合集自动开启自动连播,单个视频自动禁用自动连播,支持特定合集设置 // @author Ace // @match https://www.bilibili.com/video/* // @icon https://www.bilibili.com/favicon.ico // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // ==/UserScript== (function() { 'use strict'; // 设置项:特定合集列表 const SETTING_KEY = 'bilibili_auto_play_settings'; // 获取设置 function getSettings() { return GM_getValue(SETTING_KEY, { enabledCollections: [], // 启用自动连播的特定合集 hotkeys: { toggleAutoPlay: 'Ctrl+Shift+L', // 切换连播状态快捷键 toggleCollection: 'Ctrl+Shift+A' // 快捷指定当前合集快捷键 }, showInfobar: true, // 是否显示切换成功提示 autoEnableNonAdded: true, // 选择不添加到列表时是否自动开启连播 autoDisableNewCollections: true // 新合集是否自动关闭连播 }); } // 保存设置 function saveSettings(settings) { GM_setValue(SETTING_KEY, settings); } // 添加特定合集 function addCollection(collectionId) { const settings = getSettings(); if (!settings.enabledCollections.includes(collectionId)) { settings.enabledCollections.push(collectionId); saveSettings(settings); showInfobar(`已添加合集 ${collectionId} 到自动连播列表`); } else { showInfobar(`合集 ${collectionId} 已在列表中`); } } // 移除特定合集 function removeCollection(collectionId) { const settings = getSettings(); const index = settings.enabledCollections.indexOf(collectionId); if (index > -1) { settings.enabledCollections.splice(index, 1); saveSettings(settings); // 如果移除的是当前视频的合集,启用手动控制 const currentCollectionId = getCollectionId(); if (currentCollectionId === collectionId) { enableManualControl(); // 根据设置决定连播状态 if (settings.autoEnableNonAdded !== false) { setAutoPlayState(true); } else { setAutoPlayState(false); } } showInfobar(`已从自动连播列表移除合集 ${collectionId}`); } else { showInfobar(`合集 ${collectionId} 不在列表中`); } } // 查看设置 function viewSettings() { const settings = getSettings(); const collections = settings.enabledCollections.length > 0 ? settings.enabledCollections.join('\n') : '暂无特定合集设置'; showInfobar(`自动连播合集列表:\n${collections}`); } // 注册菜单命令 GM_registerMenuCommand('添加当前合集到自动连播', function() { // 获取合集ID const collectionId = getCollectionId(); if (collectionId) { addCollection(collectionId); } else { // 如果没有合集ID,使用视频ID作为备选 const videoId = getVideoIdFromUrl(); if (videoId) { addCollection(videoId); } else { showInfobar('当前页面不是B站视频页面'); } } }); GM_registerMenuCommand('从自动连播移除当前合集', function() { // 获取合集ID const collectionId = getCollectionId(); if (collectionId) { removeCollection(collectionId); } else { // 如果没有合集ID,使用视频ID作为备选 const videoId = getVideoIdFromUrl(); if (videoId) { removeCollection(videoId); } else { showInfobar('当前页面不是B站视频页面'); } } }); GM_registerMenuCommand('查看自动连播助手设置', viewSettings); // 从URL获取视频标识(支持AV号和BV号) function getVideoIdFromUrl() { // 匹配BV号:BV开头,后跟大小写字母和数字,共12位 const bvMatch = window.location.href.match(/BV([A-Za-z0-9]{10,})/); if (bvMatch) return bvMatch[1]; // 匹配AV号:av开头,后跟数字 const avMatch = window.location.href.match(/av(\d+)/); if (avMatch) return avMatch[1]; return null; } // 获取合集唯一ID(sid) function getCollectionId() { // 尝试从页面中提取合集sid const collectionLink = document.querySelector('.video-pod__header .header-top .left .title.jumpable'); if (collectionLink) { const href = collectionLink.getAttribute('href'); if (href) { const sidMatch = href.match(/sid=(\d+)/); if (sidMatch) return sidMatch[1]; } } // 如果无法从页面获取,尝试从URL获取 const sidFromUrl = window.location.href.match(/sid=(\d+)/); if (sidFromUrl) return sidFromUrl[1]; return null; } // 检查是否为合集页面 function isCollectionPage() { // 合集页面特征:包含"自动连播"文字且是合集相关的按钮 const autoPlayBtn = document.querySelector('.continuous-btn'); if (!autoPlayBtn) return false; // 检查按钮是否包含"自动连播"文字 const autoPlayText = autoPlayBtn.querySelector('.txt'); if (!autoPlayText || autoPlayText.textContent !== '自动连播') return false; // 检查按钮父级结构,确保是合集页面的控制 return autoPlayBtn.closest('.video-pod') !== null; } // 检查是否为单个视频页面 function isSingleVideoPage() { // 单个视频页面特征:包含"接下来播放"标题 const elements = document.querySelectorAll('.title-txt'); for (let element of elements) { if (element.textContent.includes('接下来播放')) { return true; } } return false; } // 检查当前视频是否在启用列表中 function isInEnabledList() { // 优先使用合集ID const collectionId = getCollectionId(); if (collectionId) { const settings = getSettings(); return settings.enabledCollections.includes(collectionId); } // 兼容处理:如果没有合集ID,使用视频ID const videoId = getVideoIdFromUrl(); if (!videoId) return false; const settings = getSettings(); return settings.enabledCollections.includes(videoId); } // 设置自动连播状态 function setAutoPlayState(enabled) { // 检查设置界面是否打开,如果打开则不执行任何操作 const settingsOverlay = document.getElementById('bilibili-auto-play-settings-overlay'); if (settingsOverlay && settingsOverlay.style.display === 'block') { return false; } const switchBtn = document.querySelector('.continuous-btn .switch-btn'); if (!switchBtn) return false; const isCurrentlyEnabled = switchBtn.classList.contains('on'); // 如果状态不同,则进行切换 if (isCurrentlyEnabled !== enabled) { // 对于锁定状态的按钮,直接修改类名而不点击 if (switchBtn.style.pointerEvents === 'none') { // 直接修改类名来反映正确状态 if (enabled) { switchBtn.classList.add('on'); } else { switchBtn.classList.remove('on'); } return true; } else { // 对于未锁定的按钮,点击切换 const switchBlock = switchBtn.querySelector('.switch-block'); if (switchBlock) { switchBlock.click(); return true; } } } return false; } // 观察页面变化 function observePageChanges() { const observer = new MutationObserver(() => { // 只有当连播按钮存在时才执行操作 const continuousBtn = document.querySelector('.continuous-btn'); if (continuousBtn) { // 检查是否为有效的视频页面 const videoId = getVideoIdFromUrl(); if (videoId) { if (isCollectionPage() && isInEnabledList()) { // 合集在启用列表中,强制开启连播并锁定 const switchBtn = continuousBtn.querySelector('.switch-btn'); if (switchBtn) { // 确保按钮处于开启状态 setAutoPlayState(true); // 如果按钮未锁定,禁用手动控制 if (switchBtn.style.pointerEvents !== 'none') { disableManualControl(); } } } else if (isCollectionPage()) { // 合集不在启用列表中 const settings = getSettings(); // 如果是新合集且设置了自动关闭连播 if (settings.autoDisableNewCollections) { // 确保连播关闭 setAutoPlayState(false); } // 启用手动控制 enableManualControl(); } else { // 单个视频,禁用连播 setAutoPlayState(false); } } } }); // 只监听播放器区域的变化,而不是整个document.body const playerContainer = document.querySelector('.bpx-player'); if (playerContainer) { observer.observe(playerContainer, { childList: true, subtree: true }); } else { // 如果播放器容器不存在,回退到监听body,但添加延迟 observer.observe(document.body, { childList: true, subtree: true }); } } // 初始化 function init() { // 页面加载完成后执行 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { initLogic(); }, 1000); // 延迟执行,确保页面元素加载完成 }); } else { setTimeout(() => { initLogic(); }, 1000); } } // 初始化逻辑 function initLogic() { // 创建设置界面 createSettingsUI(); // 检查是否为合集页面 if (isCollectionPage()) { // 获取合集ID const collectionId = getCollectionId(); const settings = getSettings(); // 判断是否为新合集(不在列表中) const isNewCollection = !settings.enabledCollections.includes(collectionId); // 如果是新合集,且设置了自动关闭连播 if (isNewCollection && settings.autoDisableNewCollections) { // 立即关闭连播 setAutoPlayState(false); showInfobar('检测到新合集,已根据设置自动关闭连播'); } // 如果合集不在列表中,显示通知 if (isNewCollection) { showNotification( '检测到当前为合集页面,是否将其添加到自动连播列表?', { confirmText: '添加', cancelText: '不添加', autoClose: false, // 不自动关闭,必须用户选择 onConfirm: () => { // 添加到自动连播列表 addCollection(collectionId); // 立即开启连播 setAutoPlayState(true); // 禁用手动控制 disableManualControl(); showInfobar('已添加到自动连播列表,连播按钮已锁定'); }, onCancel: () => { const settings = getSettings(); // 根据设置决定是否自动开启连播 if (settings.autoEnableNonAdded !== false) { // 自动开启连播 setAutoPlayState(true); showInfobar('已自动开启连播,您可手动关闭'); } else { // 不自动开启连播 setAutoPlayState(false); showInfobar('未自动开启连播,您可手动开启'); } // 修改observePageChanges,使其只在合集在列表中时才强制开启连播 observePageChangesForNonAddedCollection(); } } ); } else { // 如果已经在列表中,自动开启连播并禁用手动控制 setAutoPlayState(true); disableManualControl(); } } else { // 单个视频,禁用连播 setAutoPlayState(false); } // 观察页面变化 if (isCollectionPage()) { const collectionId = getCollectionId(); const settings = getSettings(); if (settings.enabledCollections.includes(collectionId)) { observePageChanges(); } } else { observePageChanges(); } // 观察更多播放设置区域,注入连播设置入口 observeMoreSettingsArea(); // 注册快捷键监听 registerHotkeyListener(); } // 为非添加的合集观察页面变化 function observePageChangesForNonAddedCollection() { const observer = new MutationObserver(() => { // 只有当连播按钮存在时才执行操作 const continuousBtn = document.querySelector('.continuous-btn'); if (continuousBtn) { // 如果设置了新合集自动关闭连播 const settings = getSettings(); if (settings.autoDisableNewCollections) { // 确保连播关闭 setAutoPlayState(false); } } }); // 只监听播放器区域的变化 const playerContainer = document.querySelector('.bpx-player'); if (playerContainer) { observer.observe(playerContainer, { childList: true, subtree: true }); } else { // 如果播放器容器不存在,回退到监听body observer.observe(document.body, { childList: true, subtree: true }); } } // 禁用连播按钮的手动控制 function disableManualControl() { const switchBtn = document.querySelector('.continuous-btn .switch-btn'); if (switchBtn) { // 先确保连播已经开启 setAutoPlayState(true); // 移除所有现有事件监听器 const newSwitchBtn = switchBtn.cloneNode(true); switchBtn.parentNode.replaceChild(newSwitchBtn, switchBtn); // 确保新按钮保持开启状态 newSwitchBtn.classList.add('on'); const newSwitchBlock = newSwitchBtn.querySelector('.switch-block'); if (newSwitchBlock) { newSwitchBlock.style.transform = 'none'; } // 阻止点击事件 newSwitchBtn.style.pointerEvents = 'none'; newSwitchBtn.style.opacity = '0.8'; } } // 启用连播按钮的手动控制 function enableManualControl() { const switchBtn = document.querySelector('.continuous-btn .switch-btn'); if (switchBtn) { // 检查按钮是否被锁定 if (switchBtn.style.pointerEvents === 'none') { // 获取当前按钮状态 const isOn = switchBtn.classList.contains('on'); // 创建新按钮,保留原始按钮的HTML结构但不保留事件监听器 const newSwitchBtn = document.createElement('div'); newSwitchBtn.className = switchBtn.className; newSwitchBtn.innerHTML = switchBtn.innerHTML; // 恢复按钮状态 newSwitchBtn.style.pointerEvents = ''; newSwitchBtn.style.opacity = ''; // 确保开关状态正确 const newSwitchBlock = newSwitchBtn.querySelector('.switch-block'); if (newSwitchBlock) { if (isOn) { newSwitchBtn.classList.add('on'); } else { newSwitchBtn.classList.remove('on'); } } // 替换按钮 switchBtn.parentNode.replaceChild(newSwitchBtn, switchBtn); } } } // 显示设置界面 function showSettings() { loadSettingsToUI(); applyTheme(); document.getElementById('bilibili-auto-play-settings-overlay').style.display = 'block'; // 禁止背景滚动 document.body.style.overflow = 'hidden'; document.documentElement.style.overflow = 'hidden'; } // 隐藏设置界面 function hideSettings() { document.getElementById('bilibili-auto-play-settings-overlay').style.display = 'none'; // 恢复背景滚动 document.body.style.overflow = 'auto'; document.documentElement.style.overflow = 'auto'; } // 应用主题 function applyTheme() { const settingsPanel = document.getElementById('bilibili-auto-play-settings'); if (!settingsPanel) return; // 检测系统主题 const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; // 移除旧主题类 settingsPanel.classList.remove('bilibili-settings-light', 'bilibili-settings-dark'); // 添加新主题类 if (isDark) { settingsPanel.classList.add('bilibili-settings-dark'); } else { settingsPanel.classList.add('bilibili-settings-light'); } } // 加载设置到界面 function loadSettingsToUI() { const settings = getSettings(); // 添加防御性检查,确保设置结构完整 const hotkeys = settings.hotkeys || { toggleAutoPlay: 'Ctrl+Shift+L', toggleCollection: 'Ctrl+Shift+A' }; document.getElementById('toggleAutoPlay-hotkey').value = hotkeys.toggleAutoPlay; document.getElementById('toggleCollection-hotkey').value = hotkeys.toggleCollection; document.getElementById('show-infobar').checked = settings.showInfobar !== false; document.getElementById('auto-enable-non-added').checked = settings.autoEnableNonAdded !== false; document.getElementById('auto-disable-new-collections').checked = settings.autoDisableNewCollections === true; // 加载连播列表 updateCollectionList(); } // 保存界面设置 function saveSettingsFromUI() { const settings = getSettings(); // 确保hotkeys对象存在 if (!settings.hotkeys) { settings.hotkeys = { toggleAutoPlay: 'Ctrl+Shift+L', toggleCollection: 'Ctrl+Shift+A' }; } settings.hotkeys.toggleAutoPlay = document.getElementById('toggleAutoPlay-hotkey').value; settings.hotkeys.toggleCollection = document.getElementById('toggleCollection-hotkey').value; settings.showInfobar = document.getElementById('show-infobar').checked; settings.autoEnableNonAdded = document.getElementById('auto-enable-non-added').checked; settings.autoDisableNewCollections = document.getElementById('auto-disable-new-collections').checked; saveSettings(settings); // 保存后自动关闭设置界面 hideSettings(); showInfobar('设置已保存'); } // 更新连播列表 function updateCollectionList() { const settings = getSettings(); const collectionList = document.getElementById('collection-list'); const collectionCount = document.getElementById('collection-count'); if (!collectionList || !collectionCount) return; // 清空当前列表 collectionList.innerHTML = ''; if (settings.enabledCollections.length === 0) { // 显示空列表提示 collectionList.innerHTML = '