您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
bilibili直播间显示开播时间,直播时长,粉丝数,人气值,分区,在线人数,封面,其他关注直播,pk等信息
当前为
/// ==UserScript== // @name bilibili直播间显示更多信息 // @description bilibili直播间显示开播时间,直播时长,粉丝数,人气值,分区,在线人数,封面,其他关注直播,pk等信息 // @version 3.11 // @author wqz // @match https://live.bilibili.com/* // @icon https://www.bilibili.com/favicon.ico // @license MIT // @grant unsafeWindow // @namespace https://greasyfork.org/users/1060750 // ==/UserScript== ;(function () { const isme = false let Config = { livetime: { enable: true, // 显示直播开始时间,持续时间 }, fans: { enable: true, // 显示粉丝,人气 updateFrequency: 1000, // 粉丝,人气刷新频率(ms) maxWidth: 350, // 显示最大宽度(显示有问题才需要考虑改) }, rank: { //(目前b站官方已经显示在线人数了 2024-05-05) enable: true, // 显示高能榜(在线人数) showmode: 0, // 0:(同1,不做修改) ,1:[总在线人数], 2:[非0值在线人数/总人数], 3:[非0在线人数] updateFrequency: 1000, // 在线人数刷新频率(ms) }, cover: { enable: true, // 显示直播封面 location: 'afterbegin', // 封面显示位置(afterbegin,beforeend) }, pk: { enable: true, // 显示pk相关信息 updateFrequency: 5 * 1000, // 刷新频率(ms) }, area: { enable: true, // 显示分区信息 updateFrequency: 10 * 1000, // 刷新频率(ms) }, otherLive: { enable: true, // 显示其他关注的直播 updateFrequency: 60 * 1000, // 刷新频率(ms) showTime: true, // 是否在头像下面显示直播时长 location: 'right', // 显示位置(left,right) opacity: 1.0, // 透明度 maxHeight: 550, // 最大显示高度(px) imgSize: 40, // 头像大小 }, } const W = typeof unsafeWindow === 'undefined' ? window : unsafeWindow let G = { roomId_short: 0, // 直播房间号(short,从当前url获取) roomId: 0, // 直播房间号 uid: 0, // 主播uid async init() { this.roomId_short = api.getRoomID() if (!this.roomId_short) return false liveInfo = await api.getLiveInfo(this.roomId_short) this.uid = liveInfo.data.uid this.roomId = liveInfo.data.room_id console.log(`roomId(short_id): ${this.roomId_short} `) console.log(`roomId: ${this.roomId} `) console.log(`uid: ${this.uid}`) return true }, } let Dao = { liveInfo: null, liveStartTimeStamp: null, // 开播时间时间戳 rankCount: 0, // 非零在线人数 从api获取 guardCount: 0, // 舰长数 rank1: 0, // websocket获取 rank2: 0, // websocket获取 aliveList: null, // 其他关注直播 inpk: false, pk: { pkInfo: null, roomId: null, uid: null, uname: null, liveInfo: null, rankCount: 0, guardCount: 0, }, async initData() { this.liveInfo = await api.getLiveInfo(G.roomId) // console.log('liveInfo', this.liveInfo) this.liveStartTimeStamp = await api.getLiveTimeStamp(G.roomId) // console.log('liveStartTimeStamp', this.liveStartTimeStamp) Config.otherLive.enable && (this.aliveList = await api.getAliveList()) Config.rank.enable && Config.rank.showmode >= 0 && ut.hook_wrapper() }, updateData() { const update_liveInfo = async () => { this.liveInfo = await api.getLiveInfo(G.roomId) } const update_rankCount = async () => { this.rankCount = await api.getOnlinePeople(G.roomId, G.uid) this.guardCount = await api.getGuardCount(G.roomId, G.uid) } const update_aliveList = async () => { this.aliveList = await api.getAliveList() } const update_pkInfo = async () => { const pkInfo = await api.getPkInfo(G.roomId) if (pkInfo.data.pk_id) { newpk = !this.inpk this.inpk = true this.pk.pkInfo = pkInfo.data.init_info.room_id == G.roomId ? pkInfo.data.match_info : pkInfo.data.init_info this.pk.roomId = this.pk.pkInfo.room_id this.pk.uid = this.pk.pkInfo.uid this.pk.uname = this.pk.pkInfo.uname this.pk.liveInfo = await api.getLiveInfo(this.pk.roomId) this.pk.rankCount = await api.getOnlinePeople(this.pk.roomId, this.pk.uid) this.pk.guardCount = await api.getGuardCount(this.pk.roomId, this.pk.uid) if (newpk) { console.log( `pk开始(${this.pk.roomId}) ${this.pk.uname} : ${this.pk.liveInfo.data.title}` ) } } else { this.inpk = false this.pk.pkInfo = null this.pk.roomId = null this.pk.uid = null this.pk.liveInfo = null this.pk.rankCount = 0 this.pk.guardCount = 0 } } Config.fans.enable && update_liveInfo(), setInterval(update_liveInfo, Config.fans.updateFrequency) Config.rank.enable && update_rankCount(), setInterval(update_rankCount, Config.rank.updateFrequency) Config.otherLive.enable && update_aliveList(), setInterval(update_aliveList, Config.otherLive.updateFrequency) Config.pk.enable && update_pkInfo(), setInterval(update_pkInfo, Config.pk.updateFrequency) }, async run() { // 定时更新数据 await this.initData() this.updateData() }, } // 直播时间,开播时长 let LiveTimeModule = { html: ` <div class="live-skin-normal-a-text livetimeContainer" > <div id="liveStartTime">0000-00-00 00:00:00</div> <div id="liveDuration">0小时0分钟0秒</div> </div> `, css: ` .livetimeContainer { display: flex; margin-left: 10px; user-select: text; flex-direction: column; opacity: 1; } `, dom: { liveStartTime: null, // 开播时间 liveDuration: null, // 直播持续时间 }, initialized: false, prefix: ['', ''], // perfix: ['开播时间:', '直播时长:'], async initUI() { ut.addCSS(this.css) let container = await ut.waitForElement( '#head-info-vm > div > div > div.upper-row > div.left-ctnr.left-header-area' ) if (container) { container.insertAdjacentHTML('beforeend', this.html) this.dom.liveStartTime = document.getElementById('liveStartTime') this.dom.liveDuration = document.getElementById('liveDuration') this.initialized = true } this.dom.liveStartTime.textContent = `${this.prefix[0]}${Dao.liveInfo.data.live_time}` }, updateUI() { if (liveInfo.data.live_status == 0) { this.dom.liveStartTime.textContent = `当前状态:未开播` return } if (liveInfo.data.live_status == 2) { this.dom.liveStartTime.textContent = `当前状态:轮播中` // liveStatus = liveInfo.data.live_status (0:未开播 1:直播中 2:轮播中)`); return } const currentTime = new Date() const startTime = new Date(Dao.liveStartTimeStamp * 1000) const elapsedSeconds = Math.floor((currentTime - startTime) / 1000) let timeText = ut.formatTime(elapsedSeconds, '{h}小时 {mm}分钟 {ss}秒') timeText = timeText.startsWith('0小时 ') ? timeText.slice(4) : timeText this.dom.liveDuration.textContent = `${this.prefix[1]}${timeText}` }, async run() { await this.initUI() this.updateUI() setInterval(() => this.updateUI(), 1000) }, } // 粉丝,人气 let FansModule = { html: ` <div id="fans" class="right-text live-skin-normal-a-text v-middle preserve-space" > `, css: ` #fans{ display: flex; opacity: 1; margin-bottom: 2px; padding-left: 15px; justify-content: flex-end; } .preserve-space{ white-space: pre; } .fansContainer{ display: flex; flex-direction: row; flex-wrap: wrap-reverse; align-content: center; justify-content: right; align-items: center; max-width: ${Config.fans.maxWidth}px; } `, dom: { fans: null, }, initialized: false, prefix: ['粉丝:', '人气:'], async initUI() { ut.addCSS(this.css) let container = await ut.waitForElement( '#head-info-vm > div > div > div.upper-row > div.right-ctnr' ) if (container) { container.insertAdjacentHTML('beforeend', this.html) container.classList.add('fansContainer') this.dom.fans = document.getElementById('fans') this.initialized = true } }, updateUI() { // const pkfans = inpk() ? ` / ${pk.liveInfo.data.attention}` : '' // const pkonline = inpk() ? ` / ${pk.liveInfo.data.online}` : '' // this.dom.fans.textContent = `粉丝:${liveInfo.data.attention}${pkfans} 人气:${liveInfo.data.online}${pkonline}`; this.dom.fans.textContent = Dao.inpk ? `${this.prefix[0]}${Dao.liveInfo.data.attention} / ${Dao.pk.liveInfo.data.attention} ${this.prefix[1]}${Dao.liveInfo.data.online} / ${Dao.pk.liveInfo.data.online}` : `${this.prefix[0]}${Dao.liveInfo.data.attention} ${this.prefix[1]}${Dao.liveInfo.data.online}` }, async run() { await this.initUI() this.updateUI() setInterval(() => this.updateUI(), Config.fans.updateFrequency) }, } // 高能用户 let RankModule = { dom: { rank: null, guard: null, }, initialized: false, prefix: ['高能用户', '大航海'], dirtyfix: false, async initUI() { let container = await ut.waitForElement('#rank-list-ctnr-box > div.tabs > ul') if (container) { this.dom.rank = container.firstElementChild this.dom.guard = container.lastElementChild this.initialized = true } }, updateUI() { thisguard = ut.getFirstNumber(this.dom.guard.textContent) if (Dao.inpk) { // todo textContent 会破坏了原来的功能 this.dom.rank.textContent = `${this.prefix[0]}(${Dao.rankCount}/${Dao.pk.rankCount})` this.dom.guard.textContent = `${this.prefix[1]}(${Dao.guardCount}/${Dao.pk.guardCount})` this.dirtyfix = true // fix return } else if (this.dirtyfix) { this.dom.rank.textContent = `${this.prefix[0]}(${Dao.rank2})` this.dom.guard.textContent = `${this.prefix[1]}(${Dao.guardCount})` } switch (Config.rank.showmode) { case 1: this.dom.rank.textContent = `${this.prefix[0]}(${Dao.rank2})` break case 2: this.dom.rank.textContent = `${this.prefix[0]}(${Dao.rank1}/${Dao.rank2})` break case 3: this.dom.rank.textContent = `${this.prefix[0]}(${Dao.rankCount})` break default: break } }, async run() { await this.initUI() this.updateUI() setInterval(() => this.updateUI(), Config.rank.updateFrequency) }, } // 直播封面 let CoverModule = { html: ` <div data-v-03a54292 class="announcement-cntr" > <div data-v-03a54292 class="header"> <p data-v-03a54292 style="color:#ff6699">直播封面 <span id="updateButton" data-v-03a54292>2020-9-24 点击刷新</span> </p> </div> <div data-v-03a54292 class="content"> <img alt="直播封面" id="cover" > </div> </div> `, css: ` #cover{ width: 100%; height: auto; } `, dom: { cover: null, updateButton: null, }, initialized: false, rankPrefix: null, async initUI() { ut.addCSS(this.css) let CoverContainer = await ut.waitForElement( '#sections-vm > div.section-block.f-clear.z-section-blocks > div.right-container' ) if (CoverContainer) { // CoverContainer.insertAdjacentHTML("beforeend", this.html) CoverContainer.insertAdjacentHTML(Config.cover.location, this.html) this.dom.cover = document.getElementById('cover') this.dom.updateButton = document.getElementById('updateButton') this.dom.cover.addEventListener('click', () => this.updateUI()) this.dom.updateButton.addEventListener('click', () => this.updateUI()) this.initialized = true } }, updateUI() { const timestr = ut.formatDate(new Date(), 'YYYY-MM-DD hh:mm:ss') this.dom.updateButton.textContent = `${timestr} 更新` this.dom.cover.src = Dao.liveInfo.data.user_cover }, async run() { await this.initUI() this.updateUI() setInterval(() => this.updateUI(), 60 * 1000) }, } // 其他关注直播 let otherLiveModule = { html: { roomCardContainer: ` <div id = "roomCardContainer"></div> `, roomCard: ` <div class="roomCard"> <a href="{room.link}" target="_blank"> <img class="roomAvatar" src="{room.face}" alt="{room.title}" title="{room.uname} : {room.title}"> </a> {roomText} </div> `, pkRoomCard: ` <div class="roomCard"> <a href="{room.link}" target="_blank"> <img id="pkAvatar" src="{room.face}" alt="{room.title}" title="{room.uname} : {room.title}"> </a> {roomText} </div> `, }, css: ` #roomCardContainer { position: fixed; z-index: 9999; display: flex; flex-direction: column; flex-wrap: ${Config.otherLive.location == 'right' ? 'wrap-reverse' : 'wrap'}; opacity:${Config.otherLive.opacity}; align-content: center; justify-content: flex-end; align-items: center; max-height: ${Config.otherLive.maxHeight}px; ${Config.otherLive.location}: 5px; top: calc(50% + 32px); transform: translateY(-50%); } #roomCardContainer .roomCard{ padding-bottom: 5px; padding-left: 5px; display: flex; justify-content: center; align-items: center; flex-direction: column; align-content: center } #roomCardContainer .roomCard .roomText{ /* border-radius: 20%; border: 1px solid #0095ff; */ min-width: -webkit-fill-available; text-align: center; background-color: rgba(255, 255, 255, 0.5); } #roomCardContainer .roomCard .roomAvatar { /* 头像O */ border: 2px solid #0095ff; border-radius: 50%; opacity: 1; width: ${Config.otherLive.imgSize}px; height: ${Config.otherLive.imgSize}px; } #roomCardContainer .roomCard #pkAvatar { /* 头像O */ border: 2px solid #ff0000; border-radius: 50%; opacity: 1; width: ${Config.otherLive.imgSize}px; height: ${Config.otherLive.imgSize}px; } `, dom: { aliveList: null, }, initialized: false, async initUI() { ut.addCSS(this.css) let body = document.querySelector('body') body.insertAdjacentHTML('beforeend', this.html.roomCardContainer) this.dom.aliveList = document.getElementById('roomCardContainer') if (isme) { ut.mapKey('5', () => ut.toggleShow(otherLiveModule.dom.aliveList, 'flex')) ut.mapKey('6', () => ut.changeOpacity(otherLiveModule.dom.aliveList, -0.2)) ut.mapKey('7', () => ut.changeOpacity(otherLiveModule.dom.aliveList, 0.2)) } this.initialized = true }, updateUI() { this.dom.aliveList = document.getElementById('roomCardContainer') this.dom.aliveList.innerHTML = '' let cnt = 0 if (Dao.inpk) { const pkmiliSeconds = new Date() - new Date(Dao.pk.liveInfo.data.live_time) const pkSeconds = Math.floor(pkmiliSeconds / 1000) let pk_roomLiveTime = ut.formatTime(pkSeconds, '{h}h{mm}m', (Ltrip0 = true)) let pk_roomText = Config.otherLive.showTime ? `<div class="roomText"> ${pk_roomLiveTime} </div> ` : '' const pkRoomCard = this.html.pkRoomCard .replace('{room.link}', `https://live.bilibili.com/${Dao.pk.roomId}`) .replace(/{room.title}/g, Dao.pk.liveInfo.data.title) .replace('{room.face}', Dao.pk.pkInfo.face) .replace('{room.uname}', Dao.pk.uname) .replace('{roomText}', pk_roomText) this.dom.aliveList.insertAdjacentHTML('beforeend', pkRoomCard) cnt++ } for (const [index, room] of Dao.aliveList.entries()) { if (room.room_id == G.roomId) continue let roomLiveTime = ut.formatTime(room.live_time, '{h}h{mm}m', (Ltrip0 = true)) let roomText = Config.otherLive.showTime ? `<div class="roomText"> ${roomLiveTime} </div> ` : '' const roomCard = this.html.roomCard .replace('{room.link}', room.link) .replace('{room.face}', room.face) .replace(/{room.title}/g, room.title) .replace('{room.uname}', room.uname) .replace('{roomText}', roomText) this.dom.aliveList.insertAdjacentHTML('beforeend', roomCard) if (++cnt >= 24) break } }, async run() { await this.initUI() this.updateUI() const updateFrequency = Math.min(Config.otherLive.updateFrequency, Config.pk.updateFrequency) setInterval(() => this.updateUI(), updateFrequency) }, } // 分区信息 let areaModule = { html: { area: ` <span class="live-skin-normal-a-text v-middle myArea"> 分区: <a class="areaLink" id="fArea" href="父分区链接">父分区</a> - <a class="areaLink" id="area" href="子分区链接">子分区</a> </span> `, pk_area: ` <span class="live-skin-normal-a-text v-middle myArea" id="pk_container"> pk分区: <a class="areaLink" id="pk_fArea" href="父分区链接">父分区</a> - <a class="areaLink" id="pk_area" href="子分区链接">子分区</a> </span> `, }, css: ` .myArea{ font-size: 16px; margin-bottom: 20px; color: #61666d; } .areaLink { color: black; } .areaLink:hover { color: #ff6699; } .areaLink:active { color: black; } #pk_container{ padding-left:50px; } `, dom: { area: null, fArea: null, pk_container: null, pk_area: null, pk_fArea: null, }, initialized: false, async initUI() { ut.addCSS(this.css) let container = await ut.waitForElement( '#sections-vm > div.section-block.f-clear.z-section-blocks > div.left-container > div.room-feed.trends > ul' ) if (container) { container.insertAdjacentHTML('beforeend', this.html.area) container.insertAdjacentHTML('beforeend', this.html.pk_area) this.dom.area = document.getElementById('area') this.dom.fArea = document.getElementById('fArea') this.dom.pk_container = document.getElementById('pk_container') this.dom.pk_area = document.getElementById('pk_area') this.dom.pk_fArea = document.getElementById('pk_fArea') this.initialized = true } }, updateUI() { // 页面变化可能dom找不到 this.dom.area = document.getElementById('area') this.dom.fArea = document.getElementById('fArea') this.dom.area.textContent = Dao.liveInfo.data.area_name this.dom.fArea.textContent = Dao.liveInfo.data.parent_area_name this.dom.area.href = `https://live.bilibili.com/p/eden/area-tags?parentAreaId=${Dao.liveInfo.data.parent_area_id}&areaId=${Dao.liveInfo.data.area_id}` this.dom.fArea.href = `https://live.bilibili.com/p/eden/area-tags?parentAreaId=${Dao.liveInfo.data.parent_area_id}&areaId=0` if (!Dao.inpk) { this.dom.pk_container.style.display = 'none' } else { this.dom.pk_container.style.display = '' this.dom.pk_area.textContent = Dao.pk.liveInfo.data.area_name this.dom.pk_fArea.textContent = Dao.pk.liveInfo.data.parent_area_name this.dom.pk_area.href = `https://live.bilibili.com/p/eden/area-tags?parentAreaId=${Dao.pk.liveInfo.data.parent_area_id}&areaId=${Dao.pk.liveInfo.data.area_id}` this.dom.pk_fArea.href = `https://live.bilibili.com/p/eden/area-tags?parentAreaId=${Dao.pk.liveInfo.data.parent_area_id}&areaId=0` } }, async run() { await this.initUI() this.updateUI() setInterval(() => this.updateUI(), 5 * 1000) }, } async function main() { initSucceed = await G.init() if (!initSucceed) return console.log(`G.init done`) await Dao.run() console.log(`Dao.run`) tasklist = [] Config.livetime.enable && tasklist.push(LiveTimeModule.run()) Config.fans.enable && tasklist.push(FansModule.run()) Config.rank.enable && tasklist.push(RankModule.run()) Config.cover.enable && tasklist.push(CoverModule.run()) Config.otherLive.enable && tasklist.push(otherLiveModule.run()) Config.area.enable && tasklist.push(areaModule.run()) await ut.sleep(200) //等数据初始化完成 await Promise.all(tasklist) } const api = { // ============================== api ============================== // liveInfo = get https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${roomId} // 开播时间 = liveInfo.data.live_time // 主播uid = liveInfo.data.uid // 真roomId = liveInfo.data.room_id // liveStatus = liveInfo.data.live_status (0:未开播 1:直播中 2:轮播中)`); // 粉丝数 = liveInfo.data.attention // 人气 = liveInfo.data.online // 分区 = liveInfo.data.area_name // 父分区 = liveInfo.data.parent_area_name // pk信息 `https://api.live.bilibili.com/xlive/general-interface/v1/battle/getInfoById?room_id=${roomId}&pk_version=6`; // getOnlineGoldRank = https://api.live.bilibili.com/xlive/general-interface/v1/rank/getOnlineGoldRank?ruid=${uid}&roomId=${roomId}&page=1&pageSize=1 // 在线人数 = getOnlineGoldRank.data.onlineNum // room_init = `https://api.live.bilibili.com/room/v1/Room/room_init?id=${roomId}`; // 开播时间戳 = room_init.data.live_time // alive = `https://api.live.bilibili.com/xlive/web-ucenter/v1/xfetter/GetWebList?page=1`; // 其他关注直播间 = alive.data.rooms // ================================================================= // 获取直播间id getRoomID() { try { const urlpathname = W.location.pathname console.log(`urlpathname: ${urlpathname} `) return urlpathname.match(/\d{3,}/)[0] } catch (error) { console.log(`getRoomID error`) return null } }, // 获取直播开始时间时间戳 async getLiveTimeStamp(roomId) { // https://api.live.bilibili.com/room/v1/Room/room_init?id=60989 const url = `https://api.live.bilibili.com/room/v1/Room/room_init?id=${roomId}` return (await ut.fetchURL(url)).data.live_time }, // 获取直播信息数据 async getLiveInfo(roomId) { const url = `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${roomId}` return await ut.fetchURL(url) }, // 获取在线人数 async getOnlinePeople(roomId, uid) { // 计算规则 https://ngabbs.com/read.php?tid=29562585 // https://api.live.bilibili.com/xlive/general-interface/v1/rank/getOnlineGoldRank?ruid=2978046&roomId=60989&page=1&pageSize=1 const url = `https://api.live.bilibili.com/xlive/general-interface/v1/rank/getOnlineGoldRank?ruid=${uid}&roomId=${roomId}&page=1&pageSize=1` return (await ut.fetchURL(url)).data.onlineNum }, // 获取舰长数量 async getGuardCount(roomId, uid) { const url = `https://api.live.bilibili.com/xlive/app-room/v2/guardTab/topList?roomid=${roomId}&page=1&ruid=${uid}&page_size=0` return (await ut.fetchURL(url)).data.info.num }, // 获取关注直播列表 async getAliveList() { let roomlist = [] const url = `https://api.live.bilibili.com/xlive/web-ucenter/v1/xfetter/GetWebList?page=1` let res = (await ut.fetchURL(url, true)).data roomlist = [].concat(res.rooms) if (res.count > 10) { for (let page = 2; page <= Math.ceil(res.count / 10); page++) { const nextPageUrl = `https://api.live.bilibili.com/xlive/web-ucenter/v1/xfetter/GetWebList?page=${page}` const nextPageRes = (await ut.fetchURL(nextPageUrl, true)).data roomlist = roomlist.concat(nextPageRes.rooms) } } roomlist.sort((a, b) => a.live_time - b.live_time) return roomlist }, // 获取pk信息 async getPkInfo(roomId) { const url = `https://api.live.bilibili.com/xlive/general-interface/v1/battle/getInfoById?room_id=${roomId}&pk_version=6` return await ut.fetchURL(url) }, } const ut = { async fetchURL(url, useCookie = false) { try { const response = await fetch(url, { credentials: useCookie ? 'include' : 'same-origin', }) if (!response.ok) throw new Error(`请求${url}错误 response.status : ${response.status}`) const data = await response.json() return data } catch (error) { throw new Error(`请求${url}错误 error.message: ${error.message}`) } }, // 添加CSS addCSS(css) { let myStyle = document.createElement('style') myStyle.textContent = css let doc = document.head || document.documentElement doc.appendChild(myStyle) }, sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)) }, formatTime(seconds, format, Ltrip0) { const hours = Math.floor(seconds / 3600) const minutes = Math.floor((seconds % 3600) / 60) const remainingSeconds = seconds % 60 let formattedTime = format .replace('{hh}', hours.toString().padStart(2, '0')) .replace('{mm}', minutes.toString().padStart(2, '0')) .replace('{ss}', remainingSeconds.toString().padStart(2, '0')) .replace('{h}', hours) .replace('{m}', minutes) .replace('{s}', remainingSeconds) Ltrip0 && formattedTime.startsWith('0h') && (formattedTime = formattedTime.slice(2)) return formattedTime }, getFirstNumber(text) { // 使用正则表达式匹配第一个数字 const match = text.match(/\d+/) // 如果找到匹配的数字,则返回第一个匹配结果 if (match) { return parseInt(match[0], 10) // 将匹配的字符串转换为整数并返回 } // 如果未找到数字,则返回 null 或其他指定的默认值 return 0 }, formatDate(date, format) { const year = date.getFullYear() const month = date.getMonth() + 1 // 月份是从 0 开始的 const day = date.getDate() const hours = date.getHours() const minutes = date.getMinutes() const seconds = date.getSeconds() const formattedDate = format .replace('YYYY', year) .replace('YY', year % 100) .replace('MM', (month < 10 ? '0' : '') + month) .replace('DD', (day < 10 ? '0' : '') + day) .replace('hh', (hours < 10 ? '0' : '') + hours) .replace('mm', (minutes < 10 ? '0' : '') + minutes) .replace('ss', (seconds < 10 ? '0' : '') + seconds) return formattedDate }, waitForElement(selector, interval = 200, timeout = 5000) { return new Promise((resolve) => { const checkExist = setInterval(() => { const element = document.querySelector(selector) if (element) { clearInterval(checkExist) clearTimeout(timeoutTimer) resolve(element) } }, interval) const timeoutTimer = setTimeout(() => { clearInterval(checkExist) resolve(null) }, timeout) }) }, toggleShow(dom, display) { dom.style.display = dom.style.display == 'none' ? display : 'none' }, changeOpacity(dom, dif) { let currentOpacity = dom.style.opacity === '' ? 1 : parseFloat(dom.style.opacity) dom.style.opacity = Math.max(0, Math.min(parseFloat(currentOpacity) + dif, 1)) console.log('dom.style.opacity', dom.style.opacity) }, hook_wrapper() { let g_rank_count = 0 let g_online_count = 0 function on_online_rank_count(obj) { const rank_count = obj.data.count const online_count = obj.data.online_count let change = false if (rank_count && rank_count !== g_rank_count) { g_rank_count = rank_count change = true } if (online_count && online_count !== g_online_count) { g_online_count = online_count change = true } if (change) { Dao.rank1 = g_rank_count Dao.rank2 = g_online_count // const showers = document.querySelectorAll("#rank-list-ctnr-box > div.tabs > ul > li.item") // showers[0].innerText = '高能用户(' + g_rank_count + '/' + g_online_count + ')'; } } const cb_map = { ONLINE_RANK_COUNT: on_online_rank_count, } Array.prototype.push = new Proxy(Array.prototype.push, { apply(target, thisArg, argArray) { try { if (argArray && argArray.length > 0) { for (let i = 0; i < argArray.length; i++) { if (argArray[i] && argArray[i].cmd) { if (cb_map[argArray[i].cmd]) { cb_map[argArray[i].cmd](argArray[i]) } } else { break } } } } catch (e) { console.error(e) } return Reflect.apply(target, thisArg, argArray) }, }) }, mapKey(key, func) { document.addEventListener('keydown', function (event) { if (event.key === key) { func() } }) }, } // await Utils.sleep(3000) if (W.location.pathname != '/p/html/live-web-mng/index.html') main() })()