Chaturbate CDN Switcher

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

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==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);

})();