您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
B站首页显示Antfu专区!
当前为
// ==UserScript== // @name B站 Antfu // @namespace https://github.com/lilei2603/bilibili-follow // @version 2.1 // @description B站首页显示Antfu专区! // @author Lei // @match https://www.bilibili.com/* // @icon https://avatars.githubusercontent.com/u/11247099?v=4 // @grant GM_addStyle // @license MIT // ==/UserScript== ; (function () { 'use strict' // 用户列表 let USERS = [] // 当前用户索引 let currentUserIndex = 0 let currentPage = 1 let page = 0 // 视频列表 let videoList = [] // 是否显示我的关注窗口 let isShowFavorite = false // 窗口滚动距离 let scrollTop = 0 let totalCount = 0 const API = { // 获取用户视频列表 getNewVideo: async () => { let res = await fetch( `https://api.bilibili.com/x/space/arc/search?mid=${USERS[currentUserIndex].mid}&ps=30&tid=0&pn=${currentPage}&order=pubdate&jsonp=jsonp` ) const json = await res.json() totalCount = json.data.page.count videoList = videoList.concat(json.data.list.vlist) }, // 根据用户ID获取用户信息 getUserInfoByMid: async (mid) => { let res = await fetch(`https://api.bilibili.com/x/space/acc/info?mid=${mid}&jsonp=jsonp`) const json = await res.json() return json } } // 用户初始化 function initUser() { // 从缓存中获取用户列表 if(!localStorage.getItem('favorites')) { USERS = [ { key_words: 'AnthonyFu一个托尼', mid: 668380, avatar: 'http://i1.hdslb.com/bfs/face/519cb17285e6b9450a738472cb0b95aeb8676547.jpg', } ] localStorage.setItem('favorites', JSON.stringify(USERS)) }else { USERS = JSON.parse(localStorage.getItem('favorites')) } } // 播放量格式化 function bigNumber(num) { return num > 10000 ? `${(num / 10000).toFixed(2)}万` : num } function s2d(string) { return new DOMParser().parseFromString(string, 'text/html').body .childNodes[0] } // 播放时长格式化 function timeFormat(time) { let res = [] let [s = 0, m = 0] = time.split(':').reverse() res.unshift(String(s).padStart(2, '0')) res.unshift(String(m % 60).padStart(2, '0')) res.unshift(String(parseInt(m / 60)).padStart(2, '0')) return res.join(':') } // 日期时间格式化 function timeago(dateTimeStamp) { const minute = 60; //把分,时,天,周,半个月,一个月用毫秒表示 const hour = minute * 60; const day = hour * 24; // const week = day * 7; const month = day * 30; const now = parseInt(new Date().getTime() / 1000); //获取当前时间毫秒 const diffValue = now - dateTimeStamp;//时间差 let result = '' if (diffValue < 0) { return; } const minC = diffValue / minute; //计算时间差的分,时,天,周,月 const hourC = diffValue / hour; const dayC = diffValue / day; const monthC = diffValue / month; const datetime = new Date(); datetime.setTime(dateTimeStamp * 1000); const Nyear = datetime.getFullYear(); const Nmonth = datetime.getMonth() + 1; const Ndate = datetime.getDate(); if (dayC >= 1 && dayC < 2) { result = '昨天' } else if (hourC >= 1 && hourC <= 23) { result = ' ' + parseInt(hourC) + '小时前' } else if (minC >= 1 && minC <= 59) { result = ' ' + parseInt(minC) + '分钟前' } else if (diffValue >= 0 && diffValue <= minute) { result = '刚刚' } else if (monthC < new Date().getMonth() + 1) { result = Nmonth + '-' + Ndate } else { result = Nyear + '-' + Nmonth + '-' + Ndate } return result; } // 换一换 async function refresh() { page++ if(videoList.length < totalCount){ currentPage++ await API.getNewVideo() }else { if(videoList.length % totalCount == 0){ currentPage = 1 }else{ currentPage++ } await API.getNewVideo() } drawVideos() } // 绘制视频 function drawVideos() { const VIDEO_DOM = document.querySelector('#bili_custom .variety-body') VIDEO_DOM.innerHTML = '' videoList .slice(page * 10, page * 10 + 14) .forEach((item) => { const title = item.title.replace(/<em class="keyword">(.*?)<\/em>/g, '$1') const pic = item.pic.replace(/http/g, 'https') + '@672w_378h_1c' const webp = pic + '.webp' let DOM = s2d(` <div class="bili-video-card" data-report="partition_recommend.content"> <div class="bili-video-card__skeleton hide"> <div class="bili-video-card__skeleton--cover"></div> <div class="bili-video-card__skeleton--info"> <div class="bili-video-card__skeleton--right"> <p class="bili-video-card__skeleton--text"></p> <p class="bili-video-card__skeleton--text short"></p> <p class="bili-video-card__skeleton--light"></p> </div> </div> </div> <div class="bili-video-card__wrap __scale-wrap"><a href="//www.bilibili.com/video/${item.bvid}" target="_blank" data-mod="partition_recommend" data-idx="content" data-ext="click"> <div class="bili-video-card__image __scale-player-wrap"> <div class="bili-video-card__image--wrap"> <div class="bili-watch-later" style="display: none;"><svg class="bili-watch-later__icon"> <use xlink:href="#widget-watch-later"></use> </svg><span class="bili-watch-later__tip" style="display: none;"></span></div> <picture class="v-img bili-video-card__cover"> <!----> <source srcset="${webp}" type="image/webp"><img src="${pic}" alt="${title}" loading="lazy" onload=""> </picture> <div class="v-inline-player"></div> </div> <div class="bili-video-card__mask"> <div class="bili-video-card__stats"> <div class="bili-video-card__stats--left"><span class="bili-video-card__stats--item"><svg class="bili-video-card__stats--icon"> <use xlink:href="#widget-video-play-count"></use> </svg><span class="bili-video-card__stats--text">${bigNumber(item.play)}</span></span><span class="bili-video-card__stats--item"><svg class="bili-video-card__stats--icon"> <use xlink:href="#widget-video-danmaku"></use> </svg><span class="bili-video-card__stats--text">${item.comment}</span></span></div><span class="bili-video-card__stats__duration">${timeFormat(item.length)}</span> </div> </div> </div> </a> <div class="bili-video-card__info __scale-disable"> <div class="bili-video-card__info--right"> <h3 class="bili-video-card__info--tit" title="${title}"><a href="//www.bilibili.com/video/${item.bvid}" target="_blank" data-mod="partition_recommend" data-idx="content" data-ext="click">${title}</a></h3> <div class="bili-video-card__info--bottom"> <a class="bili-video-card__info--owner" href="//space.bilibili.com/${item.mid}" target="_blank" data-mod="partition_recommend" data-idx="content" data-ext="click"><svg class="bili-video-card__info--owner__up"> <use xlink:href="#widget-up"></use> </svg><span class="bili-video-card__info--author">${item.author}</span><span class="bili-video-card__info--date">· ${timeago(item.created)}</span></a> </div> </div> </div> </div> </div> `) VIDEO_DOM.append(DOM) }) } // 绘制我的关注按钮 function drawFavorites() { const refreshBtn = document.querySelector('.custom-refresh') const refreshBtnParent = refreshBtn.parentNode const favorite = s2d(` <button class="primary-btn roll-btn favorite-btn" id="favorite-btn"> <svg style="transform: rotate(180deg);"> <use xlink:href="#widget-arrow"></use> </svg> <span>我的关注</span> </button> `) refreshBtnParent.insertBefore(favorite, refreshBtn) const modal = s2d(` <div class="custom-modal"> <div class="modal-mask"></div> <div class="modal-wrap"> <div class="modal" role="document"> <div tabindex="0" aria-hidden="true" style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div> <div class="modal-content"><button type="button" aria-label="Close" class="modal-close"><span class="modal-close-x"><span role="img" aria-label="close" class="anticon anticon-close modal-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"> <path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"> </path> </svg></span></span></button> <div class="modal-header"> <div class="modal-title">我的关注</div> </div> <div class="modal-body"></div> <div class="modal-footer"> <div> <span>用户ID:</span> <input id="mid" style="height: 25px; padding: 5px;" /> </div> <button class="btn btn-primary" type="button"> <span>添 加</span> </button> </div> </div> <div tabindex="0" aria-hidden="true" style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div> </div> </div> </div> `) document.querySelector('#bili_custom').append(modal) appendFavoriteItem() } // 添加关注用户项 function appendFavoriteItem() { document.querySelector('.modal-body').innerHTML = '' USERS.forEach((item, index) => { const DOM = s2d(` <div class="modal-content-item"> <img src="${item.avatar}" style="width: 34px; height: 34px; border-radius: 17px; margin-right: 12px;"> <span>${item.key_words}</span> </div> `) document.querySelector('.modal-body').append(DOM) DOM.addEventListener('click', () => { currentUserIndex = index showFavorite() injectDOM() }) }) } // 操作我的关注弹窗 function showFavorite() { isShowFavorite = !isShowFavorite // 获取点击按钮时,当前窗口滚动距离 if(window.pageYOffset != 0) { scrollTop = window.pageYOffset } // 如果显示弹窗,则禁用页面滚动 document.body.style.position = isShowFavorite ? 'fixed' : 'static' // 设置页面滚动高度 document.body.style.top = isShowFavorite ? `-${scrollTop}px` : '0'; window.scrollTo(0, scrollTop) document.querySelector('.custom-modal').style.display = isShowFavorite ? 'block' : 'none' appendFavoriteItem() } // 添加用户信息 async function appendFavorite() { const mid = document.querySelector('#mid') const isUserExist = USERS.findIndex(item => item.mid == mid) if(isUserExist == -1) { const userInfo = await API.getUserInfoByMid(mid.value) if(userInfo.code === 0) { USERS.push({ mid: mid.value, key_words: userInfo.data.name, avatar: userInfo.data.face }) }else{ alert('获取用户信息失败') } localStorage.setItem('favorites', JSON.stringify(USERS)) getMyFavorite() mid.value = '' }else{ alert('用户已存在') } } // 获取用户信息 function getMyFavorite() { const favorites = localStorage.getItem('favorites') if(favorites) { USERS = JSON.parse(favorites) } appendFavoriteItem() } // 关注信息 async function useLive() { const { data } = await API.getUserInfoByMid(USERS[currentUserIndex].mid); return { liveRoomUrl: data.live_room.url, isLive: data.live_room.liveStatus === 1 } } // 包装根据bool切换值 function wrapperSwitchValue(flag) { return (t, f) => flag ? t : f } // 初始化容器 async function injectDOM() { // currentUserIndex = mid ? USERS.findIndex(item => item.mid == mid) : 0 videoList = [] currentPage = 1 page = 0 const spaceVideoUrl = `https://space.bilibili.com/${USERS[currentUserIndex].mid}/video` const { liveRoomUrl, isLive } = await useLive(); const switchLiving = wrapperSwitchValue(isLive); const DOM = ` <div id="bili_custom"> <section class="bili-grid"> <div class="variety-area"> <div class="area-header"> <div class="left"> <div class="avatar-container"> <a href="${switchLiving(liveRoomUrl,spaceVideoUrl)}" target="_blank" class="space-user-avatar"> <div class="avatar-wrap ${switchLiving('live-ani','')}"> <div> <div class="bili-avatar" style="width: 34px;height:34px;"> <img class="bili-avatar-img bili-avatar-face bili-avatar-img-radius" data-src="${USERS[currentUserIndex].avatar}" alt="" src="${USERS[currentUserIndex].avatar}"> <span class="bili-avatar-icon bili-avatar-right-icon bili-avatar-size-60"></span> </div> </div> <div class="a-cycle a-cycle-1"></div> <div class="a-cycle a-cycle-2"></div> <div class="a-cycle a-cycle-3"></div> </div> <div class="live-tab" style="${switchLiving('', 'display:none;')}"><img style="width: 15px;height: 15px;" src="//s1.hdslb.com/bfs/static/jinkela/space/assets/live.gif" alt="live" class="live-gif"> </div> </a> </div> <a class="title" href="https://space.bilibili.com/${USERS[currentUserIndex].mid}/video" target="_blank"> <span>${USERS[currentUserIndex].key_words}</span> </a> </div> <div class="right"> <button class="primary-btn roll-btn custom-refresh"> <svg style="transform: rotate(0deg);"> <use xlink:href="#widget-roll"></use> </svg> <span>换一换</span> </button> <a class="primary-btn see-more" href="https://space.bilibili.com/${USERS[currentUserIndex].mid}/video" target="_blank"> <span>查看更多</span> <svg> <use xlink:href="#widget-arrow"></use> </svg> </a> </div> </div> <div class="variety-body"></div> </div> </section> </div>` let content = document.querySelector('.bili-layout') if (document.querySelector('#bili_custom')) { content.removeChild(document.querySelector('#bili_custom')) } let anchor = document.querySelectorAll('.bili-grid')[2] let init = s2d(DOM) document.querySelector('#bili_custom') && document.querySelector('#bili_custom').remove() // 插入初始模版 console.log(anchor) content.insertBefore(init, anchor) // 插入关注UP主按钮 drawFavorites() // 插入最新视频 await API.getNewVideo() drawVideos() // 点击事件 document.querySelector('.custom-refresh').addEventListener('click', refresh) document.querySelector('.favorite-btn').addEventListener('click', showFavorite) document.querySelector('.anticon').addEventListener('click', showFavorite) document.querySelector('.btn-primary').addEventListener('click', appendFavorite) } window.addEventListener( 'load', async () => { initUser() await injectDOM() }, false, ) GM_addStyle(` .space-user-avatar { width: 34px; min-width: 34px; } .space-user-avatar .avatar-wrap { position: relative; width: 100%; height: 34px; } .space-user-avatar .avatar-wrap.live-ani .a-cycle { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); width: 34px; height: 34px; border: 1px solid #f69; border-radius: 50%; z-index: 1; opacity: 0; animation: scaleUpCircle 1.5s linear; animation-iteration-count: infinite; } @keyframes scaleUpCircle{ 0%{ transform:translate(-50%,-50%) scale(1); opacity:1 } to { transform:translate(-50%,-50%) scale(1.5); opacity:0 } } .space-user-avatar .avatar-wrap.live-ani .a-cycle-1 { animation-delay: 0s; } .space-user-avatar .avatar-wrap.live-ani .a-cycle-2 { animation-delay: .5s; } .space-user-avatar .avatar-wrap.live-ani .a-cycle-3 { animation-delay: 1s; } .avatar-container { position: relative; margin-right:15px; } .space-user-avatar .live-tab { position: absolute; left: 50%; bottom: 0; transform: translate(-50%,50%); height: 15px; white-space: nowrap; display: -ms-flexbox; display: flex; -ms-flex-align: center; align-items: center; -ms-flex-pack: center; justify-content: center; background: #f69; color: #fff; border-radius: 8px; border: 1.5px solid #fff; padding: 5px 10px; font-size: 12px; z-index: 2; } .favorite-btn { background-color: #fb7299!important; color: #ffffff!important; } .favorite-btn:hover { background-color: #fc8bab!important; } #bili_custom { position: relative; } .custom-modal { display: none; } .modal-mask { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1000; height: 100%; background-color: #00000073; } .modal-wrap { position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto; outline: 0; z-index: 1000; } .modal { box-sizing: border-box; padding: 0 0 24px; color: #000000d9; font-size: 14px; font-variant: tabular-nums; line-height: 1.5715; list-style: none; font-feature-settings: "tnum"; pointer-events: none; position: relative; top: 100px; width: 500px; height: 500px; margin: 0 auto; transform-origin: 9px 204px; } .modal-content { position: relative; background-color: #fff; background-clip: padding-box; border: 0; border-radius: 2px; box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d; pointer-events: auto; } .modal-close { position: absolute; top: 0; right: 0; z-index: 10; padding: 0; color: #00000073; font-weight: 700; line-height: 1; text-decoration: none; background: transparent; border: 0; outline: 0; cursor: pointer; transition: color .3s; } .modal-close-x { display: block; width: 56px; height: 56px; font-size: 16px; font-style: normal; line-height: 56px; text-align: center; text-transform: none; text-rendering: auto; } .anticon { display: inline-block; color: inherit; font-style: normal; line-height: 0; text-align: center; text-transform: none; vertical-align: -0.125em; text-rendering: optimizelegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .anticon:hover { color: #fb7299; } .modal-header { padding: 16px 24px; color: #000000d9; background: #fff; border-bottom: 1px solid #f0f0f0; border-radius: 2px 2px 0 0; } .modal-title { margin: 0; color: #000000d9; font-weight: 500; font-size: 16px; line-height: 22px; word-wrap: break-word; } .modal-body { padding: 24px; font-size: 14px; line-height: 1.5715; word-wrap: break-word; } .modal-footer { padding: 10px 16px; text-align: right; background: transparent; border-top: 1px solid #f0f0f0; border-radius: 0 0 2px 2px; display: flex; justify-content: space-between; align-items: center; } .btn { line-height: 1.5715; position: relative; display: inline-block; font-weight: 400; white-space: nowrap; text-align: center; background-image: none; border: 1px solid transparent; box-shadow: 0 2px #00000004; cursor: pointer; transition: all .3s cubic-bezier(.645,.045,.355,1); -webkit-user-select: none; -moz-user-select: none; user-select: none; touch-action: manipulation; height: 32px; padding: 4px 15px; font-size: 14px; border-radius: 2px; color: #000000d9; border-color: #d9d9d9; background: #fff; outline: 0; } .btn:hover { border-color: #e3e5e7!important; background-color: #e3e5e7!important; } .btn-primary { margin-left: 20px; color: #fff; border-color: #fb7299; background: #fb7299!important; text-shadow: 0 -1px 0 rgb(0 0 0 / 12%); box-shadow: 0 2px #0000000b; } .btn-primary:hover { border-color: #fc8bab!important; background-color: #fc8bab!important; } .modal-body { display: flex; flex-wrap: wrap; align-content: start; justify-content: space-between; height: 300px; overflow-y: auto; } .modal-content-item { cursor: pointer; box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.16); padding: 0 12px; width: 45%; height: 60px; margin-bottom: 12px; display: flex; align-items: center; } .modal-content-item:hover { background-color: #efefef; } `) })()
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址