// ==UserScript== // @name B站推流码获取工具 // @namespace https://github.com/smathsp // @version 1.5 // @description 获取第三方推流码 // @author smathsp // @license GPL-3.0 // @match *://*.bilibili.com/* // @icon https://www.bilibili.com/favicon.ico // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_xmlhttpRequest // @grant GM_setClipboard // @grant GM_notification // @connect api.live.bilibili.com // @connect passport.bilibili.com // @run-at document-end // @downloadURL https://update.greasyfork.icu/scripts/536798/B%E7%AB%99%E6%8E%A8%E6%B5%81%E7%A0%81%E8%8E%B7%E5%8F%96%E5%B7%A5%E5%85%B7.user.js // @updateURL https://update.greasyfork.icu/scripts/536798/B%E7%AB%99%E6%8E%A8%E6%B5%81%E7%A0%81%E8%8E%B7%E5%8F%96%E5%B7%A5%E5%85%B7.meta.js // ==/UserScript== (function () { "use strict"; // 存储键名常量 const STORAGE_KEYS = { LAST_ROOM_ID: "bili_last_roomid", DARK_MODE: "bili_dark_mode", IS_LIVE_STARTED: "isLiveStarted", STREAM_INFO: "streamInfo", LAST_GROUP_ID: "bili_last_groupid", LAST_AREA_ID: "bili_last_areaid", AREA_LIST_TIME: "bili_area_list_time", AREA_LIST: "bili_area_list", USER_MID: "bili_user_mid", LAST_TITLE: "bili_last_title", }; // API URL Constants const API_URL_AREA_LIST = "https://api.live.bilibili.com/room/v1/Area/getList?show_pinyin=1"; const API_URL_START_LIVE = "https://api.live.bilibili.com/room/v1/Room/startLive"; const API_URL_UPDATE_ROOM = "https://api.live.bilibili.com/room/v1/Room/update"; const API_URL_STOP_LIVE = "https://api.live.bilibili.com/room/v1/Room/stopLive"; // 示例:将 GM_xmlhttpRequest Promise 化 function gmRequest(options) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ ...options, onload: resolve, onerror: reject, // GM_xmlhttpRequest 的 onerror 通常也传递 response 对象 ontimeout: reject, // 同样 onabort: reject, // 同样 }); }); } // SVG 图标常量 const SUN_SVG = ''; const MOON_SVG = ''; const CLOSE_SVG = ''; // 插入全局样式表,统一亮暗色模式 function insertGlobalStyle() { if (document.getElementById("bili-stream-global-style")) return; const style = document.createElement("style"); style.id = "bili-stream-global-style"; style.innerHTML = ` :root { --bili-bg: #fff; --bili-fg: #222; --bili-panel-shadow: 0 2px 10px rgba(0,0,0,0.1); --bili-border: #eee; --bili-input-bg: #fff; --bili-input-fg: #222; --bili-input-border: #ddd; --bili-tip-bg: #fef0f1; --bili-tip-fg: #d92b46; --bili-tip-border: #fb7299; --bili-btn-main: #fb7299; --bili-btn-main-hover: #fc8bab; --bili-btn-main-disabled: #bfbfbf; --bili-btn-stop: #ff4b4b; --bili-btn-stop-hover: #d9363e; --bili-btn-stop-disabled: #999; --bili-btn-text: #fff; --bili-title-color: #fb7299; --bili-label-color: #666; --bili-tip-yellow-bg: #fffbe6; --bili-tip-yellow-border: #faad14; --bili-tip-yellow-fg: #faad14; --bili-tip-green-bg: #e6ffed; --bili-tip-green-border: #52c41a; --bili-tip-green-fg: #389e0d; } .bili-dark-mode { --bili-bg: #232324; --bili-fg: #eee; --bili-panel-shadow: 0 2px 10px rgba(0,0,0,0.6); --bili-border: #444; --bili-input-bg: #18181a; --bili-input-fg: #eee; --bili-input-border: #444; --bili-tip-bg: #2d2326; --bili-tip-fg: #ffb6c1; --bili-tip-border: #fb7299; --bili-btn-main: #fb7299; --bili-btn-main-hover: #fc8bab; --bili-btn-main-disabled: #bfbfbf; --bili-btn-stop: #ff4b4b; --bili-btn-stop-hover: #d9363e; --bili-btn-stop-disabled: #999; --bili-btn-text: #fff; --bili-title-color: #fb7299; --bili-label-color: #aaa; --bili-tip-yellow-bg: #3a2d1a; --bili-tip-yellow-border: #faad14; --bili-tip-yellow-fg: #ffd666; --bili-tip-green-bg: #1e2b22; --bili-tip-green-border: #52c41a; --bili-tip-green-fg: #b7eb8f; } #bili-stream-code-panel { background-color: var(--bili-bg) !important; color: var(--bili-fg) !important; box-shadow: var(--bili-panel-shadow) !important; border-radius: 8px; padding: 15px; font-family: "Microsoft YaHei", sans-serif; } #bili-result { background-color: var(--bili-bg) !important; color: var(--bili-fg) !important; border: 1px solid var(--bili-border) !important; border-radius: 4px; margin-top: 15px; padding: 10px; } .bili-input { background: var(--bili-input-bg) !important; color: var(--bili-input-fg) !important; border: 1px solid var(--bili-input-border) !important; border-radius: 4px; padding: 8px; font-size: 14px; } .bili-select { background: var(--bili-input-bg) !important; color: var(--bili-input-fg) !important; border: 1px solid var(--bili-input-border) !important; border-radius: 4px; padding: 8px; font-size: 14px; } #bili-room-id, #bili-title, #server-addr, #stream-code { background: var(--bili-input-bg) !important; color: var(--bili-input-fg) !important; border: 1px solid var(--bili-input-border) !important; border-radius: 4px; padding: 8px; font-size: 14px; } #bili-area-group, #bili-area { background: var(--bili-input-bg) !important; color: var(--bili-input-fg) !important; border: 1px solid var(--bili-input-border) !important; border-radius: 4px; padding: 8px; font-size: 14px; } .bili-important-tip { background-color: var(--bili-tip-bg) !important; color: var(--bili-tip-fg) !important; border-left: 4px solid var(--bili-tip-border) !important; border-radius: 4px; margin-top: 8px; padding: 8px; } .bili-tip-yellow { background: var(--bili-tip-yellow-bg); border-left: 4px solid var(--bili-tip-yellow-border); color: var(--bili-tip-yellow-fg); border-radius: 4px; margin-top: 8px; padding: 8px; } .bili-tip-green { background: var(--bili-tip-green-bg); border-left: 4px solid var(--bili-tip-green-border); color: var(--bili-tip-green-fg); border-radius: 4px; margin-top: 8px; padding: 8px; } .bili-btn-main { background: var(--bili-btn-main); color: var(--bili-btn-text); border: none; border-radius: 4px; padding: 10px; cursor: pointer; font-size: 14px; transition: background 0.3s, opacity 0.3s; } .bili-btn-main:hover:not(:disabled) { background: var(--bili-btn-main-hover); } .bili-btn-main:disabled { background: var(--bili-btn-main-disabled); opacity: 0.5; cursor: not-allowed; } .bili-btn-stop { background: var(--bili-btn-stop); color: var(--bili-btn-text); border: none; border-radius: 4px; padding: 10px; cursor: pointer; font-size: 14px; transition: background 0.3s, opacity 0.3s; } .bili-btn-stop:hover:not(:disabled) { background: var(--bili-btn-stop-hover); } .bili-btn-stop:disabled { background: var(--bili-btn-stop-disabled); opacity: 0.5; cursor: not-allowed; } .bili-title { color: var(--bili-title-color); font-size: 18px; margin: 0; } .bili-label { color: var(--bili-label-color); font-size: 14px; } .bili-copy-btn { margin-left: 5px; background: var(--bili-btn-main); color: var(--bili-btn-text); border: none; border-radius: 4px; padding: 8px; cursor: pointer; transition: background 0.3s; } .bili-copy-btn:disabled { background: var(--bili-btn-main-disabled); cursor: not-allowed; } .bili-copy-btn:hover:not(:disabled) { background: var(--bili-btn-main-hover); } .bili-message { color: var(--bili-fg); font-size: 15px; margin: 0; } .bili-message-error { color: red; } `; document.head.appendChild(style); } // 全局变量 let roomId = null; // 当前房间ID let csrf = null; // CSRF令牌 let startLiveButton = null; // “开始直播”按钮引用 let stopLiveButton = null; // “结束直播”按钮引用 let isLiveStarted = GM_getValue(STORAGE_KEYS.IS_LIVE_STARTED, false); // 直播状态 let streamInfo = GM_getValue(STORAGE_KEYS.STREAM_INFO, null); // 推流信息缓存 // 请求头 const headers = { accept: "application/json, text/plain, */*", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", "content-type": "application/x-www-form-urlencoded; charset=UTF-8", origin: "https://link.bilibili.com", referer: "https://link.bilibili.com/p/center/index", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", }; // 开始直播数据模板 const startData = { room_id: "", platform: "android_link", area_v2: "", backup_stream: "0", csrf_token: "", csrf: "", }; // 停止直播数据模板 const stopData = { room_id: "", platform: "android_link", csrf_token: "", csrf: "", }; // 修改直播标题数据模板 const titleData = { room_id: "", platform: "android_link", title: "", csrf_token: "", csrf: "", }; // 初始化入口 function init() { try { insertGlobalStyle(); // 插入全局样式 removeExistingComponents(); // 清理旧组件 createUI(); // 创建UI(只创建一次主面板) restoreLiveState(); // 恢复直播状态 // setInterval(checkFloatButton, 5000); // 定期检查浮动按钮 - REMOVED } catch (error) { console.error("B站推流码获取工具初始化失败:", error); } } // 移除已存在的组件 function removeExistingComponents() { const existingPanel = document.getElementById("bili-stream-code-panel"); if (existingPanel) existingPanel.remove(); const existingButton = document.getElementById("bili-stream-float-button"); if (existingButton) existingButton.remove(); // 清空按钮引用,防止旧引用干扰 startLiveButton = null; stopLiveButton = null; } // 创建UI(只创建一次主面板) function createUI() { // 若主面板已存在则不再重复创建 if (!document.getElementById("bili-stream-code-panel")) { const panel = createPanel(); panel.style.display = "none"; } // 浮动按钮可重复创建(防止丢失) createFloatButton(); // 自动填充房间ID的逻辑已移至 createPanelForm } // 创建面板 function createPanel() { const panel = document.createElement("div"); panel.id = "bili-stream-code-panel"; panel.style.cssText = ` position: fixed; top: 70px; right: 10px; width: 300px; z-index: 10000; display: none; `; // 头部区域 const header = createPanelHeader(); panel.appendChild(header); // 表单区域 const form = createPanelForm(); panel.appendChild(form); // 结果区域 const resultArea = document.createElement("div"); resultArea.id = "bili-result"; resultArea.style.cssText = ` margin-top: 15px; padding: 10px; border: 1px solid #eee; border-radius: 4px; background-color: #f9f9f9; display: none; `; panel.appendChild(resultArea); document.body.appendChild(panel); // 缓存常用元素引用 return panel; } // 创建面板头部 function createPanelHeader() { const header = document.createElement("div"); header.style.cssText = "display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;"; // 标题 const title = document.createElement("h2"); title.textContent = "B站推流码获取工具"; title.className = "bili-title"; // 亮暗模式切换按钮 const modeBtn = document.createElement("button"); modeBtn.id = "bili-mode-toggle"; modeBtn.style.cssText = "width: 28px; height: 28px; border: none; background: transparent; cursor: pointer; display: flex; align-items: center; justify-content: center; margin-left: 8px;"; // SVG 图标 let isDarkMode = GM_getValue(STORAGE_KEYS.DARK_MODE, false); modeBtn.innerHTML = isDarkMode ? MOON_SVG : SUN_SVG; modeBtn.title = isDarkMode ? "切换为亮色模式" : "切换为暗色模式"; modeBtn.onclick = function () { isDarkMode = !isDarkMode; GM_setValue(STORAGE_KEYS.DARK_MODE, isDarkMode); modeBtn.innerHTML = isDarkMode ? MOON_SVG : SUN_SVG; modeBtn.title = isDarkMode ? "切换为亮色模式" : "切换为暗色模式"; applyColorMode(isDarkMode); }; // 首次渲染时应用模式 setTimeout(() => applyColorMode(isDarkMode), 0); // 关闭按钮 const closeButton = document.createElement("button"); closeButton.innerHTML = CLOSE_SVG; closeButton.style.cssText = "width: 24px; height: 24px; border: none; background: transparent; cursor: pointer; display: flex; align-items: center; justify-content: center;"; closeButton.onclick = () => { document.getElementById("bili-stream-code-panel").style.display = "none"; }; // 头部右侧按钮组 const rightBtns = document.createElement("div"); rightBtns.style.cssText = "display: flex; align-items: center; gap: 4px;"; rightBtns.appendChild(modeBtn); rightBtns.appendChild(closeButton); header.appendChild(title); header.appendChild(rightBtns); return header; } // 亮暗模式应用函数 function applyColorMode(isDark) { // 只切换 class,不再手动设置 style const root = document.documentElement; if (isDark) { root.classList.add("bili-dark-mode"); } else { root.classList.remove("bili-dark-mode"); } } // 创建面板表单 function createPanelForm() { const form = document.createElement("div"); form.style.cssText = "display: flex; flex-direction: column; gap: 10px;"; // 房间ID输入 form.appendChild(createRoomIdInput()); // 分区选择 const areaSelectionElement = createAreaSelection(); form.appendChild(areaSelectionElement); if (areaSelectionElement.loadAndBindAreaListPromise) { areaSelectionElement.loadAndBindAreaListPromise .then(() => { autoFillRoomId(); }) .catch((error) => { console.error( "Error during area list loading, or in autoFillRoomId:", error ); // Fallback if promise rejects setTimeout(autoFillRoomId, 300); }); } else { // Fallback if the promise wasn't attached console.warn( "loadAndBindAreaListPromise not found, falling back for autoFillRoomId." ); setTimeout(autoFillRoomId, 300); } // 标题输入 form.appendChild(createTitleInput()); // 按钮组 form.appendChild(createButtonGroup()); return form; } // 创建房间ID输入 function createRoomIdInput() { const container = document.createElement("div"); container.style.cssText = "display: flex; flex-direction: column; gap: 5px;"; const label = document.createElement("label"); label.textContent = "房间ID (Room ID):"; label.className = "bili-label"; const input = document.createElement("input"); input.type = "text"; input.id = "bili-room-id"; input.placeholder = "请输入你的房间ID"; input.className = "bili-input"; // 新增:输入时保存 input.addEventListener("blur", function () { GM_setValue(STORAGE_KEYS.LAST_ROOM_ID, input.value.trim()); }); container.appendChild(label); container.appendChild(input); return container; } // 创建分区选择 function createAreaSelection() { const container = document.createElement("div"); container.id = "bili-area-selection-container"; // 新增ID,方便获取 container.style.cssText = "display: flex; flex-direction: column; gap: 5px;"; const label = document.createElement("label"); label.textContent = "直播分区:"; label.className = "bili-label"; // 加载指示器 const loading = document.createElement("div"); loading.id = "bili-area-loading"; loading.textContent = "正在加载分区列表..."; loading.style.cssText = "padding: 8px; color: #666; font-size: 14px; text-align: center; cursor: pointer;"; // 分区组选择器 const groupSelect = document.createElement("select"); groupSelect.id = "bili-area-group"; groupSelect.className = "bili-select"; groupSelect.style.cssText = "margin-bottom: 8px; display: none;"; // 子分区选择器 const areaSelect = document.createElement("select"); areaSelect.id = "bili-area"; areaSelect.className = "bili-select"; areaSelect.style.cssText = "display: none;"; // 统一事件绑定 groupSelect.addEventListener("change", function () { const areaList = getCachedAreaList() || []; const selectedIndex = this.options[this.selectedIndex].dataset.index; GM_setValue(STORAGE_KEYS.LAST_GROUP_ID, groupSelect.value); updateAreaSelectors( areaList, Number(selectedIndex), groupSelect, areaSelect ); }); areaSelect.addEventListener("change", function () { GM_setValue(STORAGE_KEYS.LAST_AREA_ID, areaSelect.value); GM_setValue(STORAGE_KEYS.LAST_GROUP_ID, groupSelect.value); }); loading.onclick = function () { if ( loading.style.color === "rgb(255, 75, 75)" || loading.style.color === "#ff4b4b" ) { loadAndBindAreaList(); } }; container.appendChild(label); container.appendChild(loading); container.appendChild(groupSelect); container.appendChild(areaSelect); // 合并后的分区刷新函数 function updateAreaSelectors( areaList, groupIdx = 0, groupSel = groupSelect, areaSel = areaSelect ) { groupSel.innerHTML = ""; areaSel.innerHTML = ""; areaList.forEach((group, idx) => { const option = document.createElement("option"); option.value = group.id; option.textContent = group.name; option.dataset.index = idx; groupSel.appendChild(option); }); // 恢复上次大类 const lastGroupId = GM_getValue(STORAGE_KEYS.LAST_GROUP_ID); if (lastGroupId) { for (let i = 0; i < groupSel.options.length; i++) { if (groupSel.options[i].value == lastGroupId) { groupSel.selectedIndex = i; groupIdx = i; break; } } } if (areaList[groupIdx] && areaList[groupIdx].list) { areaList[groupIdx].list.forEach((area) => { const option = document.createElement("option"); option.value = area.id; option.textContent = area.name; areaSel.appendChild(option); }); } // 恢复上次分区id const lastAreaId = GM_getValue(STORAGE_KEYS.LAST_AREA_ID); if (lastAreaId && areaSel.options.length > 0) { for (let i = 0; i < areaSel.options.length; i++) { if (areaSel.options[i].value == lastAreaId) { areaSel.selectedIndex = i; break; } } } // 显示选择器 loading.style.display = "none"; groupSel.style.display = "block"; areaSel.style.display = "block"; } // 加载分区数据 function loadAndBindAreaList() { return new Promise(async (resolve, reject) => { // Make the wrapping function async // 返回 Promise loading.style.display = "block"; groupSelect.style.display = "none"; areaSelect.style.display = "none"; loading.textContent = "正在加载分区列表..."; loading.style.color = "#666"; const cachedList = getCachedAreaList(); if (cachedList) { updateAreaSelectors(cachedList, 0, groupSelect, areaSelect); resolve(); // 解析 Promise return; } try { const response = await gmRequest({ // Use await with gmRequest method: "GET", url: API_URL_AREA_LIST, headers: headers, }); const result = JSON.parse(response.responseText); if (result.code === 0) { cacheAreaList(result.data); updateAreaSelectors(result.data, 0, groupSelect, areaSelect); resolve(); // 解析 Promise } else { console.error("Area list API error:", result); showAreaLoadError(); reject(new Error("Failed to load area list")); // 拒绝 Promise } } catch (errorResponse) { // Catch errors from gmRequest console.error("Area list request error:", errorResponse); showAreaLoadError(); reject(errorResponse); // 拒绝 Promise } }); } // 将 Promise 附加到容器元素,以便在 createUI 中访问 container.loadAndBindAreaListPromise = loadAndBindAreaList(); return container; } // 创建标题输入 function createTitleInput() { const container = document.createElement("div"); container.style.cssText = "display: flex; flex-direction: column; gap: 5px;"; const label = document.createElement("label"); label.textContent = "直播标题:"; label.className = "bili-label"; const input = document.createElement("input"); input.type = "text"; input.id = "bili-title"; input.placeholder = "请输入直播标题"; input.className = "bili-input"; // 新增:输入时保存 input.addEventListener("blur", function () { GM_setValue(STORAGE_KEYS.LAST_TITLE, input.value.trim()); }); container.appendChild(label); container.appendChild(input); return container; } // 创建按钮组 function createButtonGroup() { const container = document.createElement("div"); container.style.cssText = "display: flex; gap: 10px; margin-top: 10px;"; // 开始直播按钮 startLiveButton = document.createElement("button"); startLiveButton.textContent = "获取推流码并开始直播"; startLiveButton.className = "bili-btn-main"; startLiveButton.style.flex = "1"; startLiveButton.onclick = startLive; // 结束直播按钮 stopLiveButton = document.createElement("button"); stopLiveButton.textContent = "结束直播"; stopLiveButton.className = "bili-btn-stop"; stopLiveButton.style.flex = "1"; stopLiveButton.disabled = true; stopLiveButton.onclick = stopLive; container.appendChild(startLiveButton); container.appendChild(stopLiveButton); return container; } // 创建浮动按钮 function createFloatButton() { const button = document.createElement("div"); button.id = "bili-stream-float-button"; button.innerHTML = ''; button.style.cssText = ` position: fixed; bottom: 20px; right: 20px; width: 48px; height: 48px; border-radius: 50%; background-color: #fb7299; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); z-index: 10001; transition: transform 0.3s; `; button.onmouseover = function () { this.style.transform = "scale(1.1)"; }; button.onmouseout = function () { this.style.transform = "scale(1)"; }; button.onclick = togglePanel; document.body.appendChild(button); return button; } // 显示/隐藏面板 function togglePanel() { const panel = document.getElementById("bili-stream-code-panel"); if (!panel) return; // 理论上不会发生 panel.style.display = panel.style.display === "none" || !panel.style.display ? "block" : "none"; } // 检查浮动按钮 function checkFloatButton() { if (!document.getElementById("bili-stream-float-button")) { createFloatButton(); } } // 全局 loadAreaList 函数已移除,其功能由 createAreaSelection 内的 loadAndBindAreaList 处理 // 显示分区加载错误信息 function showAreaLoadError() { const loading = document.getElementById("bili-area-loading"); if (loading) { loading.textContent = "无法加载分区列表,请稍后刷新重试"; loading.style.color = "#ff4b4b"; } // 显示通知 GM_notification({ text: "无法加载直播分区列表,请检查网络连接或登录状态", title: "B站推流码获取工具", timeout: 5000, }); } // 更新分区选择器 function updateAreaSelectors(areaList) { const loading = document.getElementById("bili-area-loading"); const groupSelect = document.getElementById("bili-area-group"); const areaSelect = document.getElementById("bili-area"); // 防止 loading 取不到时报错 if (!loading || !groupSelect || !areaSelect) return; // 隐藏加载提示 loading.style.display = "none"; // 显示选择器 groupSelect.style.display = "block"; areaSelect.style.display = "block"; // 清空选择器 groupSelect.innerHTML = ""; areaSelect.innerHTML = ""; // 添加分区大类 areaList.forEach((group, index) => { const option = document.createElement("option"); option.value = group.id; option.textContent = group.name; option.dataset.index = index; groupSelect.appendChild(option); }); // 默认显示第一个分区大类的子分区 if (areaList.length > 0 && areaList[0].list) { areaList[0].list.forEach((area) => { const option = document.createElement("option"); option.value = area.id; option.textContent = area.name; areaSelect.appendChild(option); }); } // 分区大类变更事件 groupSelect.addEventListener("change", function () { const selectedIndex = this.options[this.selectedIndex].dataset.index; const selectedGroup = areaList[selectedIndex]; // 清空子分区 areaSelect.innerHTML = ""; if (selectedGroup && selectedGroup.list) { selectedGroup.list.forEach((area) => { const option = document.createElement("option"); option.value = area.id; option.textContent = area.name; areaSelect.appendChild(option); }); } }); } // 获取缓存的分区列表 function getCachedAreaList() { const timeStamp = GM_getValue(STORAGE_KEYS.AREA_LIST_TIME); if (!timeStamp) return null; const now = new Date().getTime(); const oneDay = 24 * 60 * 60 * 1000; // 超过一天则认为过期 if (now - timeStamp > oneDay) return null; const listStr = GM_getValue(STORAGE_KEYS.AREA_LIST); if (!listStr) return null; try { return JSON.parse(listStr); } catch (e) { return null; } } // 缓存分区列表 function cacheAreaList(areaList) { GM_setValue(STORAGE_KEYS.AREA_LIST, JSON.stringify(areaList)); GM_setValue(STORAGE_KEYS.AREA_LIST_TIME, new Date().getTime()); } // 拆分 autoFillRoomId 内部逻辑 function getRoomIdFromUrl() { const urlMatch = window.location.href.match(/live\.bilibili\.com\/(\d+)/); return urlMatch && urlMatch[1] ? urlMatch[1] : null; } function getRoomIdFromElement() { const roomElement = document.querySelector(".room-info-anchor-name"); if (roomElement) { const href = roomElement.getAttribute("href"); if (href) { const match = href.match(/\/(\d+)/); if (match && match[1]) { return match[1]; } } } return null; } function getRoomIdFromHistory() { return GM_getValue(STORAGE_KEYS.LAST_ROOM_ID); } function getCsrfToken() { const csrfCookie = document.cookie .split("; ") .find((row) => row.startsWith("bili_jct=")); return csrfCookie ? csrfCookie.split("=")[1] : null; } function autoFillRoomId() { const lastRoomId = GM_getValue(STORAGE_KEYS.LAST_ROOM_ID); const lastAreaId = GM_getValue(STORAGE_KEYS.LAST_AREA_ID); const lastTitle = GM_getValue(STORAGE_KEYS.LAST_TITLE); if (streamInfo && streamInfo.roomId) { document.getElementById("bili-room-id").value = streamInfo.roomId; roomId = streamInfo.roomId; if (document.getElementById("bili-title") && streamInfo.title) { document.getElementById("bili-title").value = streamInfo.title; } } else { let foundRoomId = getRoomIdFromUrl() || getRoomIdFromElement(); if (!foundRoomId && window.location.href.includes("space.bilibili.com")) { const midMatch = window.location.href.match( /space\.bilibili\.com\/(\d+)/ ); if (midMatch && midMatch[1]) { GM_setValue(STORAGE_KEYS.USER_MID, midMatch[1]); } } if (!foundRoomId) { foundRoomId = getRoomIdFromHistory(); } if (foundRoomId) { document.getElementById("bili-room-id").value = foundRoomId; roomId = foundRoomId; GM_setValue(STORAGE_KEYS.LAST_ROOM_ID, foundRoomId); } else if (lastRoomId) { document.getElementById("bili-room-id").value = lastRoomId; roomId = lastRoomId; } } if (document.getElementById("bili-title") && lastTitle) { document.getElementById("bili-title").value = lastTitle; } // 移除 setTimeout,因为现在依赖 Promise // setTimeout(() => { if (lastAreaId) { const areaSelect = document.getElementById("bili-area"); if (areaSelect) { for (let i = 0; i < areaSelect.options.length; i++) { if (areaSelect.options[i].value == lastAreaId) { areaSelect.selectedIndex = i; break; } } } } // }, 500); csrf = getCsrfToken(); } // 恢复直播状态 function restoreLiveState() { if (isLiveStarted && streamInfo) { setTimeout(() => { const panel = document.getElementById("bili-stream-code-panel"); if (panel) { // 不再自动展开面板,只恢复按钮和推流信息 // panel.style.display = 'block'; // 更新按钮状态 updateButtonsForLive(true); // 恢复推流信息 restoreStreamInfo(); } }, 500); } } // 推流信息区输入框和按钮也用 class function restoreStreamInfo() { if (!streamInfo) return; const resultArea = document.getElementById("bili-result"); if (!resultArea) return; const rtmpAddr = streamInfo.rtmpAddr; const rtmpCode = streamInfo.rtmpCode; const resultHTML = `
服务器地址:
推流码:
重要提示:
1. 长时间无信号会自动关闭直播
2. 推流码如果变动会有提示
服务器地址:
推流码:
重要提示:
1. 长时间无信号会自动关闭直播
2. 推流码如果变动会有提示
服务器地址:
推流码:
重要提示:
1. 长时间无信号会自动关闭直播
2. 推流码如果变动会有提示