Chaturbate CDN Switcher

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

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

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

您需要先安装一款用户脚本管理器扩展,例如 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);

})();