// ==UserScript== // @name Xbox CLoud Gaming优化整合 // @name:zh-CN Xbox CLoud Gaming优化整合 // @namespace http://tampermonkey.net/xbox/nft // @version 3.1.8 // @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反馈】 // @downloadURL none // ==/UserScript== (function () { 'use strict'; // Your code here... var nftxboxversion='v3.1.8'; //========↓↓↓↓↓是各个功能的初始设置,仅第一次运行脚本有效↓↓↓↓↓========// //★★ 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 = ''; const ICON_VIDEO_SETTINGS = ''; 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() { var userAgent = userAgentOriginal.toLowerCase(); if (userAgent.indexOf('safari') !== -1 && userAgent.indexOf('chrome') === -1) { return true; } else { return false; } } } // 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.isSafari())), 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(`
左右
上下
`); $(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 += ``; dom += `
`; dom += ``; dom += `
`; dom += `
`; dom += `
`; dom += `
`; dom += `
`; dom += `
`; dom += `
`; dom += `
`; dom += `` dom += `
捐赠:微信支付宝
` dom = ''; $('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 () { if($(this).val()==1){ if(!confirm("确定要屏蔽触控吗?")){ $('#disableTouchControlsOff').click(); return; } } needrefresh = 1; naifeitian.setValue('disableTouchControlsGM', $(this).val()); $('.closeSetting1').text('确定'); }); $(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(`
⚙️ 设置
`); $(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"); })();