Add microphone action for Game Bar

This commit is contained in:
redphx 2024-05-11 21:15:22 +07:00
parent 5c9202119b
commit 362c5386d1
7 changed files with 143 additions and 5 deletions

View File

@ -213,6 +213,7 @@ function main() {
// Setup UI
addCss();
Toast.setup();
getPref(PrefKey.GAME_BAR_ENABLED) && GameBar.getInstance();
BX_FLAGS.PreloadUi && setupStreamUi();
StreamBadges.setupEvents();

View File

@ -0,0 +1,81 @@
import { BxEvent, XcloudEvent } from "@utils/bx-event";
import { BxIcon } from "@utils/bx-icon";
import { createButton, ButtonStyle, CE } from "@utils/html";
import { t } from "@utils/translation";
import { BaseGameBarAction } from "./action-base";
enum MicrophoneState {
REQUESTED = 'Requested',
ENABLED = 'Enabled',
MUTED = 'Muted',
NOT_ALLOWED = 'NotAllowed',
NOT_FOUND = 'NotFound',
}
export class MicrophoneAction extends BaseGameBarAction {
$content: HTMLElement;
visible: boolean = false;
constructor() {
super();
const onClick = (e: Event) => {
BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
const state = this.$content.getAttribute('data-enabled');
const enableMic = state === 'true' ? false : true;
try {
window.BX_EXPOSED.streamSession.tryEnableChatAsync(enableMic);
this.$content.setAttribute('data-enabled', enableMic.toString());
} catch (e) {
console.log(e);
}
};
const $btnDefault = createButton({
style: ButtonStyle.GHOST,
icon: BxIcon.MICROPHONE,
title: t('show-touch-controller'),
onClick: onClick,
classes: ['bx-activated'],
});
const $btnMuted = createButton({
style: ButtonStyle.GHOST,
icon: BxIcon.MICROPHONE_MUTED,
title: t('hide-touch-controller'),
onClick: onClick,
});
this.$content = CE('div', {},
$btnDefault,
$btnMuted,
);
this.reset();
window.addEventListener(BxEvent.STREAM_EVENT_TARGET_READY, e => {
const eventTarget = window.BX_EXPOSED.eventTarget as EventTarget;
eventTarget.addEventListener(XcloudEvent.MICROPHONE_STATE_CHANGED, e => {
const state = window.BX_EXPOSED.streamSession.microphoneState as MicrophoneState;
const enabled = state === MicrophoneState.ENABLED;
this.$content.setAttribute('data-enabled', enabled.toString());
// Show the button in Game Bar if the mic is enabled
this.$content.classList.remove('bx-gone');
});
});
}
render(): HTMLElement {
return this.$content;
}
reset(): void {
this.visible = false;
this.$content.classList.add('bx-gone');
this.$content.setAttribute('data-enabled', 'false');
}
}

View File

@ -6,6 +6,7 @@ import { BxIcon } from "@utils/bx-icon";
import type { BaseGameBarAction } from "./action-base";
import { STATES } from "@utils/global";
import { PrefKey, getPref } from "@utils/preferences";
import { MicrophoneAction } from "./action-microphone";
export class GameBar {
@ -38,6 +39,7 @@ export class GameBar {
this.actions = [
new ScreenshotAction(),
...(STATES.hasTouchSupport && (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== 'off') ? [new TouchControlAction()] : []),
new MicrophoneAction(),
];
// Render actions

View File

@ -4,6 +4,7 @@ import { getPref, PrefKey } from "@utils/preferences";
import { VibrationManager } from "@modules/vibration-manager";
import { BxLogger } from "@utils/bx-logger";
import { hashCode } from "@utils/utils";
import { BxEvent } from "@/utils/bx-event";
type PatchArray = (keyof typeof PATCHES)[];
@ -496,6 +497,45 @@ BxLogger.info('patchRemotePlayMkb', ${configsVar});
str = str.replace(text, newCode);
return str;
},
exposeEventTarget(str: string) {
const text ='this._eventTarget=new EventTarget';
if (!str.includes(text)) {
return false;
}
const newCode = `
window.BX_EXPOSED.eventTarget = ${text},
window.dispatchEvent(new Event('${BxEvent.STREAM_EVENT_TARGET_READY}'))
`;
str = str.replace(text, newCode);
return str;
},
exposeStreamSession(str: string) {
const text =',this._connectionType=';
if (!str.includes(text)) {
return false;
}
const newCode = `;
window.BX_EXPOSED.streamSession = this;
const orgSetMicrophoneState = this.setMicrophoneState.bind(this);
this.setMicrophoneState = (e) => {
console.log(e);
orgSetMicrophoneState(e);
};
window.dispatchEvent(new Event('${BxEvent.STREAM_SESSION_READY}'))
true` + text;
str = str.replace(text, newCode);
return str;
},
};
let PATCH_ORDERS: PatchArray = [
@ -503,6 +543,8 @@ let PATCH_ORDERS: PatchArray = [
'overrideSettings',
'broadcastPollingMode',
'exposeStreamSession',
getPref(PrefKey.UI_LAYOUT) !== 'default' && 'websiteLayout',
getPref(PrefKey.LOCAL_CO_OP_ENABLED) && 'supportLocalCoOp',
getPref(PrefKey.GAME_FORTNITE_FORCE_CONSOLE) && 'forceFortniteConsole',
@ -538,6 +580,8 @@ let PLAYING_PATCH_ORDERS: PatchArray = [
'patchStreamHud',
'playVibration',
'exposeEventTarget',
// Patch volume control for normal stream
getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && !getPref(PrefKey.STREAM_COMBINE_SOURCES) && 'patchAudioMediaStream',
// Patch volume control for combined audio+video stream

View File

@ -120,8 +120,13 @@ export function injectStreamMenuButtons() {
let $elm: HTMLElement | null = $node as HTMLElement;
// Ignore SVG elements
if ($elm instanceof SVGSVGElement) {
return;
}
// Error Page: .PureErrorPage.ErrorScreen
if ($elm.className.includes('PureErrorPage')) {
if ($elm.className?.includes('PureErrorPage')) {
BxEvent.dispatch(window, BxEvent.STREAM_ERROR_PAGE);
return;
}
@ -133,7 +138,7 @@ export function injectStreamMenuButtons() {
}
// Render badges
if ($elm.className.startsWith('StreamMenu-module__container')) {
if ($elm.className?.startsWith('StreamMenu-module__container')) {
BxEvent.dispatch(window, BxEvent.STREAM_MENU_SHOWN);
const $btnCloseHud = document.querySelector('button[class*=StreamMenu-module__backButton]');
@ -175,7 +180,7 @@ export function injectStreamMenuButtons() {
return;
}
if ($elm.className.startsWith('Overlay-module_') || $elm.className.startsWith('InProgressScreen')) {
if ($elm.className?.startsWith('Overlay-module_') || $elm.className?.startsWith('InProgressScreen')) {
$elm = $elm.querySelector('#StreamHud');
}

View File

@ -9,7 +9,6 @@ import { StreamStats } from "@modules/stream/stream-stats";
import { TouchController } from "@modules/touch-controller";
import { t } from "@utils/translation";
import { VibrationManager } from "@modules/vibration-manager";
import { GameBar } from "../game-bar/game-bar";
import { Screenshot } from "@/utils/screenshot";
@ -477,7 +476,6 @@ export function setupStreamUi() {
StreamStats.render();
Screenshot.setup();
getPref(PrefKey.GAME_BAR_ENABLED) && GameBar.getInstance();
}
updateVideoPlayerCss();

View File

@ -19,6 +19,9 @@ export enum BxEvent {
STREAM_WEBRTC_CONNECTED = 'bx-stream-webrtc-connected',
STREAM_WEBRTC_DISCONNECTED = 'bx-stream-webrtc-disconnected',
STREAM_EVENT_TARGET_READY = 'bx-stream-event-target-ready',
STREAM_SESSION_READY = 'bx-stream-session-ready',
CUSTOM_TOUCH_LAYOUTS_LOADED = 'bx-custom-touch-layouts-loaded',
REMOTE_PLAY_READY = 'bx-remote-play-ready',
@ -31,6 +34,10 @@ export enum BxEvent {
GAME_BAR_ACTION_ACTIVATED = 'bx-game-bar-action-activated',
}
export enum XcloudEvent {
MICROPHONE_STATE_CHANGED = 'microphoneStateChanged',
}
export namespace BxEvent {
export function dispatch(target: HTMLElement | Window, eventName: string, data?: any) {
if (!eventName) {