// ==UserScript== // @name Bilibili直播纯音频模式 // @description 在bilibili直播界面,点击右下角切换直播/音频模式,减少资源占用,方便黑听 // @version 1.1 // @author Li Li // @match *://live.bilibili.com/* // @grant none // @tag bilibili直播 音频播放 // ==/UserScript== ;(() => { // src/style.css var style_default = `.bili-audio-btn { position: fixed; top: 0; left: 0; width: 50px; height: 50px; border-radius: 50%; border: none; background: transparent; padding: 11px; cursor: grab; user-select: none; display: flex; align-items: center; justify-content: center; transition: background 0.2s ease, box-shadow 0.2s ease; z-index: 9999; box-shadow: 0 4px 8px rgba(0,0,0,0.3); } ` // src/svg.js var videoPath = 'M800 224h-51.2l44.8-44.8c19.2-19.2 19.2-57.6 0-76.8s-57.6-19.2-76.8 0l-128 121.6h-153.6l-128-121.6c-19.2-19.2-57.6-19.2-76.8 0s-19.2 57.6 0 76.8L275.2 224H224c-89.6 0-160 70.4-160 160v345.6c0 96 70.4 166.4 160 166.4h569.6c89.6 0 160-70.4 160-160V384c6.4-89.6-64-160-153.6-160z m44.8 505.6c0 32-25.6 57.6-57.6 57.6H230.4c-32 0-57.6-25.6-57.6-57.6V390.4c0-32 25.6-57.6 57.6-57.6h556.8c32 0 57.6 25.6 57.6 57.6v339.2zM345.6 448c-32 0-57.6 25.6-57.6 57.6v57.6c0 32 25.6 57.6 57.6 57.6s57.6-25.6 57.6-57.6v-57.6c0-32-25.6-57.6-57.6-57.6z m332.8 0c-32 0-57.6 25.6-57.6 57.6v57.6c0 32 25.6 57.6 57.6 57.6s57.6-25.6 57.6-57.6v-57.6c0-32-25.6-57.6-57.6-57.6z', audioPath = 'M768 436.906667v53.76a21.333333 21.333333 0 0 1-21.333333 21.333333h-42.666667a21.333333 21.333333 0 0 1-21.333333-21.333333v-11.52a115.626667 115.626667 0 0 0-64-100.693334c-7.68-4.266667-14.506667-9.386667-21.333334-14.08v384A161.28 161.28 0 0 1 426.666667 896a161.28 161.28 0 0 1-170.666667-149.333333 161.28 161.28 0 0 1 170.666667-149.333334 183.04 183.04 0 0 1 85.333333 20.906667V149.333333a21.333333 21.333333 0 0 1 21.333333-21.333333h42.666667a21.333333 21.333333 0 0 1 21.333333 21.333333v7.253334a113.92 113.92 0 0 0 61.013334 103.253333A197.12 197.12 0 0 1 768 436.906667z' function createSvg(pathD, color) { let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') ;(svg.setAttribute('viewBox', '0 0 1024 1024'), svg.setAttribute('width', '28'), svg.setAttribute('height', '28'), svg.setAttribute('fill', color)) let path = document.createElementNS('http://www.w3.org/2000/svg', 'path') return (path.setAttribute('d', pathD), svg.appendChild(path), svg) } var videoSvg = color => createSvg(videoPath, color), audioSvg = color => createSvg(audioPath, color) // src/drag.js var STORAGE_KEY = 'bilibili_audio_btn_pos' function loadPosition(btn) { let saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}') return saved.x != null && saved.y != null ? ((btn.style.transform = `translate(${saved.x}px, ${saved.y}px)`), !0) : !1 } function makeDraggable(btn) { let startX = 0, startY = 0, originX = 0, originY = 0 function onPointerDown(e) { btn.setPointerCapture(e.pointerId) let r = btn.getBoundingClientRect() ;((startX = e.clientX), (startY = e.clientY), (originX = r.left), (originY = r.top), (btn.dragged = !1), (btn.style.cursor = 'grabbing'), e.preventDefault()) } function onPointerMove(e) { if (!btn.hasPointerCapture(e.pointerId)) return btn.dragged = !0 let x = Math.max( 0, Math.min( originX + (e.clientX - startX), window.innerWidth - btn.offsetWidth ) ), y = Math.max( 0, Math.min( originY + (e.clientY - startY), window.innerHeight - btn.offsetHeight ) ) btn.style.transform = `translate(${x}px, ${y}px)` } function onPointerUp(e) { if ( btn.hasPointerCapture(e.pointerId) && (btn.releasePointerCapture(e.pointerId), (btn.style.cursor = ''), btn.dragged) ) { let r = btn.getBoundingClientRect() localStorage.setItem( STORAGE_KEY, JSON.stringify({ x: r.left, y: r.top }) ) } } ;(btn.addEventListener('pointerdown', onPointerDown), btn.addEventListener('pointermove', onPointerMove), btn.addEventListener('pointerup', onPointerUp)) } // src/audio.js var audioOnly = !1, audioElement = null function startAudio(src) { ;((audioElement = document.createElement('audio')), (audioElement.autoplay = !0), (audioElement.controls = !1), (audioElement.style.display = 'none'), document.body.appendChild(audioElement), (audioElement.src = src)) } function stopAudio() { audioElement && (audioElement.pause(), (audioElement.src = ''), audioElement.remove(), (audioElement = null)) } function toggle(videos, btn, onUpdate) { if (audioOnly) ((audioOnly = !1), onUpdate('video'), videos.forEach(v => (v.style.display = '')), stopAudio()) else { ;((audioOnly = !0), onUpdate('audio'), videos.forEach(v => (v.style.display = 'none'))) let src = Array.from(videos).find(v => v.currentSrc || v.src)?.currentSrc || Array.from(videos).find(v => v.src)?.src if (!src) return audioElement || startAudio(src) } } function isAudioOnly() { return audioOnly } // src/observer.js function observeVideoChanges(btn, updateUi) { let observer = new MutationObserver(() => { isAudioOnly() && document.querySelectorAll('video').forEach(video => { video.style.display !== 'none' && toggle(document.querySelectorAll('video'), btn, updateUi) }) }) return ( observer.observe(document.body, { childList: !0, subtree: !0 }), observer ) } // src/index.js if ( window.top !== window.self && !document.querySelector('.bili-audio-btn') ) { let updateUi = function (mode) { ;((currentMode = mode), (btn.innerHTML = ''), btn.appendChild( mode === 'audio' ? audioSvg(audioColor) : videoSvg(videoColor) )) }, videoColor = '#00A1D6', audioColor = '#27ae60', style = document.createElement('style') ;((style.textContent = style_default), document.head.appendChild(style)) let btn = document.createElement('button') if ( ((btn.className = 'bili-audio-btn'), btn.appendChild(videoSvg(videoColor)), !loadPosition(btn)) ) { let x = window.innerWidth - 50 - btn.offsetWidth, y = window.innerHeight - 50 - btn.offsetHeight btn.style.transform = `translate(${x}px, ${y}px)` } ;(document.body.appendChild(btn), makeDraggable(btn)) let currentMode = 'video' ;(btn.addEventListener('click', () => { if (btn.dragged) return let videos = document.querySelectorAll('video') toggle(videos, btn, updateUi) }), btn.addEventListener('mouseover', () => { ;((btn.style.background = currentMode === 'audio' ? audioColor : videoColor), btn.querySelector('svg').setAttribute('fill', '#FFFFFF'), (btn.style.boxShadow = '0 8px 16px rgba(0,0,0,0.4)')) }), btn.addEventListener('mouseout', () => { ;((btn.style.background = 'transparent'), btn .querySelector('svg') .setAttribute( 'fill', currentMode === 'audio' ? audioColor : videoColor ), (btn.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)')) }), observeVideoChanges(btn, updateUi)) } })()