Skip to content

Instantly share code, notes, and snippets.

@Dobby233Liu
Last active February 6, 2026 09:26
Show Gist options
  • Select an option

  • Save Dobby233Liu/5c3117cc6ce0b5ffe5e8a33f51c93e83 to your computer and use it in GitHub Desktop.

Select an option

Save Dobby233Liu/5c3117cc6ce0b5ffe5e8a33f51c93e83 to your computer and use it in GitHub Desktop.
Show liveroom category and play count: Adds a badge to show the category and play count in liveroom cards
// ==UserScript==
// @name Show liveroom category and play count
// @namespace io.github.dobby233liu.userscripts.bilibili.liveshowroomarea
// @version 1.1.7c
// @description Adds a badge to show the category and play count in liveroom cards
// @author Liu Wenyuan
// @match https://*.bilibili.com/*
// @exclude *://message.bilibili.com/pages/nav/header_sync*
// @exclude *://message.bilibili.com/pages/nav/index_new_pc_sync*
// @exclude *://s1.hdslb.com/bfs/seed/jinkela/short/cols/*
// @require https://unpkg.com/arrive@2.5.2/minified/arrive.min.js#sha256-tIcpmxEDTbj4LvjrVQOMki7ASpQFVc5GwOuiN/1Y5Ew=
// @grant GM_addStyle
// @run-at document-body
// @icon64 https://i1.hdslb.com/bfs/live/cdf8c5a456de00c456bc6dede3c19569ef2c40bf.png
// ==/UserScript==
(function() {
"use strict";
const NUMERIC_REGEX = /^[0-9]+$/;
function getRid(link) {
const roomUrl = new URL(link.href);
if (roomUrl.hostname != "live.bilibili.com") {
console.warn("No room id found in", link.href);
return;
}
const rid = roomUrl.pathname.split("/")[1];
if (!rid || !rid.match(NUMERIC_REGEX)) {
console.warn("No room id found in", link.href);
return;
}
return rid;
}
async function _getInfoByRoom(rid) {
let data = null;
try {
const res = await fetch(
"https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=" + encodeURIComponent(rid),
{
mode: "cors",
credentials: "include"
}
);
if (!res.ok) throw new Error("Status: " + res.status);
const resObj = await res.json();
if (resObj.code != 0 || !resObj.data) throw new Error(JSON.stringify(resObj));
data = resObj.data;
} catch (err) {
console.error("getInfoByRoom for", rid, "failed with:", err);
}
return data;
}
const roomInfoRequests = new Map();
const ROOM_INFO_EXPIRE_TIMEOUT = (1/4) * 60 * 1000;
setInterval(() => {
const now = Date.now();
for (const [k, v] of roomInfoRequests.entries()) {
if ((now - v[1]) > ROOM_INFO_EXPIRE_TIMEOUT) {
roomInfoRequests.delete(k);
}
}
}, ROOM_INFO_EXPIRE_TIMEOUT);
function getInfoByRoom(rid) {
let shouldRequest = true;
let ret = roomInfoRequests.get(rid);
if (ret && (Date.now() - ret[1]) <= ROOM_INFO_EXPIRE_TIMEOUT) {
shouldRequest = false;
} else {
roomInfoRequests.delete(rid);
}
if (shouldRequest) {
ret = [_getInfoByRoom(rid), Date.now()];
roomInfoRequests.set(rid, ret);
}
return ret[0];
}
function getAreaName({ parent_area_name, area_name }, rid) {
const areaDisplay = [/*parent_area_name?.trim() ?? "",*/ area_name?.trim() ?? ""].join("-").trim();
if (areaDisplay == "") {
console.warn("No area name for", rid);
return "";
}
return areaDisplay;
}
function getAreaViewUrl({ parent_area_id, area_id }) {
function isAreaIdNull(id) { return !id || id == "0"; }
const urlObj = new URL("https://live.bilibili.com/p/eden/area-tags");
if (!isAreaIdNull(parent_area_id)) {
urlObj.searchParams.set("parentAreaId", parent_area_id);
}
if (!isAreaIdNull(area_id)) {
urlObj.searchParams.set("areaId", area_id);
}
if (urlObj.search == "") return;
return urlObj.href;
}
GM_addStyle(`
.living-section-title__text { height: auto; }
.living-section-category-badge-container {
margin-top: 3px;
text-align: right;
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 20px;
}
.living-section-category-badge {
border: 1px var(--brand_pink) solid;
color: var(--brand_pink);
padding: 2px;
border-radius: 4px;
font-size: 12px; transform: scale(0.8);
}
a[href].living-section-category-badge-link:hover {
color: var(--brand_pink_active);
}
a[href].bili-dyn-card-live__desc__category-link:hover {
color: var(--brand_blue_hover);
}
`);
document.body.arrive(".space-main .aside-card .living-section", { existing: true }, async function(card) {
if (card.querySelector(".living-section-tips")) {
console.debug("Room not online", card);
return;
}
const titleContainer = card.querySelector(".living-section-title");
if (!titleContainer) {
console.warn("No title container element in", card);
return;
}
const link = card.querySelector(".living-section-cover > a[href]");
if (!link) {
console.warn("No link found for", card);
return;
}
const rid = getRid(link);
if (!rid) return;
const data = await getInfoByRoom(rid);
if (!data) return;
if (!data?.room_info) {
console.warn("No room info data for", rid);
return;
}
const areaDisplay = getAreaName(data.room_info, rid);
if (areaDisplay == "") return;
const watchedDisplay = data.watched_show?.num;
const areaBadgeContainer = titleContainer.appendChild(document.createElement("div"));
areaBadgeContainer.className = "living-section-category-badge-container";
const areaBadge = areaBadgeContainer.appendChild(document.createElement("span"));
areaBadge.className = "living-section-category-badge";
const areaLink = areaBadge.appendChild(document.createElement("a"));
areaLink.className = "living-section-category-badge-link";
areaLink.innerText = areaDisplay;
const areaViewUrl = getAreaViewUrl(data.room_info);
if (areaViewUrl) areaLink.href = areaViewUrl;
areaLink.target = "_blank";
if (watchedDisplay) {
areaBadge.appendChild(document.createTextNode(" · "));
const watchedNumIcon = areaBadge.appendChild(document.createElement("i"));
watchedNumIcon.className = "vui_icon sic-BDC-playdata_square_line";
watchedNumIcon.style.fontVariationSettings = '"strk" 3';
watchedNumIcon.style.marginRight = "2px";
areaBadge.appendChild(document.createTextNode(watchedDisplay));
}
});
document.body.arrive(".bili-dyn-card-live", { existing: true }, async function(card) {
const rid = getRid(card);
if (!rid) return;
const bottomline = card.querySelector(".bili-dyn-card-live__desc");
if (!bottomline) {
console.warn("No bottomline found for", card);
return;
}
const data = await getInfoByRoom(rid);
if (!data) return;
if (!data?.room_info) {
console.warn("No room info data for", rid);
return;
}
const areaDisplay = getAreaName(data.room_info, rid);
if (areaDisplay == "") return;
const watchedDisplay = data.watched_show?.num ? `${data.watched_show.num}人看过` : "";
if (watchedDisplay == "") {
console.warn("No watched num for", rid);
}
bottomline.replaceChildren();
const areaLink = bottomline.appendChild(document.createElement("a"));
areaLink.className = "bili-dyn-card-live__desc__category-link";
areaLink.innerText = areaDisplay;
const areaViewUrl = getAreaViewUrl(data.room_info);
if (areaViewUrl) areaLink.href = areaViewUrl;
areaLink.target = "_blank";
if (watchedDisplay != "") {
bottomline.appendChild(document.createTextNode(" · "));
bottomline.appendChild(document.createTextNode(watchedDisplay));
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment