Chaturbate CDN Switcher

锁定 edge23-sea 节点以解决因 CDN 自动分配导致的视频 404 错误或播放卡顿问题,并提供一键切换开关。

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Chaturbate CDN Switcher
// @namespace    mmcdn-edge-switcher
// @version      2.5
// @description  锁定 edge23-sea 节点以解决因 CDN 自动分配导致的视频 404 错误或播放卡顿问题,并提供一键切换开关。
// @author       RYUJO
// @license      MIT
// @match        https://chaturbate.com/*
// @match        https://*.chaturbate.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=chaturbate.com
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const TARGET = 'edge23-sea.live.mmcdn.com';
    const REGEX = /([a-z0-9-]+)\.live\.mmcdn\.com/gi;
    
    // 状态管理:锁定 edge23-sea 节点解决 404 错误
    if (localStorage.getItem('mmcdn_force_mode') === null) localStorage.setItem('mmcdn_force_mode', 'true');
    let isEnabled = localStorage.getItem('mmcdn_force_mode') === 'true';

    // === 1. RYUJO 水印 (仅保留在控制台) ===
    const RYUJO_ART = `
██████╗ ██╗   ██╗██╗   ██╗     ██╗ ██████╗ 
██╔══██╗╚██╗ ██╔╝██║   ██║     ██║██╔═══██╗
██████╔╝ ╚████╔╝ ██║   ██║     ██║██║   ██║
██╔══██╗  ╚██╔╝  ██║   ██║██   ██║██║   ██║
██║  ██║   ██║   ╚██████╔╝╚█████╔╝╚██████╔╝
╚═╝  ╚═╝   ╚═╝    ╚═════╝  ╚════╝  ╚═════╝ `;

    console.log(`%c${RYUJO_ART}`, 'color: #06b6d4; font-weight: bold; font-family: monospace;');
    console.log(
        `%c RYUJO %c CDN Switcher %c ${isEnabled ? 'ACTIVE' : 'DISABLED'} `,
        'background:#0891b2; color:white; padding:3px 5px; border-radius:3px 0 0 3px; font-weight:bold;',
        'background:#334155; color:white; padding:3px 5px;',
        `background:${isEnabled ? '#10b981' : '#ef4444'}; color:white; padding:3px 5px; border-radius:0 3px 3px 0; font-weight:bold;`
    );

    // === 2. 核心拦截逻辑 (解决 404 问题) ===
    function fixUrl(str) {
        if (!isEnabled || typeof str !== 'string') return str;

        // 仅拦截视频流相关请求,不干扰图片加载
        const isStream = /\.m3u8($|\?)/i.test(str) || /\.ts($|\?)/i.test(str);
        
        if (isStream && str.indexOf('.live.mmcdn.com') !== -1 && str.indexOf(TARGET) === -1) {
            const newUrl = str.replace(REGEX, TARGET);
            
            console.groupCollapsed(`%c ⚡ RYUJO HIJACK %c Stream Redirected `, 'background:#06b6d4;color:white;padding:1px 4px;border-radius:3px;', 'color:#06b6d4;font-weight:bold;');
            console.log('%cOriginal:%c ' + str, 'color:#94a3b8;', 'color:#ef4444;text-decoration:line-through;');
            console.log('%cTarget:  %c ' + newUrl, 'color:#94a3b8;', 'color:#10b981;font-weight:bold;');
            console.groupEnd();
            
            return newUrl;
        }
        return str;
    }

    // --- A. Hls.js Hook ---
    Object.defineProperty(window, 'Hls', {
        configurable: true, enumerable: true,
        get: function() { return this._ryujo_hls; },
        set: function(val) {
            this._ryujo_hls = val;
            if (val && val.prototype && !val.prototype._patched) {
                const oldLoad = val.prototype.loadSource;
                val.prototype.loadSource = function(url) { return oldLoad.call(this, fixUrl(url)); };
                val.prototype._patched = true;
            }
        }
    });

    // --- B. 网络层 Hooks ---
    const _fetch = window.fetch;
    window.fetch = function (i, n) {
        if (typeof i === 'string') i = fixUrl(i);
        else if (i instanceof Request) i = new Request(fixUrl(i.url), i);
        return _fetch.call(this, i, n);
    };

    const _open = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (m, u, ...a) {
        return _open.apply(this, [m, fixUrl(u), ...a]);
    };

    // === 3. 极简指示灯 UI (提供一键切换开关) ===
    function injectUI() {
        if (document.getElementById('ryujo-dot')) return;
        const dot = document.createElement('div');
        dot.id = 'ryujo-dot';
        dot.style.cssText = `
            position: fixed; top: 12px; right: 12px; z-index: 2147483647;
            width: 10px; height: 10px; border-radius: 50%;
            background: ${isEnabled ? '#06b6d4' : '#475569'};
            cursor: pointer; border: 2px solid #fff;
            box-shadow: 0 0 8px ${isEnabled ? '#06b6d4' : 'transparent'};
            transition: 0.3s;
        `;
        dot.title = `Chaturbate CDN Switcher: ${isEnabled ? 'ON' : 'OFF'}`;
        dot.onclick = () => {
            localStorage.setItem('mmcdn_force_mode', !isEnabled);
            location.reload();
        };
        (document.body || document.documentElement).appendChild(dot);
    }

    if (document.readyState === 'loading') window.addEventListener('DOMContentLoaded', injectUI);
    else injectUI();
    setInterval(injectUI, 3000);

})();