Greasy Fork

Twitch Status Badge at img

スレッド内の書き込みにTwitch のリンクがあったら、shields.ioの配信状況バッジを挿入します。

当前为 2025-06-13 提交的版本,查看 最新版本

// ==UserScript==
// @name         Twitch Status Badge at img
// @namespace    https://github.com/uzuky
// @version      1.2
// @description  スレッド内の書き込みにTwitch のリンクがあったら、shields.ioの配信状況バッジを挿入します。
// @author       uzuky
// @license      MIT
// @match        https://*.2chan.net/b/res/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    /* shields.ioのバッジの見た目をカスタマイズする設定
     * 詳細は https://shields.io/badges/twitch-status
     */
    const shieldOptions = {
        style: 'flat',          // スタイル (plastic, flat, flat-square, for-the-badge, social)
        logo: 'twitch',         // 表示するロゴ (https://simpleicons.org/ にあるやつ)
        logoColor: 'white',     // ロゴの色
        //logoSize: '',           // ロゴのサイズ
        label: '',              // ラベルの文字
        //labelColor: '',         // ラベルの色
        //color: '',              // ラベルの背景色
        //cacheSeconds: '',       // 配信状況がキャッシュされる秒数 デフォルトの5分より短くしても意味がない
    };

    function createShieldUrl(channelName) {
        const params = new URLSearchParams(shieldOptions);
        return `https://img.shields.io/twitch/status/${channelName}?${params.toString()}`;
    }

    function addBadges() {
        const links = document.querySelectorAll('.thre a');
        const twitchRegex = /^https?:\/\/(?:www\.)?twitch\.tv\/([a-zA-Z0-9_]+)/;

        links.forEach(aTag => {
            if (aTag.querySelector('img')) {
                return;
            }
            const href = aTag.href;
            const match = href.match(twitchRegex);

            // チャンネルのURL && バッジが追加されていない
            if (match && match[1] && !aTag.dataset.twitchBadgeAdded) {
                const channelName = match[1];

                const img = document.createElement('img');
                img.src = createShieldUrl(channelName);
                img.alt = `Twitch Status for ${channelName}`;
                img.title = `${channelName} の配信状況`;
                img.style.marginLeft = '8px';
                img.style.verticalAlign = 'middle';
                img.style.height = '20px';

                aTag.appendChild(img);

                aTag.dataset.twitchBadgeAdded = 'true';
            }
        });
    }

    addBadges();

    // 動的にコンテンツが読み込まれるのに対応するため、ページの更新を監視して再度バッジを挿入する
    const observer = new MutationObserver((mutations) => {
        clearTimeout(observer.timer);
        observer.timer = setTimeout(addBadges, 500);
    });

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

})();