吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1054|回复: 6
上一主题 下一主题
收起左侧

[其他原创] 快手直播监听(刷新版)

[复制链接]
跳转到指定楼层
楼主
hezhiwen123 发表于 2026-2-4 15:19 回帖奖励
适用人群与场景
  • 忠实粉丝:不想错过心仪主播的每一秒开播画面,但无法时刻盯着手机或电脑。
  • 直播有奖: 有些主播会有粉丝直播在线时长奖励,看直播时长最长的会有奖励


[JavaScript] 纯文本查看 复制代码
// ==UserScript==
// [url=home.php?mod=space&uid=170990]@name[/url]         快手直播监听(刷新版)
// [url=home.php?mod=space&uid=467642]@namespace[/url]    http://tampermonkey.net/
// [url=home.php?mod=space&uid=1248337]@version[/url]      1.0.1
// @description  通过刷新页面检测当前主播是否开播
// [url=home.php?mod=space&uid=686208]@AuThor[/url]       DouyinLiveRecorder
// [url=home.php?mod=space&uid=195849]@match[/url]        https://live.kuaishou.com/*
// [url=home.php?mod=space&uid=593100]@Icon[/url]         https://www.kuaishou.com/favicon.ico
// [url=home.php?mod=space&uid=609072]@grant[/url]        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_notification
// @grant        unsafeWindow
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // ==================== 配置项 ====================
    const CONFIG = {
        refreshInterval: GM_getValue('refreshInterval', 30000),
        autoStart: GM_getValue('autoStart', false),
        enableNotification: GM_getValue('enableNotification', true),
        notLiveXpath: '//*[@id="app"]/div[3]/div[2]/div[1]/div/div[1]/div/div[1]/div/div/div/div[1]/div/div[1]/div[2]/div/div/div[2]/p/span',
        notLiveText: '主播尚未开播',
    };

    // ==================== 状态变量 ====================
    let isMonitoring = GM_getValue('state_monitoring', false);
    let monitorStartTime = GM_getValue('state_startTime', null);
    let liveStartTime = GM_getValue('state_liveStartTime', null);
    let isCurrentlyLive = GM_getValue('state_isLive', false);
    let totalLiveDuration = GM_getValue('totalLiveDuration', 0);
    let checkCount = GM_getValue('state_checkCount', 0);
    let refreshTimer = null;

    // ==================== 日志系统 ====================
    const logHistory = JSON.parse(GM_getValue('logHistory', '[]'));
    const MAX_LOGS = 50;

    function addLog(message, type = 'info') {
        const time = new Date().toLocaleTimeString();
        const logEntry = { time, message, type };
        logHistory.unshift(logEntry);
        if (logHistory.length > MAX_LOGS) logHistory.pop();
        GM_setValue('logHistory', JSON.stringify(logHistory));
        console.log(`[快手监听-刷新版] ${time} - ${message}`);
        updateLogPanel();
        return logEntry;
    }

    function log(msg) { addLog(msg, 'info'); }
    function logSuccess(msg) { addLog(msg, 'success'); }
    function logError(msg) { addLog(msg, 'error'); }
    function logWarn(msg) { addLog(msg, 'warn'); }

    // ==================== 工具函数 ====================
    function formatDuration(seconds) {
        if (!seconds || seconds < 0) return '0秒';
        const hours = Math.floor(seconds / 3600);
        const minutes = Math.floor((seconds % 3600) / 60);
        const secs = Math.floor(seconds % 60);
        if (hours > 0) return `${hours}时${minutes}分${secs}秒`;
        if (minutes > 0) return `${minutes}分${secs}秒`;
        return `${secs}秒`;
    }

    function getMonitorDuration() {
        if (!monitorStartTime) return 0;
        return Math.floor((Date.now() - monitorStartTime) / 1000);
    }

    function getLiveDuration() {
        if (!liveStartTime) return 0;
        return Math.floor((Date.now() - liveStartTime) / 1000);
    }

    function getElementByXPath(xpath) {
        return document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    }

    // ==================== 核心检测 ====================
    function checkLiveStatus() {
        const element = getElementByXPath(CONFIG.notLiveXpath);

        if (element && element.textContent.includes(CONFIG.notLiveText)) {
            return { isLive: false, message: '主播尚未开播' };
        } else {
            const videoPlayer = document.querySelector('video');
            const livePlayer = document.querySelector('.player-container, .live-player, [class*="player"]');

            if (videoPlayer || livePlayer) {
                return { isLive: true, message: '检测到直播画面' };
            } else {
                return { isLive: false, message: '页面加载中...' };
            }
        }
    }

    function doCheck() {
        checkCount++;
        GM_setValue('state_checkCount', checkCount);
        updateDisplay();

        const result = checkLiveStatus();

        if (result.isLive) {
            if (!isCurrentlyLive) {
                isCurrentlyLive = true;
                liveStartTime = Date.now();
                GM_setValue('state_isLive', true);
                GM_setValue('state_liveStartTime', liveStartTime);

                logSuccess(`&#128308; 【开播】${result.message}`);
                showNotification('&#128308; 主播开播了!', '直播已开始');

                stopRefresh();
                updateStatusIndicator(true);
            } else {
                log(`&#128308; 直播中... ${result.message}`);
            }
        } else {
            if (isCurrentlyLive) {
                const sessionLive = getLiveDuration();
                totalLiveDuration += sessionLive;
                GM_setValue('totalLiveDuration', totalLiveDuration);
                logWarn(`&#9899; 【下播】本次直播: ${formatDuration(sessionLive)}`);

                isCurrentlyLive = false;
                liveStartTime = null;
                GM_setValue('state_isLive', false);
                GM_setValue('state_liveStartTime', null);
            }

            log(`&#9898; 【未开播】${result.message},${CONFIG.refreshInterval/1000}秒后刷新...`);
            updateStatusIndicator(false);
            scheduleRefresh();
        }
    }

    function scheduleRefresh() {
        if (refreshTimer) clearTimeout(refreshTimer);

        refreshTimer = setTimeout(() => {
            log('&#128260; 刷新页面...');
            location.reload();
        }, CONFIG.refreshInterval);
    }

    function stopRefresh() {
        if (refreshTimer) {
            clearTimeout(refreshTimer);
            refreshTimer = null;
        }
    }

    // ==================== 监听控制 ====================
    function startMonitor() {
        if (isMonitoring) {
            logWarn('监听已在运行中');
            return;
        }

        isMonitoring = true;
        monitorStartTime = Date.now();
        checkCount = 0;

        GM_setValue('state_monitoring', true);
        GM_setValue('state_startTime', monitorStartTime);
        GM_setValue('state_checkCount', 0);

        logSuccess(`&#9654;&#65039; 开始监听,刷新间隔: ${CONFIG.refreshInterval/1000}秒`);
        log(`&#128204; 当前页面: ${location.href}`);

        createPanel();
        updateDisplay();
        updateStatusIndicator(false);

        setTimeout(doCheck, 2000);
    }

    function stopMonitor() {
        stopRefresh();

        if (monitorStartTime) {
            const sessionDuration = getMonitorDuration();
            logSuccess(`&#9209;&#65039; 停止监听,监听时长: ${formatDuration(sessionDuration)}`);
        }

        if (isCurrentlyLive && liveStartTime) {
            const sessionLive = getLiveDuration();
            totalLiveDuration += sessionLive;
            GM_setValue('totalLiveDuration', totalLiveDuration);
            log(`&#128202; 保存开播时长: ${formatDuration(sessionLive)}`);
        }

        isMonitoring = false;
        isCurrentlyLive = false;
        monitorStartTime = null;
        liveStartTime = null;

        GM_setValue('state_monitoring', false);
        GM_setValue('state_startTime', null);
        GM_setValue('state_isLive', false);
        GM_setValue('state_liveStartTime', null);

        updateStatusIndicator(false, true);
    }

    function resetStats() {
        totalLiveDuration = 0;
        checkCount = 0;
        GM_setValue('totalLiveDuration', 0);
        GM_setValue('state_checkCount', 0);
        updateDisplay();
        log('&#128260; 统计已重置');
    }

    function showNotification(title, text) {
        if (CONFIG.enableNotification && typeof GM_notification !== 'undefined') {
            GM_notification({ title, text, timeout: 5000 });
        }
    }

    // ==================== UI面板 ====================
    let panel = null;
    let displayInterval = null;

    function createPanel() {
        if (panel) return;

        panel = document.createElement('div');
        panel.id = 'ks-refresh-panel';
        panel.innerHTML = `
            <style>
                #ks-refresh-panel {
                    position: fixed;
                    top: 100px;
                    right: 20px;
                    width: 320px;
                    background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
                    border-radius: 12px;
                    box-shadow: 0 8px 32px rgba(0,0,0,0.4);
                    z-index: 999999;
                    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                    color: #fff;
                    overflow: hidden;
                }
                #ks-refresh-panel .panel-header {
                    padding: 12px 15px;
                    background: rgba(255,107,53,0.9);
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    cursor: move;
                    font-weight: bold;
                    font-size: 14px;
                }
                #ks-refresh-panel .close-btn { cursor: pointer; font-size: 18px; opacity: 0.8; }
                #ks-refresh-panel .close-btn:hover { opacity: 1; }
                #ks-refresh-panel .panel-body { padding: 15px; }
                #ks-refresh-panel .status-section {
                    background: rgba(255,255,255,0.05);
                    border-radius: 8px;
                    padding: 12px;
                    margin-bottom: 15px;
                }
                #ks-refresh-panel .status-row {
                    display: flex;
                    justify-content: space-between;
                    padding: 6px 0;
                    font-size: 13px;
                }
                #ks-refresh-panel .status-row .label { color: #888; }
                #ks-refresh-panel .status-row .value { color: #4CAF50; font-weight: bold; }
                #ks-refresh-panel .status-dot {
                    display: inline-block;
                    width: 8px;
                    height: 8px;
                    border-radius: 50%;
                    margin-right: 6px;
                }
                #ks-refresh-panel .status-dot.monitoring { background: #ffc107; animation: blink 1s infinite; }
                #ks-refresh-panel .status-dot.live { background: #ff4444; animation: pulse-glow 1s infinite; }
                #ks-refresh-panel .status-dot.stopped { background: #666; }
                @keyframes blink { 0%,100% { opacity:1; } 50% { opacity:0.4; } }
                @keyframes pulse-glow { 0%,100% { box-shadow: 0 0 5px #ff4444; } 50% { box-shadow: 0 0 15px #ff4444; } }
                #ks-refresh-panel .btn-row { display: flex; gap: 8px; margin-bottom: 12px; }
                #ks-refresh-panel button {
                    flex: 1;
                    padding: 10px;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                    font-size: 13px;
                    font-weight: bold;
                    transition: all 0.2s;
                }
                #ks-refresh-panel .btn-start { background: #4CAF50; color: #fff; }
                #ks-refresh-panel .btn-start:hover { background: #45a049; }
                #ks-refresh-panel .btn-stop { background: #f44336; color: #fff; }
                #ks-refresh-panel .btn-stop:hover { background: #d32f2f; }
                #ks-refresh-panel .btn-secondary { background: #333; color: #fff; }
                #ks-refresh-panel .btn-secondary:hover { background: #444; }
                #ks-refresh-panel .settings-section {
                    background: rgba(255,255,255,0.05);
                    border-radius: 8px;
                    padding: 10px;
                    margin-bottom: 10px;
                }
                #ks-refresh-panel .settings-label { font-size: 12px; color: #888; margin-bottom: 8px; }
                #ks-refresh-panel .interval-control { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
                #ks-refresh-panel .interval-control input {
                    width: 70px;
                    padding: 6px 8px;
                    background: #0f0f1a;
                    border: 1px solid #333;
                    border-radius: 4px;
                    color: #fff;
                    font-size: 14px;
                }
                #ks-refresh-panel .interval-unit { color: #888; font-size: 13px; }
                #ks-refresh-panel .btn-apply {
                    padding: 6px 12px;
                    background: #ff6b35;
                    color: #fff;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 12px;
                }
                #ks-refresh-panel .btn-apply:hover { background: #e55a2b; }
                #ks-refresh-panel .interval-presets { display: flex; gap: 6px; }
                #ks-refresh-panel .interval-presets button {
                    flex: 1;
                    padding: 5px 8px;
                    background: #222;
                    color: #aaa;
                    border: 1px solid #333;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 11px;
                }
                #ks-refresh-panel .interval-presets button:hover {
                    background: #333;
                    color: #fff;
                    border-color: #ff6b35;
                }
                #ks-refresh-panel .log-section { background: #0a0a12; border-radius: 8px; margin-top: 10px; }
                #ks-refresh-panel .log-header {
                    padding: 8px 12px;
                    font-size: 12px;
                    color: #888;
                    border-bottom: 1px solid #222;
                    display: flex;
                    justify-content: space-between;
                }
                #ks-refresh-panel .log-content {
                    padding: 8px;
                    font-size: 11px;
                    font-family: 'Consolas', monospace;
                    line-height: 1.6;
                    max-height: 150px;
                    min-height: 80px;
                    overflow-y: auto;
                }
                #ks-refresh-panel .log-entry { margin: 2px 0; }
                #ks-refresh-panel .log-entry.info { color: #888; }
                #ks-refresh-panel .log-entry.success { color: #4CAF50; }
                #ks-refresh-panel .log-entry.error { color: #f44336; }
                #ks-refresh-panel .log-entry.warn { color: #ff9800; }
                #ks-refresh-panel .log-time { color: #555; margin-right: 8px; }
            </style>

            <div class="panel-header">
                <span>&#128260; 快手直播监听(刷新版)</span>
                <span class="close-btn">&#10005;</span>
            </div>

            <div class="panel-body">
                <div class="status-section">
                    <div class="status-row">
                        <span class="label">状态</span>
                        <span class="value"><span class="status-dot stopped" id="ks-status-dot"></span><span id="ks-status-text">未启动</span></span>
                    </div>
                    <div class="status-row">
                        <span class="label">监听时长</span>
                        <span class="value" id="ks-monitor-duration">0秒</span>
                    </div>
                    <div class="status-row">
                        <span class="label">本次开播</span>
                        <span class="value" id="ks-live-duration">0秒</span>
                    </div>
                    <div class="status-row">
                        <span class="label">累计开播</span>
                        <span class="value" id="ks-total-live">${formatDuration(totalLiveDuration)}</span>
                    </div>
                    <div class="status-row">
                        <span class="label">刷新次数</span>
                        <span class="value" id="ks-check-count">${checkCount}</span>
                    </div>
                </div>

                <div class="btn-row">
                    <button class="btn-start">&#9654; 开始监听</button>
                    <button class="btn-stop">&#9209; 停止</button>
                </div>
                <div class="btn-row">
                    <button class="btn-secondary">&#128260; 立即刷新</button>
                    <button class="btn-secondary">&#128202; 重置统计</button>
                </div>

                <div class="settings-section">
                    <div class="settings-label">刷新间隔</div>
                    <div class="interval-control">
                        <input type="number" id="ks-interval" value="${CONFIG.refreshInterval/1000}" min="1" max="300">
                        <span class="interval-unit">秒</span>
                        <button class="btn-apply">应用</button>
                    </div>
                    <div class="interval-presets">
                        <button>1秒</button>
                        <button>5秒</button>
                        <button>15秒</button>
                        <button>30秒</button>
                    </div>
                </div>

                <div class="log-section">
                    <div class="log-header">
                        <span>&#128203; 运行日志</span>
                        <div>
                            <span style="cursor:pointer;margin-right:10px">&#128203;复制</span>
                            <span style="cursor:pointer">&#128465;&#65039;清空</span>
                        </div>
                    </div>
                    <div class="log-content" id="ks-log-content">
                        <div class="log-entry info">等待操作...</div>
                    </div>
                </div>
            </div>
        `;

        document.body.appendChild(panel);
        makeDraggable(panel, panel.querySelector('.panel-header'));
        displayInterval = setInterval(updateDisplay, 1000);
        updateLogPanel();
    }

    function updateLogPanel() {
        const logContent = document.getElementById('ks-log-content');
        if (!logContent) return;
        logContent.innerHTML = logHistory.slice(0, 30).map(entry =>
            `<div class="log-entry ${entry.type}"><span class="log-time">${entry.time}</span>${entry.message}</div>`
        ).join('');
    }

    function updateDisplay() {
        const el1 = document.getElementById('ks-monitor-duration');
        const el2 = document.getElementById('ks-live-duration');
        const el3 = document.getElementById('ks-total-live');
        const el4 = document.getElementById('ks-check-count');
        if (el1) el1.textContent = formatDuration(getMonitorDuration());
        if (el2) el2.textContent = formatDuration(getLiveDuration());
        if (el3) el3.textContent = formatDuration(totalLiveDuration + getLiveDuration());
        if (el4) el4.textContent = checkCount;
    }

    function updateStatusIndicator(isLive, isStopped = false) {
        const dot = document.getElementById('ks-status-dot');
        const text = document.getElementById('ks-status-text');
        if (!dot || !text) return;
        if (isStopped) {
            dot.className = 'status-dot stopped';
            text.textContent = '已停止';
        } else if (isLive) {
            dot.className = 'status-dot live';
            text.textContent = '&#128308; 直播中!';
        } else if (isMonitoring) {
            dot.className = 'status-dot monitoring';
            text.textContent = '监听中...';
        } else {
            dot.className = 'status-dot stopped';
            text.textContent = '未启动';
        }
    }

    function makeDraggable(element, handle) {
        let isDragging = false;
        let offsetX, offsetY;
        handle.addEventListener('mousedown', (e) => {
            isDragging = true;
            offsetX = e.clientX - element.offsetLeft;
            offsetY = e.clientY - element.offsetTop;
            e.preventDefault();
        });
        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                element.style.left = (e.clientX - offsetX) + 'px';
                element.style.top = (e.clientY - offsetY) + 'px';
                element.style.right = 'auto';
            }
        });
        document.addEventListener('mouseup', () => { isDragging = false; });
    }

    // ==================== 全局函数 ====================
    const global = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;

    global.ksRefreshStart = startMonitor;
    global.ksRefreshStop = stopMonitor;
    global.ksRefreshReset = resetStats;

    global.ksRefreshSetInterval = function(val) {
        val = Math.max(1, Math.min(300, parseInt(val) || 30)); // 最小1秒
        CONFIG.refreshInterval = val * 1000;
        GM_setValue('refreshInterval', CONFIG.refreshInterval);
        const input = document.getElementById('ks-interval');
        if (input) input.value = val;
        log(`&#9201;&#65039; 刷新间隔已更新: ${val}秒`);
    };

    global.ksRefreshClearLogs = function() {
        logHistory.length = 0;
        GM_setValue('logHistory', '[]');
        updateLogPanel();
        log('&#128465;&#65039; 日志已清空');
    };

    global.ksRefreshCopyLogs = function() {
        const logText = logHistory.map(entry => `[${entry.time}] ${entry.message}`).join('\n');
        navigator.clipboard.writeText(logText).then(() => {
            log('&#9989; 日志已复制到剪贴板');
        }).catch(() => {
            const textarea = document.createElement('textarea');
            textarea.value = logText;
            document.body.appendChild(textarea);
            textarea.select();
            document.execCommand('copy');
            document.body.removeChild(textarea);
            log('&#9989; 日志已复制到剪贴板');
        });
    };

    // ==================== 菜单命令 ====================
    if (typeof GM_registerMenuCommand !== 'undefined') {
        GM_registerMenuCommand('&#128260; 打开监听面板', () => { createPanel(); panel.style.display = 'block'; });
        GM_registerMenuCommand('&#9654;&#65039; 开始监听', startMonitor);
        GM_registerMenuCommand('&#9209;&#65039; 停止监听', stopMonitor);
    }

    // ==================== 自动启动 ====================
    window.addEventListener('load', () => {
        setTimeout(() => {
            createPanel();
            log('&#9989; 快手直播监听(刷新版)已加载');
            log(`&#128205; 当前页面: ${location.pathname}`);

            if (isMonitoring) {
                log('&#128260; 恢复监听状态...');
                updateStatusIndicator(isCurrentlyLive);
                setTimeout(doCheck, 2000);
            } else {
                log('&#128161; 点击"开始监听"开始检测');
            }
        }, 1500);
    });
})();

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
smatei 发表于 2026-2-7 11:32
这个怎么实现
3#
fff2743 发表于 2026-2-9 09:34
4#
siqi47 发表于 2026-2-9 10:29
要是再有个录制功能就好了。自动监听,开播后,就自动录制。
5#
 楼主| hezhiwen123 发表于 2026-2-9 16:09 |楼主
siqi47 发表于 2026-2-9 10:29
要是再有个录制功能就好了。自动监听,开播后,就自动录制。

有.不过我写的是网页版
6#
 楼主| hezhiwen123 发表于 2026-2-9 16:10 |楼主
fff2743 发表于 2026-2-9 09:34
这个如何用啊  能下吗?

油猴脚本里 复制粘贴
7#
吾爱破解大法 发表于 2026-5-5 16:52


大佬怎么我复制粘贴后 提示无效
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-17 02:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表