显示当前所有直播
// ==UserScript== // @name Bilibili 动态页显示当前所有直播 // @description 显示当前所有直播 // @version 3.0 // @author Myitian // @license MIT // @namespace myitian.bili.tPage-showMoreLives // @match t.bilibili.com/* // @icon https://www.bilibili.com/favicon.ico // @grant none // ==/UserScript== /** * 获取 JSON * @param {string | URL} url * @returns {Promise<XMLHttpRequest>} */ function getJSON(url) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open('get', url, true); xhr.withCredentials = true; xhr.responseType = 'json'; xhr.onload = () => { if (xhr.status == 200) { resolve(xhr); } else { reject(xhr); } }; xhr.send(); }); } /** * 打开面板 */ async function moreBtn() { // 基底div const base = document.createElement('div'); base.className = 'sml-base' base.id = 'sml-base'; base.innerHTML = ` <style class="sml-style" id="sml-style"> .sml-mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #000; filter: alpha(opacity=65); -ms-filter: "alpha(opacity=65)"; opacity: .65; z-index: 10000; } .sml-mainbox { position: fixed; top: 50%; left: 50%; --width: 1000px; --height: 666px; width: var(--width); height: var(--height); margin-left: calc(var(--width) / -2); margin-top: calc(var(--height) / -2); border-radius: 4px; background-color: #fff; z-index: 10001; } .sml-livepanel { height: calc(100% - 58px); width: 100%; overflow: auto; } @media screen and (max-width:1030px) { .sml-mainbox { --width: 756px; } } @media screen and (max-width:786px) { .sml-mainbox { --width: 512px; } } @media screen and (max-width:542px) { .sml-mainbox { --width: 268px; } } @media screen and (max-height:690px) { .sml-mainbox { --height: 590px; } } @media screen and (max-height:614px) { .sml-mainbox { --height: 514px; } } @media screen and (max-height:538px) { .sml-mainbox { --height: 438px; } } @media screen and (max-height:462px) { .sml-mainbox { --height: 362px; } } @media screen and (max-height:386px) { .sml-mainbox { --height: 286px; } } @media screen and (max-height:310px) { .sml-mainbox { --height: 210px; } } @media screen and (max-height:234px) { .sml-mainbox { --height: 134px; } } .sml-title { position: relative; padding: 0 20px; height: 50px; line-height: 50px; font-size: 16px; color: #222; border-bottom: 1px solid #e5e9ef; } .sml-icon { position: absolute; line-height: 50px; width: 13px; height: 50px; } .sml-refresh { right: 60px; background: url("data:image/png; base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALBAMAAABbgmoVAAAAJFBMVEUAAACZoqqZoqqZoqqZoqqZoqqZoqqZoqqZoqqZoqqZoqqZoqpJ643QAAAAC3RSTlMAv4DPn2AwQN+PUFHSbdYAAABLSURBVAjXY9hQvHtzAQPDpi0JKZsYGHYHMDBEL2DYoRTAwGLAICg4gYFNgAEKxBgYJgoKMJgwMARpbGBYDVK5gYEJpG8DAwPIlA0ABXgUExBfrckAAAAASUVORK5CYII=") no-repeat 50%; cursor: pointer; } .sml-exit { right: 20px; background: url("data:image/png; base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANBAMAAACAxflPAAAAIVBMVEUAAACao6qZpa2bpKqapaqZo6qboqqapKuZoqqao6qZoqqZADmtAAAACnRSTlMA6kFUMM2Eat+9b+FOdgAAAENJREFUCNdjgAFOBSDhysDAJMTAwCIMZCsqMDgaMIAEQFyQQKEBmGZfBtHhmAjUAlLMJATmGgC1gLgQM1hBijXgxgMACcIFDbl0pdMAAAAASUVORK5CYII=") no-repeat 50%; cursor: pointer; } .sml-titlesp2 { color: #99a2aa; letter-spacing: 0; font-size: 14px; } .sml-livecontainer { display: inline-flex; flex-grow: 0; flex-shrink: 0; position: relative; margin-bottom: 10px; margin-top: 10px; margin-left: 16px; height: 56px; -webkit-box-align: center; -ms-flex-align: center; align-items: center; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; width: 224px; } .sml-a1 { width: 38px; height: 38px; border-radius: 22px; position: relative; margin: 1px; margin-right: 11px; -ms-flex-negative: 0; flex-shrink: 0; background-size: cover; background-color: #ddd; -webkit-box-shadow: 0 0 0 1px #f25d8e; box-shadow: 0 0 0 1px #f25d8e; border: 1px solid #fff; } .sml-a2 { text-overflow: ellipsis; overflow: hidden; word-break: keep-all; max-width: 176px; padding-right: 16px; letter-spacing: 0; } .sml-upname { font-size: 14px; color: #222; line-height: 20px; max-height: 20px; display: -webkit-box; -webkit-line-clamp: 1; } .sml-areaname { font-size: 12px; color: #999; line-height: 20px; max-height: 20px; display: -webkit-box; -webkit-line-clamp: 1; } .sml-areaname:hover { color: #23ade5 } .sml-livename { font-size: 12px; color: #6d757a; line-height: 16px; word-break: break-all; word-break: break-word; text-overflow: ellipsis; max-height: 32px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .sml-word { word-break: break-all; word-break: break-word; text-overflow: ellipsis; -webkit-box-orient: vertical; overflow: hidden; } .sml-center { text-align: center; } .sml-block { display: block; } </style> <div class="sml-mask sml-block" id="sml-mask"></div> <div class="sml-mainbox sml-block" id="sml-mainbox"> <div class="sml-title"><!-- --><span>正在直播</span><!-- --><span class="sml-titlesp2" id="sml-titlesp2">(0)</span><!-- --><a class="sml-refresh sml-icon"></a><!-- --><a class="sml-exit sml-icon"></a><!-- --></div> <div class="sml-livepanel sml-block" id="sml-livepanel"></div> </div> `; const refresh = base.querySelector('.sml-refresh'); refresh.onclick = loadContent; const exit = base.querySelector('.sml-exit'); exit.onclick = exitBtn; document.body.appendChild(base); await loadContent(); } /** * 加载内容 */ async function loadContent() { const livepanel = document.querySelector('#sml-livepanel'); livepanel.innerHTML = ''; const titlesp2 = document.querySelector('#sml-titlesp2'); let liveCount = 0; let hasMore = true; let page = 1; let maxPage = 1; while (hasMore && page <= maxPage) { try { const xhr = await getJSON(`https://api.live.bilibili.com/xlive/web-ucenter/user/following?page_size=29&page=${page}`); const jsondata = xhr.response.data; maxPage = jsondata.totalPage; for (const user of jsondata.list) { if (user.live_status == 0) { hasMore = false; break; } liveCount++; const roomid = user.roomid; const link = `https://live.bilibili.com/${roomid}` // 容器div const livecontainer = document.createElement('div'); livecontainer.className = 'sml-livecontainer'; // UP主头像a const a1 = document.createElement('a'); a1.className = 'sml-block sml-a1'; a1.style = `background-image:url('${user.face}@50w_50h.png');`; a1.href = link; a1.target = '_blank'; // 直播信息a const a2 = document.createElement('a'); a2.className = 'sml-block sml-a2'; a2.href = link; a2.target = '_blank'; // UP主名称div const upname = document.createElement('div'); upname.className = 'sml-word sml-block sml-upname'; upname.innerText = user.uname; // 分区名称div const areaname = document.createElement('a'); areaname.className = 'sml-word sml-block sml-areaname'; areaname.target = '_blank'; // 直播名称div const livename = document.createElement('div'); livename.className = 'sml-word sml-block sml-livename'; livename.innerText = user.title; getJSON(`https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=${roomid}`).then(xhr => { const jsondata = xhr.response.data.room_info; // 获取房间信息 areaname.innerText = jsondata.area_name; areaname.href = `https://live.bilibili.com/p/eden/area-tags?parentAreaId=${jsondata.parent_area_id}&areaId=${jsondata.area_id}`; }, _ => { // 房间信息获取失败(非200) }); a2.appendChild(upname); a2.appendChild(areaname); a2.appendChild(livename); livecontainer.appendChild(a1); livecontainer.appendChild(a2); livepanel.appendChild(livecontainer); } } catch (xhr) { const err = document.createElement('p'); err.className = 'sml-center'; err.innerText = `直播数据获取失败!(${xhr.status} ${xhr.statusText})`; livepanel.appendChild(err); } page++; titlesp2.innerText = `(${liveCount})`; // 人数 } if (liveCount == 0) { const err = document.createElement('p'); err.className = 'sml-center'; err.innerText = '当前无直播'; livepanel.appendChild(err); } } /** * 退出面板 */ function exitBtn() { document.body.removeChild(document.querySelector('#sml-base')); } /** * 重新放置元素,清除事件处理器 * @param {HTMLElement} oldEelement * @returns {HTMLElement} */ function replaceElement(oldEelement) { const newElement = document.createElement(oldEelement.tagName); for (const t of oldEelement.attributes) { newElement.setAttribute(t.name, t.value); } for (const t of oldEelement.childNodes) { newElement.appendChild(t); } const parent = oldEelement.parentElement; const next = oldEelement.nextSibling; parent.removeChild(oldEelement); parent.insertBefore(newElement, next); return newElement; } /** * 替换按钮功能 */ function replaceBtnFunc() { const moreBtnEle = document.querySelector('.bili-dyn-live-users__more'); if (moreBtnEle) { if (!document.querySelector('#sml-stop-event')) { const moreBtnEleNew = replaceElement(moreBtnEle); moreBtnEleNew.id = 'sml-stop-event'; moreBtnEleNew.addEventListener('click', moreBtn); } window.removeEventListener('keydown', replaceBtnFunc); window.removeEventListener('mousemove', replaceBtnFunc); window.removeEventListener('mousedown', replaceBtnFunc); } } window.addEventListener('keydown', replaceBtnFunc); window.addEventListener('mousemove', replaceBtnFunc); window.addEventListener('mousedown', replaceBtnFunc);