Greasy Fork is available in English.

Xbox CLoud Gaming优化整合

整合和修改现有脚本,优化项详见脚本说明。【若你有好的想法或者BUG可以进xbox云游戏QQ交流1群531602832,2群313340764反馈】

La data de 17-08-2023. Vezi ultima versiune.

// ==UserScript==
// @name                 Xbox CLoud Gaming优化整合
// @name:zh-CN           Xbox CLoud Gaming优化整合
// @namespace            http://tampermonkey.net/xbox/nft
// @version              3.1.7
// @author               奈非天
// @license               MIT
// @match                https://www.xbox.com/*/play*
// @run-at               document-start
// @grant                unsafeWindow
// @require              https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.2.1/jquery.min.js
// @original-script      https://greasyfork.org/zh-CN/scripts/455741-xbox-cloud-gaming%E4%BC%98%E5%8C%96%E6%95%B4%E5%90%88
// @description:zh-cn    整合和修改现有脚本,优化项详见脚本说明。【若你有好的想法或者BUG可以进xbox云游戏QQ交流1群531602832,2群313340764反馈】
// @description          整合和修改现有脚本,优化项详见脚本说明。【若你有好的想法或者BUG可以进xbox云游戏QQ交流1群531602832,2群313340764反馈】
// ==/UserScript==
(function () {
    'use strict';
    // Your code here...

    var nftxboxversion='v3.1.6';

    //========↓↓↓↓↓是各个功能的初始设置,仅第一次运行脚本有效↓↓↓↓↓========//
    //★★ 1=开   0=关 ★★//

    //免代理直连
    let no_need_VPN_play = 1;

    let regionsList = {'韩服': '168.126.63.1', '美服': '4.2.2.2', '日服': '210.131.113.123'}

    //欺骗IP
    let fakeIp = regionsList['美服'];

    //选择语言
    let chooseLanguage = 1;
    //智能语言报错时默认使用的语言,简体zh-CN,繁体zh-TW,总开关是上一行的chooseLanguage
    let IfErrUsedefaultGameLanguage = 'zh-CN';

    //高码率,禁用后最高8M,码率720P画质
    let high_bitrate = 1;

    //强制触控
    let autoOpenOC = 0;

    //禁止检测网络状况
    let disableCheckNetwork = 1;

    //自动全屏
    let autoFullScreen = 0;

    //锁定云游戏服务器,注意此项并非是云游戏区域(默认关闭)
    let blockXcloudServer = 0;
    let blockXcloudServerList = ['AustraliaEast', 'AustraliaSouthEast', 'BrazilSouth', 'EastUS', 'EastUS2', 'JapanEast', 'KoreaCentral', 'NorthCentralUs', 'SouthCentralUS', 'UKSouth', 'WestEurope', 'WestUS', 'WestUS2'];
    let defaultXcloudServer = 'KoreaCentral';

    //去视频黑边
    let video_stretch={
        'default':'none',
        'options':{
            'none':'无',
            'fill':'填充',
            'setting':'微调',
        },
        'name':'video_stretchGM'}

    let video_stretch_x_y={
        'x':0,
        'y':0,
        'name':'video_stretch_x_yGM'
    }

    let noPopSetting=0;

    //屏蔽触控(默认关闭)
    let disableTouchControls=0;
    let canShowOC=false;
    let autoShowTouch=true;

    let STATS_SHOW_WHEN_PLAYING={"default":false,"name":"STATS_SHOW_WHEN_PLAYINGGM"};

    let STATS_POSITION={'default':'top-left','options':{'top-left': '上左','top-center': '上中','top-right': '上右'},'name':'STATS_POSITIONGM'}

    let STATS_TRANSPARENT={"default":false,"name":"STATS_TRANSPARENTGM"};
    let STATS_OPACITY={
        'default': 80,
        'min': 10,
        'max': 100,
        'name':'STATS_OPACITYGM'
    };

    let STATS_TEXT_SIZE={
        'default':'0.9rem',
        'options':{
            '0.9rem': '小',
            '1.0rem': '中',
            '1.1rem': '大',
        },
        'name':'STATS_TEXT_SIZEGM'}

    let STATS_CONDITIONAL_FORMATTING={"default":false,"name":"STATS_CONDITIONAL_FORMATTINGGM"};

    let VIDEO_CLARITY={
        'default': 0,
        'min': 0,
        'max': 3,
        'name':'VIDEO_CLARITYGM'
    }

    let VIDEO_CONTRAST={
        'default': 100,
        'min': 0,
        'max': 150,
        'name':'VIDEO_CONTRASTGM'
    }

    let VIDEO_SATURATION={
        'default': 100,
        'min': 0,
        'max': 150,
        'name':'VIDEO_SATURATIONGM'
    }
    let VIDEO_BRIGHTNESS={
        'default': 100,
        'min': 0,
        'max': 150,
        'name':'VIDEO_BRIGHTNESSGM'
    }

    //========↑↑↑↑↑是各个功能的初始设置,仅第一次运行脚本有效↑↑↑↑↑========//

    const originFetch = fetch;
    let regionsMenuItemList = [];
    let languageMenuItemList = [];
    let default_language_list = {'智能简繁': 'Auto', '简体': 'zh-CN', '繁体': 'zh-TW'}
    let xcloud_game_language = default_language_list['简体'];//
    let useCustomfakeIp = 0;
    let customfakeIp = '';

    var STREAM_WEBRTC;
    const ICON_STREAM_STATS = '<path d="M12.005 5C9.184 5 6.749 6.416 5.009 7.903c-.87.743-1.571 1.51-2.074 2.18-.251.335-.452.644-.605.934-.434.733-.389 1.314-.004 1.98a6.98 6.98 0 0 0 .609.949 13.62 13.62 0 0 0 2.076 2.182C6.753 17.606 9.188 19 12.005 19s5.252-1.394 6.994-2.873a13.62 13.62 0 0 0 2.076-2.182 6.98 6.98 0 0 0 .609-.949c.425-.737.364-1.343-.004-1.98-.154-.29-.354-.599-.605-.934-.503-.669-1.204-1.436-2.074-2.18C17.261 6.416 14.826 5 12.005 5zm0 2c2.135 0 4.189 1.135 5.697 2.424.754.644 1.368 1.32 1.773 1.859.203.27.354.509.351.733s-.151.462-.353.732c-.404.541-1.016 1.214-1.77 1.854C16.198 15.881 14.145 17 12.005 17s-4.193-1.12-5.699-2.398a11.8 11.8 0 0 1-1.77-1.854c-.202-.27-.351-.508-.353-.732s.149-.463.351-.733c.406-.54 1.019-1.215 1.773-1.859C7.816 8.135 9.87 7 12.005 7zm.025 1.975c-1.645 0-3 1.355-3 3s1.355 3 3 3 3-1.355 3-3-1.355-3-3-3zm0 2c.564 0 1 .436 1 1s-.436 1-1 1-1-.436-1-1 .436-1 1-1z"/>';
    const ICON_VIDEO_SETTINGS = '<path d="M16 9.144A6.89 6.89 0 0 0 9.144 16 6.89 6.89 0 0 0 16 22.856 6.89 6.89 0 0 0 22.856 16 6.9 6.9 0 0 0 16 9.144zm0 11.427c-2.507 0-4.571-2.064-4.571-4.571s2.064-4.571 4.571-4.571 4.571 2.064 4.571 4.571-2.064 4.571-4.571 4.571zm15.704-7.541c-.065-.326-.267-.607-.556-.771l-4.26-2.428-.017-4.802c-.001-.335-.15-.652-.405-.868-1.546-1.307-3.325-2.309-5.245-2.953-.306-.103-.641-.073-.923.085L16 3.694l-4.302-2.407c-.282-.158-.618-.189-.924-.086a16.02 16.02 0 0 0-5.239 2.964 1.14 1.14 0 0 0-.403.867L5.109 9.84.848 12.268a1.14 1.14 0 0 0-.555.771 15.22 15.22 0 0 0 0 5.936c.064.326.267.607.555.771l4.261 2.428.017 4.802c.001.335.149.652.403.868 1.546 1.307 3.326 2.309 5.245 2.953.306.103.641.073.923-.085L16 28.306l4.302 2.407a1.13 1.13 0 0 0 .558.143 1.18 1.18 0 0 0 .367-.059c1.917-.648 3.695-1.652 5.239-2.962.255-.216.402-.532.405-.866l.021-4.807 4.261-2.428a1.14 1.14 0 0 0 .555-.771 15.21 15.21 0 0 0-.003-5.931zm-2.143 4.987l-4.082 2.321a1.15 1.15 0 0 0-.429.429l-.258.438a1.13 1.13 0 0 0-.174.601l-.022 4.606a13.71 13.71 0 0 1-3.623 2.043l-4.117-2.295a1.15 1.15 0 0 0-.559-.143h-.546c-.205-.005-.407.045-.586.143l-4.119 2.3a13.74 13.74 0 0 1-3.634-2.033l-.016-4.599a1.14 1.14 0 0 0-.174-.603l-.257-.437c-.102-.182-.249-.333-.429-.437l-4.085-2.328a12.92 12.92 0 0 1 0-4.036l4.074-2.325a1.15 1.15 0 0 0 .429-.429l.258-.438a1.14 1.14 0 0 0 .175-.601l.021-4.606a13.7 13.7 0 0 1 3.625-2.043l4.11 2.295a1.14 1.14 0 0 0 .585.143h.52c.205.005.407-.045.586-.143l4.119-2.3a13.74 13.74 0 0 1 3.634 2.033l.016 4.599a1.14 1.14 0 0 0 .174.603l.257.437c.102.182.249.333.429.438l4.085 2.327a12.88 12.88 0 0 1 .007 4.041h.007z" fill-rule="nonzero"/>';

    let windowCtx = self.window;
    if (self.unsafeWindow) {
        console.log("使用unsafeWindow模式");
        windowCtx = self.unsafeWindow;
    } else {
        console.log("使用原生模式");
    }

    let naifeitian = {
        isType(obj) {
            return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase();
        },
        getValue(key) {
            try {
                return JSON.parse(localStorage.getItem(key));
            } catch (e) {
                return localStorage.getItem(key);
            }
        },

        setValue(key, value) {
            if (this.isType(value) === 'object' || this.isType(value) === 'array' || this.isType(value)==='boolean') {
                return localStorage.setItem(key, JSON.stringify(value));
            }
            return localStorage.setItem(key, value);
        },
        isValidIP(ip) {
            var reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
            return reg.test(ip);
        },
        isNumber(val) {
            return !isNaN(parseFloat(val)) && isFinite(val);
        },
        killTouchMove(v){
            $(v).on('touchmove', false);
        },
        renewTouchMove(v){
            $(v).off('touchmove', false);
        },
        toElement(key, onChange) {
            const CE = createElement;
            const setting = key;
            const currentValue = key['default']==undefined?key:key['default'];

            let $control;
            if (setting['options']!=undefined) {

                $control = CE('select', {id: 'xcloud_setting_' + key['name']});

                for (let value in setting.options) {
                    const label = setting.options[value];

                    const $option = CE('option', {value: value}, label);
                    $control.appendChild($option);
                }

                $control.value = currentValue;
                $control.addEventListener('change', e => {
                    key['default']=e.target.value;

                    this.setValue(key['name'], key);
                    onChange && onChange(e);
                });

            } else if (typeof setting.default === 'number') {
                $control = CE('input', {'type': 'number', 'min': setting.min, 'max': setting.max});

                $control.value = currentValue;
                $control.addEventListener('change', e => {
                    let value = Math.max(setting.min, Math.min(setting.max, parseInt(e.target.value)));
                    e.target.value = value;

                    key['default']=e.target.value
                    this.setValue(key['name'],key );
                    onChange && onChange(e);
                });
            } else {
                $control = CE('input', {'type': 'checkbox'});
                $control.checked = currentValue;

                $control.addEventListener('change', e => {
                    key['default']=e.target.checked
                    this.setValue(key['name'], key);
                    onChange && onChange(e);
                });
            }

            $control.id = `xcloud_setting_${key.name}`;
            return $control;
        },
        addMeta (name, content) {
            const meta = document.createElement('meta');
            meta.content = content;
            meta.name = name;
            document.getElementsByTagName('head')[0].appendChild(meta);
        },
        isSafari() {
            return /^((?!chrome|android).)*safari/i.test(userAgentOriginal);
        },
        isiPhone() {
            return /iPhone|iPod/i.test(userAgentOriginal);
        },

        isSafariForiPhone() {
            return this.isSafari() && this.isiPhone();
        }
    }

    // Quickly create a tree of elements without having to use innerHTML
    function createElement(elmName, props = {}) {
        let $elm;
        const hasNs = 'xmlns' in props;

        if (hasNs) {
            $elm = document.createElementNS(props.xmlns, elmName);
        } else {
            $elm = document.createElement(elmName);
        }

        for (let key in props) {
            if (key === 'xmlns') {
                continue;
            }

            if (!props.hasOwnProperty(key) || $elm.hasOwnProperty(key)) {
                continue;
            }

            if (hasNs) {
                $elm.setAttributeNS(null, key, props[key]);
            } else {
                $elm.setAttribute(key, props[key]);
            }
        }

        for (let i = 2, size = arguments.length; i < size; i++) {
            const arg = arguments[i];
            const argType = typeof arg;

            if (argType === 'string' || argType === 'number') {
                $elm.textContent = arg;
            } else if (arg) {
                $elm.appendChild(arg);
            }
        }

        return $elm;
    }

    function setMachineFullScreen() {
        try {
            let element = document.documentElement;
            if (element.requestFullscreen) {
                element.requestFullscreen();
            } else if (element.mozRequestFullScreen) {
                element.mozRequestFullScreen();
            } else if (element.msRequestFullscreen) {
                element.msRequestFullscreen();
            } else if (element.webkitRequestFullscreen) {
                element.webkitRequestFullScreen();
            }
            screen?.orientation?.lock("landscape");
        } catch (e) {
        }
    }

    function exitMachineFullscreen() {
        try {
            screen?.orientation?.unlock();
            if (document.exitFullScreen) {
                document.exitFullScreen();
            } else if (document.mozCancelFullScreen) {
                document.mozCancelFullScreen();
            } else if (document.webkitExitFullscreen) {
                document.webkitExitFullscreen();
            } else if (element.msExitFullscreen) {
                element.msExitFullscreen();
            }
        } catch (e) {
        }
    }


    blockXcloudServerList = naifeitian.getValue("blockXcloudServerListGM") == null ? blockXcloudServerList : naifeitian.getValue("blockXcloudServerListGM");
    naifeitian.setValue("blockXcloudServerListGM", blockXcloudServerList);

    no_need_VPN_play = naifeitian.getValue("no_need_VPN_playGM") == null ? no_need_VPN_play : naifeitian.getValue("no_need_VPN_playGM");
    naifeitian.setValue("no_need_VPN_playGM", no_need_VPN_play);

    chooseLanguage = naifeitian.getValue("chooseLanguageGM") == null ? chooseLanguage : naifeitian.getValue("chooseLanguageGM");
    naifeitian.setValue("chooseLanguageGM", chooseLanguage);

    IfErrUsedefaultGameLanguage = naifeitian.getValue("IfErrUsedefaultGameLanguageGM") == null ? IfErrUsedefaultGameLanguage : naifeitian.getValue("IfErrUsedefaultGameLanguageGM");
    naifeitian.setValue("IfErrUsedefaultGameLanguageGM", IfErrUsedefaultGameLanguage);

    fakeIp = naifeitian.getValue("fakeIpGM") == null ? fakeIp : naifeitian.getValue("fakeIpGM");
    naifeitian.setValue("fakeIpGM", fakeIp);

    high_bitrate = naifeitian.getValue("high_bitrateGM") == null ? high_bitrate : naifeitian.getValue("high_bitrateGM");
    naifeitian.setValue("high_bitrateGM", high_bitrate);

    autoOpenOC = naifeitian.getValue("autoOpenOCGM") == null ? autoOpenOC : naifeitian.getValue("autoOpenOCGM");
    naifeitian.setValue("autoOpenOCGM", autoOpenOC);

    disableCheckNetwork = naifeitian.getValue("disableCheckNetworkGM") == null ? disableCheckNetwork : naifeitian.getValue("disableCheckNetworkGM");
    naifeitian.setValue("disableCheckNetworkGM", disableCheckNetwork);

    defaultXcloudServer = naifeitian.getValue("defaultXcloudServerGM") == null ? defaultXcloudServer : naifeitian.getValue("defaultXcloudServerGM");
    naifeitian.setValue("defaultXcloudServerGM", defaultXcloudServer);

    blockXcloudServer = naifeitian.getValue("blockXcloudServerGM") == null ? blockXcloudServer : naifeitian.getValue("blockXcloudServerGM");
    naifeitian.setValue("blockXcloudServerGM", blockXcloudServer);

    xcloud_game_language = naifeitian.getValue("xcloud_game_languageGM") == null ? xcloud_game_language : naifeitian.getValue("xcloud_game_languageGM");
    naifeitian.setValue("xcloud_game_languageGM", xcloud_game_language);

    useCustomfakeIp = naifeitian.getValue("useCustomfakeIpGM") == null ? useCustomfakeIp : naifeitian.getValue("useCustomfakeIpGM");
    naifeitian.setValue("useCustomfakeIpGM", useCustomfakeIp);

    customfakeIp = naifeitian.getValue("customfakeIpGM") == null ? customfakeIp : naifeitian.getValue("customfakeIpGM");
    naifeitian.setValue("customfakeIpGM", customfakeIp);

    autoFullScreen = naifeitian.getValue("autoFullScreenGM") == null ? autoFullScreen : naifeitian.getValue("autoFullScreenGM");
    naifeitian.setValue("autoFullScreenGM", autoFullScreen);


    disableTouchControls = naifeitian.getValue("disableTouchControlsGM") == null ? disableTouchControls : naifeitian.getValue("disableTouchControlsGM");
    naifeitian.setValue("disableTouchControlsGM", disableTouchControls);


    STATS_SHOW_WHEN_PLAYING = naifeitian.getValue("STATS_SHOW_WHEN_PLAYINGGM") == null ? STATS_SHOW_WHEN_PLAYING : naifeitian.getValue("STATS_SHOW_WHEN_PLAYINGGM");
    naifeitian.setValue("STATS_SHOW_WHEN_PLAYINGGM", STATS_SHOW_WHEN_PLAYING);

    STATS_POSITION = naifeitian.getValue("STATS_POSITIONGM") == null ? STATS_POSITION: naifeitian.getValue("STATS_POSITIONGM");
    naifeitian.setValue("STATS_POSITIONGM", STATS_POSITION);

    STATS_TRANSPARENT = naifeitian.getValue("STATS_TRANSPARENTGM") == null ? STATS_TRANSPARENT : naifeitian.getValue("STATS_TRANSPARENTGM");
    naifeitian.setValue("STATS_TRANSPARENTGM", STATS_TRANSPARENT);

    STATS_OPACITY = naifeitian.getValue("STATS_OPACITYGM") == null ? STATS_OPACITY : naifeitian.getValue("STATS_OPACITYGM");
    naifeitian.setValue("STATS_OPACITYGM", STATS_OPACITY);

    STATS_TEXT_SIZE = naifeitian.getValue("STATS_TEXT_SIZEGM") == null ? STATS_TEXT_SIZE : naifeitian.getValue("STATS_TEXT_SIZEGM");
    naifeitian.setValue("STATS_TEXT_SIZEGM", STATS_TEXT_SIZE);


    STATS_CONDITIONAL_FORMATTING = naifeitian.getValue("STATS_CONDITIONAL_FORMATTINGGM") == null ? STATS_CONDITIONAL_FORMATTING : naifeitian.getValue("STATS_CONDITIONAL_FORMATTINGGM");
    naifeitian.setValue("STATS_CONDITIONAL_FORMATTINGGM", STATS_CONDITIONAL_FORMATTING);


    noPopSetting = naifeitian.getValue("noPopSettingGM") == null ? noPopSetting : naifeitian.getValue("noPopSettingGM");
    naifeitian.setValue("noPopSettingGM", noPopSetting);

    autoShowTouch = naifeitian.getValue("autoShowTouchGM") == null ? autoShowTouch : naifeitian.getValue("autoShowTouchGM");
    naifeitian.setValue("autoShowTouchGM", autoShowTouch);

    VIDEO_CLARITY = naifeitian.getValue("VIDEO_CLARITYGM") == null ? VIDEO_CLARITY : naifeitian.getValue("VIDEO_CLARITYGM");
    naifeitian.setValue("VIDEO_CLARITYGM", VIDEO_CLARITY);

    VIDEO_CONTRAST = naifeitian.getValue("VIDEO_CONTRASTGM") == null ? VIDEO_CONTRAST : naifeitian.getValue("VIDEO_CONTRASTGM");
    naifeitian.setValue("VIDEO_CONTRASTGM", VIDEO_CONTRAST);

    VIDEO_SATURATION = naifeitian.getValue("VIDEO_SATURATIONGM") == null ? VIDEO_SATURATION : naifeitian.getValue("VIDEO_SATURATIONGM");
    naifeitian.setValue("VIDEO_SATURATIONGM", VIDEO_SATURATION);


    VIDEO_BRIGHTNESS = naifeitian.getValue("VIDEO_BRIGHTNESSGM") == null ? VIDEO_BRIGHTNESS : naifeitian.getValue("VIDEO_BRIGHTNESSGM");
    naifeitian.setValue("VIDEO_BRIGHTNESSGM", VIDEO_BRIGHTNESS);


    video_stretch = naifeitian.getValue("video_stretchGM") == null ? video_stretch : naifeitian.getValue("video_stretchGM");
    naifeitian.setValue("video_stretchGM", video_stretch);

    video_stretch_x_y = naifeitian.getValue("video_stretch_x_yGM") == null ? video_stretch_x_y : naifeitian.getValue("video_stretch_x_yGM");
    naifeitian.setValue("video_stretch_x_yGM", video_stretch_x_y);

    class StreamStats {
        static #interval;
        static #updateInterval = 1000;

    static #$container;
    static #$fps;
    static #$rtt;
    static #$dt;
    static #$pl;
    static #$fl;
    static #$br;

    static #$settings;

    static #lastStat;

    static start() {

        StreamStats.#$container.classList.remove('better-xcloud-gone');
        StreamStats.#interval = setInterval(StreamStats.update, StreamStats.#updateInterval);
    }

    static stop() {
        clearInterval(StreamStats.#interval);

        StreamStats.#$container.classList.add('better-xcloud-gone');
        StreamStats.#interval = null;
        StreamStats.#lastStat = null;
    }

    static toggle() {
        StreamStats.#isHidden() ? StreamStats.start() : StreamStats.stop();
    }

    static #isHidden = () => StreamStats.#$container.classList.contains('better-xcloud-gone');

    static update() {
        if (StreamStats.#isHidden() || !STREAM_WEBRTC) {
            StreamStats.stop();
            return;
        }

        const PREF_STATS_CONDITIONAL_FORMATTING = STATS_CONDITIONAL_FORMATTING;
        STREAM_WEBRTC.getStats().then(stats => {
            stats.forEach(stat => {
                let grade = '';
                if (stat.type === 'inbound-rtp' && stat.kind === 'video') {
                    // FPS
                    StreamStats.#$fps.textContent = stat.framesPerSecond || 0;

                    // Packets Lost
                    const packetsLost = stat.packetsLost;
                    const packetsReceived = stat.packetsReceived;
                    const packetsLostPercentage = (packetsLost * 100 / ((packetsLost + packetsReceived) || 1)).toFixed(2);
                    StreamStats.#$pl.textContent = `${packetsLost} (${packetsLostPercentage}%)`;

                    // Frames Dropped
                    const framesDropped = stat.framesDropped;
                    if(framesDropped!==undefined){
                        const framesReceived = stat.framesReceived;
                        const framesDroppedPercentage = (framesDropped * 100 / ((framesDropped + framesReceived) || 1)).toFixed(2);
                        StreamStats.#$fl.textContent = `${framesDropped} (${framesDroppedPercentage}%)`;
                    }

                    if (StreamStats.#lastStat) {
                        const lastStat = StreamStats.#lastStat;
                        // Bitrate
                        const timeDiff = stat.timestamp - lastStat.timestamp;
                        const bitrate = 8 * (stat.bytesReceived - lastStat.bytesReceived) / timeDiff / 1000;
                        StreamStats.#$br.textContent = `${bitrate.toFixed(2)} Mbps`;

                        // Decode time
                        const totalDecodeTimeDiff = stat.totalDecodeTime - lastStat.totalDecodeTime;
                        const framesDecodedDiff = stat.framesDecoded - lastStat.framesDecoded;
                        const currentDecodeTime = totalDecodeTimeDiff / framesDecodedDiff * 1000;
                        StreamStats.#$dt.textContent = `${currentDecodeTime.toFixed(2)}ms`;

                        if (PREF_STATS_CONDITIONAL_FORMATTING['default']) {
                            grade = (currentDecodeTime > 12) ? 'bad' : (currentDecodeTime > 9) ? 'ok' : (currentDecodeTime > 6) ? 'good' : '';
                        }
                        StreamStats.#$dt.setAttribute('data-grade', grade);
                    }

                    StreamStats.#lastStat = stat;
                } else if (stat.type === 'candidate-pair' && stat.state === 'succeeded') {
                    // Round Trip Time
                    const roundTripTime = typeof stat.currentRoundTripTime !== 'undefined' ? stat.currentRoundTripTime * 1000 : '???';
                    StreamStats.#$rtt.textContent = `${roundTripTime}ms`;

                    if (PREF_STATS_CONDITIONAL_FORMATTING['default']) {
                        grade = (roundTripTime > 100) ? 'bad' : (roundTripTime > 75) ? 'ok' : (roundTripTime > 40) ? 'good' : '';
                    }
                    StreamStats.#$rtt.setAttribute('data-grade', grade);
                }
            });
        });
    }

    static #refreshStyles() {
        const PREF_POSITION = STATS_POSITION['default'];
        const PREF_TRANSPARENT = STATS_TRANSPARENT['default'];
        const PREF_OPACITY = STATS_OPACITY['default'];
        const PREF_TEXT_SIZE = STATS_TEXT_SIZE['default'];

        StreamStats.#$container.setAttribute('data-position', PREF_POSITION);
        StreamStats.#$container.setAttribute('data-transparent', PREF_TRANSPARENT);
        StreamStats.#$container.style.opacity = PREF_OPACITY + '%';
        StreamStats.#$container.style.fontSize = PREF_TEXT_SIZE;
    }

    static hideSettingsUi() {
        StreamStats.#$settings.style.display = 'none';
    }

    static #toggleSettingsUi() {
        const display = StreamStats.#$settings.style.display;
        StreamStats.#$settings.style.display = display === 'block' ? 'none' : 'block';
    }

    static render() {
        if (StreamStats.#$container) {
            return;
        }

        const CE = createElement;
        StreamStats.#$container = CE('div', {'class': 'better-xcloud-stats-bar better-xcloud-gone'},
                                     CE('label', {}, '帧率'),
                                     StreamStats.#$fps = CE('span', {}, 0),
                                     CE('label', {}, '延迟'),
                                     StreamStats.#$rtt = CE('span', {}, '0ms'),
                                     CE('label', {}, '解码'),
                                     StreamStats.#$dt = CE('span', {}, '0ms'),
                                     CE('label', {}, '码率'),
                                     StreamStats.#$br = CE('span', {}, '0 Mbps'),
                                     CE('label', {}, '丢包'),
                                     StreamStats.#$pl = CE('span', {}, '0 (0.00%)'),
                                     CE('label', {}, '丢帧'),
                                     StreamStats.#$fl = CE('span', {}, '0 (0.00%)'));

        let clickTimeout;
        StreamStats.#$container.addEventListener('mousedown', e => {
            clearTimeout(clickTimeout);
            if (clickTimeout) {
                // Double-clicked
                clickTimeout = null;
                StreamStats.#toggleSettingsUi();
                return;
            }

            clickTimeout = setTimeout(() => {
                clickTimeout = null;
            }, 400);
        });

        document.documentElement.appendChild(StreamStats.#$container);

        const refreshFunc = e => {
            StreamStats.#refreshStyles()
        };
        const $position = naifeitian.toElement(STATS_POSITION, refreshFunc);

        let $close;
        const $showStartup = naifeitian.toElement(STATS_SHOW_WHEN_PLAYING, refreshFunc);
        const $transparent = naifeitian.toElement(STATS_TRANSPARENT, refreshFunc);
        const $formatting = naifeitian.toElement(STATS_CONDITIONAL_FORMATTING, refreshFunc);
        const $opacity = naifeitian.toElement(STATS_OPACITY, refreshFunc);
        const $textSize = naifeitian.toElement(STATS_TEXT_SIZE, refreshFunc);

        StreamStats.#$settings = CE('div', {'class': 'better-xcloud-stats-settings'},
                                    CE('b', {}, '状态条设置'),
                                    CE('div', {},
                                       CE('label', {'for': `xcloud_setting_STATS_SHOW_WHEN_PLAYING`}, '游戏启动时显示状态条'),
                                       $showStartup
                                      ),
                                    CE('div', {},
                                       CE('label', {}, '位置'),
                                       $position
                                      ),
                                    CE('div', {},
                                       CE('label', {}, '字体大小'),
                                       $textSize
                                      ),
                                    CE('div', {},
                                       CE('label', {'for': `xcloud_setting_STATS_OPACITY`}, '透明度 (10-100%)'),
                                       $opacity
                                      ),
                                    CE('div', {},
                                       CE('label', {'for': `xcloud_setting_STATS_TRANSPARENT`}, '背景透明'),
                                       $transparent
                                      ),
                                    CE('div', {},
                                       CE('label', {'for': `xcloud_setting_STATS_CONDITIONAL_FORMATTING`}, '数值颜色'),
                                       $formatting
                                      ),
                                    $close = CE('button', {}, '关闭'));

        $close.addEventListener('click', e => StreamStats.hideSettingsUi());
        document.documentElement.appendChild(StreamStats.#$settings);

        StreamStats.#refreshStyles();
    }
}
 function numberPicker(key, suffix='', disabled=false) {
    const setting = key.name;
    let value = key.default;
    let $text, $decBtn, $incBtn;

    const MIN = key.min;
    const MAX= key.max;

    const CE = createElement;
    const $wrapper = CE('div', {},
                        $decBtn = CE('button', {'data-type': 'dec'}, '-'),
                        $text = CE('span', {}, value + suffix),
                        $incBtn = CE('button', {'data-type': 'inc'}, '+'),
                       );

    if (disabled) {
        $incBtn.disabled = true;
        $incBtn.classList.add('better-xcloud-hidden');

        $decBtn.disabled = true;
        $decBtn.classList.add('better-xcloud-hidden');
        return $wrapper;
    }

    let interval;
    let isHolding = false;

    const onClick = e => {
        if (isHolding) {
            e.preventDefault();
            isHolding = false;

            return;
        }

        const btnType = e.target.getAttribute('data-type');
        if (btnType === 'dec') {
            value = (value <= MIN) ? MIN : value - 1;
        } else {
            value = (value >= MAX) ? MAX : value + 1;
        }

        $text.textContent = value + suffix;

        key['default']=value;

        naifeitian.setValue(key['name'], key);

        updateVideoPlayerCss();

        isHolding = false;
    }

    const onMouseDown = e => {
        isHolding = true;

        const args = arguments;
        interval = setInterval(() => {
            const event = new Event('click');
            event.arguments = args;

            e.target.dispatchEvent(event);
        }, 200);
    };

    const onMouseUp = e => {
        clearInterval(interval);
        isHolding = false;
    };

    $decBtn.addEventListener('click', onClick);
    $decBtn.addEventListener('mousedown', onMouseDown);
    $decBtn.addEventListener('mouseup', onMouseUp);
    $decBtn.addEventListener('touchstart', onMouseDown);
    $decBtn.addEventListener('touchend', onMouseUp);

    $incBtn.addEventListener('click', onClick);
    $incBtn.addEventListener('mousedown', onMouseDown);
    $incBtn.addEventListener('mouseup', onMouseUp);
    $incBtn.addEventListener('touchstart', onMouseDown);
    $incBtn.addEventListener('touchend', onMouseUp);

    return $wrapper;
}

function setupVideoSettingsBar() {
    const CE = createElement;
    let $stretchInp;
    const refreshFunc = e => {
        updateVideoPlayerCss();
    };
    const $stretch = naifeitian.toElement(video_stretch, refreshFunc);
    const $wrapper = CE('div', {'class': 'better-xcloud-quick-settings-bar'},
                        CE('div', {},
                           CE('label', {'for': 'better-xcloud-quick-setting-stretch'}, '去黑边'),
                           $stretch),
                        CE('div', {},
                           CE('label', {}, '清晰'),
                           numberPicker(VIDEO_CLARITY, '',naifeitian.isSafariForiPhone())),
                        CE('div', {},
                           CE('label', {}, '饱和'),
                           numberPicker(VIDEO_SATURATION, '%')),
                        CE('div', {},
                           CE('label', {}, '对比'),
                           numberPicker(VIDEO_CONTRAST, '%')),
                        CE('div', {},
                           CE('label', {}, '亮度'),
                           numberPicker(VIDEO_BRIGHTNESS, '%'))
                       );


    $stretch.addEventListener('change', e => {
        if(e.target.value=='setting'){
            $('#video_stretch_x_y').css('display','block');
        }else{
            $('#video_stretch_x_y').css('display','none');
        }
        video_stretch.default=e.target.value;
        naifeitian.setValue('video_stretchGM',video_stretch);
        updateVideoPlayerCss();
    });

    document.documentElement.appendChild($wrapper);
    if($stretch.id=='xcloud_setting_video_stretchGM'){
        var dom= $('#xcloud_setting_video_stretchGM');
        dom.after(`<div id="video_stretch_x_y" style="display: ${video_stretch.default=='setting'?'block':'none'}">
                       <lable>左右
                         <input type=\'text\'class="video_stretch_x_y_Listener" id="video_stretch_x" style="width:35px" value="${video_stretch_x_y['x']}"/>
                       </lable><br/>
                       <lable>上下
                         <input type=\'text\'class="video_stretch_x_y_Listener" id="video_stretch_y" style="width:35px" value="${video_stretch_x_y['y']}"/>
                       </lable>
                    </div>`);

        $(document).on('blur','.video_stretch_x_y_Listener', function () {
            var newval=$(this).val();
            if (naifeitian.isNumber($(this).val())) {
                if($(this).attr('id')=='video_stretch_x'){
                    video_stretch_x_y['x']=newval;
                    naifeitian.setValue('video_stretch_x_yGM',video_stretch_x_y);
                }else{
                    video_stretch_x_y['y']=newval;
                    naifeitian.setValue('video_stretch_x_yGM',video_stretch_x_y);
                }
            }else{
                $(this).val("0");
                video_stretch_x_y['x']=0;
                video_stretch_x_y['y']=0;
                naifeitian.setValue('video_stretch_x_yGM',video_stretch_x_y);
            }
            updateVideoPlayerCss();
        });
    }
}

function cloneStreamMenuButton($orgButton, label, svg_icon) {
    const $button = $orgButton.cloneNode(true);
    $button.setAttribute('aria-label', label);
    $button.querySelector('div[class*=label]').textContent = label;

    const $svg = $button.querySelector('svg');
    $svg.innerHTML = svg_icon;
    $svg.setAttribute('viewBox', '0 0 24 24');

    return $button;
}

function HookProperty(object, property, value) {
    Object.defineProperty(object, property, {
        value: value
    });
}
let userAgentOriginal=windowCtx.navigator.userAgent;
let fakeuad = {
    "brands": [
        {
            "brand": "Microsoft Edge",
            "version": "999"
        },
        {
            "brand": "Chromium",
            "version": "999"
        },
        {
            "brand": "Not=A?Brand",
            "version": "24"
        }
    ],
    "mobile": false,
    "platform": "Windows"
};

try{
    HookProperty(windowCtx.navigator, "userAgent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/999.0.0.0 Safari/537.36 Edg/999.0.0.0");
    HookProperty(windowCtx.navigator, "appVersion", "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/999.0.0.0 Safari/537.36 Edg/999.0.0.0");
    HookProperty(windowCtx.navigator, "platform", "Win32");
    HookProperty(windowCtx.navigator, "appName", "Netscape");
    HookProperty(windowCtx.navigator, "appCodeName", "Mozilla");
    HookProperty(windowCtx.navigator, "product", "Gecko");
    HookProperty(windowCtx.navigator, "vendor", "Google Inc.");
    HookProperty(windowCtx.navigator, "vendorSub", "");
    HookProperty(windowCtx.navigator, "maxTouchPoints", 10);
    HookProperty(windowCtx.navigator, "userAgentData", fakeuad);
    if (disableCheckNetwork == 1) {
        Object.defineProperty(windowCtx.navigator, 'connection', {
            get: () => undefined,
        });
    }
    HookProperty(windowCtx.navigator, "standalone", true);

}catch(e){}


windowCtx.fetch = (...arg) => {
    let arg0 = arg[0];
    let url = "";
    let isRequest = false;
    switch (typeof arg0) {
        case "object":
            url = arg0.url;
            isRequest = true;
            break;
        case "string":
            url = arg0;
            break;
        default:
            break;
    }

    if (url.indexOf('/v2/login/user') > -1) {//xgpuweb.gssv-play-prod.xboxlive.com
        return new Promise((resolve, reject) => {
            if (isRequest && arg0.method == "POST") {
                arg0.json().then(json => {
                    let body = JSON.stringify(json);
                    if (no_need_VPN_play == 1) {
                        console.log('xff欺骗开始' + url);
                        if (useCustomfakeIp == 1 && naifeitian.isValidIP(customfakeIp)) {
                            arg[0].headers.set('x-forwarded-for', customfakeIp);
                            console.log('自定义IP:' + customfakeIp);
                        } else {
                            arg[0].headers.set('x-forwarded-for', fakeIp);
                        }
                    }

                    arg[0] = new Request(url, {
                        method: arg0.method,
                        headers: arg0.headers,
                        body: body,

                    });
                    originFetch(...arg).then(res => {
                        console.log('xff欺骗结束');
                        res.json().then(json => {
                            let newServerList = [];
                            let currentAutoServer;
                            json["offeringSettings"]["regions"].forEach((region) => {
                                newServerList.push(region["name"]);
                                if (region["isDefault"] === true) {
                                    currentAutoServer = region["name"];
                                }
                            });
                            naifeitian.setValue("blockXcloudServerListGM", newServerList);
                            blockXcloudServerList = newServerList;
                            if (blockXcloudServerList.indexOf(defaultXcloudServer) == -1) {
                                naifeitian.setValue("defaultXcloudServerGM", "");
                                defaultXcloudServer = "";
                                blockXcloudServer = 0;
                                naifeitian.setValue("blockXcloudServerGM", 0);
                            }
                            if (blockXcloudServer == 1) {
                                console.log('修改服务器开始');
                                json["offeringSettings"]["allowRegionSelection"] = true;
                                let selectedServer = defaultXcloudServer;
                                if (selectedServer !== "Auto" && newServerList.includes(selectedServer)) {
                                    json["offeringSettings"]["regions"].forEach((region) => {
                                        if (region["name"] === selectedServer) {
                                            region["isDefault"] = true;
                                        } else {
                                            region["isDefault"] = false;
                                        }
                                    });
                                }
                                console.log('修改服务器结束');
                            }
                            let body = JSON.stringify(json);
                            let newRes = new Response(body, {
                                status: res.status,
                                statusText: res.statusText,
                                headers: res.headers
                            })
                            resolve(newRes);
                        }).catch(err => {
                            reject(err);
                        });
                    }).catch(err => {
                        reject(err);
                    });
                });

            } else {
                console.error("[ERROR] Not a request.");
                return originFetch(...arg);
            }
        });
    } else if (url.indexOf('/cloud/play') > -1) {
        document.documentElement.style.overflowY = "hidden";
        if (autoFullScreen == 1) {
            setMachineFullScreen();
        }
        if(noPopSetting==0){
            $('#popSetting').css('display','none');
        }

        if (chooseLanguage == 1) {
            return new Promise(async (resolve, reject) => {
                console.log('语言开始');
                let selectedLanguage = xcloud_game_language;
                console.log('语言选择:' + selectedLanguage);
                if (selectedLanguage == 'Auto') {
                    const regex = /\/([a-zA-Z0-9]+)\/?/gm;
                    let matches;
                    let latestMatch;
                    while ((matches = regex.exec(document.location.pathname)) !== null) {
                        if (matches.index === regex.lastIndex) {
                            regex.lastIndex++;
                        }
                        matches.forEach((match, groupIndex) => {
                            // console.log(`Found match, group ${groupIndex}: ${match}`);
                            latestMatch = match;
                        });
                    }
                    if (latestMatch) {
                        let pid = latestMatch;
                        try {
                            let res = await fetch(
                                "https://catalog.gamepass.com/products?market=US&language=en-US&hydration=PCInline", {
                                    "headers": {
                                        "content-type": "application/json;charset=UTF-8",
                                    },
                                    "body": "{\"Products\":[\"" + pid + "\"]}",
                                    "method": "POST",
                                    "mode": "cors",
                                    "credentials": "omit"
                                });
                            let jsonObj = await res.json();
                            let languageSupport = jsonObj["Products"][pid]["LanguageSupport"]
                            for (let language of Object.keys(default_language_list)) {
                                if (default_language_list[language] in languageSupport) {
                                    selectedLanguage = default_language_list[language];
                                    break;
                                }
                            }
                            if (selectedLanguage == 'Auto') {
                                //防止接口没有返回支持语言
                                selectedLanguage = IfErrUsedefaultGameLanguage;
                            }

                        } catch (e) {
                        }
                    }
                }

                if (isRequest && arg0.method == "POST") {
                    arg0.json().then(json => {

                        json["settings"]["locale"] = selectedLanguage;

                        json["settings"]["osName"]= high_bitrate==1?'windows':'android';
                        let body = JSON.stringify(json);

                        arg[0] = new Request(url, {
                            method: arg0.method,
                            headers: arg0.headers,
                            body: body,
                            mode: arg0.mode,
                            credentials: arg0.credentials,
                            cache: arg0.cache,
                            redirect: arg0.redirect,
                            referrer: arg0.referrer,
                            integrity: arg0.integrity
                        });
                        originFetch(...arg).then(res => {
                            console.log(`语言结束, 选择语言: ${selectedLanguage}.`)
                            resolve(res);

                        }).catch(err => {
                            reject(err);
                        });
                    });
                } else {
                    console.error("[ERROR] Not a request.");
                    return originFetch(...arg);
                }
            });
        } else {
            return originFetch(...arg);
        }
    } else if (url.indexOf('/configuration') > -1) {
        // Enable CustomTouchOverlay
        console.log('修改触摸开始')
        return new Promise((resolve, reject) => {
            originFetch(...arg).then(res => {
                res.json().then(json => {
                    // console.error(json);
                    if(autoOpenOC == 1 && disableTouchControls==0){
                        let inputOverrides = JSON.parse(json.clientStreamingConfigOverrides || '{}') || {};
                        inputOverrides.inputConfiguration = {
                            enableTouchInput: true,
                            maxTouchPoints: 10,
                        };
                        json.clientStreamingConfigOverrides = JSON.stringify(inputOverrides);
                        let cdom=$('#BabylonCanvasContainer-main').children();
                        if(cdom.length>0){
                            canShowOC=false;
                        }else{
                            canShowOC=true;
                        }
                    }
                    let body = JSON.stringify(json);
                    let newRes = new Response(body, {
                        status: res.status,
                        statusText: res.statusText,
                        headers: res.headers
                    })
                    resolve(newRes);

                    console.log('修改触摸结束')
                }).catch(err => {
                    reject(err);
                });
            }).catch(err => {
                reject(err);
            });
        });
    } else {
        return originFetch(...arg);
    }
}
if(autoOpenOC == 1 && disableTouchControls==0 && autoShowTouch){
    windowCtx.RTCPeerConnection.prototype.originalCreateDataChannelGTC = windowCtx.RTCPeerConnection.prototype.createDataChannel;
    windowCtx.RTCPeerConnection.prototype.createDataChannel = function (...params) {
        let dc = this.originalCreateDataChannelGTC(...params);
        let paddingMsgTimeoutId = 0;
        if (dc.label == "message") {
            dc.addEventListener("message", function (de) {
                if (typeof (de.data) == "string") {
                    // console.debug(de.data);
                    let msgdata = JSON.parse(de.data);
                    if (msgdata.target == "/streaming/touchcontrols/showlayoutv2") {
                        clearTimeout(paddingMsgTimeoutId);
                    } else if (msgdata.target == "/streaming/touchcontrols/showtitledefault") {

                        if(!canShowOC){
                            clearTimeout(paddingMsgTimeoutId);
                        }else{
                            if (msgdata.pluginHookMessage !== true) {
                                clearTimeout(paddingMsgTimeoutId);
                                paddingMsgTimeoutId = setTimeout(() => {
                                    dc.dispatchEvent(new MessageEvent('message', {
                                        data: '{"content":"{\\"layoutId\\":\\"\\"}","target":"/streaming/touchcontrols/showlayoutv2","type":"Message","pluginHookMessage":true}'
                                    }));
                                }, 1000);
                            }
                        }
                    }
                }
            });
        }
        return dc;
    }
}
function exitGame() {
    StreamStats.stop();
    const $quickBar = document.querySelector('.better-xcloud-quick-settings-bar');
    if ($quickBar) {
        $quickBar.style.display = 'none';
    }
    document.documentElement.style.overflowY = "";
    if (autoFullScreen == 1) {
        exitMachineFullscreen();
    }
    if(noPopSetting==0){
        $('#popSetting').css('display', 'block');
    }
    setCloudGameingclick(1000);
}

let needrefresh = 0;
function initSettingBox() {


    let dom = '';
    dom += `<label  style="display: block;text-align:left;"><div   style="display: inline;">选择语言:</div>`;
    dom += `<input type="radio" class="chooseLanguageListener settingsBoxInputRadio" style="outline:none;" name='chooseLanguage' id="chooseLanguageOn" value="1" ${chooseLanguage == 1 ? 'checked' : ''}><label for="chooseLanguageOn" style="padding-right: 15px;">开</label>`;
    dom += `<input type="radio" class="chooseLanguageListener settingsBoxInputRadio" style="outline:none;" name='chooseLanguage' id="chooseLanguageOff" value="0" ${chooseLanguage == 0 ? 'checked' : ''}><label for="chooseLanguageOff" style="padding-right: 25px;">关</label>`;

    dom += `<label class=" chooseLanguageBlock" style="text-align:left;display:` + (chooseLanguage == 1 ? 'block' : 'none') + `"><div   style="display: inline;">语言:</div>`;

    Object.keys(default_language_list).forEach(languageChinese => {
        dom += `<input type="radio" class="languageSingleListener settingsBoxInputRadio" style="outline:none;" name='selectLanguage' id="${default_language_list[languageChinese]}" value="${default_language_list[languageChinese]}" ${xcloud_game_language == default_language_list[languageChinese] ? 'checked' : ''}><label for="${default_language_list[languageChinese]}" style="padding-right: 15px;">${languageChinese}</label>`;
    });
    dom += `</label>`;

    dom += `</label>`;

    dom += `<label class=" IfErrUsedefaultGameLanguageBlock" style="display:` + (xcloud_game_language == 'Auto' ? 'block' : 'none') + `;text-align:left;"><div   style="display: inline;">智能错误时使用:</div>`;
    dom += `<input type="radio" style="outline:none;" name='IfErrUsedefaultGameLanguage' class="IfErrUsedefaultGameLanguageListener settingsBoxInputRadio" id="IfErrUsedefaultGameLanguageCN" value="zh-CN" ${IfErrUsedefaultGameLanguage == 'zh-CN' ? 'checked' : ''}><label for="IfErrUsedefaultGameLanguageCN" style="padding-right: 15px;">简体</label>`;
    dom += `<input type="radio" style="outline:none;" name='IfErrUsedefaultGameLanguage' class="IfErrUsedefaultGameLanguageListener settingsBoxInputRadio" id="IfErrUsedefaultGameLanguageTW" value="zh-TW" ${IfErrUsedefaultGameLanguage == 'zh-TW' ? 'checked' : ''}><label for="IfErrUsedefaultGameLanguageTW" style="padding-right: 15px;">繁体</label>`;
    dom += `</label><hr style="background-color: black;width:95%" />`;

    dom += `<label class="" style="display: block;text-align:left;"><div   style="display: inline;">免代理直连:</div>`;
    dom += `<input type="radio" class='noNeedVpnListener settingsBoxInputRadio' style="outline:none;" name='noNeedVpn' id="noNeedVpnOpen" value="1" ${no_need_VPN_play == 1 ? 'checked' : ''}><label for="noNeedVpnOpen" style="padding-right: 15px;">开</label>`;
    dom += `<input type="radio" class='noNeedVpnListener settingsBoxInputRadio' style="outline:none;" name='noNeedVpn' id="noNeedVpnOff" value="0" ${no_need_VPN_play == 0 ? 'checked' : ''}><label for="noNeedVpnOff" style="padding-right: 15px;">关</label>`;
    dom += `</label>`;

    dom += `<label class=" chooseRegionsBlock" style="text-align:left;display:` + (no_need_VPN_play == 1 ? 'block' : 'none') + `"><div   style="display: inline;">选服:</div>`;

    Object.keys(regionsList).forEach(region => {
        dom += `<input type="radio" class="regionSingleListener settingsBoxInputRadio" style="outline:none;" name='selectRegion' id="${region}" value="${regionsList[region]}" ${fakeIp == regionsList[region] ? 'checked' : ''}><label for="${region}" style="padding-right: 15px;">${region}</label>`;
    });
    dom += `<div style="display:block">`
        dom += `<input type="radio" class="regionSingleListener settingsBoxInputRadio" style="outline:none;" name='selectRegion' id="customfakeIp" value="customfakeIp" ${useCustomfakeIp == 1 ? 'checked' : ''}><label for="customfakeIp" style="padding-right: 15px;">自定义IP:</label>`;

    dom += `<input type='text' style="display: ` + (useCustomfakeIp == 1 ? 'inline' : 'none') + `;outline: none;width: 125px;" id="customfakeIpInput" class="customfakeIpListener" value="${customfakeIp}" placeholder="请输入IP"/>`
        dom += `</div>`
        dom += `</label><hr style="background-color: black;width:95%" />`;

    dom += `<label class="" style="display: block;text-align:left;"><div   style="display: inline;">分辨率:</div>`;
    dom += `<input type="radio" class="high_bitrateListener settingsBoxInputRadio" style="outline:none;" name='highBitrate' id="high_bitrateOn" value="1" ${high_bitrate == 1 ? 'checked' : ''}><label for="high_bitrateOn" style="padding-right: 15px;">1080P</label>`;
    dom += `<input type="radio" class="high_bitrateListener settingsBoxInputRadio" style="outline:none;" name='highBitrate' id="high_bitrateOff" value="0" ${high_bitrate == 0 ? 'checked' : ''}><label for="high_bitrateOff" style="padding-right: 25px;">720P</label>`;
    dom += `</label><hr style="background-color: black;width:95%" />`;

    dom += `<label class="" style="display: block;text-align:left;"><div   style="display: inline;">禁止检测网络状况:</div>`;
    dom += `<input type="radio" class="disableCheckNetworkListener settingsBoxInputRadio" style="outline:none;" name='disableCheckNetwork' id="disableCheckNetworkOn" value="1" ${disableCheckNetwork == 1 ? 'checked' : ''}><label for="disableCheckNetworkOn" style="padding-right: 15px;">开</label>`;
    dom += `<input type="radio" class="disableCheckNetworkListener settingsBoxInputRadio" style="outline:none;" name='disableCheckNetwork' id="disableCheckNetworkOff" value="0" ${disableCheckNetwork == 0 ? 'checked' : ''}><label for="disableCheckNetworkOff" style="padding-right: 25px;">关</label>`;
    dom += `</label><hr style="background-color: black;width:95%" />`;

    dom += `<label class="" style="display: block;text-align:left;"><div   style="display: inline;">强制触控:</div>`;
    dom += `<input type="radio" class="autoOpenOCListener settingsBoxInputRadio" style="outline:none;" name='autoOpenOC' id="autoOpenOCOn" value="1" ${autoOpenOC == 1 ? 'checked' : ''}><label for="autoOpenOCOn" style="padding-right: 15px;">开</label>`;
    dom += `<input type="radio" class="autoOpenOCListener settingsBoxInputRadio" style="outline:none;" name='autoOpenOC' id="autoOpenOCOff" value="0" ${autoOpenOC == 0 ? 'checked' : ''}><label for="autoOpenOCOff" style="padding-right: 25px;">关</label>`;
    dom += `<div id="autoShowTouchBox" style="padding-right: 0px;display: ${autoOpenOC == 1 ? 'inline' : 'none'}"><input type="checkbox" class="autoShowTouchListener settingsBoxInputRadio" style="outline:none;" name='autoShowTouch' id="autoShowTouch" ${autoShowTouch == true ? 'checked' : ''}><label for="autoShowTouch">自动弹出</label></div>`;
    dom += `</label><hr style="background-color: black;width:95%" />`;

    dom += `<label class="" style="display: block;text-align:left;"><div   style="display: inline;">屏蔽触控:</div>`;
    dom += `<input type="radio" class="disableTouchControlsListener settingsBoxInputRadio" style="outline:none;" name='disableTouchControls' id="disableTouchControlsOn" value="1" ${disableTouchControls == 1 ? 'checked' : ''}><label for="disableTouchControlsOn" style="padding-right: 15px;">开</label>`;
    dom += `<input type="radio" class="disableTouchControlsListener settingsBoxInputRadio" style="outline:none;" name='disableTouchControls' id="disableTouchControlsOff" value="0" ${disableTouchControls == 0 ? 'checked' : ''}><label for="disableTouchControlsOff" style="padding-right: 25px;">关</label>`;
    dom += `</label><hr style="background-color: black;width:95%" />`;

    dom += `<label class="" style="display: block;text-align:left;"><div   style="display: inline;">自动全屏:</div>`;
    dom += `<input type="radio" class="autoFullScreenListener settingsBoxInputRadio" style="outline:none;" name='autoFullScreen' id="autoFullScreenOn" value="1" ${autoFullScreen == 1 ? 'checked' : ''}><label for="autoFullScreenOn" style="padding-right: 15px;">开</label>`;
    dom += `<input type="radio" class="autoFullScreenListener settingsBoxInputRadio" style="outline:none;" name='autoFullScreen' id="autoFullScreenOff" value="0" ${autoFullScreen == 0 ? 'checked' : ''}><label for="autoFullScreenOff" style="padding-right: 25px;">关</label>`;
    dom += `</label><hr style="background-color: black;width:95%" />`;

    dom += `<label class="" style="display: block;text-align:left;"><div   style="display: inline;">选区(降低延迟):</div>`;
    dom += `<input type="radio" class="blockXcloudServerListener settingsBoxInputRadio" style="outline:none;" name='blockXcloudServer' id="blockXcloudServerOn" value="1" ${blockXcloudServer == 1 ? 'checked' : ''}><label for="blockXcloudServerOn" style="padding-right: 15px;">开</label>`;
    dom += `<input type="radio" class="blockXcloudServerListener settingsBoxInputRadio" style="outline:none;" name='blockXcloudServer' id="blockXcloudServerOff" value="0" ${blockXcloudServer == 0 ? 'checked' : ''}><label for="blockXcloudServerOff" style="padding-right: 25px;">关</label>`;

    dom += `<select class="blockServerBlock" style="outline: none;display:` + (blockXcloudServer == 1 ? 'block' : 'none') + `">`;
    dom += `<option style="display:none"></option>`
        blockXcloudServerList.forEach(serverName => {
            dom += `<option value="${serverName}" ${defaultXcloudServer == serverName ? 'selected' : ''}>${serverName}</option>`;
        });
    dom += `</select>`;
    dom += `</label><hr style="background-color: black;width:95%" />`;

    dom += `<label class="" style="display: block;text-align:left;"><div   style="display: inline;">设置悬浮窗:</div>`;
    dom += `<input type="radio" class="noPopSettingListener settingsBoxInputRadio" style="outline:none;" name='noPopSetting' id="noPopSettingOn" value="0" ${noPopSetting == 0 ? 'checked' : ''}><label for="noPopSettingOn" style="padding-right: 15px;">显示</label>`;
    dom += `<input type="radio" class="noPopSettingListener settingsBoxInputRadio" style="outline:none;" name='noPopSetting' id="noPopSettingOff" value="1" ${noPopSetting == 1 ? 'checked' : ''}><label for="noPopSettingOff" style="padding-right: 25px;">隐藏</label>`;
    dom += `</label><hr style="background-color: black;width:95%" />`;

    dom += `<button class="closeSetting1 closeSetting2" style="outline: none;">关闭</button>`

        dom += `<div style="text-align: right;margin-top: 8px;font-size: 16px;"><lable>捐赠:</lable><a style="margin-right:15px;outline: none;color: #107c10;text-decoration: underline;" href="https://greasyfork.org/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBMVNFQVE9PSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--14c278e3f72d104cff50bf130d4039229fc25a6b/wx.png?locale=zh-CN">微信</a><a style="outline: none;color: #107c10;text-decoration: underline;" href="https://greasyfork.org/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBMU9FQVE9PSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--5fc08aaa8407cc6099654d65455b7966bf2c60ee/alipay.png?locale=zh-CN">支付宝</a></div>`
        dom = '<div style="padding: 20px;color: black;display:none;" class="settingsBackgroud" id=\'settingsBackgroud\'>' + `<div class="settingsBox">` + dom + `</div>` + '</div>';

    $('body').append(dom);

    $(document).on('change', '.autoShowTouchListener', function () {
        let newVal=$(this).attr('checked')=='checked';
        if(newVal){
            $(this).removeAttr('checked');
        }else{
            $(this).attr('checked');
        }
        naifeitian.setValue('autoShowTouchGM', !newVal);
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.disableTouchControlsListener', function () {
        naifeitian.setValue('disableTouchControlsGM', $(this).val());

    });
    $(document).on('click', '.noPopSettingListener', function () {
        naifeitian.setValue('noPopSettingGM', $(this).val());
        noPopSetting=$(this).val();
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.closeSetting1', function () {

        $('#settingsBackgroud').css('display', 'none');
        if (needrefresh == 1) {
            history.go(0);
        }
    });

    $(document).on('click', '.chooseLanguageListener', function () {
        if ($(this).val() == 0) {
            $('.chooseLanguageBlock').css('display', 'none');
            $('.IfErrUsedefaultGameLanguageBlock').css('display', 'none');
        } else {
            $('.chooseLanguageBlock').css('display', 'block');

            if (naifeitian.getValue('xcloud_game_languageGM') == 'Auto') {
                $('.IfErrUsedefaultGameLanguageBlock').css('display', 'block');
            }
        }
        naifeitian.setValue('chooseLanguageGM', $(this).val());
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.languageSingleListener', function () {
        if ($(this).val() != 'Auto') {
            $('.IfErrUsedefaultGameLanguageBlock').css('display', 'none');
        } else {
            $('.IfErrUsedefaultGameLanguageBlock').css('display', 'block');
        }
        naifeitian.setValue('xcloud_game_languageGM', $(this).val());
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.IfErrUsedefaultGameLanguageListener', function () {
        naifeitian.setValue('IfErrUsedefaultGameLanguageGM', $(this).val());
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.noNeedVpnListener', function () {
        if ($(this).val() == 0) {
            $('.chooseRegionsBlock').css('display', 'none');;
        } else {
            $('.chooseRegionsBlock').css('display', 'block');
        }
        naifeitian.setValue('no_need_VPN_playGM', $(this).val());
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.regionSingleListener', function () {
        if ($(this).val() == 'customfakeIp') {
            naifeitian.setValue('useCustomfakeIpGM', 1);
            $('#customfakeIpInput').css('display', 'inline');
        } else {
            naifeitian.setValue('fakeIpGM', $(this).val());
            naifeitian.setValue('useCustomfakeIpGM', 0);
            $('#customfakeIpInput').css('display', 'none');
        }
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('blur', '.customfakeIpListener', function () {
        if (naifeitian.isValidIP($(this).val())) {
            naifeitian.setValue('customfakeIpGM', $(this).val());
        } else {
            $(this).val("");
            naifeitian.setValue('customfakeIpGM', '');
            alert('IP格式错误!');
        }
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.high_bitrateListener', function () {
        naifeitian.setValue('high_bitrateGM', $(this).val());
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.autoFullScreenListener', function () {
        naifeitian.setValue('autoFullScreenGM', $(this).val());
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.disableCheckNetworkListener', function () {
        naifeitian.setValue('disableCheckNetworkGM', $(this).val());
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.autoOpenOCListener', function () {

        if($(this).val()==0){
            $('#autoShowTouchBox').css('display','none');
        }else{
            $('#autoShowTouchBox').css('display','inline');
        }

        naifeitian.setValue('autoOpenOCGM', $(this).val());
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('click', '.blockXcloudServerListener', function () {
        if ($(this).val() == 0) {
            $('.blockServerBlock').css('display', 'none');
        } else {
            $('.blockServerBlock').css('display', 'block');
        }
        naifeitian.setValue('blockXcloudServerGM', $(this).val());
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });

    $(document).on('change', '.blockServerBlock', function () {
        naifeitian.setValue('defaultXcloudServerGM', $(this).val());
        needrefresh = 1;
        $('.closeSetting1').text('确定');
    });
}

$(document).ready(function () {
    setTimeout(function () {
        var popCss = `

#popSetting {
	width: 76px;
	height: 33px;
	background: #fff;
	position: absolute;
    top: 30%;
    cursor: pointer;
	box-sizing: border-box;
	background-size: 100% 100%;
	overflow: hidden;
    font-family: Arial;
  font-size: 18px;
  line-height: 30px;
  font-weight: bold;
  color: #000000bf;
  border: 2px solid;
  border-radius: 10px;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none ;
}
.better-xcloud-hidden {
    visibility: hidden !important;
}

.better-xcloud-stats-bar {
    display: block;
    user-select: none;
    position: fixed;
    top: 0;
    background-color: #000;
    color: #fff;
    font-family: Consolas, "Courier New", Courier, monospace;
    font-size: 0.9rem;
    padding-left: 8px;
    z-index: 1000;
    text-wrap: nowrap;
}

.better-xcloud-stats-bar[data-position=top-left] {
    left: 20px;
}

.better-xcloud-stats-bar[data-position=top-right] {
    right: 0;
}

.better-xcloud-stats-bar[data-position=top-center] {
    transform: translate(-50%, 0);
    left: 50%;
}

.better-xcloud-stats-bar[data-transparent=true] {
    background: none;
    filter: drop-shadow(1px 0 0 #000) drop-shadow(-1px 0 0 #000) drop-shadow(0 1px 0 #000) drop-shadow(0 -1px 0 #000);
}

.better-xcloud-stats-bar label {
    margin: 0 8px 0 0;
    font-family: Bahnschrift, Arial, Helvetica, sans-serif;
    font-size: inherit;
    font-weight: bold;
    vertical-align: middle;
}

.better-xcloud-stats-bar span {
    min-width: 60px;
    display: inline-block;
    text-align: right;
    padding-right: 8px;
    margin-right: 8px;
    border-right: 2px solid #fff;
    vertical-align: middle;
}

.better-xcloud-stats-bar span[data-grade=good] {
    color: #6bffff;
}

.better-xcloud-stats-bar span[data-grade=ok] {
    color: #fff16b;
}

.better-xcloud-stats-bar span[data-grade=bad] {
    color: #ff5f5f;
}

.better-xcloud-stats-bar span:first-of-type {
    min-width: 30px;
}

.better-xcloud-stats-bar span:last-of-type {
    border: 0;
    margin-right: 0;
}

.better-xcloud-stats-settings {
    display: none;
    position: fixed;
    top: 50%;
    left: 50%;
    margin-right: -50%;
    transform: translate(-50%, -50%);
    width: 420px;
    padding: 20px;
    border-radius: 8px;
    z-index: 1000;
    background: #1a1b1e;
    color: #fff;
    font-weight: 400;
    font-size: 16px;
    font-family: "Segoe UI", Arial, Helvetica, sans-serif;
    box-shadow: 0 0 6px #000;
    user-select: none;
}

.better-xcloud-stats-settings *:focus {
    outline: none !important;
}

.better-xcloud-stats-settings > b {
    color: #fff;
    display: block;
    font-family: Bahnschrift, Arial, Helvetica, sans-serif;
    font-size: 26px;
    font-weight: 400;
    line-height: 32px;
    margin-bottom: 12px;
}

.better-xcloud-stats-settings > div {
    display: flex;
    margin-bottom: 8px;
    padding: 2px 4px;
}

.better-xcloud-stats-settings label {
    flex: 1;
    margin-bottom: 0;
    align-self: center;
}

.better-xcloud-stats-settings button {
    padding: 8px 32px;
    margin: 20px auto 0;
    border: none;
    border-radius: 4px;
    display: block;
    background-color: #2d3036;
    text-align: center;
    color: white;
    text-transform: uppercase;
    font-family: Bahnschrift, Arial, Helvetica, sans-serif;
    font-weight: 400;
    line-height: 18px;
    font-size: 14px;
}

@media (hover: hover) {
    .better-xcloud-stats-settings button:hover {
        background-color: #515863;
    }
}

.better-xcloud-stats-settings button:focus {
    background-color: #515863;
}

.better-xcloud-gone {
    display: none !important;
}

.better-xcloud-quick-settings-bar {
    display: none;
    user-select: none;
    -webkit-user-select: none;
    position: fixed;
    bottom: 0;
    left: 50%;
    transform: translate(-50%, 0);
    z-index: 9999;
    padding: 16px;
    width: 600px;
    background: #1a1b1e;
    color: #fff;
    border-radius: 8px 8px 0 0;
    font-weight: 400;
    font-size: 14px;
    font-family: Bahnschrift, Arial, Helvetica, sans-serif;
    text-align: center;
    box-shadow: 0px 0px 6px #000;
    opacity: 0.95;
}

.better-xcloud-quick-settings-bar *:focus {
    outline: none !important;
}

.better-xcloud-quick-settings-bar > div {
    flex: 1;
}

.better-xcloud-quick-settings-bar label {
    font-size: 16px;
    display: block;
    margin-bottom: 8px;
}

.better-xcloud-quick-settings-bar input {
    width: 22px;
    height: 22px;
}

.better-xcloud-quick-settings-bar button {
    border: none;
    width: 22px;
    height: 22px;
    margin: 0 4px;
    line-height: 22px;
    background-color: #515151;
    color: #fff;
    border-radius: 4px;
}

@media (hover: hover) {
    .better-xcloud-quick-settings-bar button:hover {
        background-color: #414141;
        color: white;
    }
}

.better-xcloud-quick-settings-bar button:active {
        background-color: #414141;
        color: white;
    }

.better-xcloud-quick-settings-bar span {
    display: inline-block;
    width: 40px;
    font-weight: bold;
    font-family: Consolas, "Courier New", Courier, monospace;
}
#game-stream video {
    visibility: hidden;
}

.closeSetting1 {
      color: #0099CC;
      background: transparent;
      border: 2px solid #0099CC;
      border-radius: 6px;
      border: none;
      color: white;
      padding: 3px 13px;
      text-align: center;
      display: inline-block;
      font-size: 16px;
      margin: 4px 2px;
      -webkit-transition-duration: 0.4s; /* Safari */
      transition-duration: 0.4s;
      cursor: pointer;
      text-decoration: none;
      text-transform: uppercase;
     }
      .closeSetting2 {
      background-color: white;
      color: black;
      border: 2px solid #008CBA;
      display: block;
      margin: 0 auto;
      margin-top: 5px;
     }
    .closeSetting2:hover {
      background-color: #008CBA;
      color: white;
     }
    .settingsBackgroud{
				position: fixed;
				left: 0px;
				top: 3%;
				background: #0000;
				width: 100%;
				height: 100%;
                overflow: scroll;
			}
			.settingsBox{
				position: relative;
				background: wheat;
				width: fit-content;
                height: fit-content;
				border-radius: 5px;
				margin: 5% auto;
                padding: 20px;
                font-family: '微软雅黑';
                line-height: 22px;
			}
           .settingsBoxInputRadio{
                background-color: initial;
                cursor: default;
                appearance: auto;
                box-sizing: border-box;
                margin: 3px 3px 0px 5px;
                padding: initial;
                padding-top: initial;
                padding-right: initial;
                padding-bottom: initial;
                padding-left: initial;
                border: initial;
                -webkit-appearance: checkbox;
                accent-color: dodgerblue;
            }

            #StreamHud >div{
        background-color:rgba(255,0,0,0)!important;
        }

        #StreamHud >button{
        background-color:rgba(0,0,0,0)!important;
        }
        #StreamHud >button > div{
        opacity:0.3!important;
        }
`;

        if (disableTouchControls==1) {
            popCss += `
#MultiTouchSurface, #BabylonCanvasContainer-main {
    display: none !important;
}
`};

        var xfbasicStyle = document.createElement('style');
        xfbasicStyle.innerHTML = popCss;
        var docxf = document.head || document.documentElement;
        docxf.appendChild(xfbasicStyle);
        if(noPopSetting==0){
            $('body').append(`<div id="popSetting" style="display:block">⚙️ 设置</div>`);
            $(document).on('click', '#popSetting', function () {
                $('#settingsBackgroud').css('display', '');
            });
        }


        initSettingBox();
        updateVideoPlayerCss();
        StreamStats.render();
        setupVideoSettingsBar();
    }, 2000);

});

var timer;
var mousehidding = false;
$(document).mousemove(function () {
    if (mousehidding) {
        mousehidding = false;
        return;
    }
    if (timer) {
        clearTimeout(timer);
        timer = 0;
    }
    $('html').css({
        cursor: ''
    });
    timer = setTimeout(function () {
        mousehidding = true;
        $('html').css({
            cursor: 'none'
        });
    }, 2000);
});

$(window).on('popstate', function () {
    exitGame();
});

var _pushState = window.history.pushState;
window.history.pushState = function() {
    if(noPopSetting==0){
        if(arguments[2].substring(arguments[2].length,arguments[2].length-5)=='/play'){
            $('#popSetting').css('display','block');

        }else{
            $('#popSetting').css('display','none');
        }
    }
    exitGame();
    return _pushState.apply(this, arguments);
}

window.onpopstate = function(event) {
    if (event.state) {
        if(window.location.href.slice(-5)=='/play'){
            exitGame();
        }
    }
};


RTCPeerConnection.prototype.orgAddIceCandidate = RTCPeerConnection.prototype.addIceCandidate;
RTCPeerConnection.prototype.addIceCandidate = function(...args) {

    STREAM_WEBRTC = this;
    return this.orgAddIceCandidate.apply(this, args);
}

function getVideoPlayerFilterStyle() {
    const filters = [];

    const clarity = VIDEO_CLARITY['default'];
    if (clarity != 0) {
        const level = 7 - (clarity - 1); // 5,6,7
        const matrix = `0 -1 0 -1 ${level} -1 0 -1 0`;
        document.getElementById('better-xcloud-filter-clarity-matrix').setAttributeNS(null, 'kernelMatrix', matrix);

        filters.push(`url(#better-xcloud-filter-clarity)`);
    }

    const saturation = VIDEO_SATURATION['default'];
    if (saturation != 100) {
        filters.push(`saturate(${saturation}%)`);
    }

    const contrast = VIDEO_CONTRAST['default'];
    if (contrast != 100) {
        filters.push(`contrast(${contrast}%)`);
    }

    const brightness =VIDEO_BRIGHTNESS['default'];
    if (brightness != 100) {
        filters.push(`brightness(${brightness}%)`);
    }

    return filters.join(' ');
}


function updateVideoPlayerCss() {
    let $elm = document.getElementById('better-xcloud-video-css');
    if (!$elm) {
        const CE = createElement;

        $elm = CE('style', {id: 'better-xcloud-video-css'});
        document.documentElement.appendChild($elm);

        // Setup SVG filters
        const $svg = CE('svg', {
            'id': 'better-xcloud-video-filters',
            'xmlns': 'http://www.w3.org/2000/svg',
            'class': 'better-xcloud-gone',
        }, CE('defs', {'xmlns': 'http://www.w3.org/2000/svg'},
              CE('filter', {'id': 'better-xcloud-filter-clarity', 'xmlns': 'http://www.w3.org/2000/svg'},
                 CE('feConvolveMatrix', {'id': 'better-xcloud-filter-clarity-matrix', 'order': '3', 'xmlns': 'http://www.w3.org/2000/svg'}))
             )
                       );
        document.documentElement.appendChild($svg);
    }

    let filters = getVideoPlayerFilterStyle();
    let css = '';
    if (filters) {
        css += `filter: ${filters} !important;`;
    }

    if (video_stretch.default=='fill') {
        css += 'object-fit: fill !important;';
    }

    if (video_stretch.default=='setting') {
        css += `transform: scaleX(` + (video_stretch_x_y.x*1 + 1) + `) scaleY(` + (video_stretch_x_y.y*1 + 1) + `) !important;`;
    }

    if (css) {
        css = `#game-stream video {${css}}`;
    }

    $elm.textContent = css;
}
function injectVideoSettingsButton() {
    const $screen = document.querySelector('#PageContent section[class*=PureScreens]');
    if (!$screen) {
        return;
    }

    if ($screen.xObserving) {
        return;
    }

    $screen.xObserving = true;
    const $quickBar = document.querySelector('.better-xcloud-quick-settings-bar');
    const $parent = $screen.parentElement;
    const hideQuickBarFunc = e => {
        e.stopPropagation();
        if (e.target != $parent && e.target.id !== 'MultiTouchSurface' && !e.target.querySelector('#BabylonCanvasContainer-main')) {
            return;
        }

        // Hide Quick settings bar
        $quickBar.style.display = 'none';

        $parent.removeEventListener('click', hideQuickBarFunc);
        $parent.removeEventListener('touchstart', hideQuickBarFunc);

        if (e.target.id === 'MultiTouchSurface') {
            e.target.removeEventListener('touchstart', hideQuickBarFunc);
        }
    }
    const observer = new MutationObserver(mutationList => {
        mutationList.forEach(item => {
            if (item.type !== 'childList') {
                return;
            }

            item.addedNodes.forEach(async node => {
                if (!node.className || !node.className.startsWith('StreamMenu')) {
                    return;
                }

                const $orgButton = node.querySelector('div > div > button');
                if (!$orgButton) {
                    return;
                }

                // Create Video Settings button
                const $btnVideoSettings = cloneStreamMenuButton($orgButton, '视频调整', ICON_VIDEO_SETTINGS);
                $btnVideoSettings.addEventListener('click', e => {
                    e.preventDefault();
                    e.stopPropagation();

                    // Close HUD
                    $btnCloseHud.click();

                    // Show Quick settings bar
                    $quickBar.style.display = 'flex';

                    $parent.addEventListener('click', hideQuickBarFunc);
                    $parent.addEventListener('touchstart', hideQuickBarFunc);

                    const $touchSurface = document.getElementById('MultiTouchSurface');
                    $touchSurface && $touchSurface.style.display != 'none' && $touchSurface.addEventListener('touchstart', hideQuickBarFunc);
                });
                // Add button at the beginning
                $orgButton.parentElement.insertBefore($btnVideoSettings, $orgButton.parentElement.firstChild);

                // Hide Quick bar when closing HUD
                const $btnCloseHud = document.querySelector('button[class*=StreamMenu-module__backButton]');
                $btnCloseHud.addEventListener('click', e => {
                    $quickBar.style.display = 'none';
                });

                // Create Stream Stats button
                const $btnStreamStats = cloneStreamMenuButton($orgButton, '流监控', ICON_STREAM_STATS);
                $btnStreamStats.addEventListener('click', e => {
                    e.preventDefault();
                    e.stopPropagation();

                    // Toggle Stream Stats
                    StreamStats.toggle();
                });

                // Insert after Video Settings button
                $orgButton.parentElement.insertBefore($btnStreamStats, $btnVideoSettings);

            });
        });
    });
    observer.observe($screen, {subtree: true, childList: true});
}

function patchVideoApi() {

    // Show video player when it's ready
    var showFunc;
    showFunc = function() {
        this.style.visibility = 'visible';
        this.removeEventListener('playing', showFunc);

        if (!this.videoWidth) {
            return;
        }

        STREAM_WEBRTC.getStats().then(stats => {

            if (STATS_SHOW_WHEN_PLAYING['default']) {
                StreamStats.start();
            }
        });
    }
    HTMLMediaElement.prototype.orgPlay = HTMLMediaElement.prototype.play;
    HTMLMediaElement.prototype.play = function() {

        //         if ( this.className.startsWith('XboxSplashVideo')) {
        //             this.volume = 0;
        //             this.style.display = 'none';
        //             this.dispatchEvent(new Event('ended'));

        //             return {
        //                 catch: () => {},
        //             };
        //         }

        this.addEventListener('playing', showFunc);
        injectVideoSettingsButton();

        return this.orgPlay.apply(this);
    };
}

patchVideoApi();
function cloudGameclickevent(cgbtitleInterval,s){
    var dom=$("[href='/play'] > h1");
    if(dom.length>0){
        clearInterval(cgbtitleInterval);
        setTimeout(()=>{
            dom=$("[href='/play'] > h1");
            if(dom.text()!='⚙️ 设置'+nftxboxversion){
                dom.text('⚙️ 设置'+nftxboxversion);
                dom.parent().removeAttr("href");
                $('.'+dom.parents('header').attr('class')).on('click','a',()=>{
                    $('#settingsBackgroud').css('display','');
                });
                //check一次
                setTimeout(()=>{cloudGameclickevent(cgbtitleInterval)},5000);
            }
        },s);
    }
}

let mslogotimeOut=0;
function mslogoClickevent(mslogoInterval,s){
    let mslogodom=$($('header>div>div>button')[1]);
    if(mslogodom.length>0){
        clearInterval(mslogoInterval);
        mslogodom=mslogodom.next();
        if(mslogodom.text()==("⚙️ 设置"+nftxboxversion)){return;}
        mslogodom.removeAttr('href');
        mslogodom.css("color",'white');
        mslogodom.text("⚙️ 设置"+nftxboxversion);
        mslogodom.click(()=>{
            $('#settingsBackgroud').css('display','');
        });
        setTimeout(()=>{mslogoClickevent(mslogoInterval)},5000);
    }
    mslogotimeOut=mslogotimeOut+1;
    if(mslogotimeOut>10){
        mslogotimeOut=0;
        clearInterval(mslogoInterval);
    }
}
function setCloudGameingclick(s){
    let cgbtitleInterval=setInterval(()=>{
        cloudGameclickevent(cgbtitleInterval,s);
    },1000);
    let mslogoInterval=setInterval(()=>{
        mslogoClickevent(mslogoInterval,s);
    },1000);
}

setCloudGameingclick(3000);

console.log("all done");
})();