Simplify stream menu (#58)

* Add "Simplify Stream's menu" setting

* Fix Smart TV layout

* Fix not able to hide Video bar if the "Disable touch controller" is on

* Combine Region + Server badges into one

* Reduce badge's font-size from 16 to 14px

* Fix battery level showing .99999

* Don't show battery badge when it's 100%

* Fix showing incorrect Audio codec in Safari

* Add "Safari on macOS" User-Agent profile

* Fix showing incorrect Video codec in Safari

* Use "-webkit-user-select" for Safari

* Update Video badge's color
This commit is contained in:
redphx 2023-08-04 16:19:18 +07:00 committed by GitHub
parent 0c80e3ab1d
commit 4d2b6c5ef7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -69,7 +69,6 @@ class StreamBadges {
static get BADGE_IN() { return 'in'; }; static get BADGE_IN() { return 'in'; };
static get BADGE_OUT() { return 'out'; }; static get BADGE_OUT() { return 'out'; };
static get BADGE_REGION() { return 'region'; };
static get BADGE_SERVER() { return 'server'; }; static get BADGE_SERVER() { return 'server'; };
static get BADGE_VIDEO() { return 'video'; }; static get BADGE_VIDEO() { return 'video'; };
static get BADGE_AUDIO() { return 'audio'; }; static get BADGE_AUDIO() { return 'audio'; };
@ -126,13 +125,14 @@ class StreamBadges {
// Battery // Battery
let batteryLevel = '100%'; let batteryLevel = '100%';
let batteryLevelInt = 100;
if (navigator.getBattery) { if (navigator.getBattery) {
try { try {
const currentLevel = (await navigator.getBattery()).level * 100; batteryLevelInt = Math.round((await navigator.getBattery()).level * 100);
batteryLevel = `${currentLevel}%`; batteryLevel = `${batteryLevelInt}%`;
if (currentLevel != StreamBadges.startBatteryLevel) { if (batteryLevelInt != StreamBadges.startBatteryLevel) {
const diffLevel = currentLevel - StreamBadges.startBatteryLevel; const diffLevel = Math.round(batteryLevelInt - StreamBadges.startBatteryLevel);
const sign = diffLevel > 0 ? '+' : ''; const sign = diffLevel > 0 ? '+' : '';
batteryLevel += ` (${sign}${diffLevel}%)`; batteryLevel += ` (${sign}${diffLevel}%)`;
} }
@ -164,6 +164,14 @@ class StreamBadges {
const $elm = StreamBadges.#cachedDoms[name]; const $elm = StreamBadges.#cachedDoms[name];
$elm && ($elm.lastElementChild.textContent = value); $elm && ($elm.lastElementChild.textContent = value);
if (name === StreamBadges.BADGE_BATTERY) {
if (StreamBadges.startBatteryLevel === 100 && batteryLevelInt === 100) {
$elm.style.display = 'none';
} else {
$elm.style = '';
}
}
} }
} }
@ -218,15 +226,18 @@ class StreamBadges {
batteryLevel = '100%'; batteryLevel = '100%';
} }
// Server + Region
let server = StreamBadges.region;
server += '@' + (StreamBadges.ipv6 ? 'IPv6' : 'IPv4');
const BADGES = [ const BADGES = [
[StreamBadges.BADGE_PLAYTIME, '1m', '#ff004d'], [StreamBadges.BADGE_PLAYTIME, '1m', '#ff004d'],
[StreamBadges.BADGE_BATTERY, batteryLevel, '#00b543'], [StreamBadges.BADGE_BATTERY, batteryLevel, '#00b543'],
[StreamBadges.BADGE_IN, StreamBadges.#humanFileSize(0), '#29adff'], [StreamBadges.BADGE_IN, StreamBadges.#humanFileSize(0), '#29adff'],
[StreamBadges.BADGE_OUT, StreamBadges.#humanFileSize(0), '#ff77a8'], [StreamBadges.BADGE_OUT, StreamBadges.#humanFileSize(0), '#ff77a8'],
[StreamBadges.BADGE_BREAK], [StreamBadges.BADGE_BREAK],
[StreamBadges.BADGE_REGION, StreamBadges.region, '#ff6c24'], [StreamBadges.BADGE_SERVER, server, '#ff6c24'],
[StreamBadges.BADGE_SERVER, StreamBadges.ipv6 ? 'IPv6' : 'IPv4', '#065ab5'], video ? [StreamBadges.BADGE_VIDEO, video, '#742f29'] : null,
video ? [StreamBadges.BADGE_VIDEO, video, '#754665'] : null,
audio ? [StreamBadges.BADGE_AUDIO, audio, '#5f574f'] : null, audio ? [StreamBadges.BADGE_AUDIO, audio, '#5f574f'] : null,
]; ];
@ -445,12 +456,14 @@ class StreamStats {
class UserAgent { class UserAgent {
static get PROFILE_EDGE_WINDOWS() { return 'edge-windows'; } static get PROFILE_EDGE_WINDOWS() { return 'edge-windows'; }
static get PROFILE_SAFARI_MACOS() { return 'safari-macos'; }
static get PROFILE_SMARTTV_TIZEN() { return 'smarttv-tizen'; } static get PROFILE_SMARTTV_TIZEN() { return 'smarttv-tizen'; }
static get PROFILE_DEFAULT() { return 'default'; } static get PROFILE_DEFAULT() { return 'default'; }
static get PROFILE_CUSTOM() { return 'custom'; } static get PROFILE_CUSTOM() { return 'custom'; }
static #USER_AGENTS = { static #USER_AGENTS = {
[UserAgent.PROFILE_EDGE_WINDOWS]: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.188', [UserAgent.PROFILE_EDGE_WINDOWS]: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.188',
[UserAgent.PROFILE_SAFARI_MACOS]: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.2 Safari/605.1.1',
[UserAgent.PROFILE_SMARTTV_TIZEN]: 'Mozilla/5.0 (SMART-TV; LINUX; Tizen 7.0) AppleWebKit/537.36 (KHTML, like Gecko) 94.0.4606.31/7.0 TV Safari/537.36', [UserAgent.PROFILE_SMARTTV_TIZEN]: 'Mozilla/5.0 (SMART-TV; LINUX; Tizen 7.0) AppleWebKit/537.36 (KHTML, like Gecko) 94.0.4606.31/7.0 TV Safari/537.36',
} }
@ -506,6 +519,7 @@ class Preferences {
static get USER_AGENT_PROFILE() { return 'user_agent_profile'; } static get USER_AGENT_PROFILE() { return 'user_agent_profile'; }
static get USER_AGENT_CUSTOM() { return 'user_agent_custom'; } static get USER_AGENT_CUSTOM() { return 'user_agent_custom'; }
static get STREAM_HIDE_TOUCH_CONTROLLER() { return 'stream_hide_touch_controller'; } static get STREAM_HIDE_TOUCH_CONTROLLER() { return 'stream_hide_touch_controller'; }
static get STREAM_SIMPLIFY_MENU() { return 'stream_simplify_menu'; }
static get SCREENSHOT_BUTTON_POSITION() { return 'screenshot_button_position'; } static get SCREENSHOT_BUTTON_POSITION() { return 'screenshot_button_position'; }
static get BLOCK_TRACKING() { return 'block_tracking'; } static get BLOCK_TRACKING() { return 'block_tracking'; }
@ -617,6 +631,10 @@ class Preferences {
'label': 'Disable touch controller', 'label': 'Disable touch controller',
'default': false, 'default': false,
}, },
[Preferences.STREAM_SIMPLIFY_MENU]: {
'label': 'Simplify Stream\'s menu',
'default': false,
},
[Preferences.HIDE_IDLE_CURSOR]: { [Preferences.HIDE_IDLE_CURSOR]: {
'label': 'Hide mouse cursor while playing', 'label': 'Hide mouse cursor while playing',
'default': false, 'default': false,
@ -639,6 +657,7 @@ class Preferences {
'options': { 'options': {
[UserAgent.PROFILE_DEFAULT]: 'Default', [UserAgent.PROFILE_DEFAULT]: 'Default',
[UserAgent.PROFILE_EDGE_WINDOWS]: 'Edge on Windows', [UserAgent.PROFILE_EDGE_WINDOWS]: 'Edge on Windows',
[UserAgent.PROFILE_SAFARI_MACOS]: 'Safari on macOS',
[UserAgent.PROFILE_SMARTTV_TIZEN]: 'Samsung Smart TV', [UserAgent.PROFILE_SMARTTV_TIZEN]: 'Samsung Smart TV',
[UserAgent.PROFILE_CUSTOM]: 'Custom', [UserAgent.PROFILE_CUSTOM]: 'Custom',
}, },
@ -864,6 +883,7 @@ function addCss() {
.better_xcloud_settings { .better_xcloud_settings {
background-color: #151515; background-color: #151515;
user-select: none; user-select: none;
-webkit-user-select: none;
color: #fff; color: #fff;
font-family: "Segoe UI", Arial, Helvetica, sans-serif font-family: "Segoe UI", Arial, Helvetica, sans-serif
} }
@ -987,9 +1007,9 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
.better-xcloud-badges { .better-xcloud-badges {
position: absolute; position: absolute;
top: 155px;
margin-left: 0px; margin-left: 0px;
user-select: none; user-select: none;
-webkit-user-select: none;
} }
.better-xcloud-badge { .better-xcloud-badge {
@ -998,6 +1018,7 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
line-height: 24px; line-height: 24px;
color: #fff; color: #fff;
font-family: Bahnschrift Semibold, Arial, Helvetica, sans-serif; font-family: Bahnschrift Semibold, Arial, Helvetica, sans-serif;
font-size: 14px;
font-weight: 400; font-weight: 400;
margin: 0 8px 8px 0; margin: 0 8px 8px 0;
box-shadow: 0px 0px 6px #000; box-shadow: 0px 0px 6px #000;
@ -1053,6 +1074,7 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
.better-xcloud-stats-bar { .better-xcloud-stats-bar {
display: block; display: block;
user-select: none; user-select: none;
-webkit-user-select: none;
position: fixed; position: fixed;
top: 0; top: 0;
background-color: #000; background-color: #000;
@ -1139,6 +1161,7 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
font-family: "Segoe UI", Arial, Helvetica, sans-serif; font-family: "Segoe UI", Arial, Helvetica, sans-serif;
box-shadow: 0 0 6px #000; box-shadow: 0 0 6px #000;
user-select: none; user-select: none;
-webkit-user-select: none;
} }
.better-xcloud-stats-settings *:focus { .better-xcloud-stats-settings *:focus {
@ -1196,6 +1219,7 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
.better-xcloud-quick-settings-bar { .better-xcloud-quick-settings-bar {
display: none; display: none;
user-select: none; user-select: none;
-webkit-user-select: none;
position: fixed; position: fixed;
bottom: 20px; bottom: 20px;
left: 50%; left: 50%;
@ -1279,17 +1303,6 @@ div[class*=NotFocusedDialog] {
#game-stream video { #game-stream video {
visibility: hidden; visibility: hidden;
} }
/* Adjust Stream menu icon's size */
button[class*=MenuItem-module__container] {
min-width: auto !important;
width: 100px !important;
}
button[class*=MenuItem-module__container] div[class*=MenuItem-module__label] {
margin-left: 8px !important;
margin-right: 8px !important;
}
`; `;
// Reduce animations // Reduce animations
@ -1333,6 +1346,71 @@ div[class*=StreamHUD-module__buttonsContainer] {
`; `;
} }
// Simplify Stream's menu
css += `
div[class*=StreamMenu-module__menu] {
min-width: 100vw !important;
}
`;
if (PREFS.get(Preferences.STREAM_SIMPLIFY_MENU)) {
css += `
div[class*=Menu-module__scrollable] {
--bxStreamMenuItemSize: 80px;
--streamMenuItemSize: calc(var(--bxStreamMenuItemSize) + 40px) !important;
}
.better-xcloud-badges {
top: calc(var(--streamMenuItemSize) - 20px);
}
body[data-media-type=tv] .better-xcloud-badges {
top: calc(var(--streamMenuItemSize) - 10px) !important;
}
button[class*=MenuItem-module__container] {
min-width: auto !important;
min-height: auto !important;
width: var(--bxStreamMenuItemSize) !important;
height: var(--bxStreamMenuItemSize) !important;
}
div[class*=MenuItem-module__label] {
display: none !important;
}
svg[class*=MenuItem-module__icon] {
width: 36px;
height: 100% !important;
padding: 0 !important;
margin: 0 !important;
}
`;
} else {
css += `
body[data-media-type=tv] .better-xcloud-badges {
top: calc(var(--streamMenuItemSize) + 30px);
}
body:not([data-media-type=tv]) .better-xcloud-badges {
top: calc(var(--streamMenuItemSize) + 20px);
}
body:not([data-media-type=tv]) button[class*=MenuItem-module__container] {
min-width: auto !important;
width: 100px !important;
}
body:not([data-media-type=tv]) button[class*=MenuItem-module__container]:nth-child(n+2) {
margin-left: 10px !important;
}
body:not([data-media-type=tv]) div[class*=MenuItem-module__label] {
margin-left: 8px !important;
margin-right: 8px !important;
}
`;
}
const $style = createElement('style', {}, css); const $style = createElement('style', {}, css);
document.documentElement.appendChild($style); document.documentElement.appendChild($style);
} }
@ -1855,7 +1933,8 @@ function injectVideoSettingsButton() {
const $quickBar = document.querySelector('.better-xcloud-quick-settings-bar'); const $quickBar = document.querySelector('.better-xcloud-quick-settings-bar');
const $parent = $screen.parentElement; const $parent = $screen.parentElement;
const hideQuickBarFunc = e => { const hideQuickBarFunc = e => {
if (e.target != $parent && e.target.id !== 'MultiTouchSurface') { e.stopPropagation();
if (e.target != $parent && e.target.id !== 'MultiTouchSurface' && !e.target.querySelector('#BabylonCanvasContainer-main')) {
return; return;
} }
@ -1863,7 +1942,7 @@ function injectVideoSettingsButton() {
$quickBar.style.display = 'none'; $quickBar.style.display = 'none';
$parent.removeEventListener('click', hideQuickBarFunc); $parent.removeEventListener('click', hideQuickBarFunc);
$parent.removeEventListener('touchend', hideQuickBarFunc); $parent.removeEventListener('touchstart', hideQuickBarFunc);
if (e.target.id === 'MultiTouchSurface') { if (e.target.id === 'MultiTouchSurface') {
e.target.removeEventListener('touchstart', hideQuickBarFunc); e.target.removeEventListener('touchstart', hideQuickBarFunc);
@ -1896,10 +1975,10 @@ function injectVideoSettingsButton() {
$quickBar.style.display = 'flex'; $quickBar.style.display = 'flex';
$parent.addEventListener('click', hideQuickBarFunc); $parent.addEventListener('click', hideQuickBarFunc);
$parent.addEventListener('touchend', hideQuickBarFunc); $parent.addEventListener('touchstart', hideQuickBarFunc);
const $touchSurface = document.querySelector('#MultiTouchSurface'); const $touchSurface = document.querySelector('#MultiTouchSurface');
$touchSurface && $touchSurface.addEventListener('touchstart', hideQuickBarFunc); $touchSurface && $touchSurface.style.display != 'none' && $touchSurface.addEventListener('touchstart', hideQuickBarFunc);
}); });
// Add button at the beginning // Add button at the beginning
@ -1984,41 +2063,67 @@ function patchVideoApi() {
StreamBadges.resolution = {width: this.videoWidth, height: this.videoHeight}; StreamBadges.resolution = {width: this.videoWidth, height: this.videoHeight};
StreamBadges.startTimestamp = +new Date; StreamBadges.startTimestamp = +new Date;
// Get battery level // Get battery level
if (navigator.getBattery) { if (navigator.getBattery) {
try { try {
navigator.getBattery().then(bm => { navigator.getBattery().then(bm => {
StreamBadges.startBatteryLevel = bm.level * 100; StreamBadges.startBatteryLevel = Math.round(bm.level * 100);
}); });
} catch(e) {} } catch(e) {}
} }
STREAM_WEBRTC.getStats().then(stats => { STREAM_WEBRTC.getStats().then(stats => {
const allVideoCodecs = {};
let videoCodecId;
const allAudioCodecs = {};
let audioCodecId;
stats.forEach(stat => { stats.forEach(stat => {
if (stat.type !== 'codec') { if (stat.type == 'codec') {
return; const mimeType = stat.mimeType.split('/');
} if (mimeType[0] === 'video') {
// Store all video stats
const mimeType = stat.mimeType.split('/'); allVideoCodecs[stat.id] = stat;
if (mimeType[0] === 'video') { } else if (mimeType[0] === 'audio') {
const video = { // Store all audio stats
codec: mimeType[1], allAudioCodecs[stat.id] = stat;
}; }
} else if (stat.type === 'inbound-rtp' && stat.packetsReceived > 0) {
if (video.codec === 'H264') { // Get the codecId of the video/audio track currently being used
const match = /profile-level-id=([0-9a-f]{6})/.exec(stat.sdpFmtpLine); if (stat.kind === 'video') {
video.profile = match ? match[1] : null; videoCodecId = stat.codecId;
} else if (stat.kind === 'audio') {
audioCodecId = stat.codecId;
} }
StreamBadges.video = video;
} else if (!StreamBadges.audio && mimeType[0] === 'audio') {
StreamBadges.audio = {
codec: mimeType[1],
bitrate: stat.clockRate,
};
} }
}); });
// Get video codec from codecId
if (videoCodecId) {
const videoStat = allVideoCodecs[videoCodecId];
const video = {
codec: videoStat.mimeType.substring(6),
};
if (video.codec === 'H264') {
const match = /profile-level-id=([0-9a-f]{6})/.exec(videoStat.sdpFmtpLine);
video.profile = match ? match[1] : null;
}
StreamBadges.video = video;
}
// Get audio codec from codecId
if (audioCodecId) {
const audioStat = allAudioCodecs[audioCodecId];
StreamBadges.audio = {
codec: audioStat.mimeType.substring(6),
bitrate: audioStat.clockRate,
}
}
if (PREFS.get(Preferences.STATS_SHOW_WHEN_PLAYING)) { if (PREFS.get(Preferences.STATS_SHOW_WHEN_PLAYING)) {
StreamStats.start(); StreamStats.start();
} }