Greasy Fork

Greasy Fork is available in English.

咪咕弹幕

咪咕弹幕。没有就给你搞一个呗,移动这个懒B!

当前为 2024-07-28 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         咪咕弹幕
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  咪咕弹幕。没有就给你搞一个呗,移动这个懒B!
// @icon         https://www.miguvideo.com/favicon.ico
// @author       IDCIM [email protected]
// @match        https://www.miguvideo.com/p/live/*
// @license MIT
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // 日志函数
    function log(message) {
        console.log('[咪咕弹幕脚本]', message);
    }

    // 默认设置
    const defaultSettings = {
        enabled: true,
        speed: 15,
        fontSize: 20,
        topHalfOnly: true
    };

    // 获取设置
    function getSettings() {
        return {
            enabled: GM_getValue('danmakuEnabled', defaultSettings.enabled),
            speed: GM_getValue('danmakuSpeed', defaultSettings.speed),
            fontSize: GM_getValue('danmakuFontSize', defaultSettings.fontSize),
            topHalfOnly: GM_getValue('danmakuTopHalfOnly', defaultSettings.topHalfOnly)
        };
    }

    // 保存设置
    function saveSettings(settings) {
        GM_setValue('danmakuEnabled', settings.enabled);
        GM_setValue('danmakuSpeed', settings.speed);
        GM_setValue('danmakuFontSize', settings.fontSize);
        GM_setValue('danmakuTopHalfOnly', settings.topHalfOnly);
    }

    // 等待元素出现的函数
    function waitForElement(selector, timeout = 1000) {
        return new Promise((resolve, reject) => {
            if (document.querySelector(selector)) {
                return resolve(document.querySelector(selector));
            }

            const observer = new MutationObserver(() => {
                if (document.querySelector(selector)) {
                    resolve(document.querySelector(selector));
                    observer.disconnect();
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });

            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Timeout waiting for ${selector}`));
            }, timeout);
        });
    }

    // 创建弹幕容器
    function createDanmakuContainer(playerElement) {
        const container = document.createElement('div');
        container.id = 'danmaku-container';
        container.style.cssText = `
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            pointer-events: none;
            z-index: 9999;
        `;
        playerElement.appendChild(container);
        return container;
    }

    // 创建弹幕元素函数
    function createDanmaku(avatar, name, text, settings) {
        const danmaku = document.createElement('div');
        danmaku.className = 'danmaku-item';
        danmaku.style.cssText = `
        position: absolute;
        white-space: nowrap;
        font-size: ${settings.fontSize}px;
        color: #fff;
        text-shadow: 1px 1px 2px #000;
        display: flex;
        align-items: center;
        right: -100%;
        transition: right ${settings.speed}s linear;
    `;
        danmaku.innerHTML = `
        <img src="${avatar}" alt="" style="width: 20px; height: 20px; border-radius: 50%; margin-right: 5px;">
        <span style="font-weight: bold; margin-right: 5px;">${name}</span>
        <span>${text}</span>
    `;
        const topPosition = settings.topHalfOnly ? `${Math.random() * 40}%` : `${Math.random() * 80}%`;
        danmaku.style.top = topPosition;
        return danmaku;
    }


    // 添加新弹幕函数
    function addDanmaku(container, settings) {
        if (!settings.enabled) return;

        log('开始添加弹幕');
        const items = document.querySelectorAll('.list-wrapper .list_item:not(.processed)');
        log(`找到 ${items.length} 个列表项`);

        items.forEach((item, index) => {
            item.classList.add('processed'); // 标记已处理项
            setTimeout(() => {
                const avatarImg = item.querySelector('img');
                const nameSpan = item.querySelector('.name');
                const textSpan = item.querySelector('.text');

                if (!avatarImg || !nameSpan || !textSpan) {
                    log('未找到必要的元素', item);
                    return;
                }

                const avatar = avatarImg.src;
                const name = nameSpan.textContent.trim();
                const text = textSpan.textContent.trim();

                log(`创建弹幕: ${name} - ${text}`);

                const danmaku = createDanmaku(avatar, name, text, settings);
                container.appendChild(danmaku);

                // 开始动画
                setTimeout(() => {
                    danmaku.style.right = '100%';
                }, 50);

                // 动画结束后删除弹幕
                danmaku.addEventListener('transitionend', () => {
                    danmaku.remove();
                });
            }, index * 1000); // 将每个弹幕的间隔时间改为 1 秒
        });
    }


    // 创建设置面板
    function createSettingsPanel(container, settings) {
        const panel = document.createElement('div');
        panel.id = 'danmaku-settings-panel';
        panel.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 20px;
            border-radius: 10px;
            z-index: 10001;
            display: none;
        `;
        panel.innerHTML = `
            <h2 style="margin-top: 0;">弹幕设置</h2>
            <label>
                <input type="checkbox" id="danmaku-enabled" ${settings.enabled ? 'checked' : ''}>
                启用弹幕
            </label><br><br>
            <label>
                弹幕速度:
                <input type="range" id="danmaku-speed" min="5" max="30" value="${settings.speed}">
                <span id="speed-value">${settings.speed}</span>秒
            </label><br><br>
            <label>
                字体大小:
                <input type="range" id="danmaku-font-size" min="12" max="24" value="${settings.fontSize}">
                <span id="font-size-value">${settings.fontSize}</span>px
            </label><br><br>
            <label>
                <input type="checkbox" id="danmaku-top-half" ${settings.topHalfOnly ? 'checked' : ''}>
                仅在上半区显示
            </label><br><br>
            <button id="save-settings" style="color:#000;padding:5px;">保存设置</button>
            <button id="close-settings" style="color:#000;padding:5px;">关闭设置</button>
        `;
        document.body.appendChild(panel);

        // 更新显示的值
        document.getElementById('danmaku-speed').addEventListener('input', (e) => {
            document.getElementById('speed-value').textContent = e.target.value;
        });
        document.getElementById('danmaku-font-size').addEventListener('input', (e) => {
            document.getElementById('font-size-value').textContent = e.target.value;
        });

        // 保存设置
        // 保存设置并立即应用
        document.getElementById('save-settings').addEventListener('click', () => {
            const newSettings = {
                enabled: document.getElementById('danmaku-enabled').checked,
                speed: parseInt(document.getElementById('danmaku-speed').value),
                fontSize: parseInt(document.getElementById('danmaku-font-size').value),
                topHalfOnly: document.getElementById('danmaku-top-half').checked
            };
            saveSettings(newSettings);
            panel.style.display = 'none';

            // 更新容器的弹幕设置
            Object.assign(settings, newSettings);

            // 显示或隐藏弹幕容器
            if (settings.enabled) {
                container.style.display = 'block';
                clearDanmaku(container);
                addDanmaku(container, settings); // 立即添加一次弹幕
                clearInterval(addDanmakuInterval); // 清除原有的定时器
                addDanmakuInterval = setInterval(() => addDanmaku(container, settings), 15000); // 重新启动定时器
            } else {
                container.style.display = 'none';
                clearInterval(addDanmakuInterval); // 停止定时器
            }
        });



        // 关闭设置面板
        document.getElementById('close-settings').addEventListener('click', () => {
            panel.style.display = 'none';
        });

        return panel;
    }

    // 清除所有弹幕
    function clearDanmaku(container) {
        while (container.firstChild) {
            container.removeChild(container.firstChild);
        }
    }

    // 创建设置按钮
    function createSettingsButton(panel) {
        const button = document.createElement('div');
        const span = document.createElement('span');
        span.textContent = '弹幕设置';
        span.style.cssText = `
            display:block;
            color:#f00f00;
            cursor:pointer;
            font-size:14px;
        `;
        button.append(span);
        button.style.cssText = `
            z-index: 10000;
            width:50px;
            margin-left:20px;
            display:flex;
            align-items:center;
        `;
        button.addEventListener('click', () => {
            panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
        });

        // 将按钮插入到 .shareBar .share-info .top 的最后一个元素之后
        const shareInfoTop = document.querySelector('.shareBar .share-info .top');
        if (shareInfoTop) {
            shareInfoTop.appendChild(button);
        } else {
            document.body.appendChild(button);
        }
    }

    // 初始化脚本时只显示当前弹幕
    async function initScript() {
        log('等待#mod-player元素出现');
        try {
            const playerElement = await waitForElement('#mod-player');
            log('#mod-player元素已找到,开始初始化弹幕系统');

            const settings = getSettings();
            const container = createDanmakuContainer(playerElement);
            const settingsPanel = createSettingsPanel(container, settings);
            createSettingsButton(settingsPanel);

            container.style.display = settings.enabled ? 'block' : 'none';

            let addDanmakuInterval;
            if (settings.enabled) {
                addDanmaku(container, settings); // 立即添加一次当前的弹幕
                addDanmakuInterval = setInterval(() => addDanmaku(container, settings), 15000); // 每15秒刷新一次
            }

        } catch (error) {
            log('错误:' + error.message);
        }
    }


    // 运行脚本
    initScript();
})();