// ==UserScript== // @name Bilibili直播纯音频模式 // @description 在bilibili直播界面,点击右下角切换直播/音频模式,减少资源占用,方便黑听 // @version 1.0 // @author Li Li // @match *://live.bilibili.com/* // @grant none // @tag bilibili直播 音频播放 // ==/UserScript== (function() { 'use strict'; let audioOnly = false; let hlsScriptLoaded = false; let audioElement = null; let currentHls = null; const videoColor = '#00A1D6'; const audioColor = '#27ae60'; const videoSVG = ``; const audioSVG = ``; const btn = document.createElement('button'); btn.innerHTML = videoSVG; btn.style.position = 'fixed'; btn.style.width = '50px'; btn.style.height = '50px'; btn.style.borderRadius = '50%'; btn.style.border = 'none'; btn.style.background = 'transparent'; btn.style.padding = '11px'; btn.style.cursor = 'pointer'; btn.style.display = 'flex'; btn.style.alignItems = 'center'; btn.style.justifyContent = 'center'; btn.style.transition = 'all 0.2s ease'; btn.style.zIndex = 9999; btn.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)'; // 恢复上次拖拽位置 const savedPos = JSON.parse(localStorage.getItem('bilibili_audio_btn_pos') || '{}'); if(savedPos.left && savedPos.top){ btn.style.left = savedPos.left; btn.style.top = savedPos.top; } else { btn.style.right = '50px'; btn.style.bottom = '50px'; } document.body.appendChild(btn); function setIconColor(color){ const svg = btn.querySelector('svg'); if(svg) svg.setAttribute('fill', color); } // 悬浮效果 btn.addEventListener('mouseover', ()=>{ btn.style.background = audioOnly ? audioColor : videoColor; setIconColor('#FFFFFF'); btn.style.boxShadow = '0 8px 16px rgba(0,0,0,0.4)'; }); btn.addEventListener('mouseout', ()=>{ btn.style.background = 'transparent'; setIconColor(audioOnly ? audioColor : videoColor); btn.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)'; }); // 拖拽逻辑 let isDragging = false; btn.addEventListener('mousedown', e=>{ isDragging = true; e.preventDefault(); }); document.addEventListener('mousemove', e=>{ if(!isDragging) return; // 鼠标在按钮中心 let left = e.clientX - btn.offsetWidth/2; let top = e.clientY - btn.offsetHeight/2; // 边界限制 left = Math.max(0, Math.min(left, window.innerWidth - btn.offsetWidth)); top = Math.max(0, Math.min(top, window.innerHeight - btn.offsetHeight)); btn.style.left = left + 'px'; btn.style.top = top + 'px'; btn.style.right = 'auto'; btn.style.bottom = 'auto'; }); document.addEventListener('mouseup', ()=>{ if(!isDragging) return; isDragging = false; localStorage.setItem('bilibili_audio_btn_pos', JSON.stringify({ left: btn.style.left, top: btn.style.top })); }); btn.addEventListener('click', toggleAudioMode); async function loadHlsJs(){ if(hlsScriptLoaded) return; hlsScriptLoaded = true; return new Promise((resolve,reject)=>{ const script = document.createElement('script'); script.src='https://cdn.jsdelivr.net/npm/hls.js@latest'; script.onload=()=>resolve(); script.onerror=reject; document.head.appendChild(script); }); } async function toggleAudioMode(){ const videos = document.querySelectorAll('video'); if(!audioOnly){ // 切换到音频模式 audioOnly = true; btn.innerHTML = audioSVG; setIconColor(audioColor); videos.forEach(v=>v.style.display='none'); await loadHlsJs(); const src = Array.from(videos).find(v=>v.currentSrc||v.src)?.currentSrc || Array.from(videos).find(v=>v.src)?.src; if(!src) return; if(!audioElement){ audioElement = document.createElement('audio'); audioElement.autoplay = true; audioElement.controls = false; audioElement.style.display='none'; document.body.appendChild(audioElement); if(Hls.isSupported()){ currentHls = new Hls({autoStartLoad:true,startLevel:-1,maxBufferLength:30}); currentHls.loadSource(src); currentHls.attachMedia(audioElement); currentHls.on(Hls.Events.MANIFEST_PARSED, ()=>{ audioElement.play().catch(()=>{}); }); } else { audioElement.src = src; setTimeout(()=>audioElement.play().catch(()=>{}),0); } } } else { // 切回视频模式 audioOnly = false; btn.innerHTML = videoSVG; setIconColor(videoColor); videos.forEach(v=>v.style.display=''); if(audioElement){ audioElement.pause(); audioElement.src=''; if(currentHls){currentHls.destroy();currentHls=null;} audioElement.remove(); audioElement=null; } } } // 观察动态加载的视频 const observer = new MutationObserver(()=>{ if(audioOnly){ document.querySelectorAll('video').forEach(video=>{ if(video.style.display!=='none') toggleAudioMode(); }); } }); observer.observe(document.body,{childList:true,subtree:true}); })();