// ==UserScript==
// @name 斗鱼全民星推荐自动领取
// @namespace http://tampermonkey.net/
// @version 1.3
// @description 自动打开、领取并切换直播间处理全民星推荐活动红包,并尝试自动暂停视频。
// @author ysl
// @match *://www.douyu.com/6657*
// @match *://www.douyu.com/*
// @match *://www.douyu.com/topic/*?rid=[0-9]*
// @grant GM_openInTab
// @grant GM_closeTab
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @grant GM_log
// @grant GM_xmlhttpRequest
// @connect list-www.douyu.com
// @run-at document-idle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// --- 常量与配置 ---
const CONTROL_ROOM_ID = "6657";
const TEMP_CONTROL_ROOM_RID = "6979222";
const SCRIPT_PREFIX = "[全民星推荐助手]";
const CHECK_INTERVAL = 5000;
const POPUP_WAIT_TIMEOUT = 15000;
const POPUP_CHECK_INTERVAL = 1000;
const CLOSE_POPUP_DELAY = 4000;
const PANEL_WAIT_TIMEOUT = 10000;
const ELEMENT_WAIT_TIMEOUT = 30000;
const LOAD_DELAY_THRESHOLD = 3;
const MIN_DELAY = 1000;
const MAX_DELAY = 2500;
const OPEN_TAB_DELAY = 1000;
const CLOSE_TAB_DELAY = 1500;
const INITIAL_SCRIPT_DELAY = 3000;
const DRAGGABLE_BUTTON_ID = 'douyu-qmx-starter-button';
const BUTTON_POS_STORAGE_KEY = 'douyu_qmx_button_position';
const API_URL = "https://www.douyu.com/japi/livebiznc/web/anchorstardiscover/redbag/square/list";
const MAX_TAB_LIFETIME_MS = 10 * 60 * 1000;
// 自动暂停相关配置
const AUTO_PAUSE_ENABLED = true; // 是否启用自动暂停
const AUTO_PAUSE_CHECK_INTERVAL = 2000; // 检查是否需要暂停的间隔 (ms)
const AUTO_PAUSE_DELAY_AFTER_ACTION = 5000; // 执行红包操作后,延迟多久再开始检查暂停 (ms)
// --- 选择器 ---
const SELECTORS = {
redEnvelopeContainer: "#layout-Player-aside div.LiveNewAnchorSupportT-enter",
countdownTimer: "span.LiveNewAnchorSupportT-enter--bottom",
popupModal: "body > div.LiveNewAnchorSupportT-pop",
openButton: "div.LiveNewAnchorSupportT-singleBag--btnOpen",
closeButton: "div.LiveNewAnchorSupportT-pop--close",
criticalElement: "#js-player-video", // 视频播放器容器
// 新增:播放/暂停按钮选择器
pauseButton: "div.pause-c594e8:not(.removed-9d4c42)", // 播放中状态的暂停按钮
playButton: "div.play-8dbf03:not(.removed-9d4c42)", // 暂停中状态的播放按钮
};
// --- 状态变量 ---
let mainIntervalId = null;
let pauseIntervalId = null; // 新增:暂停循环定时器
let isWaitingForPopup = false;
let isSwitchingRoom = false;
let notFoundCounter = 0;
let isDragging = false;
let dragOffsetX = 0;
let dragOffsetY = 0;
let openedRoomIds = new Set();
let tabStartTime = 0;
let isPausedByScript = false; // 新增:标记是否由脚本暂停
let lastActionTime = 0; // 新增:记录上次红包操作时间
// --- 辅助函数 ---
function log(message) {
GM_log(`${SCRIPT_PREFIX} ${message}`);
console.log(`${SCRIPT_PREFIX} ${message}`);
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function getRandomDelay(min = MIN_DELAY, max = MAX_DELAY) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
async function safeClick(element, description, delayBefore = true, delayAfter = true) {
if (!element) {
log(`[点击失败] 无法找到元素: ${description}`);
return false;
}
try {
const style = window.getComputedStyle(element);
if (style.display === 'none' || style.visibility === 'hidden' || element.offsetParent === null || element.disabled) {
log(`[点击失败] 元素存在但不可见或不可交互: ${description}`);
return false;
}
if (delayBefore) {
const waitBefore = getRandomDelay(MIN_DELAY / 2, MAX_DELAY / 2);
// log(`准备点击 ${description},先等待 ${waitBefore}ms`); // 减少日志输出
await sleep(waitBefore);
}
log(`尝试点击: ${description}`);
element.click();
if (delayAfter) {
const waitAfter = getRandomDelay();
log(`点击 ${description} 后等待 ${waitAfter}ms`);
await sleep(waitAfter);
}
return true;
} catch (error) {
log(`[点击异常] ${description} 时发生错误: ${error.message}`);
console.error(`Click error on ${description}:`, error);
return false;
}
}
async function findElement(selector, timeout = PANEL_WAIT_TIMEOUT, parent = document) {
// log(`开始查找元素: ${selector} (超时 ${timeout}ms)`); // 减少日志输出
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const element = parent.querySelector(selector);
if (element) {
const style = window.getComputedStyle(element);
// 检查元素是否真实可见
if (style.display !== 'none' && style.visibility !== 'hidden' && element.offsetWidth > 0 && element.offsetHeight > 0) {
// log(`找到可见元素: ${selector}`); // 减少日志输出
return element;
}
}
await sleep(300);
}
log(`查找元素超时: ${selector}`);
return null;
}
// --- API 调用 ---
function getRoomsFromApi(count) {
// ... (与原脚本相同)
return new Promise((resolve, reject) => {
log(`开始调用 API 获取房间列表: ${API_URL}`);
GM_xmlhttpRequest({
method: "GET",
url: API_URL,
headers: {
'Referer': 'https://www.douyu.com/',
'User-Agent': navigator.userAgent
},
responseType: "json",
timeout: 10000,
onload: function(response) {
log(`API 响应状态: ${response.status}`);
if (response.status >= 200 && response.status < 300 && response.response) {
const data = response.response;
if (data.error === 0 && data.data && Array.isArray(data.data.redBagList)) {
log(`API 返回成功,原始找到 ${data.data.redBagList.length} 个房间。`);
const roomUrls = data.data.redBagList
.map(item => item.rid)
.filter(rid => rid)
.slice(0, count * 2) // 多获取一些以备用
.map(rid => `https://www.douyu.com/${rid}`);
log(`提取到 ${roomUrls.length} 个 URL。`);
resolve(roomUrls);
} else {
log(`API 返回数据格式错误或 error 不为 0: error=${data.error}, msg=${data.msg}`);
reject(new Error(`API 数据错误: ${data.msg || '未知错误'}`));
}
} else {
log(`API 请求失败,状态码: ${response.status}`);
reject(new Error(`API 请求失败,状态码: ${response.status}`));
}
},
onerror: function(error) {
log(`API 请求网络错误: ${error.statusText || '未知网络错误'}`);
console.error("API onerror:", error);
reject(new Error(`API 网络错误: ${error.statusText || '未知'}`));
},
ontimeout: function() {
log("API 请求超时。");
reject(new Error("API 请求超时"));
}
});
});
}
// --- 标签页关闭函数 ---
async function closeCurrentTab() {
// ... (与原脚本相同)
log("尝试关闭当前标签页...");
if (mainIntervalId) {
log("关闭前停止主循环定时器。");
clearInterval(mainIntervalId);
mainIntervalId = null;
}
if (pauseIntervalId) { // 新增
log("关闭前停止暂停循环定时器。");
clearInterval(pauseIntervalId);
pauseIntervalId = null;
}
await sleep(500); // 确保定时器已清除
try {
log("优先尝试 GM_closeTab()...");
GM_closeTab();
log("GM_closeTab() 已调用 (若标签页未关闭,则无效或被阻止)。");
} catch (e) {
log(`GM_closeTab() 失败或不可用: ${e.message}`);
log("尝试备用方法: window.close()...");
try {
window.close();
log("备用关闭方法 window.close() 已调用。");
} catch (e2) {
log(`备用关闭方法也失败: ${e2.message}`);
}
}
}
// --- 控制页面 相关函数 ---
function makeDraggable(element) {
// ... (与原脚本相同)
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
const savedPos = GM_getValue(BUTTON_POS_STORAGE_KEY);
if (savedPos && savedPos.top && savedPos.left) {
element.style.top = savedPos.top;
element.style.left = savedPos.left;
log(`恢复按钮位置: top=${savedPos.top}, left=${savedPos.left}`);
} else {
// 默认位置
element.style.top = '100px';
element.style.left = '20px';
}
element.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event;
if (e.button !== 0) return; // 仅左键拖动
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
isDragging = true;
element.style.cursor = 'grabbing';
log("开始拖拽按钮");
}
function elementDrag(e) {
if (!isDragging) return;
e = e || window.event;
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
let newTop = element.offsetTop - pos2;
let newLeft = element.offsetLeft - pos1;
// 边界检测
newTop = Math.max(0, Math.min(window.innerHeight - element.offsetHeight, newTop));
newLeft = Math.max(0, Math.min(window.innerWidth - element.offsetWidth, newLeft));
element.style.top = newTop + "px";
element.style.left = newLeft + "px";
}
function closeDragElement(e) {
// 确保只有在拖拽开始后才执行
if (e.button !== 0 && isDragging) return; // 如果是其他鼠标按钮释放,且正在拖拽,则不处理
document.onmouseup = null;
document.onmousemove = null;
if (isDragging) { // 只有当拖拽发生时才保存
isDragging = false;
element.style.cursor = 'grab';
log("结束拖拽按钮");
GM_setValue(BUTTON_POS_STORAGE_KEY, { top: element.style.top, left: element.style.left });
log(`保存按钮位置: top=${element.style.top}, left=${element.style.left}`);
} else {
// 如果没有拖拽,只是点击,也恢复鼠标样式
element.style.cursor = 'grab';
}
}
}
async function openOneNewTab() {
// ... (与原脚本相同)
const startButton = document.getElementById(DRAGGABLE_BUTTON_ID);
if (!startButton || startButton.disabled) return;
startButton.disabled = true;
startButton.innerHTML = '正在查找... <span class="count">(已开: ' + openedRoomIds.size + ')</span>';
log("开始通过 API 查找下一个可打开的房间...");
try {
const apiRoomList = await getRoomsFromApi(10); // 获取多一些房间以增加找到新房间的几率
let foundNewUrl = null;
let foundNewRid = null;
if (apiRoomList && apiRoomList.length > 0) {
log(`API 返回 ${apiRoomList.length} 个房间,开始查找未打开的...`);
for (const url of apiRoomList) {
const ridMatch = url.match(/\/(\d+)/);
if (ridMatch && ridMatch[1]) {
const rid = ridMatch[1];
if (!openedRoomIds.has(rid)) {
foundNewUrl = url;
foundNewRid = rid;
log(`找到未打开的房间: rid=${rid}, url=${url}`);
break;
}
}
}
} else {
log("API 未返回有效的房间列表。");
}
if (foundNewUrl && foundNewRid) {
log(`准备打开新标签页: ${foundNewUrl}`);
try {
GM_openInTab(foundNewUrl, { active: false, setParent: true }); // setParent 可能有助于管理
openedRoomIds.add(foundNewRid);
log(`房间 rid=${foundNewRid} 已添加到打开列表。当前列表大小: ${openedRoomIds.size}`);
startButton.innerHTML = '再打开一个 <span class="count">(已开: ' + openedRoomIds.size + ')</span>';
await sleep(OPEN_TAB_DELAY); // 等待标签页打开
} catch (e) {
log(`打开标签页 ${foundNewUrl} 时出错: ${e.message}`);
startButton.innerHTML = '打开出错,重试? <span class="count">(已开: ' + openedRoomIds.size + ')</span>';
}
} else {
log("在 API 列表中未能找到新的、未打开的房间。");
startButton.innerHTML = '无新房间可开 <span class="count">(已开: ' + openedRoomIds.size + ')</span>';
await sleep(2000); // 给用户看提示的时间
// 只有在按钮仍然是 disabled 状态时才恢复文本,避免覆盖用户快速再次点击的情况
if (!startButton.disabled) {
startButton.innerHTML = '再打开一个 <span class="count">(已开: ' + openedRoomIds.size + ')</span>';
}
}
} catch (error) {
log(`查找或打开房间时发生错误: ${error.message}`);
startButton.innerHTML = '查找出错,重试? <span class="count">(已开: ' + openedRoomIds.size + ')</span>';
} finally {
startButton.disabled = false; // 确保按钮最终会恢复可用
log("按钮已恢复可用。");
}
}
function setupLauncherUI() {
// ... (与原脚本相同)
log("设置控制页面 UI...");
GM_addStyle(`
#${DRAGGABLE_BUTTON_ID} {
position: fixed;
z-index: 99999;
background-color: #ff5d23; /* 斗鱼橙色 */
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: grab;
font-size: 14px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
transition: background-color 0.2s, opacity 0.3s;
opacity: 0.9; /* 轻微透明,不那么突兀 */
}
#${DRAGGABLE_BUTTON_ID}:hover {
background-color: #e04a10; /* 鼠标悬停颜色加深 */
opacity: 1;
}
#${DRAGGABLE_BUTTON_ID}:active {
cursor: grabbing;
background-color: #c8400a; /* 点击时颜色再加深 */
}
#${DRAGGABLE_BUTTON_ID}:disabled {
background-color: #cccccc;
cursor: not-allowed;
opacity: 0.7;
}
#${DRAGGABLE_BUTTON_ID} span.count {
font-size: 10px;
margin-left: 5px;
opacity: 0.7;
}
`);
// 防止重复创建按钮
if (document.getElementById(DRAGGABLE_BUTTON_ID)) {
log("启动按钮已存在。");
return;
}
const button = document.createElement('button');
button.id = DRAGGABLE_BUTTON_ID;
button.innerHTML = '打开一个房间 <span class="count">(已开: 0)</span>'; // 初始计数为0
button.onclick = openOneNewTab; // 点击事件
document.body.appendChild(button);
makeDraggable(button); // 使按钮可拖拽
log("启动按钮已创建并可拖拽。");
}
// --- 工作页面 相关函数 ---
// 新增:自动暂停视频函数
async function autoPauseVideo() {
if (!AUTO_PAUSE_ENABLED) return; // 如果未启用则跳过
if (isWaitingForPopup || isSwitchingRoom) return; // 如果正在进行其他操作,不暂停
// 检查距离上次红包操作时间是否足够长
if (Date.now() - lastActionTime < AUTO_PAUSE_DELAY_AFTER_ACTION) {
// log("距离上次红包操作时间较近,暂时不执行自动暂停。");
return;
}
const pauseBtn = document.querySelector(SELECTORS.pauseButton);
if (pauseBtn) { // 如果能找到暂停按钮,说明视频正在播放
log("检测到视频正在播放,尝试自动暂停...");
if (await safeClick(pauseBtn, "暂停按钮", false, false)) { // 点击暂停时不加额外延迟
isPausedByScript = true;
log("视频已通过脚本暂停。");
} else {
log("尝试点击暂停按钮失败。");
}
} else {
// log("视频已暂停或暂停按钮不可见。"); // 减少日志
}
}
async function waitForPopupAndClick() {
log("开始等待中间红包弹窗...");
isWaitingForPopup = true;
const popup = await findElement(SELECTORS.popupModal, POPUP_WAIT_TIMEOUT);
if (!popup) {
log("等待红包弹窗超时或未找到。");
isWaitingForPopup = false;
return false;
}
log("红包弹窗已出现。查找打开按钮...");
const openBtn = popup.querySelector(SELECTORS.openButton);
if (await safeClick(openBtn, "红包弹窗的打开按钮")) {
lastActionTime = Date.now(); // 记录操作时间
log(`红包打开按钮已点击,等待 ${CLOSE_POPUP_DELAY}ms 后尝试关闭弹窗...`);
await sleep(CLOSE_POPUP_DELAY);
log("尝试关闭领取结果弹窗...");
const finalPopup = document.querySelector(SELECTORS.popupModal); // 重新获取,弹窗可能已变
if (finalPopup) {
const closeBtn = finalPopup.querySelector(SELECTORS.closeButton);
if (!await safeClick(closeBtn, "领取结果弹窗的关闭按钮", true, false)) { // 关闭时不加延后延迟
log("关闭按钮未找到或点击失败。");
} else {
log("关闭按钮已点击。");
}
} else {
log("领取结果弹窗似乎已自动消失或无法重新找到。");
}
isWaitingForPopup = false;
return true;
} else {
log("错误:找到了弹窗,但找不到或无法点击打开按钮。");
// 尝试关闭弹窗,避免卡住
const closeBtn = popup.querySelector(SELECTORS.closeButton);
if (closeBtn) {
log("尝试关闭无法操作的红包弹窗...");
await safeClick(closeBtn, "无法操作的红包弹窗的关闭按钮", false, false);
}
isWaitingForPopup = false;
return false;
}
}
async function handleSwitchRoom() {
// ... (逻辑与原脚本类似,确保停止定时器)
if (isSwitchingRoom) {
log("已在执行切换房间操作,本次跳过。");
return;
}
isSwitchingRoom = true;
log("开始尝试通过 API 获取下一个房间并切换...");
try {
const currentRoomId = window.location.pathname.match(/\/(\d+)/)?.[1] || window.location.search.match(/rid=(\d+)/)?.[1];
log(`当前房间 ID: ${currentRoomId || '未知'}`);
const roomList = await getRoomsFromApi(5); // 获取多个房间备选
let nextUrl = null;
if (roomList && roomList.length > 0) {
for (const url of roomList) {
const nextRoomId = url.match(/\/(\d+)/)?.[1];
if (nextRoomId && nextRoomId !== currentRoomId) { // 确保不是当前房间
log(`找到下一个不同的房间: ${url}`);
nextUrl = url;
break;
} else if (!currentRoomId && nextRoomId) { // 如果当前房间ID未知,选第一个
log(`当前房间 ID 未知,选择第一个获取到的房间: ${url}`);
nextUrl = url;
break;
}
}
// 如果遍历完还没找到不同的,但列表不为空,且第一个不是当前房间,也用第一个
if (!nextUrl && roomList.length > 0 && roomList[0].match(/\/(\d+)/)?.[1] !== currentRoomId) {
log("未找到与当前不同的房间,但API列表非空,尝试使用列表第一个(如果它不是当前房间)");
nextUrl = roomList[0];
}
}
if (nextUrl) {
log(`确定下一个房间链接: ${nextUrl}`);
log("准备打开新标签页并关闭当前页...");
try {
GM_openInTab(nextUrl, { active: false, setParent: true });
log(`新标签页打开指令已发送: ${nextUrl}`);
if (mainIntervalId) {
clearInterval(mainIntervalId);
mainIntervalId = null;
log("当前页主循环已停止。");
}
if (pauseIntervalId) { // 新增
clearInterval(pauseIntervalId);
pauseIntervalId = null;
log("当前页暂停循环已停止。");
}
await sleep(CLOSE_TAB_DELAY + getRandomDelay(0, 500)); // 稍微增加延迟给新标签页加载时间
await closeCurrentTab();
// isSwitchingRoom 会在 closeCurrentTab 后由页面关闭而自然结束,这里无需再设置
} catch (tabError) {
log(`打开或关闭标签页时发生错误: ${tabError.message}`);
if (mainIntervalId) clearInterval(mainIntervalId);
if (pauseIntervalId) clearInterval(pauseIntervalId); // 新增
isSwitchingRoom = false; // 出错时需要重置状态
}
} else {
log("未能从 API 获取到合适的下一个房间链接。可能没有其他活动房间了。停止当前页面脚本。");
if (mainIntervalId) clearInterval(mainIntervalId);
if (pauseIntervalId) clearInterval(pauseIntervalId); // 新增
mainIntervalId = null;
pauseIntervalId = null;
isSwitchingRoom = false;
log("尝试关闭这个最后的标签页...");
await sleep(CLOSE_TAB_DELAY);
await closeCurrentTab();
}
} catch (error) {
log(`通过 API 切换房间时发生严重错误: ${error.message}`);
console.error(error);
isSwitchingRoom = false;
// 发生错误时,也尝试停止定时器
if (mainIntervalId) {
clearInterval(mainIntervalId);
mainIntervalId = null;
log("因错误停止当前页主循环。");
}
if (pauseIntervalId) { // 新增
clearInterval(pauseIntervalId);
pauseIntervalId = null;
log("因错误停止当前页暂停循环。");
}
}
}
async function mainLoop() {
try {
if (isWaitingForPopup || isSwitchingRoom) {
return;
}
if (tabStartTime && (Date.now() - tabStartTime > MAX_TAB_LIFETIME_MS)) {
log(`标签页已达到最大生存时间 (${MAX_TAB_LIFETIME_MS / 60000} 分钟),准备切换。`);
await handleSwitchRoom();
return;
}
const redEnvelopeDiv = document.querySelector(SELECTORS.redEnvelopeContainer);
if (!redEnvelopeDiv) {
notFoundCounter++;
if (notFoundCounter === 1) log("首次未找到红包区域,可能已领完或加载中...");
if (notFoundCounter >= LOAD_DELAY_THRESHOLD) {
log(`红包区域连续 ${notFoundCounter} 次未找到,判定为活动结束或页面异常,触发切换房间。`);
notFoundCounter = 0;
await handleSwitchRoom();
}
return;
}
if (notFoundCounter > 0) {
log("重新找到了红包区域。");
notFoundCounter = 0;
}
// 检查红包区域是否可见
const style = window.getComputedStyle(redEnvelopeDiv);
if (style.display === 'none' || style.visibility === 'hidden' || redEnvelopeDiv.offsetParent === null) {
// log("红包区域不可见,等待下次检查..."); // 减少不必要的日志
return;
}
const statusSpan = redEnvelopeDiv.querySelector(SELECTORS.countdownTimer);
if (statusSpan) {
const statusText = statusSpan.textContent.trim();
if (statusText.includes(':')) {
// log(`等待倒计时: ${statusText}`);
} else if (statusText.includes('抢') || statusText.includes('领')) {
log(`检测到可点击状态: "${statusText}"`);
// 在点击红包前,如果视频是脚本暂停的,不需要主动恢复,因为点击会打断暂停
// isPausedByScript = false; // 重置标记,因为用户(脚本)即将交互
if (await safeClick(redEnvelopeDiv, "右下角红包区域")) {
await waitForPopupAndClick();
// 红包操作完成后,重置上次操作时间,以便autoPauseVideo可以重新计时
lastActionTime = Date.now();
} else {
log("尝试点击右下角红包区域失败。");
await sleep(getRandomDelay()); // 失败后稍作等待
}
} else if (statusText === "") {
// log("红包状态文本为空,可能在加载中,等待下次检查...");
}
else {
log(`红包区域状态未知: "${statusText}",等待下次检查...`);
}
} else {
log("警告:在红包区域内找不到状态元素 (span.LiveNewAnchorSupportT-enter--bottom)。");
}
} catch (error) {
log(`主循环发生未捕获错误: ${error.message}`);
console.error("Main loop error:", error);
// 发生错误时,也尝试停止定时器
if (mainIntervalId) {
log("因主循环错误,停止定时器。");
clearInterval(mainIntervalId);
mainIntervalId = null;
}
if (pauseIntervalId) { // 新增
log("因主循环错误,停止暂停循环定时器。");
clearInterval(pauseIntervalId);
pauseIntervalId = null;
}
}
}
// --- 脚本初始化 ---
async function initializeScript() {
log("脚本初始化...");
const currentUrl = window.location.href;
const isOldControlRoom = currentUrl.includes(`/${CONTROL_ROOM_ID}`);
const isNewControlRoom = currentUrl.includes(`/topic/`) && currentUrl.includes(`rid=${TEMP_CONTROL_ROOM_RID}`);
if (isOldControlRoom || isNewControlRoom) {
let controlRoomType = isOldControlRoom ? `旧版 (${CONTROL_ROOM_ID})` : `新版 (rid=${TEMP_CONTROL_ROOM_RID})`;
log(`当前是控制页面 (${controlRoomType})。`);
setupLauncherUI();
} else if (currentUrl.match(/douyu\.com\/(\d+)/) || currentUrl.match(/douyu\.com\/topic\/.*rid=(\d+)/)) {
log("当前是工作页面。");
tabStartTime = Date.now();
lastActionTime = Date.now(); // 初始化上次操作时间
log(`等待关键元素 "${SELECTORS.criticalElement}" 出现 (最长 ${ELEMENT_WAIT_TIMEOUT / 1000} 秒)...`);
const criticalElement = await findElement(SELECTORS.criticalElement, ELEMENT_WAIT_TIMEOUT);
if (criticalElement) {
log(`关键元素已找到。`);
// 启动主循环
if(mainIntervalId) clearInterval(mainIntervalId);
await sleep(CHECK_INTERVAL); // 给页面更多渲染时间
log("开始主循环...");
mainIntervalId = setInterval(mainLoop, CHECK_INTERVAL);
// 启动自动暂停循环 (如果启用)
if (AUTO_PAUSE_ENABLED) {
if(pauseIntervalId) clearInterval(pauseIntervalId);
log("自动暂停功能已启用,开始暂停检查循环...");
pauseIntervalId = setInterval(autoPauseVideo, AUTO_PAUSE_CHECK_INTERVAL);
}
} else {
log(`等待关键元素 "${SELECTORS.criticalElement}" 超时或未找到。脚本在当前页面可能无法正常工作。`);
log("尝试关闭此无法正常初始化的标签页...");
await sleep(CLOSE_TAB_DELAY);
await closeCurrentTab();
}
} else {
log("当前页面不是指定的控制页或直播间工作页,脚本不活动。 URL:", currentUrl);
}
}
// --- 启动 ---
log(`脚本将在 ${INITIAL_SCRIPT_DELAY}ms 后开始初始化...`);
setTimeout(initializeScript, INITIAL_SCRIPT_DELAY);
})();