您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
油管哔哩哔哩视频播放器下添加更多倍速播放按钮及更多配置。
// ==UserScript== // @name Youtube Bilibili Video Player Enhancer Tools // @name:zh 油管哔哩哔哩视频播放器增强工具 // @name:zh-CN 油管哔哩哔哩视频播放器增强工具 // @name:en Youtube Bilibili Video Player Enhancer Tools // @name:en-US Youtube Bilibili Video Player Enhancer Tools // @description Adds more speed buttons and more settings to YouTube and Bilibili video players. // @description:en Adds more speed buttons and more settings to YouTube and Bilibili video players. // @description:en-US Adds more speed buttons and more settings to YouTube and Bilibili video players. // @description:zh 油管哔哩哔哩视频播放器下添加更多倍速播放按钮及更多配置。 // @description:zh-CN 油管哔哩哔哩视频播放器下添加更多倍速播放按钮及更多配置。 // @namespace com.julong.tampermonkey.TubeBiliVideoPlayerEnhancerTools // @version 1.0.5 // @author [email protected] // @homepage https://github.com/julong111/tampermonkey-TubeBili // @supportURL https://github.com/julong111/tampermonkey-TubeBili/issues // @match *://*.youtube.com* // @match *://*.bilibili.com* // @include *://*.youtube.com* // @include *://*.bilibili.com* // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @icon https://www.youtube.com/s/desktop/3748dff5/img/favicon_48.png // @charset UTF-8 // @require https://scriptcat.org/lib/513/2.1.0/ElementGetter.js#sha256=aQF7JFfhQ7Hi+weLrBlOsY24Z2ORjaxgZNoni7pAz5U= // @license MIT // ==/UserScript== // 广告跳过,自动网页全屏(待实现) //<div class="ytp-skip-ad" id="skip-ad:r" style=""> //<button class="ytp-skip-ad-button ytp-ad-component--clickable" id="skip-button:s" style="opacity: 0.5;"> //<div class="ytp-skip-ad-button__text">Skip</div> //<span class="ytp-skip-ad-button__icon"> //<svg height="100%" viewBox="-6 -6 36 36" width="100%"><path d="M5,18l10-6L5,6V18L5,18z M19,6h-2v12h2V6z" fill="#fff"></path></svg></span></button></div> (function () { 'use strict'; const i18nConfig = { // 中文配置 zh: { menu_settings: "设置面板", menu_save: "保存", menu_close: "关闭", Youtube_AutoTheaterMode: "Youtube - 自动视频网页全屏", Youtube_AutoRate2x: "Youtube - 自动2倍速播放", Youtube_AutoRemoveMiniplayer: "Youtube - 自动移除MiniPlayer按钮", Bilibili_AutoWebFullscreen: "Bilibili - 自动视频网页全屏", Bilibili_AutoRate2x: "Bilibili - 自动2倍速播放", Bilibili_AutoRemovePip: "Bilibili - 自动移除画中画按钮", Bilibili_AutoRemoveWide: "Bilibili - 自动移除宽屏按钮", Bilibili_AutoRemoveSpeed: "Bilibili - 自动移除原始倍速按钮", Bilibili_AutoRemoveComments: "Bilibili - 自动移除评论输入区", Bilibili_AutoRemoveSettings: "Bilibili - 自动移除设置按钮", }, // 英文配置 en: { menu_settings: "Settings Panel", menu_save: "Save", menu_close: "Close", Youtube_AutoTheaterMode: "Youtube - Auto Theater Mode", Youtube_AutoRate2x: "Youtube - Auto 2x Playback", Youtube_AutoRemoveMiniplayer: "Youtube - Auto Remove MiniPlayer Button", Bilibili_AutoWebFullscreen: "Bilibili - Auto Web Fullscreen", Bilibili_AutoRate2x: "Bilibili - Auto 2x Playback", Bilibili_AutoRemovePip: "Bilibili - Auto Remove Picture-in-Picture Button", Bilibili_AutoRemoveWide: "Bilibili - Auto Remove Wide Button", Bilibili_AutoRemoveSpeed: "Bilibili - Auto Remove Original Speed Button", Bilibili_AutoRemoveComments: "Bilibili - Auto Remove Comments Input Area", Bilibili_AutoRemoveSettings: "Bilibili - Auto Remove Settings Button", } }; const settingPanelStyles = ` #minimalSettingsPanel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 350px; padding: 15px; background-color: #f9f9f9; border: 1px solid #ccc; border-radius: 5px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); z-index: 99999; font-family: sans-serif; display: none; } #minimalSettingsPanel.show { display: block; } #minimalSettingsPanel h2 { margin: 0 0 10px; font-size: 1.1em; text-align: center; } #minimalSettingsPanel .setting-item { margin-bottom: 10px; } #minimalSettingsPanel .buttons { margin-top: 15px; text-align: right; } #minimalSettingsPanel button { padding: 5px 10px; cursor: pointer; border: 1px solid #ccc; background-color: #eee; border-radius: 3px; }`; let isYoutubeListenerRegistered = false; let youtubeLiveStreamCheck = null; let currentVideoTitle = document.querySelector('title').text; const Common = { speeds: [0.5, 1.0, 1.5, 2.0, 3.0], colors: ['#072525', '#287F54', '#C22544'], currentLang: 'en', settingPanelItems: [], settingPanelInitialized: false, settingPanelElement: null, detectLanguage: function () { let userLang = navigator.language.toLowerCase(); if (userLang.startsWith('zh')) { return 'zh'; } if (userLang.startsWith('en')) { return 'en'; } return 'en'; }, geti18nText: function (key) { return i18nConfig[Common.currentLang][key]; }, initializePanel: function () { let panel = document.createElement("div"); panel.id = "minimalSettingsPanel"; let title = document.createElement("h2"); title.textContent = Common.geti18nText("menu_settings"); panel.appendChild(title); for (const [key, item] of Object.entries(Common.settingPanelItems)) { let functionDiv = document.createElement("div"); functionDiv.className = "setting-item"; panel.appendChild(functionDiv); let functionValue = GM_getValue(item.dataKey, false); let input1 = document.createElement("input"); input1.type = "checkbox"; input1.checked = functionValue; input1.id = item.classId; functionDiv.appendChild(input1); let label1 = document.createElement("label"); label1.setAttribute("for", item.classId); label1.textContent = item.text; functionDiv.appendChild(label1); } let buttons = document.createElement("div"); buttons.className = "buttons"; let saveBtn = document.createElement("button"); saveBtn.id = "saveBtn"; saveBtn.textContent = Common.geti18nText("menu_save"); saveBtn.addEventListener("click", () => { Common.saveSettings(); }); let closeBtn = document.createElement("button"); closeBtn.id = "closeBtn"; closeBtn.textContent = Common.geti18nText("menu_close"); closeBtn.addEventListener("click", () => { Common.togglePanel(); }); buttons.appendChild(saveBtn); buttons.appendChild(closeBtn); panel.appendChild(buttons); document.body.appendChild(panel); Common.settingPanelElement = panel; Common.settingPanelInitialized = true; }, saveSettings: function () { for (const [key, item] of Object.entries(Common.settingPanelItems)) { const isChecked = document.getElementById(item.classId).checked; GM_setValue(item.dataKey, isChecked); } Common.settingPanelElement.classList.toggle('show'); }, togglePanel: function () { if (!Common.settingPanelInitialized) { Common.initializePanel(); } Common.settingPanelElement.classList.toggle('show'); }, initSettingItems: function (currentUrl) { if (currentUrl.includes("youtube.com")) { Common.settingPanelItems = { // Youtube_AutoTheaterMode: { // classId: "Youtube-AutoTheaterMode", // text: geti18nText("Youtube_AutoTheaterMode"), // dataKey: "Youtube-AutoTheaterMode", // }, Youtube_AutoRate2x: { classId: "Youtube-AutoRate2x", text: Common.geti18nText("Youtube_AutoRate2x"), dataKey: "Youtube-AutoRate2x", }, Youtube_AutoRemoveMiniplayer: { classId: "Youtube-AutoRemoveMiniplayer", text: Common.geti18nText("Youtube_AutoRemoveMiniplayer"), dataKey: "Youtube-AutoRemoveMiniplayer", }, }; } else if (currentUrl.includes("bilibili.com")) { Common.settingPanelItems = { Bilibili_AutoWebFullscreen: { classId: "Bilibili-AutoWebFullscreen", text: Common.geti18nText("Bilibili_AutoWebFullscreen"), dataKey: "Bilibili-AutoWebFullscreen", }, Bilibili_AutoRate2x: { classId: "Bilibili-AutoRate2x", text: Common.geti18nText("Bilibili_AutoRate2x"), dataKey: "Bilibili-AutoRate2x", }, Bilibili_AutoRemovePip: { classId: "Bilibili-AutoRemovePip", text: Common.geti18nText("Bilibili_AutoRemovePip"), dataKey: "Bilibili-AutoRemovePip", }, Bilibili_AutoRemoveWide: { classId: "Bilibili-AutoRemoveWide", text: Common.geti18nText("Bilibili_AutoRemoveWide"), dataKey: "Bilibili-AutoRemoveWide", }, Bilibili_AutoRemoveSpeed: { classId: "Bilibili-AutoRemoveSpeed", text: Common.geti18nText("Bilibili_AutoRemoveSpeed"), dataKey: "Bilibili-AutoRemoveSpeed", }, Bilibili_AutoRemoveComments: { classId: "Bilibili-AutoRemoveComments", text: Common.geti18nText("Bilibili_AutoRemoveComments"), dataKey: "Bilibili-AutoRemoveComments", }, Bilibili_AutoRemoveSettings: { classId: "Bilibili-AutoRemoveSettings", text: Common.geti18nText("Bilibili_AutoRemoveSettings"), dataKey: "Bilibili-AutoRemoveSettings", }, }; }; }, createSpeedButtons: function (selector, panelCallback, btnClickCallback) { let bgColor = Common.colors[0]; let speedListDiv = document.createElement('div'); speedListDiv.id = 'speedButtons'; speedListDiv.style.display = 'flex'; speedListDiv.style.alignItems = 'center'; speedListDiv.style.justifyContent = 'center'; speedListDiv.style.height = '100%'; const handleButtonClick = (speed) => { document.getElementsByTagName('video')[0].playbackRate = speed; }; for (let i = 0; i < Common.speeds.length; i++) { if (Common.speeds[i] >= 1) { bgColor = Common.colors[1]; } if (Common.speeds[i] >= 1.5) { bgColor = Common.colors[2]; } let btn = document.createElement('button'); btn.style.backgroundColor = bgColor; btn.style.marginRight = '1px'; btn.style.border = '1px solid #D3D3D3'; btn.style.borderRadius = '2px'; btn.style.color = '#ffffff'; btn.style.cursor = 'pointer'; btn.style.fontFamily = 'Arial, "Helvetica Neue", Helvetica, sans-serif'; btn.style.display = 'flex'; btn.style.justifyContent = 'center'; btn.style.alignItems = 'center'; btn.style.width = '38px'; btn.style.height = '24px'; btn.style.fontSize = '14px'; btn.textContent = Common.speeds[i].toString() + '×'; btn.addEventListener('click', () => { btnClickCallback ? btnClickCallback(Common.speeds[i]) : handleButtonClick(Common.speeds[i]); }); speedListDiv.appendChild(btn); } panelCallback(speedListDiv); }, removeSelector: function (selector) { let ele = document.querySelector(selector); if (ele) { ele.remove(); } }, setPlaybackRate: function (rate) { document.getElementsByTagName('video')[0].playbackRate = rate; }, }; const WebSite = { data: { youtubeLiveStreamStatus: false, }, selectors: { youtube: { // YouTube selectors listeners videoPanel: '#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-right-controls', liveStreamIcon: '#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-left-controls > div.ytp-time-display.notranslate.ytp-live > button', // Youtube Live Stream check miniPlayerBtn: '#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-right-controls > button.ytp-miniplayer-button.ytp-button', finishListener: 'yt-navigate-finish', liveStreamClass: 'ytp-live-badge-is-livehead', }, bilibili: { /// Bilibili selectors playerContainer: '#bilibili-player', webFullClass: 'mode-webscreen', speedBtn: '.bpx-player-control-bottom-left', videoPanel: '.bilibili-player, .bpx-player-container, #bilibiliPlayer', commentsPanel: '#bilibili-player > div > div > div.bpx-player-primary-area > div.bpx-player-video-area > div.bpx-player-control-wrap > div.bpx-player-control-entity > div.bpx-player-control-bottom > div.bpx-player-control-bottom-center', webFullBtn: '#bilibili-player > div > div > div.bpx-player-primary-area > div.bpx-player-video-area > div.bpx-player-control-wrap > div.bpx-player-control-entity > div.bpx-player-control-bottom > div.bpx-player-control-bottom-right > div.bpx-player-ctrl-btn.bpx-player-ctrl-web', pipBtn: '#bilibili-player > div > div > div.bpx-player-primary-area > div.bpx-player-video-area > div.bpx-player-control-wrap > div.bpx-player-control-entity > div.bpx-player-control-bottom > div.bpx-player-control-bottom-right > div.bpx-player-ctrl-btn.bpx-player-ctrl-pip', wideBtn: '#bilibili-player > div > div > div.bpx-player-primary-area > div.bpx-player-video-area > div.bpx-player-control-wrap > div.bpx-player-control-entity > div.bpx-player-control-bottom > div.bpx-player-control-bottom-right > div.bpx-player-ctrl-btn.bpx-player-ctrl-wide', speedsListBtn: '#bilibili-player > div > div > div.bpx-player-primary-area > div.bpx-player-video-area > div.bpx-player-control-wrap > div.bpx-player-control-entity > div.bpx-player-control-bottom > div.bpx-player-control-bottom-right > div.bpx-player-ctrl-btn.bpx-player-ctrl-playbackrate', settingsBtn: '#bilibili-player > div > div > div.bpx-player-primary-area > div.bpx-player-video-area > div.bpx-player-control-wrap > div.bpx-player-control-entity > div.bpx-player-control-bottom > div.bpx-player-control-bottom-right > div.bpx-player-ctrl-btn.bpx-player-ctrl-setting', } }, youtube: function () { const handleYoutubePage = async () => { console.log('执行Youtube页面脚本handler'); currentVideoTitle = document.querySelector('title').text; if (!document.querySelector('#speedButtons')) { try { let videopanel = await elmGetter.get(WebSite.selectors.youtube.videoPanel); console.log('添加更多倍速按钮'); Common.createSpeedButtons(WebSite.selectors.youtube.videoPanel, (moreSpeedsDiv) => { videopanel.before(moreSpeedsDiv); }, (speed) => { document.getElementsByTagName('video')[0].playbackRate = speed; WebSite.data.youtubeLiveStreamStatus = false; }); } catch (error) { console.error('Failed create speed button elements:', error); } } try { const removalConfigs = { Youtube_AutoRemoveMiniplayer: WebSite.selectors.youtube.miniPlayerBtn, }; for (const key in removalConfigs) { if (GM_getValue(Common.settingPanelItems[key].dataKey, false)) { elmGetter.get(removalConfigs[key]).then(item => { console.log('移除按钮:', removalConfigs[key]); item.remove(); }); } } } catch (error) { console.error('Failed autoremove buttons:', error); } if (GM_getValue(Common.settingPanelItems.Youtube_AutoRate2x.dataKey, false)) { console.log('设置2倍速播放'); Common.setPlaybackRate(2.0); WebSite.data.youtubeLiveStreamStatus = false; } }; if (!isYoutubeListenerRegistered) { console.log('注册Youtube监听器'); window.addEventListener(WebSite.selectors.youtube.finishListener, handleYoutubePage); isYoutubeListenerRegistered = true; } // 启动直播状态检测 youtubeLiveStreamCheck = setInterval(() => { let element = document.querySelector(WebSite.selectors.youtube.liveStreamIcon); if (element) { if (element.classList.contains(WebSite.selectors.youtube.liveStreamClass) && !WebSite.data.youtubeLiveStreamStatus && window.location.href.includes("youtube.com/watch")) { document.getElementsByTagName('video')[0].playbackRate = 1.0; console.log('已检测到直播,重置播放速度为1.0'); WebSite.data.youtubeLiveStreamStatus = true; } } }, 1000); }, bilibili: function () { const handleBilibiliPage = async () => { if (!document.querySelector('#speedButtons')) { try { await elmGetter.get(WebSite.selectors.bilibili.videoPanel); Common.createSpeedButtons(WebSite.selectors.bilibili.videoPanel, (moreSpeedsDiv) => { let ele = document.querySelector(WebSite.selectors.bilibili.speedBtn); if (ele) { ele.after(moreSpeedsDiv); } }); } catch (error) { console.error('Failed create speed button elements:', error); } } try { const removalConfigs = { Bilibili_AutoRemoveComments: WebSite.selectors.bilibili.commentsPanel, Bilibili_AutoRemovePip: WebSite.selectors.bilibili.pipBtn, Bilibili_AutoRemoveWide: WebSite.selectors.bilibili.wideBtn, Bilibili_AutoRemoveSpeed: WebSite.selectors.bilibili.speedsListBtn, Bilibili_AutoRemoveSettings: WebSite.selectors.bilibili.settingsBtn, }; for (const key in removalConfigs) { if (GM_getValue(Common.settingPanelItems[key].dataKey, false)) { elmGetter.get(removalConfigs[key]).then(item => { item.remove(); }); } } } catch (error) { console.error('Failed autoremove buttons:', error); } try { if (GM_getValue(Common.settingPanelItems.Bilibili_AutoWebFullscreen.dataKey, false)) { elmGetter.get(WebSite.selectors.bilibili.playerContainer).then(playItem => { if (playItem.classList.contains(WebSite.selectors.bilibili.webFullClass)) { return; } elmGetter.get(WebSite.selectors.bilibili.webFullBtn).then(item => { item.click(); }); }); } } catch (error) { console.error('Failed webfull or auto rate:', error); } if (GM_getValue(Common.settingPanelItems.Bilibili_AutoRate2x.dataKey, false)) { Common.setPlaybackRate(2.0); } currentVideoTitle = document.querySelector('title').text; }; handleBilibiliPage(); } } function main() { // 每次页面加载时,都先清理所有可能存在的定时器 if (youtubeLiveStreamCheck !== null) { clearInterval(youtubeLiveStreamCheck); youtubeLiveStreamCheck = null; } Common.currentLang = Common.detectLanguage(); Common.initSettingItems(window.location.href); GM_addStyle(settingPanelStyles); GM_registerMenuCommand(Common.geti18nText("menu_settings"), Common.togglePanel); const observerConfig = { childList: true }; const observer = new MutationObserver((mutations, observer) => { for (const mutation of mutations) { if (mutation.type === "childList") { if (currentVideoTitle !== mutation.addedNodes[0].data) { console.log('检测到页面变化,重新执行脚本'); console.log('Old Title:', currentVideoTitle); console.log('New Title:', mutation.addedNodes[0].data); console.log('---'); if (window.location.href.includes("youtube.com/watch")) { console.log('ob 执行Youtube脚本'); WebSite.youtube(); } else if (window.location.href.includes("bilibili.com/video")) { WebSite.bilibili(); } } } } }); const target = document.querySelector('title'); observer.observe(target, observerConfig); // 首次执行 if (window.location.href.includes("youtube.com/watch")) { console.log('main 执行Youtube脚本'); WebSite.youtube(); } else if (window.location.href.includes("bilibili.com/video")) { WebSite.bilibili(); } } main(); })();