@3dsource/angular-unreal-module 0.0.44 → 0.0.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, inject, Injectable, ChangeDetectionStrategy, Component, Pipe, DestroyRef, signal, ElementRef, input, HostListener, Input, ViewChild, computed, output } from '@angular/core';
3
- import { filter, withLatestFrom, distinctUntilChanged, switchMap, first, catchError, map as map$1, tap, delay, takeUntil, debounceTime, exhaustMap, takeWhile, skip as skip$1 } from 'rxjs/operators';
3
+ import { filter, withLatestFrom, distinctUntilChanged, switchMap, first, catchError, timeout, map as map$1, tap, takeUntil, exhaustMap, debounceTime, takeWhile, skip as skip$1, delay } from 'rxjs/operators';
4
4
  import { createAction, props, Store, provideState, createReducer, on, createFeature, createSelector } from '@ngrx/store';
5
- import { provideEffects, Actions, createEffect, ofType } from '@ngrx/effects';
6
- import { skip, share, merge, Subject, interval, of, map, from, take, fromEvent, timer, combineLatest, switchMap as switchMap$1, timeout, retryWhen, tap as tap$1, startWith, combineLatestWith, takeUntil as takeUntil$1, auditTime, EMPTY, debounceTime as debounceTime$1, scan, concatMap, animationFrameScheduler, Observable, BehaviorSubject, first as first$1, distinctUntilChanged as distinctUntilChanged$1, concat } from 'rxjs';
7
- import { Falsy, Truthy, Logger, calculateMedian, clampf, Signal, tapLog, generateUuid, COLOR_CODES, where, KeyboardNumericCode, InvertedKeyMap, Semaphore, isEmpty, lerp, getCanvasCached, getSnapshot, whereNot, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB, fitIntoRectangle } from '@3dsource/utils';
5
+ import { Actions, ofType, provideEffects, createEffect } from '@ngrx/effects';
6
+ import { skip, share, merge, Subject, interval, of, map, from, take, fromEvent, timer, throwError, defer, Observable, switchMap as switchMap$1, retry, timeout as timeout$1, tap as tap$1, startWith, combineLatestWith, takeUntil as takeUntil$1, auditTime, debounceTime as debounceTime$1, EMPTY, mergeMap, scan, concatMap, animationFrameScheduler, BehaviorSubject, combineLatest, first as first$1, distinctUntilChanged as distinctUntilChanged$1, concat } from 'rxjs';
7
+ import { Falsy, Truthy, Logger, calculateMedian, clampf, Signal, tapLog, generateUuid, COLOR_CODES, where, KeyboardNumericCode, InvertedKeyMap, Semaphore, lerp, getCanvasCached, getSnapshot, whereNot, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB, fitIntoRectangle } from '@3dsource/utils';
8
8
  import { HttpClient } from '@angular/common/http';
9
9
  import { toSignal, takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
10
10
  import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
@@ -19,21 +19,30 @@ import { FormsModule } from '@angular/forms';
19
19
  const WSCloseCode_NORMAL_CLOSURE = 3000;
20
20
  const WSCloseCode_NORMAL_AFK_TIMEOUT = 3001;
21
21
  const WSCloseCode_NORMAL_MANUAL_DISCONNECT = 3002;
22
- const WSCloseCode_NORMAL_CIRRUS_CLOSED = 3003;
22
+ const WSCloseCode_FORCE_CIRRUS_CLOSE = 3003;
23
+ const WSCloseCode_CIRRUS_PLAYER_DISCONNECTED = 1001;
24
+ const WSCloseCode_CIRRUS_ABNORMAL_CLOSURE = 1006;
25
+ const WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR = 1013;
26
+ const WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER = 1011;
23
27
  const WSCloseCodes = [
24
28
  WSCloseCode_NORMAL_CLOSURE,
25
29
  WSCloseCode_NORMAL_AFK_TIMEOUT,
26
30
  WSCloseCode_NORMAL_MANUAL_DISCONNECT,
27
- WSCloseCode_NORMAL_CIRRUS_CLOSED,
31
+ WSCloseCode_FORCE_CIRRUS_CLOSE,
32
+ WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR,
33
+ WSCloseCode_CIRRUS_ABNORMAL_CLOSURE,
28
34
  ];
29
35
 
30
36
  const DisconnectReason = {
31
- afk: 'afk',
32
- none: 'none',
33
- reset: 'reset',
34
- wsOnError: 'wsOnError',
35
- dataChannelClosed: 'dataChannelClosed',
36
- dataChannelTimeout: 'dataChannelTimeout',
37
+ Afk: 'Afk',
38
+ None: 'None',
39
+ Destroy: 'Destroy',
40
+ DataChannelClosed: 'DataChannelClosed',
41
+ DataChannelTimeout: 'DataChannelTimeout',
42
+ WebRTCError: 'WebRTCError',
43
+ WebSocketError: 'WebSocketError',
44
+ WebSocketClose: 'WebSocketClose',
45
+ DropConnection: 'DropConnection',
37
46
  };
38
47
 
39
48
  /*
@@ -90,7 +99,7 @@ const trackMixpanelEvent = createAction(scoped `track mixpanel event`, props());
90
99
  const changeLowBandwidth = createAction(scoped `change low bandwidth`, props());
91
100
  const setMaxFps = createAction(scoped `change fps`, props());
92
101
  const destroyRemoteConnections = createAction(scoped `destroyRemoteConnections`, props());
93
- const destroyConnectionsAndResetState = createAction(scoped `destroyConnections and reset`);
102
+ const destroyUnrealScene = createAction(scoped `destroyUnrealScene`);
94
103
  const setCirrusConnected = createAction(scoped `cirrusConnected`);
95
104
  const setCirrusDisconnected = createAction(scoped `cirrusDisconnected`);
96
105
  const changeStatusMainVideoOnScene = createAction(scoped `change status main video on scene`, props());
@@ -109,9 +118,10 @@ const setFreezeFrameFromVideo = createAction(scoped `set freeze frame from video
109
118
  const setEstablishingConnection = createAction(scoped `set establishing connection`, props());
110
119
  const setDataChannelConnected = createAction(scoped `set data channel connected`, props());
111
120
  const setConfig = createAction(scoped `set config`, props());
112
- const setMatchMakerUrls = createAction(scoped `set match makers urls`, props());
113
- const setErrorMessage = createAction(scoped `set error message`, props());
114
- const setViewportReady = createAction(scoped `set viewport ready`, props());
121
+ const disconnectStream = createAction(scoped `disconnectStream`, props());
122
+ const dropConnection = createAction(scoped `dropConnection`);
123
+ const setViewportReady = createAction(scoped `set viewport ready`);
124
+ const setViewportNotReady = createAction(scoped `set viewport not ready`);
115
125
  const changeStreamResolutionAction = createAction(scoped `change stream resolution`, props());
116
126
  const changeStreamResolutionSuccessAction = createAction(scoped `change stream resolution success action`, props());
117
127
  const setSignalingName = createAction(scoped `set aws instanceName`, props());
@@ -120,13 +130,16 @@ const commandStarted = createAction(scoped `command started`, props());
120
130
  const commandCompleted = createAction(scoped `command completed`, props());
121
131
  const setLoopBackCommandIsCompleted = createAction(scoped `set loopBack command is completed`);
122
132
  const showUnrealErrorMessage = createAction(scoped `show unreal error message`, props());
123
- const initSignalling = createAction(scoped `init signalling`);
124
- const setIsFreezeLoaderPercents = createAction(scoped `set is freeze loader percents`);
133
+ const initSignalling = createAction(scoped `init signalling`, (data = {
134
+ resetDisconnectionReason: true,
135
+ }) => ({
136
+ resetDisconnectionReason: data.resetDisconnectionReason,
137
+ }));
138
+ const startStream = createAction(scoped `startStream`, props());
125
139
  const resetConfig = createAction(scoped `reset config`);
126
140
  const resetAfkAction = createAction(scoped `reset afk action`);
127
141
  const resetWarnTimeout = createAction(scoped `reset config warn timeout`);
128
- const resetUnrealStateAction = createAction(scoped `reset state`);
129
- const resetUnrealState = createAction(scoped `reset unreal state`);
142
+ const abortEstablishingConnection = createAction(scoped `abortEstablishingConnection`);
130
143
 
131
144
  const SpecialKeyCodes = {
132
145
  BackSpace: 8,
@@ -237,6 +250,7 @@ const CONSOLE_COMMAND_PIXEL_QUALITY = 'PixelStreaming.FreezeFrameQuality 95';
237
250
  const FULL_HD_WIDTH = 1920;
238
251
  const FULL_HD_HEIGHT = 1080;
239
252
  const WS_TIMEOUT = 2000;
253
+ const POLLING_TIME = 4000;
240
254
  const WS_OPEN_STATE = 1;
241
255
  const DEFAULT_AFK_TIMEOUT_PERIOD = 15;
242
256
  const DEFAULT_AFK_TIMEOUT = 120;
@@ -344,8 +358,10 @@ class AFKService extends SubService {
344
358
  if (this.countdown === 0) {
345
359
  // The user failed to click so disconnect them
346
360
  this.hideOverlay();
347
- // TODO possible way (Blinking), because postponed close event: destroyRemoteConnections({ disconnectReason: DisconnectReason.afk }),
348
- this.store.dispatch(destroyConnectionsAndResetState());
361
+ this.store.dispatch(disconnectStream({
362
+ reason: DisconnectReason.Afk,
363
+ message: `AFK timeout:${this.selectWarnTimeout()} seconds, popup timeout:${this.closeTimeout} seconds`,
364
+ }));
349
365
  clearInterval(this.countdownTimer);
350
366
  }
351
367
  else {
@@ -353,10 +369,10 @@ class AFKService extends SubService {
353
369
  }
354
370
  }, 1000);
355
371
  }
356
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AFKService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
357
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AFKService }); }
372
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: AFKService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
373
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: AFKService }); }
358
374
  }
359
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AFKService, decorators: [{
375
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: AFKService, decorators: [{
360
376
  type: Injectable
361
377
  }], ctorParameters: () => [] });
362
378
 
@@ -431,10 +447,10 @@ class FreezeFrameService extends SubService {
431
447
  progress: 1,
432
448
  }));
433
449
  }
434
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: FreezeFrameService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
435
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: FreezeFrameService }); }
450
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: FreezeFrameService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
451
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: FreezeFrameService }); }
436
452
  }
437
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: FreezeFrameService, decorators: [{
453
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: FreezeFrameService, decorators: [{
438
454
  type: Injectable
439
455
  }] });
440
456
 
@@ -738,10 +754,10 @@ class VideoService extends SubService {
738
754
  error: () => ({}),
739
755
  });
740
756
  }
741
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: VideoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
742
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: VideoService }); }
757
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: VideoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
758
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: VideoService }); }
743
759
  }
744
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: VideoService, decorators: [{
760
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: VideoService, decorators: [{
745
761
  type: Injectable
746
762
  }], ctorParameters: () => [] });
747
763
 
@@ -955,10 +971,10 @@ class CommandTelemetryService {
955
971
  this.trackTime(out);
956
972
  funcToDecorate(out);
957
973
  }
958
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: CommandTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
959
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: CommandTelemetryService }); }
974
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: CommandTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
975
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: CommandTelemetryService }); }
960
976
  }
961
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: CommandTelemetryService, decorators: [{
977
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: CommandTelemetryService, decorators: [{
962
978
  type: Injectable
963
979
  }], ctorParameters: () => [] });
964
980
  function TelemetryStart(externalId) {
@@ -982,99 +998,221 @@ function OfferHandler(msg) {
982
998
  this.onOffer$.next(msg);
983
999
  }
984
1000
 
1001
+ function httpUrlToWs(url) {
1002
+ return url.replace('http://', 'ws://').replace('https://', 'wss://');
1003
+ }
1004
+
1005
+ function createWebSocket(wsUrl) {
1006
+ if (typeof window === 'undefined' || !('WebSocket' in window)) {
1007
+ return throwError(() => new Error("Your browser doesn't support WebSocket"));
1008
+ }
1009
+ Logger.info('Creating socket', wsUrl);
1010
+ return defer(() => new Observable((observer) => {
1011
+ const ws = new WebSocket(wsUrl);
1012
+ const onOpen = () => {
1013
+ observer.next(ws); // connection established
1014
+ };
1015
+ const onError = (ev) => {
1016
+ const anyEv = ev;
1017
+ const err = anyEv?.error instanceof Error
1018
+ ? anyEv.error
1019
+ : new Error('WebSocket failed to connect');
1020
+ observer.error(err);
1021
+ };
1022
+ const onClose = (ev) => {
1023
+ if (ws.readyState !== WebSocket.OPEN) {
1024
+ observer.error(new Error(`WebSocket closed before open (code ${ev.code})`));
1025
+ }
1026
+ };
1027
+ ws.addEventListener('open', onOpen);
1028
+ ws.addEventListener('error', onError);
1029
+ ws.addEventListener('close', onClose);
1030
+ return () => {
1031
+ ws.removeEventListener('open', onOpen);
1032
+ ws.removeEventListener('error', onError);
1033
+ ws.removeEventListener('close', onClose);
1034
+ };
1035
+ })).pipe(timeout({
1036
+ first: WS_TIMEOUT, // fail if no 'open' event in this window
1037
+ with: () => throwError(() => new Error(`WebSocket connection timed out after ${WS_TIMEOUT}ms`)),
1038
+ }));
1039
+ }
1040
+
985
1041
  class SignallingService extends SubService {
986
1042
  constructor() {
987
1043
  super();
1044
+ this.action$ = inject(Actions);
988
1045
  this.httpClient = inject(HttpClient);
989
1046
  this.regionsPingService = inject(RegionsPingService);
990
1047
  this.onOffer$ = new Subject();
991
1048
  this.onConfig$ = new Subject();
992
1049
  this.onWebRtcIce$ = new Subject();
993
1050
  this.onWebRtcAnswer$ = new Subject();
1051
+ this.abort$ = this.action$.pipe(ofType(abortEstablishingConnection));
994
1052
  this.wsMsgHandlers = {};
995
- this.establishingConnection = toSignal(this.store.select(unrealFeature.selectEstablishingConnection));
996
- this.isCirrusConnected = toSignal(this.store.select(unrealFeature.selectCirrusConnected));
997
- this.establishingConnectionDrop$ = this.store
998
- .select(unrealFeature.selectEstablishingConnection)
999
- .pipe(skip(1), filter(Falsy), tapLog('selectEstablishingConnection'), share());
1000
1053
  this.setHandlersFromStream();
1001
1054
  this.store
1002
1055
  .select(unrealFeature.selectDataChannelConnected)
1003
1056
  .pipe(filter(Truthy))
1004
1057
  .subscribe(() => {
1005
- this.send(JSON.stringify({
1058
+ this.send({
1006
1059
  type: 'p2pEstablished',
1007
1060
  source: 'front',
1008
1061
  sessionId: generateUuid(),
1009
- }));
1010
- });
1011
- combineLatest([
1012
- this.store
1013
- .select(selectMatchMakerUrls)
1014
- .pipe(tapLog('MatchMakerUrls changed:')),
1015
- this.store.select(selectIsAutostart).pipe(tapLog('Autostart is:')),
1016
- ])
1017
- .pipe(filter(([, autoStart]) => autoStart), map$1(([matchMakerUrls]) => [...matchMakerUrls].filter(Truthy)), filter((urls) => urls.length > 0))
1018
- .subscribe((urls) => this.connectToSignaling(urls));
1019
- this.store
1020
- .select(selectWsUrl)
1021
- .pipe(filter(Truthy))
1022
- .subscribe((url) => {
1023
- this.initWebSocket(url);
1062
+ });
1024
1063
  });
1025
1064
  }
1026
- initEstablishingConnection() {
1027
- this.showStatusMessage(UnrealStatusMessage.STARTING_YOUR_SESSION);
1028
- this.store.dispatch(setEstablishingConnection({ value: true }));
1029
- }
1030
- stopEstablishingConnection() {
1031
- this.store.dispatch(setEstablishingConnection({ value: false }));
1032
- }
1033
1065
  connectToSignaling(urlList) {
1066
+ Logger.log('CONNECT TO SIGNALLING');
1034
1067
  // Preparation for WebSocket Orchestration
1035
1068
  this.correlationId = generateUuid();
1036
1069
  this.environmentId = '7e356e8d-8fa5-4cf7-83d0-394903e7b0d6';
1037
1070
  ResetTelemetry();
1038
- if (this.establishingConnection() || this.isCirrusConnected()) {
1039
- Logger.warn(`SIGNALING REQUEST can't be called again!. Waiting for connection or error.`);
1040
- return;
1041
- }
1042
- this.initEstablishingConnection();
1043
- this.regionsPingService
1044
- .getFastest()
1045
- .pipe(first())
1046
- .subscribe((region) => {
1047
- const out = urlList.map((url) => {
1048
- if (region && !url.includes(':region')) {
1049
- return url.replace(/signallingserver/gi, `signallingserver/region:${region}`);
1050
- }
1051
- return url;
1052
- });
1053
- this.connectToSignalingAfterPing(out);
1071
+ this.startEstablishingConnection();
1072
+ return defer(() => this.getRegion().pipe(map$1((region) => this.adaptUrlsToRegion(urlList, region)), switchMap$1((urls) => this.getAwsInstance(urls).pipe(first())), tap((awsInstance) => this.store.dispatch(setAwsInstance(awsInstance))), switchMap$1(({ wsUrl }) => this.connectToCirrus(wsUrl)), filter(Truthy), first(), tap((ws) => (this.ws = ws)), takeUntil(this.abort$))).pipe(
1073
+ // Retry the whole attempt on error, advancing to the next URL each time.
1074
+ retry({
1075
+ delay: (err, attempt) => {
1076
+ Logger.info(`Retrying Signalling Connection in ${POLLING_TIME}ms. Attempt# ${attempt}.`, err?.message);
1077
+ return timer(POLLING_TIME);
1078
+ },
1079
+ // count: 10, // Optional: cap the number of attempts.
1080
+ }));
1081
+ }
1082
+ startEstablishingConnection() {
1083
+ this.showStatusMessage(UnrealStatusMessage.STARTING_YOUR_SESSION);
1084
+ this.store.dispatch(setEstablishingConnection({ value: true }));
1085
+ }
1086
+ adaptUrlsToRegion(urlList, region) {
1087
+ return urlList.map((url) => {
1088
+ if (region && !url.includes(':region')) {
1089
+ return url.replace(/signallingserver/gi, `signallingserver/region:${region}`);
1090
+ }
1091
+ return url;
1054
1092
  });
1055
1093
  }
1056
- connectToSignalingAfterPing(urls) {
1057
- const urlGen = getActiveUrl(urls);
1094
+ /**
1095
+ * Resolves an active AWS *signaling* instance and returns a WebSocket endpoint.
1096
+ *
1097
+ * The observable produced by this method is **cold** and executes inside a `defer`,
1098
+ * so each subscription (including those triggered by `retry`) pulls the **next**
1099
+ * URL candidate from a generator created by `getActiveUrl(urlsPool)`.
1100
+ *
1101
+ * ### Execution flow
1102
+ *
1103
+ * 1. **Telemetry** — Starts a `getSignaling` span for end-to-end timing.
1104
+ * 2. **Candidate URL selection** — A stateful generator (`urlGen`) is created
1105
+ * from `urlsPool`. Each resubscription (e.g. via `retry`) advances to the next URL.
1106
+ * 3. **Short-circuit for WS URLs** — If the picked candidate already matches
1107
+ * `ws://` or `wss://`, emit `{ wsUrl, pollingUrl: null, instanceName: null }`
1108
+ * immediately and complete (no HTTP request).
1109
+ * 4. **Await client/view identifiers** — `this.store.select(selectClientAndViewIds)`
1110
+ * is tapped for logging (missing IDs) and then filtered to require both `clientId`
1111
+ * and `viewId` to be truthy.
1112
+ * 5. **Single in-flight HTTP orchestration call** — Uses `exhaustMap` to issue
1113
+ * `GET {signalingUrl}{clientId}/{viewId}`. New `{clientId, viewId}` emissions
1114
+ * while the request is in flight are **ignored** until completion, preventing overlap.
1115
+ * 6. **Per-attempt timeout** — `timeout(WS_TIMEOUT)` caps how long we wait for a response.
1116
+ * On timeout, the attempt errors and the outer `retry` schedules the next URL.
1117
+ * 7. **Response validation & progress reporting** — A `filter`:
1118
+ * - Calls `TelemetryStop('getSignaling', { ...data, multi: true })` on receipt.
1119
+ * - If `data.signallingServer === ''` or `data.error`:
1120
+ * shows a status message, optionally dispatches `setStatusPercentSignallingServer`
1121
+ * with `data.info.percent`, and **throws** to fail the attempt (triggers `retry`).
1122
+ * 8. **Mapping to `AwsInstance`** — Converts the validated orchestration payload into:
1123
+ * - `wsUrl`: via `httpUrlToWs(\`\${location.protocol}//\${data.signallingServer}\`)`
1124
+ * - `pollingUrl`: the base `signalingUrl` that succeeded
1125
+ * - `instanceName`: the (validated) `data.signallingServer`
1126
+ * 9. **Retry policy** — On *any* upstream error (timeout, HTTP error, invalid payload),
1127
+ * `retry({ delay })`:
1128
+ * - Shows a “connecting” status,
1129
+ * - Logs the attempt number,
1130
+ * - Waits `WS_TIMEOUT` ms (via `timer`) and **resubscribes**, advancing `urlGen`.
1131
+ *
1132
+ * ### Concurrency semantics
1133
+ *
1134
+ * - `exhaustMap` guarantees a **single** HTTP request per attempt; subsequent
1135
+ * `{clientId, viewId}` emissions are ignored until the current request completes.
1136
+ *
1137
+ * ### Telemetry semantics
1138
+ *
1139
+ * - `TelemetryStart('getSignaling')` begins before work.
1140
+ * - `TelemetryStop('getSignaling', {...})` runs on receipt of an orchestration response.
1141
+ * If an attempt fails (e.g., timeout), stopping the span is the responsibility of
1142
+ * your telemetry layer or a `finalize` elsewhere if desired.
1143
+ *
1144
+ * ### Error & retry semantics
1145
+ *
1146
+ * - Missing/never-emitted IDs → timeout → retry with next URL.
1147
+ * - HTTP/network error → retry with next URL.
1148
+ * - Invalid orchestration payload (`error` set or empty `signallingServer`) → throw in
1149
+ * validation filter → retry with next URL.
1150
+ * - Short-circuit WS path **does not** retry (emits once, completes).
1151
+ *
1152
+ * @param urlsPool - Ordered list of base URLs to probe. Retries advance through this list
1153
+ * until a working signaling server is found. A candidate may also be a direct `ws://`/`wss://`
1154
+ * URL to short-circuit HTTP orchestration.
1155
+ *
1156
+ * @returns Observable that emits exactly one {@link AwsInstance} on success and then completes.
1157
+ * On failure, the stream errors; the built-in `retry` operator re-subscribes after `WS_TIMEOUT`
1158
+ * and advances to the next URL candidate.
1159
+ *
1160
+ * @throws Emits an error within the observable chain (not a thrown synchronous exception) when:
1161
+ * - The HTTP request fails or times out.
1162
+ * - The orchestration response indicates an error or an empty `signallingServer`.
1163
+ * - Any other operator in the chain surfaces an error.
1164
+ *
1165
+ * @remarks
1166
+ * - The URL generator (`urlGen`) is created **outside** `defer`, so resubscriptions
1167
+ * advance the pool. Creating it inside `defer` would restart from the first URL each retry.
1168
+ * - `location.protocol` is used to derive `ws` vs `wss`. If this may run in non-browser
1169
+ * contexts (e.g., SSR), guard or abstract this logic.
1170
+ * - Consider capping retries with `count` in `retry({ count, delay })` when appropriate.
1171
+ *
1172
+ * @example
1173
+ * ```ts
1174
+ * getAwsInstance(['https://signaling.example.com/', 'wss://direct.example.com'])
1175
+ * .subscribe({
1176
+ * next: ({ wsUrl }) => connect(wsUrl),
1177
+ * error: (e) => console.error('Unable to establish signaling:', e),
1178
+ * });
1179
+ * ```
1180
+ *
1181
+ * @see httpUrlToWs
1182
+ * @see selectClientAndViewIds
1183
+ * @see setStatusPercentSignallingServer
1184
+ */
1185
+ getAwsInstance(urlsPool) {
1058
1186
  TelemetryStart('getSignaling');
1059
- const createSubscription = () => {
1187
+ // Stateful iterator over candidate URLs; retries advance this sequence.
1188
+ const urlGen = getActiveUrl(urlsPool);
1189
+ return defer(() => {
1060
1190
  const signalingUrl = `${urlGen.next().value}`;
1191
+ // Short-circuit if the candidate is already a WS endpoint.
1061
1192
  if (signalingUrl.match(/^ws(s?):\/\//i)) {
1062
- this.store.dispatch(setAwsInstance({
1193
+ return of({
1063
1194
  wsUrl: signalingUrl,
1064
1195
  pollingUrl: null,
1065
1196
  instanceName: null,
1066
- }));
1067
- return;
1197
+ });
1068
1198
  }
1069
- const subscription = combineLatest([
1070
- this.store.select(unrealFeature.selectStreamClientCompanyId),
1071
- this.store.select(unrealFeature.selectStreamViewId),
1072
- ])
1073
- .pipe(tap(([clientId, viewId]) => {
1199
+ return this.store.select(selectClientAndViewIds).pipe(
1200
+ // Log missing IDs without altering the stream.
1201
+ tap(({ clientId, viewId }) => {
1074
1202
  if (!clientId || !viewId) {
1075
1203
  console.error('Client ID or View ID is not set');
1076
1204
  }
1077
- }), filter(([clientId, viewId]) => !!clientId && !!viewId), tapLog('Stream Id =>'), switchMap$1(([clientId, viewId]) => this.httpClient.get(`${signalingUrl}${clientId}/${viewId}`)), timeout(4000), filter((data) => {
1205
+ }),
1206
+ // Only proceed when both identifiers are available.
1207
+ filter(({ clientId, viewId }) => !!clientId && !!viewId),
1208
+ // Take first value
1209
+ first(),
1210
+ // Ensure a single in-flight HTTP request per attempt.
1211
+ exhaustMap(({ clientId, viewId }) => this.httpClient.get(`${signalingUrl}${clientId}/${viewId}`)),
1212
+ // Bound the time spent waiting for the orchestration response.
1213
+ timeout$1(POLLING_TIME),
1214
+ // Validate response; report progress; fail fast on invalid payloads.
1215
+ filter((data) => {
1078
1216
  TelemetryStop('getSignaling', { ...data, multi: true });
1079
1217
  if (data.signallingServer === '' || data.error) {
1080
1218
  this.showStatusMessage(data?.mm_message || 'Server not found');
@@ -1083,27 +1221,44 @@ class SignallingService extends SubService {
1083
1221
  percent: data.info.percent,
1084
1222
  }));
1085
1223
  }
1086
- throw new Error('Invalid Value');
1224
+ // Throwing here fails the attempt and activates the retry strategy.
1225
+ throw new Error(`Orchestration message => ${data?.mm_message || 'Polling retry'}`);
1087
1226
  }
1088
- this.store.dispatch(setStatusPercentSignallingServer({ percent: 100 }));
1089
1227
  return true;
1090
- }), retryWhen((error) => error.pipe(tap(() => this.showStatusMessage(UnrealStatusMessage.CONNECTING_TO_SESSION)), tap(() => Logger.log('Retrying with new URL')), delay(4000), tap(() => {
1091
- // Unsubscribe the previous subscription and create a new one
1092
- subscription.unsubscribe();
1093
- createSubscription();
1094
- }))), takeUntil(merge(this.disconnect$, this.establishingConnectionDrop$)))
1095
- .subscribe((data) => {
1096
- subscription.unsubscribe();
1097
- const wsProtocol = location.protocol.match(/https/i) ? 'wss' : 'ws';
1098
- const wsUrl = `${wsProtocol}://${data.signallingServer}`;
1099
- this.store.dispatch(setAwsInstance({
1228
+ }),
1229
+ // Map a valid orchestration payload into the final AwsInstance.
1230
+ map$1((data) => {
1231
+ const wsUrl = httpUrlToWs(`${location.protocol}//${data.signallingServer}`);
1232
+ return {
1100
1233
  wsUrl,
1101
1234
  pollingUrl: signalingUrl,
1102
1235
  instanceName: data.signallingServer,
1103
- }));
1104
- });
1105
- };
1106
- createSubscription();
1236
+ };
1237
+ }));
1238
+ }).pipe(
1239
+ // Retry the whole attempt on error, advancing to the next URL each time.
1240
+ retry({
1241
+ delay: (err, attempt) => {
1242
+ this.showStatusMessage(UnrealStatusMessage.CONNECTING_TO_SESSION);
1243
+ Logger.log(`Retrying with new URL in ${POLLING_TIME}ms. Attempt# ${attempt}`, err?.message);
1244
+ return timer(POLLING_TIME);
1245
+ },
1246
+ // count: 10, // Optional: cap the number of attempts.
1247
+ }));
1248
+ }
1249
+ connectToCirrus(wsUrl) {
1250
+ this.clearWs();
1251
+ return createWebSocket(wsUrl).pipe(tapLog('WS CREATED'), tap((ws) => {
1252
+ TelemetryStart('iceCandidate');
1253
+ this.addWsHandlers(ws);
1254
+ }));
1255
+ }
1256
+ addWsHandlers(ws) {
1257
+ ws.onmessage = OnMessageHandler.bind(this);
1258
+ ws.onclose = OnCloseHandler.bind(this);
1259
+ ws.onerror = OnErrorHandler.bind(this);
1260
+ Logger.log('WS HANDLERS ADDED');
1261
+ OnOpenHandler.call(this);
1107
1262
  }
1108
1263
  showStatusMessage(message) {
1109
1264
  this.store.dispatch(setStatusMessage({ message }));
@@ -1113,10 +1268,7 @@ class SignallingService extends SubService {
1113
1268
  * @param data
1114
1269
  */
1115
1270
  close(data) {
1116
- const { code, reason } = data || {
1117
- code: WSCloseCode_NORMAL_CLOSURE,
1118
- reason: 'NoReason',
1119
- };
1271
+ const { code, reason } = data;
1120
1272
  if (this.ws)
1121
1273
  Logger.warn('Closing existing WebSocket connection');
1122
1274
  this.ws?.close(code, reason);
@@ -1142,47 +1294,6 @@ class SignallingService extends SubService {
1142
1294
  }
1143
1295
  }
1144
1296
  }
1145
- watchTimeoutOrErrorAndReconnectLater() {
1146
- this.stopRetryTimer();
1147
- this.wsTimeoutHandler = setTimeout(() => {
1148
- Logger.warn('No WS Response, retrying new signaling');
1149
- this.showStatusMessage(UnrealStatusMessage.CONNECTING_TO_SESSION);
1150
- this.stopEstablishingConnection();
1151
- this.store.dispatch(initSignalling());
1152
- }, WS_TIMEOUT);
1153
- }
1154
- stopRetryTimer() {
1155
- clearTimeout(this.wsTimeoutHandler);
1156
- }
1157
- initWebSocket(url) {
1158
- if (!window?.['WebSocket']) {
1159
- alert("Your browser doesn't support WebSocket");
1160
- return;
1161
- }
1162
- this.clearWs();
1163
- const wsUrl = this.httpUrlToWs(url);
1164
- Logger.info('Creating socket', wsUrl);
1165
- try {
1166
- this.ws = new WebSocket(wsUrl);
1167
- }
1168
- catch (error) {
1169
- this.ws = {};
1170
- this.store.dispatch(setErrorMessage({
1171
- errorType: 'WebSocketError',
1172
- message: 'WebSocket Error',
1173
- }));
1174
- Logger.error('Error creating WebSocket', error);
1175
- }
1176
- this.ws.onmessage = OnMessageHandler.bind(this);
1177
- this.ws.onerror = OnErrorHandler.bind(this);
1178
- this.ws.onclose = OnCloseHandler.bind(this);
1179
- this.ws.onopen = OnOpenHandler.bind(this);
1180
- this.watchTimeoutOrErrorAndReconnectLater();
1181
- TelemetryStart('iceCandidate');
1182
- }
1183
- httpUrlToWs(url) {
1184
- return url.replace('http://', 'ws://').replace('https://', 'wss://');
1185
- }
1186
1297
  clearWs() {
1187
1298
  this.onWebRtcAnswer$.next(null);
1188
1299
  if (this.ws) {
@@ -1214,10 +1325,14 @@ class SignallingService extends SubService {
1214
1325
  this.wsMsgHandlers.instanceReserved = InstanceReservedHandler.bind(this);
1215
1326
  this.wsMsgHandlers.offer = OfferHandler.bind(this);
1216
1327
  }
1217
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: SignallingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1218
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: SignallingService }); }
1328
+ getRegion() {
1329
+ Logger.log('Start Getting Region');
1330
+ return this.regionsPingService.getFastest().pipe(first());
1331
+ }
1332
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: SignallingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1333
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: SignallingService }); }
1219
1334
  }
1220
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: SignallingService, decorators: [{
1335
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: SignallingService, decorators: [{
1221
1336
  type: Injectable
1222
1337
  }], ctorParameters: () => [] });
1223
1338
 
@@ -1270,17 +1385,13 @@ class WebRtcPlayerService extends SubService {
1270
1385
  Logger.info('Creating offer');
1271
1386
  let offer;
1272
1387
  try {
1273
- clearTimeout(this.webRtcErrorTimeout);
1274
1388
  offer = await this.createPeerConnection(this.cfg);
1275
- this.webRtcErrorTimeout = setTimeout(() => {
1276
- if (this.pcClient?.connectionState?.match(/connecting|failed/gi)) {
1277
- this.dispatchWebRtcError('WebRTCError: Timeout');
1278
- }
1279
- }, 10000);
1280
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1281
1389
  }
1282
- catch (error) {
1283
- this.dispatchWebRtcError('WebRTCError: Offer Error');
1390
+ catch {
1391
+ this.store.dispatch(disconnectStream({
1392
+ reason: DisconnectReason.WebRTCError,
1393
+ message: 'WebRTCError: Offer Error',
1394
+ }));
1284
1395
  }
1285
1396
  return offer;
1286
1397
  }
@@ -1338,6 +1449,8 @@ class WebRtcPlayerService extends SubService {
1338
1449
  Logger.warn('Closing existing PeerConnection');
1339
1450
  this.pcClient?.close();
1340
1451
  this.pcClient = null;
1452
+ if (this.dcClient)
1453
+ Logger.warn('Closing existing DataChannel');
1341
1454
  this.dcClient?.close();
1342
1455
  this.dcClient = null;
1343
1456
  }
@@ -1380,13 +1493,6 @@ class WebRtcPlayerService extends SubService {
1380
1493
  Logger.error("For testing you can enable HTTP microphone access Chrome by visiting chrome://flags/ and enabling 'unsafely-treat-insecure-origin-as-secure'");
1381
1494
  }
1382
1495
  }
1383
- dispatchWebRtcError(message) {
1384
- Logger.error(message);
1385
- this.store.dispatch(setErrorMessage({
1386
- errorType: 'WebRTCError',
1387
- message,
1388
- }));
1389
- }
1390
1496
  async createPeerConnection(config) {
1391
1497
  this.closePC();
1392
1498
  this.pcClient = new RTCPeerConnection(config);
@@ -1515,8 +1621,9 @@ class WebRtcPlayerService extends SubService {
1515
1621
  .pipe(first())
1516
1622
  .subscribe((e) => {
1517
1623
  Logger.warn(`[DATACHANNEL] Data channel disconnected: ${datachannel.label}(${datachannel.id})`, e);
1518
- this.store.dispatch(destroyRemoteConnections({
1519
- disconnectReason: DisconnectReason.dataChannelClosed,
1624
+ this.store.dispatch(disconnectStream({
1625
+ reason: DisconnectReason.DataChannelClosed,
1626
+ message: 'DataChannelClosed',
1520
1627
  }));
1521
1628
  });
1522
1629
  fromEvent(datachannel, 'error')
@@ -1549,8 +1656,7 @@ class WebRtcPlayerService extends SubService {
1549
1656
  text +=
1550
1657
  '\n If you are experiencing connection problems please try Google Chrome';
1551
1658
  }
1552
- this.store.dispatch(setStatusMessage({ message: text }));
1553
- this.store.dispatch(setDataChannelConnected({ value: true }));
1659
+ this.store.dispatch(setDataChannelConnected({ message: text }));
1554
1660
  }
1555
1661
  async createOffer(pcClient) {
1556
1662
  try {
@@ -1566,10 +1672,10 @@ class WebRtcPlayerService extends SubService {
1566
1672
  Logger.error("Couldn't create offer");
1567
1673
  }
1568
1674
  }
1569
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: WebRtcPlayerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1570
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: WebRtcPlayerService }); }
1675
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: WebRtcPlayerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1676
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: WebRtcPlayerService }); }
1571
1677
  }
1572
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: WebRtcPlayerService, decorators: [{
1678
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: WebRtcPlayerService, decorators: [{
1573
1679
  type: Injectable
1574
1680
  }], ctorParameters: () => [] });
1575
1681
 
@@ -1684,7 +1790,7 @@ class AggregatorService extends SubService {
1684
1790
  ?.remove();
1685
1791
  }
1686
1792
  startListenCallbacks() {
1687
- Logger.info('Start Listen Callbacks');
1793
+ Logger.info('Start Listen Unreal Callbacks');
1688
1794
  let previousJson = null;
1689
1795
  this.addResponseEventListener('unrealEvents', (data) => {
1690
1796
  try {
@@ -1788,10 +1894,10 @@ class AggregatorService extends SubService {
1788
1894
  Logger.warn(`unrecognized data received, packet ID ${view[0]}`);
1789
1895
  }
1790
1896
  }
1791
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AggregatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1792
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AggregatorService }); }
1897
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: AggregatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1898
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: AggregatorService }); }
1793
1899
  }
1794
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AggregatorService, decorators: [{
1900
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: AggregatorService, decorators: [{
1795
1901
  type: Injectable
1796
1902
  }], ctorParameters: () => [] });
1797
1903
 
@@ -1806,7 +1912,10 @@ class ConsoleExtensionsService extends SubService {
1806
1912
  return;
1807
1913
  }
1808
1914
  window.dropConnection = () => {
1809
- this.store.dispatch(destroyConnectionsAndResetState());
1915
+ this.store.dispatch(disconnectStream({
1916
+ reason: DisconnectReason.DropConnection,
1917
+ message: 'Manual Console Drop',
1918
+ }));
1810
1919
  return true;
1811
1920
  };
1812
1921
  window.toggleBandwidth = () => {
@@ -1840,10 +1949,10 @@ class ConsoleExtensionsService extends SubService {
1840
1949
  Logger.info('setWarnTime() => set time to appear the AFK window.');
1841
1950
  Logger.info('emitCommand(command) => send command to Unreal Engine.');
1842
1951
  }
1843
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ConsoleExtensionsService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1844
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ConsoleExtensionsService }); }
1952
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: ConsoleExtensionsService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1953
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: ConsoleExtensionsService }); }
1845
1954
  }
1846
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ConsoleExtensionsService, decorators: [{
1955
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: ConsoleExtensionsService, decorators: [{
1847
1956
  type: Injectable
1848
1957
  }] });
1849
1958
 
@@ -1854,10 +1963,10 @@ class DevModeService {
1854
1963
  setDevMode(value) {
1855
1964
  return localStorage.setItem('devMode', value.toString());
1856
1965
  }
1857
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: DevModeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1858
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: DevModeService, providedIn: 'root' }); }
1966
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: DevModeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1967
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: DevModeService, providedIn: 'root' }); }
1859
1968
  }
1860
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: DevModeService, decorators: [{
1969
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: DevModeService, decorators: [{
1861
1970
  type: Injectable,
1862
1971
  args: [{
1863
1972
  providedIn: 'root',
@@ -2132,10 +2241,10 @@ class UnrealCommunicatorService {
2132
2241
  callback,
2133
2242
  });
2134
2243
  }
2135
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealCommunicatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2136
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealCommunicatorService }); }
2244
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealCommunicatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2245
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealCommunicatorService }); }
2137
2246
  }
2138
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealCommunicatorService, decorators: [{
2247
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealCommunicatorService, decorators: [{
2139
2248
  type: Injectable
2140
2249
  }], ctorParameters: () => [] });
2141
2250
 
@@ -2634,7 +2743,7 @@ class RegionsPingService {
2634
2743
  }
2635
2744
  }
2636
2745
  getFastest(regionListUrl = this.unrealInitialConfig?.regionsPingUrl || '') {
2637
- return this.getProviders(regionListUrl).pipe(switchMap$1((providers) => this.getPingResult(providers)), catchError(() => of(null)), map$1((data) => data?.region_code));
2746
+ return this.getProviders(regionListUrl).pipe(switchMap$1((providers) => this.getPingResult(providers)), catchError(() => of(null)), tapLog('PingResult'), map$1((data) => data?.region_code));
2638
2747
  }
2639
2748
  getPingResult(providers) {
2640
2749
  const regions = providers.regions;
@@ -2691,6 +2800,9 @@ class RegionsPingService {
2691
2800
  credentials: 'omit', // Optional: prevents sending or receiving cookies, which can affect caching.
2692
2801
  }, timeout);
2693
2802
  }
2803
+ catch {
2804
+ //Do not remove this block, else finally will not work!
2805
+ }
2694
2806
  finally {
2695
2807
  const latency = Math.round(performance.now() - startTime);
2696
2808
  pingResults.push(latency);
@@ -2712,10 +2824,10 @@ class RegionsPingService {
2712
2824
  throw error;
2713
2825
  }
2714
2826
  }
2715
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: RegionsPingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2716
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: RegionsPingService }); }
2827
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: RegionsPingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2828
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: RegionsPingService }); }
2717
2829
  }
2718
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: RegionsPingService, decorators: [{
2830
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: RegionsPingService, decorators: [{
2719
2831
  type: Injectable
2720
2832
  }], ctorParameters: () => [] });
2721
2833
 
@@ -2755,10 +2867,10 @@ class StreamStatusTelemetryService {
2755
2867
  init() {
2756
2868
  // do nothing, just to not forget to initialize Service
2757
2869
  }
2758
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: StreamStatusTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2759
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: StreamStatusTelemetryService }); }
2870
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: StreamStatusTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2871
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: StreamStatusTelemetryService }); }
2760
2872
  }
2761
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: StreamStatusTelemetryService, decorators: [{
2873
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: StreamStatusTelemetryService, decorators: [{
2762
2874
  type: Injectable
2763
2875
  }], ctorParameters: () => [] });
2764
2876
 
@@ -2792,7 +2904,6 @@ const initialState = {
2792
2904
  viewportReady: false,
2793
2905
  dataChannelConnected: false,
2794
2906
  isVideoPlaying: false,
2795
- isFreezeLoaderPercents: false,
2796
2907
  statusPercentSignallingServer: null,
2797
2908
  statusMessage: null,
2798
2909
  errorMessage: null,
@@ -2801,7 +2912,7 @@ const initialState = {
2801
2912
  streamResolution: { width: null, height: null },
2802
2913
  freezeFrameFromVideo: { dataUrl: null, progress: null },
2803
2914
  freezeFrame: { dataUrl: null, progress: null },
2804
- disconnectReason: DisconnectReason.none,
2915
+ disconnectReason: DisconnectReason.None,
2805
2916
  awsInstance: {
2806
2917
  wsUrl: null,
2807
2918
  instanceName: null,
@@ -2811,7 +2922,6 @@ const initialState = {
2811
2922
  autoStart: true,
2812
2923
  warnTimeout: DEFAULT_AFK_TIMEOUT,
2813
2924
  },
2814
- matchMakerUrls: [],
2815
2925
  loaderCommands: {
2816
2926
  commandsInProgress: [],
2817
2927
  totalCommandsStarted: 0,
@@ -2844,12 +2954,17 @@ const unrealReducer = createReducer(initialState, on(changeLowBandwidth, (state,
2844
2954
  pollingUrl,
2845
2955
  },
2846
2956
  };
2847
- }), on(setViewportReady, (state, { value }) => {
2957
+ }), on(setViewportReady, (state) => {
2848
2958
  return {
2849
2959
  ...state,
2850
- viewportReady: value,
2851
- statusMessage: value ? null : state.statusMessage,
2852
- errorMessage: value ? null : state.errorMessage,
2960
+ viewportReady: true,
2961
+ statusMessage: null,
2962
+ errorMessage: null,
2963
+ };
2964
+ }), on(setViewportNotReady, (state) => {
2965
+ return {
2966
+ ...state,
2967
+ viewportReady: false,
2853
2968
  };
2854
2969
  }), on(updateCirrusInfo, (state, { ssInfo, ssData }) => {
2855
2970
  return {
@@ -2877,14 +2992,15 @@ const unrealReducer = createReducer(initialState, on(changeLowBandwidth, (state,
2877
2992
  progress: freezeFrame.progress || null,
2878
2993
  },
2879
2994
  };
2880
- }), on(setErrorMessage, (state, errorMessage) => {
2995
+ }), on(disconnectStream, (state, errorMessage) => {
2881
2996
  if (state.dataChannelConnected) {
2882
2997
  return state;
2883
2998
  }
2884
2999
  return {
2885
3000
  ...state,
2886
- errorMessage: errorMessage,
3001
+ errorMessage,
2887
3002
  statusMessage: null,
3003
+ wasInitialized: true,
2888
3004
  };
2889
3005
  }), on(setFreezeFrameFromVideo, (state, freezeFrameFromVideo) => {
2890
3006
  return {
@@ -2894,7 +3010,7 @@ const unrealReducer = createReducer(initialState, on(changeLowBandwidth, (state,
2894
3010
  progress: freezeFrameFromVideo.progress || null,
2895
3011
  },
2896
3012
  };
2897
- }), on(setStatusMessage, (state, { message }) => {
3013
+ }), on(setStatusMessage, setDataChannelConnected, (state, { message }) => {
2898
3014
  return {
2899
3015
  ...state,
2900
3016
  statusMessage: message,
@@ -2904,32 +3020,23 @@ const unrealReducer = createReducer(initialState, on(changeLowBandwidth, (state,
2904
3020
  ...state,
2905
3021
  establishingConnection: value,
2906
3022
  };
2907
- }), on(setIsFreezeLoaderPercents, (state) => {
2908
- return {
2909
- ...state,
2910
- isFreezeLoaderPercents: true,
2911
- };
2912
3023
  }), on(setStatusPercentSignallingServer, (state, { percent }) => {
2913
3024
  return {
2914
3025
  ...state,
2915
3026
  statusPercentSignallingServer: percent,
2916
3027
  };
2917
- }), on(setDataChannelConnected, (state, { value }) => {
3028
+ }), on(setDataChannelConnected, (state) => {
2918
3029
  return {
2919
3030
  ...state,
2920
- dataChannelConnected: value,
2921
- wasInitialized: value ? true : state.wasInitialized,
3031
+ dataChannelConnected: true,
3032
+ establishingConnection: false,
3033
+ wasInitialized: true,
2922
3034
  };
2923
- }), on(setConfig, (state, { config }) => {
3035
+ }), on(setConfig, startStream, (state, { config }) => {
2924
3036
  return {
2925
3037
  ...state,
2926
3038
  streamConfig: { ...state.streamConfig, ...config },
2927
3039
  };
2928
- }), on(setMatchMakerUrls, (state, { matchMakerUrls }) => {
2929
- return {
2930
- ...state,
2931
- matchMakerUrls,
2932
- };
2933
3040
  }), on(resetConfig, (state) => {
2934
3041
  return {
2935
3042
  ...state,
@@ -2943,20 +3050,17 @@ const unrealReducer = createReducer(initialState, on(changeLowBandwidth, (state,
2943
3050
  warnTimeout: DEFAULT_AFK_TIMEOUT,
2944
3051
  },
2945
3052
  };
2946
- }), on(setCirrusConnected, (state) => {
3053
+ }), on(initSignalling, (state, { resetDisconnectionReason = true }) => {
2947
3054
  return {
2948
3055
  ...state,
2949
- cirrusConnected: true,
3056
+ disconnectReason: resetDisconnectionReason
3057
+ ? DisconnectReason.None
3058
+ : state.disconnectReason,
2950
3059
  };
2951
- }), on(setCirrusDisconnected, (state) => {
3060
+ }), on(destroyRemoteConnections, (state, { reason }) => {
2952
3061
  return {
2953
3062
  ...state,
2954
- cirrusConnected: false,
2955
- };
2956
- }), on(initSignalling, (state) => {
2957
- return {
2958
- ...state,
2959
- disconnectReason: DisconnectReason.none,
3063
+ disconnectReason: reason,
2960
3064
  };
2961
3065
  }), on(setSignalingName, (state, { instanceName }) => {
2962
3066
  return {
@@ -3021,9 +3125,16 @@ const unrealReducer = createReducer(initialState, on(changeLowBandwidth, (state,
3021
3125
  ...state,
3022
3126
  loaderCommands: removeExileCommands(state.loaderCommands, id),
3023
3127
  };
3024
- }), on(resetUnrealState, (state) => {
3128
+ }), on(setCirrusConnected, (state) => {
3129
+ return {
3130
+ ...state,
3131
+ cirrusConnected: true,
3132
+ };
3133
+ }), on(setCirrusDisconnected, (state) => {
3025
3134
  return {
3026
3135
  ...initialState,
3136
+ establishingConnection: state.establishingConnection,
3137
+ disconnectReason: state.disconnectReason,
3027
3138
  wasInitialized: state.wasInitialized,
3028
3139
  isFirstSuccessLoad: state.isFirstSuccessLoad,
3029
3140
  matchUrls: state.matchUrls,
@@ -3033,7 +3144,12 @@ const unrealReducer = createReducer(initialState, on(changeLowBandwidth, (state,
3033
3144
  videoIntroSrc: state.videoIntroSrc,
3034
3145
  imageLoadingSrc: state.imageLoadingSrc,
3035
3146
  };
3036
- }), on(resetUnrealStateAction, () => {
3147
+ }), on(dropConnection, (state) => {
3148
+ return {
3149
+ ...state,
3150
+ establishingConnection: false,
3151
+ };
3152
+ }), on(destroyUnrealScene, () => {
3037
3153
  return initialState;
3038
3154
  }));
3039
3155
 
@@ -3050,10 +3166,10 @@ class UnrealErrorModalComponent {
3050
3166
  close() {
3051
3167
  this.dialogRef.close();
3052
3168
  }
3053
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3054
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.1", type: UnrealErrorModalComponent, isStandalone: true, selector: "app-unreal-error-modal", ngImport: i0, template: "<div class=\"src-modal src-modal--small\">\n <div class=\"src-modal__header\">\n <div\n class=\"src-modal__title\"\n [attr.data-testid]=\"'unreal-error-header-title'\"\n >\n Warning\n </div>\n </div>\n <div class=\"src-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"src-modal__footer\">\n <src-button [colorScheme]=\"'secondary'\" (onClick)=\"close()\">\n Ok\n </src-button>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "colorScheme", "size", "state", "customClass", "hasDisclosure", "isFullWidth", "isPressed", "isDisabled", "isLoading", "iconButton", "srcButtonConfig", "formID", "data-testid"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3169
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3170
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.2", type: UnrealErrorModalComponent, isStandalone: true, selector: "app-unreal-error-modal", ngImport: i0, template: "<div class=\"src-modal src-modal--small\">\n <div class=\"src-modal__header\">\n <div\n class=\"src-modal__title\"\n [attr.data-testid]=\"'unreal-error-header-title'\"\n >\n Warning\n </div>\n </div>\n <div class=\"src-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"src-modal__footer\">\n <src-button [colorScheme]=\"'secondary'\" (onClick)=\"close()\">\n Ok\n </src-button>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "colorScheme", "size", "state", "customClass", "hasDisclosure", "isFullWidth", "isPressed", "isDisabled", "isLoading", "iconButton", "srcButtonConfig", "formID", "data-testid"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3055
3171
  }
3056
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealErrorModalComponent, decorators: [{
3172
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealErrorModalComponent, decorators: [{
3057
3173
  type: Component,
3058
3174
  args: [{ selector: 'app-unreal-error-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceButtonComponent], template: "<div class=\"src-modal src-modal--small\">\n <div class=\"src-modal__header\">\n <div\n class=\"src-modal__title\"\n [attr.data-testid]=\"'unreal-error-header-title'\"\n >\n Warning\n </div>\n </div>\n <div class=\"src-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"src-modal__footer\">\n <src-button [colorScheme]=\"'secondary'\" (onClick)=\"close()\">\n Ok\n </src-button>\n </div>\n</div>\n" }]
3059
3175
  }] });
@@ -3073,23 +3189,38 @@ class UnrealEffects {
3073
3189
  this.videoService = inject(VideoService);
3074
3190
  this.dataChannelConnectionTimeout = this.unrealInitialConfig?.dataChannelConnectionTimeout ??
3075
3191
  DATA_CHANNEL_CONNECTION_TIMEOUT;
3076
- this.cirrusDisconnectEffect$ = createEffect(() => {
3077
- return this.actions$.pipe(ofType(setCirrusDisconnected), map(() => resetUnrealState()));
3192
+ this.disconnectReasonHandling$ = createEffect(() => {
3193
+ const destroyReasons = [
3194
+ DisconnectReason.Afk,
3195
+ DisconnectReason.DataChannelClosed,
3196
+ DisconnectReason.DataChannelTimeout,
3197
+ DisconnectReason.WebSocketError,
3198
+ DisconnectReason.WebSocketClose,
3199
+ DisconnectReason.WebRTCError,
3200
+ DisconnectReason.DropConnection,
3201
+ ];
3202
+ return this.actions$.pipe(ofType(disconnectStream), filter(({ reason }) => destroyReasons.some((r) => r === reason)), tap(({ reason, message }) => {
3203
+ Logger.log(`Disconnect Call=> reason:'${reason}', message:'${message}'`);
3204
+ }), map(({ reason }) => destroyRemoteConnections({ reason })));
3078
3205
  });
3079
3206
  this.destroyConnections$ = createEffect(() => {
3080
- return this.actions$.pipe(ofType(destroyConnectionsAndResetState), map(() => destroyRemoteConnections({ disconnectReason: DisconnectReason.reset })));
3081
- });
3082
- this.resetState$ = createEffect(() => {
3083
- return this.actions$.pipe(ofType(destroyConnectionsAndResetState), map(() => resetUnrealState()));
3207
+ return this.actions$.pipe(ofType(destroyUnrealScene), map(() => destroyRemoteConnections({
3208
+ reason: DisconnectReason.Destroy,
3209
+ })));
3084
3210
  });
3085
3211
  this.destroyRemoteConnections$ = createEffect(() => {
3086
- return this.actions$.pipe(ofType(destroyRemoteConnections), tap(({ disconnectReason }) => {
3212
+ return this.actions$.pipe(ofType(destroyRemoteConnections), tap(({ reason }) => {
3087
3213
  this.signallingService.close({
3088
- code: WSCloseCode_NORMAL_CIRRUS_CLOSED,
3089
- reason: disconnectReason,
3214
+ code: WSCloseCode_FORCE_CIRRUS_CLOSE,
3215
+ reason,
3090
3216
  });
3091
3217
  this.player.closePC();
3092
- }));
3218
+ }), filter(({ reason }) => reason === DisconnectReason.DropConnection), map(() => dropConnection()));
3219
+ });
3220
+ this.webrtcErrorModalComponent$ = createEffect(() => {
3221
+ return this.actions$.pipe(ofType(disconnectStream), filter(({ reason }) => reason === DisconnectReason.WebRTCError), debounceTime$1(400), switchMap$1(() => this.store
3222
+ .select(unrealFeature.selectDataChannelConnected)
3223
+ .pipe(take(1), filter(Falsy))), tap(() => this.dialog.open(WebrtcErrorModalComponent)));
3093
3224
  }, { dispatch: false });
3094
3225
  this.changeStreamResolution$ = createEffect(() => {
3095
3226
  return this.actions$.pipe(ofType(changeStreamResolutionAction), filter((size) => !!(size?.width && size?.height)), withLatestFrom(this.store.select(unrealFeature.selectStreamResolution)), filter(([newSizes, savedSizes]) => !(newSizes.width === savedSizes.width &&
@@ -3099,7 +3230,7 @@ class UnrealEffects {
3099
3230
  })));
3100
3231
  });
3101
3232
  this.forceLBMOff$ = createEffect(() => {
3102
- return fromEvent(document, 'visibilitychange').pipe(map(() => document.visibilityState === 'visible'), filter(Truthy), withLatestFrom(this.store.select(unrealFeature.selectLowBandwidth)), filter(([, isLowBandwidth]) => !!isLowBandwidth), tapLog('forceLBMOff$'), map(() => changeLowBandwidth({ lowBandwidth: false })));
3233
+ return fromEvent(document, 'visibilitychange').pipe(map(() => document.visibilityState === 'visible'), filter(Truthy), withLatestFrom(this.store.select(unrealFeature.selectLowBandwidth)), filter(([, isLowBandwidth]) => isLowBandwidth), tapLog('forceLBMOff$'), map(() => changeLowBandwidth({ lowBandwidth: false })));
3103
3234
  });
3104
3235
  this.resetFreezeFrameOnLowBandwidthTriggered$ = createEffect(() => {
3105
3236
  return this.actions$.pipe(ofType(changeLowBandwidth), map(({ lowBandwidth }) => lowBandwidth), distinctUntilChanged(), map(() => setFreezeFrame({ dataUrl: null, progress: null })));
@@ -3122,45 +3253,67 @@ class UnrealEffects {
3122
3253
  this.resetAfk$ = createEffect(() => {
3123
3254
  return this.actions$.pipe(ofType(setConfig, resetWarnTimeout, resetConfig), map(() => resetAfkAction()));
3124
3255
  });
3125
- this.resetFreezeFrameOnViewportDestroy$ = createEffect(() => {
3126
- return this.actions$.pipe(ofType(setViewportReady), filter(Falsy), map(() => setFreezeFrame({ dataUrl: null, progress: null })));
3256
+ this.abortEstablishingConnection$ = createEffect(() => {
3257
+ const viewportReady$ = this.store
3258
+ .select(unrealFeature.selectViewportReady)
3259
+ .pipe(filter(Truthy), map(() => abortEstablishingConnection()));
3260
+ const disconnectEvents$ = this.actions$.pipe(ofType(setCirrusDisconnected, destroyUnrealScene, dropConnection), map(() => abortEstablishingConnection()));
3261
+ return merge(viewportReady$, disconnectEvents$).pipe(tapLog('ABORT'));
3127
3262
  });
3128
3263
  this.setMaxFps$ = createEffect(() => {
3129
3264
  return this.actions$.pipe(ofType(setMaxFps), tap(({ maxFps }) => {
3130
3265
  this.commandsSender.sendCommandToUnreal(getSetFpsCommand(maxFps));
3131
3266
  }));
3132
3267
  }, { dispatch: false });
3133
- this.webrtcErrorModalComponent$ = createEffect(() => {
3134
- return this.actions$.pipe(ofType(setErrorMessage), debounceTime$1(400), combineLatestWith(this.store.select(unrealFeature.selectDataChannelConnected)), filter(([error, isConnected]) => {
3135
- return !isConnected && error?.errorType === 'WebRTCError';
3136
- }), tap(() => this.dialog.open(WebrtcErrorModalComponent)));
3137
- }, { dispatch: false });
3138
3268
  this.resetAfkAction$ = createEffect(() => {
3139
3269
  return this.actions$.pipe(ofType(resetAfkAction), tap(() => resetAfk()));
3140
3270
  }, { dispatch: false });
3141
- this.setDataChannelTimeoutCheck$ = createEffect(() => {
3142
- return this.actions$.pipe(ofType(setAwsInstance), filter(({ wsUrl }) => !!wsUrl), tap(() => Logger.info(`DataChannel timeout check started with next timeout: ${this.dataChannelConnectionTimeout}`)), debounceTime$1(this.dataChannelConnectionTimeout), // Timeout, if not connected, sends an error report, then reconnects
3143
- withLatestFrom(this.store.select(unrealFeature.selectSsData), this.store.select(unrealFeature.selectCirrusConnected), this.store.select(unrealFeature.selectDataChannelConnected)), filter(([, , isCirrusConnected, isDataChannelConnected]) => isCirrusConnected && !isDataChannelConnected), switchMap$1(([awsInstance, ssData]) => {
3144
- const error = `DataChannel connection timeout ${this.dataChannelConnectionTimeout}ms, requesting new signaling.`;
3145
- Logger.error(error);
3146
- return this.http
3147
- .post(awsInstance.pollingUrl || '', {
3148
- awsInstance,
3149
- connectionId: ssData?.connectionId,
3150
- href: location.href,
3151
- userAgent: navigator.userAgent,
3152
- error,
3153
- })
3154
- .pipe(catchError((error) => {
3155
- Logger.error('Error sending timeout info:', error);
3156
- return of(null);
3271
+ this.setSignalingTo100$ = createEffect(() => this.actions$.pipe(ofType(setAwsInstance), map(() => setStatusPercentSignallingServer({ percent: 100 }))));
3272
+ this.setDataChannelTimeoutCheck$ = createEffect(() => this.actions$.pipe(ofType(setAwsInstance),
3273
+ // require wsUrl present on the action payload
3274
+ filter((action) => !!action.wsUrl),
3275
+ // for each awsInstance action capture one ssData snapshot, then wait for dataChannelConnected
3276
+ switchMap$1((awsInstance) => {
3277
+ return this.store.select(unrealFeature.selectSsData).pipe(filter(Truthy), take(1), tap(() => Logger.log(`DataChannel timeout check started with next timeout: ${this.dataChannelConnectionTimeout}ms`)), switchMap$1((ssData) => {
3278
+ const abort$ = this.actions$.pipe(ofType(setCirrusDisconnected), take(1));
3279
+ return this.store
3280
+ .select(unrealFeature.selectDataChannelConnected)
3281
+ .pipe(
3282
+ // wait until data channel reports connected (truthy)
3283
+ filter(Truthy), take(1),
3284
+ // 🚫 If Cirrus (or anything else) disconnects first, abort BEFORE timeout fires.
3285
+ takeUntil(abort$),
3286
+ // if it becomes true within timeout -> complete without dispatching any action
3287
+ timeout$1(this.dataChannelConnectionTimeout),
3288
+ // successful connect → no action
3289
+ mergeMap(() => EMPTY),
3290
+ // ⏱️ Only actual timeout (or real upstream error) ends up here.
3291
+ catchError(() => {
3292
+ const error = `⏱️ DataChannel connection timeout ${this.dataChannelConnectionTimeout}ms, requesting new signaling.`;
3293
+ Logger.error(error);
3294
+ const body = {
3295
+ awsInstance,
3296
+ connectionId: ssData?.connectionId,
3297
+ href: location.href,
3298
+ userAgent: navigator.userAgent,
3299
+ error,
3300
+ };
3301
+ return this.http
3302
+ .post(awsInstance.pollingUrl || '', body)
3303
+ .pipe(catchError((postError) => {
3304
+ Logger.error('Error sending timeout info:', postError);
3305
+ return of(null);
3306
+ }),
3307
+ // map response to the destroy action which ngrx will dispatch
3308
+ map(() => disconnectStream({
3309
+ reason: DisconnectReason.DataChannelTimeout,
3310
+ message: 'Data Channel Timeout',
3311
+ })));
3157
3312
  }));
3158
- }), map(() => destroyRemoteConnections({
3159
- disconnectReason: DisconnectReason.dataChannelTimeout,
3160
- })));
3161
- });
3313
+ }));
3314
+ })));
3162
3315
  this.destroyConnectionAndRestart$ = createEffect(() => {
3163
- return this.actions$.pipe(ofType(destroyRemoteConnections), filter(({ disconnectReason }) => disconnectReason === DisconnectReason.dataChannelTimeout), tap(() => this.store.dispatch(setIsFreezeLoaderPercents())), switchMap$1(() => this.actions$.pipe(ofType(resetUnrealState), first())), map(() => initSignalling()));
3316
+ return this.actions$.pipe(ofType(destroyRemoteConnections), filter(({ reason }) => reason === DisconnectReason.DataChannelTimeout), switchMap$1(() => this.actions$.pipe(ofType(setCirrusDisconnected), first())), map(() => initSignalling()));
3164
3317
  });
3165
3318
  this.showUnrealError$ = createEffect(() => {
3166
3319
  return this.actions$.pipe(ofType(showUnrealErrorMessage), map(({ code }) => getRtcErrorMessage(code)), distinctUntilChanged(), filter(Truthy), switchMap$1((content) => this.dialog.open(UnrealErrorModalComponent, {
@@ -3198,27 +3351,48 @@ class UnrealEffects {
3198
3351
  (videoStats.aggregatedStats.framesDecoded || 0) > 15), first(), map(() => setLoopBackCommandIsCompleted()))));
3199
3352
  });
3200
3353
  this.setViewportReadyBySetLoopBackCommandIsCompleted$ = createEffect(() => {
3201
- return this.actions$.pipe(ofType(setLoopBackCommandIsCompleted), tap(() => TelemetryStop('SetViewportReady', { ready: true })), map(() => setViewportReady({ value: true })));
3354
+ return this.actions$.pipe(ofType(setLoopBackCommandIsCompleted), tap(() => TelemetryStop('SetViewportReady', { ready: true })), map(() => setViewportReady()));
3355
+ });
3356
+ this.startStream$ = createEffect(() => {
3357
+ return this.actions$.pipe(ofType(startStream), tapLog('Start Stream Pressed'), map(() => initSignalling()));
3202
3358
  });
3203
3359
  this.listenUnrealCallbackByInitSignalling$ = createEffect(() => {
3204
3360
  return this.actions$.pipe(ofType(initSignalling), switchMap$1(() => fromSignal(UnrealInternalSignalEvents.UnrealCallback).pipe(map(({ json }) => json?.commandCallback
3205
3361
  ?.correlationId), filter(Truthy), map((id) => commandCompleted({ id })))));
3206
3362
  });
3207
- this.setConfigByInitSignalling$ = createEffect(() => {
3208
- return this.actions$.pipe(ofType(initSignalling), withLatestFrom(this.store.select(unrealFeature.selectMatchUrls)), switchMap$1(() => this.store
3209
- .select(unrealFeature.selectMatchUrls)
3210
- .pipe(filter((item) => item?.length > 0))), map((matchMakerUrls) => {
3211
- if (isEmpty(matchMakerUrls)) {
3212
- throw Error('Signalling URL(s) is empty');
3363
+ this.connectToSignaling$ = createEffect(() => {
3364
+ return this.actions$.pipe(ofType(initSignalling), withLatestFrom(this.store.select(unrealFeature.selectMatchUrls), this.store.select(selectIsAutostart), this.store.select(unrealFeature.selectCirrusConnected)), filter(([, , autoStart, cirrusConnected]) => {
3365
+ if (cirrusConnected) {
3366
+ Logger.warn(`SIGNALING REQUEST can't be called when cirrus connection is already established.`);
3213
3367
  }
3214
- return setMatchMakerUrls({ matchMakerUrls });
3215
- }));
3368
+ return autoStart && !cirrusConnected;
3369
+ }), map(([, matchMakerUrls]) => [...matchMakerUrls].filter(Truthy)), filter((urls) => urls.length > 0),
3370
+ // Ensure a single in-flight request per attempt.
3371
+ exhaustMap((urls) => this.signallingService.connectToSignaling(urls)));
3372
+ }, { dispatch: false });
3373
+ /**
3374
+ * Effect: `forceViewportNotReady$`
3375
+ *
3376
+ * Ensures the viewport is explicitly marked as *not ready* to prevent
3377
+ * a temporary black screen flash when disconnecting.
3378
+ *
3379
+ * ### Why
3380
+ * - The video stream is disconnected immediately.
3381
+ * - The DataChannel closes slightly later.
3382
+ * - Closing the DataChannel resets the Unreal scene,
3383
+ * which also sets `viewPortReady` to `false`.
3384
+ *
3385
+ * Without this effect, the order of these events may cause
3386
+ * a visible flicker (video flashing to black).
3387
+ */
3388
+ this.forceViewportNotReady$ = createEffect(() => {
3389
+ return this.actions$.pipe(ofType(destroyRemoteConnections), map(() => setViewportNotReady()));
3216
3390
  });
3217
3391
  }
3218
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealEffects, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3219
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealEffects }); }
3392
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealEffects, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3393
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealEffects }); }
3220
3394
  }
3221
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealEffects, decorators: [{
3395
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealEffects, decorators: [{
3222
3396
  type: Injectable
3223
3397
  }] });
3224
3398
 
@@ -3231,16 +3405,15 @@ const selectIsFreezeFrameLoading = createSelector(unrealFeature.selectFreezeFram
3231
3405
  const selectFreezeFrameCombinedDataUrl = createSelector(selectFreezeFrameDataUrlFromVideo, selectFreezeFrameDataUrl, (dataUrlFromVideo, remoteDataUrl) => dataUrlFromVideo || remoteDataUrl || null);
3232
3406
  const selectStreamConfig = createSelector(unrealFeature.selectStreamConfig, (config) => config);
3233
3407
  const selectWarnTimeout = createSelector(selectStreamConfig, (config) => config?.warnTimeout || DEFAULT_AFK_TIMEOUT);
3234
- const selectMatchMakerUrls = createSelector(unrealFeature.selectMatchMakerUrls, (urls) => (urls || []).filter(Truthy));
3235
3408
  const selectIsAutostart = createSelector(selectStreamConfig, (config) => config?.autoStart);
3236
- const selectWsUrl = createSelector(unrealFeature.selectAwsInstance, (instance) => instance?.wsUrl);
3237
- const selectShowReconnectPopup = createSelector(unrealFeature.selectWasInitialized, unrealFeature.selectCirrusConnected, unrealFeature.selectEstablishingConnection, unrealFeature.selectImageIntroSrc, unrealFeature.selectVideoIntroSrc, (wasInitialized, connected, establishing, imageSrc, videoSrc) => wasInitialized && !connected && !establishing && !imageSrc && !videoSrc);
3238
- const selectShowLoader = createSelector(unrealFeature.selectWasInitialized, unrealFeature.selectViewportReady, selectStreamConfig, selectShowReconnectPopup, (wasInitialized, viewportReady, config, showPopup) => ((config.autoStart && !wasInitialized) || !showPopup) && !viewportReady);
3409
+ const showPopupWithoutAutoStart = createSelector(selectStreamConfig, unrealFeature.selectWasInitialized, (config, wasInitialized) => !config.autoStart && !wasInitialized);
3410
+ const selectShowReconnectPopup = createSelector(unrealFeature.selectCirrusConnected, unrealFeature.selectEstablishingConnection, (connected, establishing) => !connected && !establishing);
3411
+ const selectShowLoader = createSelector(selectShowReconnectPopup, unrealFeature.selectViewportReady, (showPopup, viewPortReady) => !showPopup && !viewPortReady);
3239
3412
  const selectIsVideoPlayingAndDataChannelConnected = createSelector(unrealFeature.selectIsVideoPlaying, unrealFeature.selectDataChannelConnected, (isVideoPlaying, isDataChannelConnected) => isVideoPlaying && isDataChannelConnected);
3240
3413
  const selectSignalingParameters = createSelector(unrealFeature.selectSsData, unrealFeature.selectAwsInstance, unrealFeature.selectSsInfo, (data, instance, ssInfo) => {
3241
3414
  const info = [];
3242
3415
  if (data) {
3243
- info.push(`${data.cirrusVersion}, ${data.branchInfo}`);
3416
+ info.push(`${data.cirrusVersion}, ${data.branchInfo}, ${data.instanceType}`);
3244
3417
  info.push(`Unreal App Version: ${data.streamerVersion}`);
3245
3418
  info.push(`APP: ${data.streamPath}${data.streamApp}`);
3246
3419
  }
@@ -3279,6 +3452,8 @@ const selectTotalProgress = createSelector(unrealFeature.selectStatusPercentSign
3279
3452
  const percentRest = lerp(0, 1 - splitPoint, commandProgress);
3280
3453
  return clampf(0, 1, percentSignaling + percentRest);
3281
3454
  });
3455
+ const selectClientAndViewIds = createSelector(unrealFeature.selectStreamClientCompanyId, unrealFeature.selectStreamViewId, (clientId, viewId) => ({ clientId, viewId }));
3456
+ const isLoaderScreenVisible = createSelector(unrealFeature.selectViewportReady, unrealFeature.selectDataChannelConnected, unrealFeature.selectCirrusConnected, (viewReady, cirrusConnected, dataChannelConnected) => !viewReady || !cirrusConnected || !dataChannelConnected);
3282
3457
 
3283
3458
  function InstanceReservedHandler(msg) {
3284
3459
  this.store.dispatch(setSignalingName({
@@ -3302,26 +3477,30 @@ function SSInfoHandler(msg) {
3302
3477
  return null;
3303
3478
  }
3304
3479
 
3480
+ const dropCodes = [
3481
+ WSCloseCode_CIRRUS_PLAYER_DISCONNECTED,
3482
+ WSCloseCode_CIRRUS_ABNORMAL_CLOSURE,
3483
+ WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER,
3484
+ WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR,
3485
+ ];
3305
3486
  function OnCloseHandler(e) {
3306
- this.stopRetryTimer();
3307
- Logger.warn(`WS closed: ${JSON.stringify(e.code)} - ${e.reason}`);
3487
+ const message = `WebSocket closed: ${JSON.stringify(e.code)} - ${e.reason}`;
3488
+ Logger.warn(message);
3308
3489
  this.store.dispatch(setCirrusDisconnected());
3490
+ if (dropCodes.includes(e.code)) {
3491
+ this.store.dispatch(dropConnection());
3492
+ }
3309
3493
  }
3310
3494
 
3311
3495
  function OnErrorHandler(e) {
3312
- clearTimeout(this.wsTimeoutHandler);
3313
3496
  Logger.warn(`WS error: ${JSON.stringify(e)}`);
3314
- this.store.dispatch(setErrorMessage({
3315
- errorType: 'WebSocketError',
3497
+ this.store.dispatch(disconnectStream({
3498
+ reason: DisconnectReason.WebSocketError,
3316
3499
  message: 'WebSocket Error',
3317
3500
  }));
3318
- this.store.dispatch(destroyRemoteConnections({ disconnectReason: DisconnectReason.wsOnError }));
3319
- this.initEstablishingConnection();
3320
- this.watchTimeoutOrErrorAndReconnectLater();
3321
3501
  }
3322
3502
 
3323
3503
  function OnMessageHandler(e) {
3324
- clearTimeout(this.wsTimeoutHandler);
3325
3504
  const handleData = (data) => {
3326
3505
  //Logger.colored(...COLOR_CODES.FROM_SIGNALING, data);
3327
3506
  const msg = JSON.parse(data);
@@ -3336,7 +3515,6 @@ function OnMessageHandler(e) {
3336
3515
  }
3337
3516
 
3338
3517
  function OnOpenHandler() {
3339
- this.stopRetryTimer();
3340
3518
  this.store.dispatch(setCirrusConnected());
3341
3519
  this.send({
3342
3520
  type: OrchestrationMessageTypes.requestReservation,
@@ -3351,8 +3529,12 @@ function OnOpenHandler() {
3351
3529
  });
3352
3530
  }
3353
3531
 
3354
- const clampAndKeepMaxPercents = () => {
3355
- return (source) => source.pipe(scan((maxProgress, currentProgress) => currentProgress > maxProgress ? currentProgress : maxProgress, 0));
3532
+ /**
3533
+ * Keeps the max of numbers seen so far; resets to 0 whenever `reset$` emits.
3534
+ * Emits `0` immediately on reset.
3535
+ */
3536
+ const clampAndKeepMaxPercents = (reset$) => {
3537
+ return (source$) => merge(source$.pipe(map((value) => ({ type: 'value', value }))), reset$.pipe(map(() => ({ type: 'reset' })))).pipe(scan((max, evt) => (evt.type === 'reset' ? 0 : Math.max(max, evt.value)), 0));
3356
3538
  };
3357
3539
 
3358
3540
  function observeCommandResponse(data, sender, timeOut = 60000, dispatchOnTimeout = true) {
@@ -3367,7 +3549,7 @@ function observeCommandResponse(data, sender, timeOut = 60000, dispatchOnTimeout
3367
3549
  : { json: { commandCallback: json } };
3368
3550
  }), filter(({ json }) => {
3369
3551
  return json.commandCallback.correlationId === correlationId;
3370
- }), timeout(timeOut), catchError(() => dispatchOnTimeout
3552
+ }), timeout$1(timeOut), catchError(() => dispatchOnTimeout
3371
3553
  ? of({ json: { commandCallback: { correlationId: 'timeout' } } })
3372
3554
  : of(null)), first(), tapLog('Command Response Observer'), filter(Truthy));
3373
3555
  setTimeout(() => sender(out), 0);
@@ -3872,10 +4054,10 @@ class ClickableOverlayComponent {
3872
4054
  constructor() {
3873
4055
  this.state = toSignal(fromSignal(UnrealInternalSignalEvents.ClickableOverlay).pipe(map$1((data) => (typeof data === 'object' ? data : null))));
3874
4056
  }
3875
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ClickableOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3876
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: ClickableOverlayComponent, isStandalone: true, selector: "app-clickable-overlay", ngImport: i0, template: "@if (state()) {\n <div\n (click)=\"state()?.onOverlayClick()\"\n [ngClass]=\"state()?.className\"\n id=\"videoPlayOverlay\"\n >\n @if (state()?.isActivityDetected) {\n <div class=\"resume-box\">\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"state()?.message\" class=\"text-number\"></div>\n </div>\n <div class=\"resume-box__text\">\n <h3 class=\"resume-box__heading\">Session will time out soon</h3>\n <p>\n No activity detected. Press 'Continue' if you wish to keep your\n session active\n </p>\n </div>\n <src-button\n [colorScheme]=\"'primary'\"\n [isFullWidth]=\"true\"\n [size]=\"'large'\"\n [data-testid]=\"'continue-session'\"\n >\n Continue\n </src-button>\n </div>\n }\n </div>\n}\n", styles: ["#videoPlayOverlay{position:absolute;z-index:30;top:0;width:100%;height:100%;font-size:1.8em;font-family:var(--src-font-family-body);background-color:#646464b3}.clickableState{display:flex;justify-content:center;align-items:center;cursor:pointer}.textDisplayState{display:flex}.hiddenState{display:none}.resume-box{width:340px;padding:32px 20px 20px;flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-color-bg-default, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .resume-box__pic{width:72px;height:72px;margin:0 auto 22px;border-radius:48px;background:#ecf0f2;padding:12px;display:flex;align-items:center;justify-content:center}.resume-box .resume-box__pic .text-number{color:var(--src-colors-text-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:30px;font-style:normal;font-weight:400;line-height:24px}.resume-box__text{margin-bottom:18px}.resume-box__text p{text-align:center;font-family:var(--src-font-family-body);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--src-color-text-default-subdued, #6b7280)}.resume-box__text .resume-box__heading{color:var(--src-color-bg-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "colorScheme", "size", "state", "customClass", "hasDisclosure", "isFullWidth", "isPressed", "isDisabled", "isLoading", "iconButton", "srcButtonConfig", "formID", "data-testid"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4057
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: ClickableOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4058
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.2", type: ClickableOverlayComponent, isStandalone: true, selector: "app-clickable-overlay", ngImport: i0, template: "@if (state()) {\n <div\n (click)=\"state()?.onOverlayClick()\"\n [ngClass]=\"state()?.className\"\n id=\"videoPlayOverlay\"\n >\n @if (state()?.isActivityDetected) {\n <div class=\"resume-box\">\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"state()?.message\" class=\"text-number\"></div>\n </div>\n <div class=\"resume-box__text\">\n <h3 class=\"resume-box__heading\">Session will time out soon</h3>\n <p>\n No activity detected. Press 'Continue' if you wish to keep your\n session active\n </p>\n </div>\n <src-button\n [colorScheme]=\"'primary'\"\n [isFullWidth]=\"true\"\n [size]=\"'large'\"\n [data-testid]=\"'continue-session'\"\n >\n Continue\n </src-button>\n </div>\n }\n </div>\n}\n", styles: ["#videoPlayOverlay{position:absolute;z-index:30;top:0;width:100%;height:100%;font-size:1.8em;font-family:var(--src-font-family-body);background-color:#646464b3}.clickableState{display:flex;justify-content:center;align-items:center;cursor:pointer}.textDisplayState{display:flex}.hiddenState{display:none}.resume-box{width:340px;padding:32px 20px 20px;flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-color-bg-default, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .resume-box__pic{width:72px;height:72px;margin:0 auto 22px;border-radius:48px;background:#ecf0f2;padding:12px;display:flex;align-items:center;justify-content:center}.resume-box .resume-box__pic .text-number{color:var(--src-colors-text-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:30px;font-style:normal;font-weight:400;line-height:24px}.resume-box__text{margin-bottom:18px}.resume-box__text p{text-align:center;font-family:var(--src-font-family-body);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--src-color-text-default-subdued, #6b7280)}.resume-box__text .resume-box__heading{color:var(--src-color-bg-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "colorScheme", "size", "state", "customClass", "hasDisclosure", "isFullWidth", "isPressed", "isDisabled", "isLoading", "iconButton", "srcButtonConfig", "formID", "data-testid"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3877
4059
  }
3878
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ClickableOverlayComponent, decorators: [{
4060
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: ClickableOverlayComponent, decorators: [{
3879
4061
  type: Component,
3880
4062
  args: [{ selector: 'app-clickable-overlay', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass, SourceButtonComponent], template: "@if (state()) {\n <div\n (click)=\"state()?.onOverlayClick()\"\n [ngClass]=\"state()?.className\"\n id=\"videoPlayOverlay\"\n >\n @if (state()?.isActivityDetected) {\n <div class=\"resume-box\">\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"state()?.message\" class=\"text-number\"></div>\n </div>\n <div class=\"resume-box__text\">\n <h3 class=\"resume-box__heading\">Session will time out soon</h3>\n <p>\n No activity detected. Press 'Continue' if you wish to keep your\n session active\n </p>\n </div>\n <src-button\n [colorScheme]=\"'primary'\"\n [isFullWidth]=\"true\"\n [size]=\"'large'\"\n [data-testid]=\"'continue-session'\"\n >\n Continue\n </src-button>\n </div>\n }\n </div>\n}\n", styles: ["#videoPlayOverlay{position:absolute;z-index:30;top:0;width:100%;height:100%;font-size:1.8em;font-family:var(--src-font-family-body);background-color:#646464b3}.clickableState{display:flex;justify-content:center;align-items:center;cursor:pointer}.textDisplayState{display:flex}.hiddenState{display:none}.resume-box{width:340px;padding:32px 20px 20px;flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-color-bg-default, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .resume-box__pic{width:72px;height:72px;margin:0 auto 22px;border-radius:48px;background:#ecf0f2;padding:12px;display:flex;align-items:center;justify-content:center}.resume-box .resume-box__pic .text-number{color:var(--src-colors-text-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:30px;font-style:normal;font-weight:400;line-height:24px}.resume-box__text{margin-bottom:18px}.resume-box__text p{text-align:center;font-family:var(--src-font-family-body);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--src-color-text-default-subdued, #6b7280)}.resume-box__text .resume-box__heading{color:var(--src-color-bg-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"] }]
3881
4063
  }] });
@@ -3886,10 +4068,10 @@ class FreezeFrameComponent {
3886
4068
  this.freezeFrameProgressMessageFromVideo = toSignal(this.store.select(selectFreezeFrameProgressMessageFromVideo));
3887
4069
  this.combinedFreeze = toSignal(this.store.select(selectFreezeFrameCombinedDataUrl));
3888
4070
  }
3889
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: FreezeFrameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3890
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: FreezeFrameComponent, isStandalone: true, selector: "app-freeze-frame", ngImport: i0, template: "<div class=\"freeze-images\">\n @if (combinedFreeze()) {\n <img\n [src]=\"combinedFreeze()\"\n class=\"videoStillImage\"\n alt=\"freezeFrameImage\"\n />\n }\n\n @if (freezeFrameProgressMessageFromVideo()) {\n <div class=\"progress-status\">\n {{ freezeFrameProgressMessageFromVideo() }}\n </div>\n }\n</div>\n", styles: [".freeze-images{pointer-events:none;position:absolute;left:0;top:0;width:100%;height:100%;z-index:1}.freeze-images .freezeFrameOverlay{top:0;left:0;width:100%;height:100%;position:absolute;object-fit:cover;object-position:center}.freeze-images .videoStillImage{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;object-fit:cover}.freeze-images .progress-status{position:absolute;top:50%;left:50%;z-index:1;display:flex;flex-direction:column;justify-content:center;align-items:center;width:auto;height:auto;margin:0;padding:10px;background-color:#ffffffb3;border:1px solid grey;transform:translate(-50%,-50%)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4071
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: FreezeFrameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4072
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.2", type: FreezeFrameComponent, isStandalone: true, selector: "app-freeze-frame", ngImport: i0, template: "<div class=\"freeze-images\">\n @if (combinedFreeze()) {\n <img\n [src]=\"combinedFreeze()\"\n class=\"videoStillImage\"\n alt=\"freezeFrameImage\"\n />\n }\n\n @if (freezeFrameProgressMessageFromVideo()) {\n <div class=\"progress-status\">\n {{ freezeFrameProgressMessageFromVideo() }}\n </div>\n }\n</div>\n", styles: [".freeze-images{pointer-events:none;position:absolute;left:0;top:0;width:100%;height:100%;z-index:1}.freeze-images .freezeFrameOverlay{top:0;left:0;width:100%;height:100%;position:absolute;object-fit:cover;object-position:center}.freeze-images .videoStillImage{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;object-fit:cover}.freeze-images .progress-status{position:absolute;top:50%;left:50%;z-index:1;display:flex;flex-direction:column;justify-content:center;align-items:center;width:auto;height:auto;margin:0;padding:10px;background-color:#ffffffb3;border:1px solid grey;transform:translate(-50%,-50%)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3891
4073
  }
3892
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: FreezeFrameComponent, decorators: [{
4074
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: FreezeFrameComponent, decorators: [{
3893
4075
  type: Component,
3894
4076
  args: [{ selector: 'app-freeze-frame', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"freeze-images\">\n @if (combinedFreeze()) {\n <img\n [src]=\"combinedFreeze()\"\n class=\"videoStillImage\"\n alt=\"freezeFrameImage\"\n />\n }\n\n @if (freezeFrameProgressMessageFromVideo()) {\n <div class=\"progress-status\">\n {{ freezeFrameProgressMessageFromVideo() }}\n </div>\n }\n</div>\n", styles: [".freeze-images{pointer-events:none;position:absolute;left:0;top:0;width:100%;height:100%;z-index:1}.freeze-images .freezeFrameOverlay{top:0;left:0;width:100%;height:100%;position:absolute;object-fit:cover;object-position:center}.freeze-images .videoStillImage{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;object-fit:cover}.freeze-images .progress-status{position:absolute;top:50%;left:50%;z-index:1;display:flex;flex-direction:column;justify-content:center;align-items:center;width:auto;height:auto;margin:0;padding:10px;background-color:#ffffffb3;border:1px solid grey;transform:translate(-50%,-50%)}\n"] }]
3895
4077
  }] });
@@ -3917,10 +4099,10 @@ class SafePipe {
3917
4099
  throw new Error(`Invalid safe type specified: ${type}`);
3918
4100
  }
3919
4101
  }
3920
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: SafePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
3921
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.1", ngImport: i0, type: SafePipe, isStandalone: true, name: "safe" }); }
4102
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: SafePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
4103
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.2", ngImport: i0, type: SafePipe, isStandalone: true, name: "safe" }); }
3922
4104
  }
3923
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: SafePipe, decorators: [{
4105
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: SafePipe, decorators: [{
3924
4106
  type: Pipe,
3925
4107
  args: [{
3926
4108
  name: 'safe',
@@ -3934,10 +4116,10 @@ class LowBandwidthModalComponent {
3934
4116
  close(value) {
3935
4117
  this.dialogRef.close(value || false);
3936
4118
  }
3937
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: LowBandwidthModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3938
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.1", type: LowBandwidthModalComponent, isStandalone: true, selector: "app-low-bandwidth-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'low-bandwidth'\" class=\"src-modal src-modal--lbm\">\n <header class=\"src-modal__header\">\n <h6 [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n Unstable Connection\n </h6>\n </header>\n <section class=\"src-modal__body\">\n <div\n [innerHtml]=\"\n 'Fluid Interactivity Modes were disabled due to an unstable connection. Showcase Gallery Mode is enabled. To regain full functionality, switch to Interactive Mode.'\n | safe: 'html'\n \"\n class=\"src-modal__scroll-box\"\n ></div>\n </section>\n <footer class=\"src-modal__footer\">\n <div class=\"src-modal__buttons\">\n <src-button\n (onClick)=\"close(true)\"\n [data-testid]=\"'switch-to-interactive-mode'\"\n >\n Switch\n </src-button>\n\n <src-button\n (onClick)=\"close()\"\n [data-testid]=\"'close-lbm-modal'\"\n colorScheme=\"primary\"\n >\n Ok\n </src-button>\n </div>\n </footer>\n</div>\n", styles: [".src-modal--lbm{width:360px}.src-modal--lbm .src-modal__body{font-size:14px;line-height:24px}.src-modal--lbm .src-modal__buttons{display:flex;gap:8px}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "colorScheme", "size", "state", "customClass", "hasDisclosure", "isFullWidth", "isPressed", "isDisabled", "isLoading", "iconButton", "srcButtonConfig", "formID", "data-testid"], outputs: ["onClick", "onSubmit"] }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4119
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: LowBandwidthModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4120
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.2", type: LowBandwidthModalComponent, isStandalone: true, selector: "app-low-bandwidth-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'low-bandwidth'\" class=\"src-modal src-modal--lbm\">\n <header class=\"src-modal__header\">\n <h6 [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n Unstable Connection\n </h6>\n </header>\n <section class=\"src-modal__body\">\n <div\n [innerHtml]=\"\n 'Fluid Interactivity Modes were disabled due to an unstable connection. Showcase Gallery Mode is enabled. To regain full functionality, switch to Interactive Mode.'\n | safe: 'html'\n \"\n class=\"src-modal__scroll-box\"\n ></div>\n </section>\n <footer class=\"src-modal__footer\">\n <div class=\"src-modal__buttons\">\n <src-button\n (onClick)=\"close(true)\"\n [data-testid]=\"'switch-to-interactive-mode'\"\n >\n Switch\n </src-button>\n\n <src-button\n (onClick)=\"close()\"\n [data-testid]=\"'close-lbm-modal'\"\n colorScheme=\"primary\"\n >\n Ok\n </src-button>\n </div>\n </footer>\n</div>\n", styles: [".src-modal--lbm{width:360px}.src-modal--lbm .src-modal__body{font-size:14px;line-height:24px}.src-modal--lbm .src-modal__buttons{display:flex;gap:8px}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "colorScheme", "size", "state", "customClass", "hasDisclosure", "isFullWidth", "isPressed", "isDisabled", "isLoading", "iconButton", "srcButtonConfig", "formID", "data-testid"], outputs: ["onClick", "onSubmit"] }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3939
4121
  }
3940
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: LowBandwidthModalComponent, decorators: [{
4122
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: LowBandwidthModalComponent, decorators: [{
3941
4123
  type: Component,
3942
4124
  args: [{ selector: 'app-low-bandwidth-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceIconButtonComponent, SafePipe, SourceButtonComponent], template: "<div [attr.data-testid]=\"'low-bandwidth'\" class=\"src-modal src-modal--lbm\">\n <header class=\"src-modal__header\">\n <h6 [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n Unstable Connection\n </h6>\n </header>\n <section class=\"src-modal__body\">\n <div\n [innerHtml]=\"\n 'Fluid Interactivity Modes were disabled due to an unstable connection. Showcase Gallery Mode is enabled. To regain full functionality, switch to Interactive Mode.'\n | safe: 'html'\n \"\n class=\"src-modal__scroll-box\"\n ></div>\n </section>\n <footer class=\"src-modal__footer\">\n <div class=\"src-modal__buttons\">\n <src-button\n (onClick)=\"close(true)\"\n [data-testid]=\"'switch-to-interactive-mode'\"\n >\n Switch\n </src-button>\n\n <src-button\n (onClick)=\"close()\"\n [data-testid]=\"'close-lbm-modal'\"\n colorScheme=\"primary\"\n >\n Ok\n </src-button>\n </div>\n </footer>\n</div>\n", styles: [".src-modal--lbm{width:360px}.src-modal--lbm .src-modal__body{font-size:14px;line-height:24px}.src-modal--lbm .src-modal__buttons{display:flex;gap:8px}\n"] }]
3943
4125
  }] });
@@ -3947,10 +4129,10 @@ class AfkRestartScreenLockerComponent {
3947
4129
  this.store = inject(Store);
3948
4130
  this.videoService = inject(VideoService);
3949
4131
  this.destroyRef = inject(DestroyRef);
3950
- this.showReconnectPopup = toSignal(this.store.select(selectShowReconnectPopup));
3951
- this.isViewportReady = toSignal(this.store.select(unrealFeature.selectViewportReady));
3952
- this.imageLoadingSrc = toSignal(this.store.select(unrealFeature.selectImageLoadingSrc));
3953
- this.streamConfig = toSignal(this.store.select(selectStreamConfig));
4132
+ this.showReconnectPopup = this.store.selectSignal(selectShowReconnectPopup);
4133
+ this.isLoaderScreenVisible = this.store.selectSignal(isLoaderScreenVisible);
4134
+ this.imageLoadingSrc = this.store.selectSignal(unrealFeature.selectImageLoadingSrc);
4135
+ this.streamConfig = this.store.selectSignal(selectStreamConfig);
3954
4136
  this.isSecondStart = signal(!!(this.streamConfig()?.autoStart && location.href.match(/^https/gi)), ...(ngDevMode ? [{ debugName: "isSecondStart" }] : []));
3955
4137
  this.playCallBack = null;
3956
4138
  }
@@ -3972,18 +4154,18 @@ class AfkRestartScreenLockerComponent {
3972
4154
  });
3973
4155
  }
3974
4156
  connect() {
3975
- this.store.dispatch(initSignalling());
4157
+ this.store.dispatch(startStream({ config: { autoStart: true } }));
3976
4158
  }
3977
4159
  onDisconnect() {
3978
4160
  this.isSecondStart.set(true);
3979
4161
  this.playCallBack = null;
3980
4162
  }
3981
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AfkRestartScreenLockerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3982
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: AfkRestartScreenLockerComponent, isStandalone: true, selector: "app-afk-restart-screen-locker", ngImport: i0, template: "@if (!isViewportReady()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (imageLoadingSrc()) {\n <img\n [ngSrc]=\"imageLoadingSrc()!\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\n\n @if (showReconnectPopup()) {\n <div class=\"stream-message-wrapper\">\n <div class=\"resume-box\">\n @if (isSecondStart()) {\n <div class=\"resume-box__text\">\n Your stream has been paused due to inactivity\n </div>\n }\n\n <src-button\n (onClick)=\"connect()\"\n [isFullWidth]=\"true\"\n [size]=\"'large'\"\n [colorScheme]=\"'primary'\"\n [data-testid]=\"'connect-button'\"\n class=\"connect-button\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </src-button>\n </div>\n </div>\n }\n </div>\n}\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .stream-message-wrapper{position:absolute;top:0;left:0;width:100%;height:100%;background-color:transparent;z-index:3;display:flex;align-items:center;justify-content:center}.resume-box{padding:16px;position:absolute;bottom:0;left:50%;gap:12px;display:flex;transform:translate(-50%,-50%);flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-color-bg-default, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .connect-button{width:100%}.resume-box__text{color:var(--src-color-gray-500, #6b7280);text-align:center;font-family:var(--src-font-family-body);font-size:14px;font-style:normal;font-weight:400;line-height:24px}.preload .message-loader{position:absolute;bottom:20px;left:50%;display:block;margin:auto;transform:translate(-50%)}.preload .message-loader>p{margin:0;line-height:1;transition:all ease .35s}.preload .message-loader>p span{font-size:10px;transition:all ease .35s}@media (min-width: 1900px){.preload .message-loader{bottom:40px}}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }, { kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "colorScheme", "size", "state", "customClass", "hasDisclosure", "isFullWidth", "isPressed", "isDisabled", "isLoading", "iconButton", "srcButtonConfig", "formID", "data-testid"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4163
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: AfkRestartScreenLockerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4164
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.2", type: AfkRestartScreenLockerComponent, isStandalone: true, selector: "app-afk-restart-screen-locker", ngImport: i0, template: "@if (isLoaderScreenVisible()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (imageLoadingSrc()) {\n <img\n [ngSrc]=\"imageLoadingSrc()!\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\n\n @if (showReconnectPopup()) {\n <div class=\"stream-message-wrapper\">\n <div class=\"resume-box\">\n @if (isSecondStart()) {\n <div class=\"resume-box__text\">\n Your stream has been paused due to inactivity\n </div>\n }\n\n <src-button\n (onClick)=\"connect()\"\n [isFullWidth]=\"true\"\n [size]=\"'large'\"\n [colorScheme]=\"'primary'\"\n [data-testid]=\"'connect-button'\"\n class=\"connect-button\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </src-button>\n </div>\n </div>\n }\n </div>\n}\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;background-color:#fff}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .stream-message-wrapper{position:absolute;top:0;left:0;width:100%;height:100%;background-color:transparent;z-index:3;display:flex;align-items:center;justify-content:center}.resume-box{padding:16px;position:absolute;bottom:0;left:50%;gap:12px;display:flex;transform:translate(-50%,-50%);flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-color-bg-default, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .connect-button{width:100%}.resume-box__text{color:var(--src-color-gray-500, #6b7280);text-align:center;font-family:var(--src-font-family-body);font-size:14px;font-style:normal;font-weight:400;line-height:24px}.preload .message-loader{position:absolute;bottom:20px;left:50%;display:block;margin:auto;transform:translate(-50%)}.preload .message-loader>p{margin:0;line-height:1;transition:all ease .35s}.preload .message-loader>p span{font-size:10px;transition:all ease .35s}@media (min-width: 1900px){.preload .message-loader{bottom:40px}}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }, { kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "colorScheme", "size", "state", "customClass", "hasDisclosure", "isFullWidth", "isPressed", "isDisabled", "isLoading", "iconButton", "srcButtonConfig", "formID", "data-testid"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3983
4165
  }
3984
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AfkRestartScreenLockerComponent, decorators: [{
4166
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: AfkRestartScreenLockerComponent, decorators: [{
3985
4167
  type: Component,
3986
- args: [{ selector: 'app-afk-restart-screen-locker', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgOptimizedImage, SourceButtonComponent, SourceButtonComponent], template: "@if (!isViewportReady()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (imageLoadingSrc()) {\n <img\n [ngSrc]=\"imageLoadingSrc()!\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\n\n @if (showReconnectPopup()) {\n <div class=\"stream-message-wrapper\">\n <div class=\"resume-box\">\n @if (isSecondStart()) {\n <div class=\"resume-box__text\">\n Your stream has been paused due to inactivity\n </div>\n }\n\n <src-button\n (onClick)=\"connect()\"\n [isFullWidth]=\"true\"\n [size]=\"'large'\"\n [colorScheme]=\"'primary'\"\n [data-testid]=\"'connect-button'\"\n class=\"connect-button\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </src-button>\n </div>\n </div>\n }\n </div>\n}\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .stream-message-wrapper{position:absolute;top:0;left:0;width:100%;height:100%;background-color:transparent;z-index:3;display:flex;align-items:center;justify-content:center}.resume-box{padding:16px;position:absolute;bottom:0;left:50%;gap:12px;display:flex;transform:translate(-50%,-50%);flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-color-bg-default, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .connect-button{width:100%}.resume-box__text{color:var(--src-color-gray-500, #6b7280);text-align:center;font-family:var(--src-font-family-body);font-size:14px;font-style:normal;font-weight:400;line-height:24px}.preload .message-loader{position:absolute;bottom:20px;left:50%;display:block;margin:auto;transform:translate(-50%)}.preload .message-loader>p{margin:0;line-height:1;transition:all ease .35s}.preload .message-loader>p span{font-size:10px;transition:all ease .35s}@media (min-width: 1900px){.preload .message-loader{bottom:40px}}\n"] }]
4168
+ args: [{ selector: 'app-afk-restart-screen-locker', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgOptimizedImage, SourceButtonComponent, SourceButtonComponent], template: "@if (isLoaderScreenVisible()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (imageLoadingSrc()) {\n <img\n [ngSrc]=\"imageLoadingSrc()!\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\n\n @if (showReconnectPopup()) {\n <div class=\"stream-message-wrapper\">\n <div class=\"resume-box\">\n @if (isSecondStart()) {\n <div class=\"resume-box__text\">\n Your stream has been paused due to inactivity\n </div>\n }\n\n <src-button\n (onClick)=\"connect()\"\n [isFullWidth]=\"true\"\n [size]=\"'large'\"\n [colorScheme]=\"'primary'\"\n [data-testid]=\"'connect-button'\"\n class=\"connect-button\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </src-button>\n </div>\n </div>\n }\n </div>\n}\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;background-color:#fff}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .stream-message-wrapper{position:absolute;top:0;left:0;width:100%;height:100%;background-color:transparent;z-index:3;display:flex;align-items:center;justify-content:center}.resume-box{padding:16px;position:absolute;bottom:0;left:50%;gap:12px;display:flex;transform:translate(-50%,-50%);flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-color-bg-default, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .connect-button{width:100%}.resume-box__text{color:var(--src-color-gray-500, #6b7280);text-align:center;font-family:var(--src-font-family-body);font-size:14px;font-style:normal;font-weight:400;line-height:24px}.preload .message-loader{position:absolute;bottom:20px;left:50%;display:block;margin:auto;transform:translate(-50%)}.preload .message-loader>p{margin:0;line-height:1;transition:all ease .35s}.preload .message-loader>p span{font-size:10px;transition:all ease .35s}@media (min-width: 1900px){.preload .message-loader{bottom:40px}}\n"] }]
3987
4169
  }] });
3988
4170
 
3989
4171
  class StatGraphComponent {
@@ -4048,10 +4230,10 @@ class StatGraphComponent {
4048
4230
  }
4049
4231
  this.draw();
4050
4232
  }
4051
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: StatGraphComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4052
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.2.1", type: StatGraphComponent, isStandalone: true, selector: "app-stat-graph", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, tickStep: { classPropertyName: "tickStep", publicName: "tickStep", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, dataTick: { classPropertyName: "dataTick", publicName: "dataTick", isSignal: false, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "resize($event)" } }, viewQueries: [{ propertyName: "graph", first: true, predicate: ["graph"], descendants: true }], ngImport: i0, template: "<div class=\"content\">\n <p>{{ label() }}: {{ current }} | Min:{{ min }} | Max:{{ max }}</p>\n <canvas #graph class=\"graph\"></canvas>\n</div>\n", styles: [".content{width:100%;height:100%}.content p{color:#fff;margin:5px 0 0;height:25px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4233
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: StatGraphComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4234
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.2.2", type: StatGraphComponent, isStandalone: true, selector: "app-stat-graph", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, tickStep: { classPropertyName: "tickStep", publicName: "tickStep", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, dataTick: { classPropertyName: "dataTick", publicName: "dataTick", isSignal: false, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "resize($event)" } }, viewQueries: [{ propertyName: "graph", first: true, predicate: ["graph"], descendants: true }], ngImport: i0, template: "<div class=\"content\">\n <p>{{ label() }}: {{ current }} | Min:{{ min }} | Max:{{ max }}</p>\n <canvas #graph class=\"graph\"></canvas>\n</div>\n", styles: [".content{width:100%;height:100%}.content p{color:#fff;margin:5px 0 0;height:25px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4053
4235
  }
4054
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: StatGraphComponent, decorators: [{
4236
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: StatGraphComponent, decorators: [{
4055
4237
  type: Component,
4056
4238
  args: [{ selector: 'app-stat-graph', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"content\">\n <p>{{ label() }}: {{ current }} | Min:{{ min }} | Max:{{ max }}</p>\n <canvas #graph class=\"graph\"></canvas>\n</div>\n", styles: [".content{width:100%;height:100%}.content p{color:#fff;margin:5px 0 0;height:25px}\n"] }]
4057
4239
  }], propDecorators: { graph: [{
@@ -4176,29 +4358,28 @@ class VideoStatsComponent {
4176
4358
  elements: this.elementsToShow,
4177
4359
  }));
4178
4360
  }
4179
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: VideoStatsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4180
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: VideoStatsComponent, isStandalone: true, selector: "app-video-stats", ngImport: i0, template: "@if (videoStatus()) {\n @if (viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div (click)=\"toggle()\" [class.min]=\"collapsed\" id=\"aggregatedStats\">\n <div class=\"forNerds\">\n <div (click)=\"$event.stopPropagation()\" class=\"static\">\n <li>\n <app-stat-graph\n [dataTick]=\"fpsTick()\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [dataTick]=\"videoQP()\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [dataTick]=\"bitrateTick()\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList$ | async; track graph) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [dataTick]=\"graph.stat | async\"\n [color]=\"graph.color\"\n />\n </li>\n }\n </div>\n </div>\n\n @for (el of videoStatus(); track el.key + $index) {\n <div (click)=\"toggleGraph($event, el.key, el.value)\" class=\"stat\">\n <span>{{ el.key }}: </span>{{ el.value | json }}\n </div>\n }\n </div>\n </div>\n @if (!collapsed) {\n <div [innerHTML]=\"ssInfo() | safe: 'html'\" class=\"ssInfo\"></div>\n }\n }\n}\n", styles: [".settings-container{position:absolute;top:65px;left:10px;z-index:1000;bottom:10px}.gear-button{left:0;width:50px;position:absolute;background:none;cursor:pointer;font-size:10px;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#fff;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}#aggregatedStats{max-height:calc(100% - 25px);overflow-y:auto;margin-top:25px;left:20px;z-index:31;max-width:400px;padding:10px;background-color:#fff3;border:1px solid grey}#aggregatedStats>*{color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000}#aggregatedStats:empty{display:none}#aggregatedStats button{position:absolute;right:5px;top:5px}#aggregatedStats.min{display:none}#aggregatedStats .forNerds{width:100%}#aggregatedStats .forNerds li{list-style-type:none;height:60px}#aggregatedStats .forNerds li app-stat-graph{display:block;width:100%;height:100%}#aggregatedStats .stat{cursor:pointer}#aggregatedStats .stat span{font-weight:700}#aggregatedStats .graph{cursor:pointer}#aggregatedStats .static{pointer-events:all}.ssInfo{position:absolute;top:0;left:50%;transform:translate(-50%);color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;text-align:center;width:80%;pointer-events:none}\n"], dependencies: [{ kind: "component", type: StatGraphComponent, selector: "app-stat-graph", inputs: ["color", "tickStep", "label", "dataTick"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: JsonPipe, name: "json" }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4361
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: VideoStatsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4362
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.2", type: VideoStatsComponent, isStandalone: true, selector: "app-video-stats", ngImport: i0, template: "@if (videoStatus()) {\n @if (viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div (click)=\"toggle()\" [class.min]=\"collapsed\" id=\"aggregatedStats\">\n <div class=\"forNerds\">\n <div (click)=\"$event.stopPropagation()\" class=\"static\">\n <li>\n <app-stat-graph\n [dataTick]=\"fpsTick()\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [dataTick]=\"videoQP()\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [dataTick]=\"bitrateTick()\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList$ | async; track graph) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [dataTick]=\"graph.stat | async\"\n [color]=\"graph.color\"\n />\n </li>\n }\n </div>\n </div>\n\n @for (el of videoStatus(); track el.key + $index) {\n <div (click)=\"toggleGraph($event, el.key, el.value)\" class=\"stat\">\n <span>{{ el.key }}: </span>{{ el.value | json }}\n </div>\n }\n </div>\n </div>\n @if (!collapsed) {\n <div [innerHTML]=\"ssInfo() | safe: 'html'\" class=\"ssInfo\"></div>\n }\n }\n}\n", styles: [".settings-container{position:absolute;top:65px;left:10px;z-index:1000;bottom:10px}.gear-button{left:0;width:50px;position:absolute;background:none;cursor:pointer;font-size:10px;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#fff;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}#aggregatedStats{max-height:calc(100% - 25px);overflow-y:auto;margin-top:25px;left:20px;z-index:31;max-width:400px;padding:10px;background-color:#fff3;border:1px solid grey}#aggregatedStats>*{color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000}#aggregatedStats:empty{display:none}#aggregatedStats button{position:absolute;right:5px;top:5px}#aggregatedStats.min{display:none}#aggregatedStats .forNerds{width:100%}#aggregatedStats .forNerds li{list-style-type:none;height:60px}#aggregatedStats .forNerds li app-stat-graph{display:block;width:100%;height:100%}#aggregatedStats .stat{cursor:pointer}#aggregatedStats .stat span{font-weight:700}#aggregatedStats .graph{cursor:pointer}#aggregatedStats .static{pointer-events:all}.ssInfo{position:absolute;top:0;left:50%;transform:translate(-50%);color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;text-align:center;width:80%;pointer-events:none}\n"], dependencies: [{ kind: "component", type: StatGraphComponent, selector: "app-stat-graph", inputs: ["color", "tickStep", "label", "dataTick"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: JsonPipe, name: "json" }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4181
4363
  }
4182
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: VideoStatsComponent, decorators: [{
4364
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: VideoStatsComponent, decorators: [{
4183
4365
  type: Component,
4184
4366
  args: [{ selector: 'app-video-stats', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StatGraphComponent, AsyncPipe, JsonPipe, SafePipe], template: "@if (videoStatus()) {\n @if (viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div (click)=\"toggle()\" [class.min]=\"collapsed\" id=\"aggregatedStats\">\n <div class=\"forNerds\">\n <div (click)=\"$event.stopPropagation()\" class=\"static\">\n <li>\n <app-stat-graph\n [dataTick]=\"fpsTick()\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [dataTick]=\"videoQP()\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [dataTick]=\"bitrateTick()\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList$ | async; track graph) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [dataTick]=\"graph.stat | async\"\n [color]=\"graph.color\"\n />\n </li>\n }\n </div>\n </div>\n\n @for (el of videoStatus(); track el.key + $index) {\n <div (click)=\"toggleGraph($event, el.key, el.value)\" class=\"stat\">\n <span>{{ el.key }}: </span>{{ el.value | json }}\n </div>\n }\n </div>\n </div>\n @if (!collapsed) {\n <div [innerHTML]=\"ssInfo() | safe: 'html'\" class=\"ssInfo\"></div>\n }\n }\n}\n", styles: [".settings-container{position:absolute;top:65px;left:10px;z-index:1000;bottom:10px}.gear-button{left:0;width:50px;position:absolute;background:none;cursor:pointer;font-size:10px;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#fff;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}#aggregatedStats{max-height:calc(100% - 25px);overflow-y:auto;margin-top:25px;left:20px;z-index:31;max-width:400px;padding:10px;background-color:#fff3;border:1px solid grey}#aggregatedStats>*{color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000}#aggregatedStats:empty{display:none}#aggregatedStats button{position:absolute;right:5px;top:5px}#aggregatedStats.min{display:none}#aggregatedStats .forNerds{width:100%}#aggregatedStats .forNerds li{list-style-type:none;height:60px}#aggregatedStats .forNerds li app-stat-graph{display:block;width:100%;height:100%}#aggregatedStats .stat{cursor:pointer}#aggregatedStats .stat span{font-weight:700}#aggregatedStats .graph{cursor:pointer}#aggregatedStats .static{pointer-events:all}.ssInfo{position:absolute;top:0;left:50%;transform:translate(-50%);color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;text-align:center;width:80%;pointer-events:none}\n"] }]
4185
4367
  }], ctorParameters: () => [] });
4186
4368
 
4187
4369
  class UnrealStatusComponent {
4188
4370
  constructor() {
4189
- this.store = inject(Store);
4190
- this.unrealInitialConfig = inject(UNREAL_CONFIG, {
4191
- optional: true,
4192
- });
4193
- this.resetLoaderTrigger$ = this.store
4194
- .select(unrealFeature.selectCirrusConnected)
4195
- .pipe(filter(Falsy));
4196
4371
  this.isDevMode = inject(DevModeService).isDevMode;
4372
+ this.unrealInitialConfig = inject(UNREAL_CONFIG);
4197
4373
  this.destroyRef = inject(DestroyRef);
4374
+ this.store = inject(Store);
4375
+ this.resetPercentageSignal$ = this.store
4376
+ .select(selectShowLoader)
4377
+ .pipe(filter(Falsy));
4378
+ this.showLoader = this.store.selectSignal(selectShowLoader);
4198
4379
  /**
4199
4380
  * An observable that emits smoothed percentage values from 0 to 100.
4200
4381
  */
4201
- this.messagePercents$ = toSignal(this.resetLoaderTrigger$.pipe(withLatestFrom(this.store.select(unrealFeature.selectIsFreezeLoaderPercents)), filter(([, isFreezeLoaderPercents]) => !isFreezeLoaderPercents), switchMap$1(() => this.store.select(selectTotalProgress).pipe(startWith(0), clampAndKeepMaxPercents(), floatToSmoothPercents(), combineLatestWith(this.store.select(selectShowLoader)), map$1(([progress, showLoader]) => (showLoader ? progress || 0 : null))))));
4382
+ this.messagePercents = toSignal(this.store.select(selectTotalProgress).pipe(clampAndKeepMaxPercents(this.resetPercentageSignal$), floatToSmoothPercents(), distinctUntilChanged(), map$1((progress) => progress || 0)));
4202
4383
  }
4203
4384
  ngOnInit() {
4204
4385
  /**
@@ -4236,7 +4417,7 @@ class UnrealStatusComponent {
4236
4417
  * A subscription that merges various status observables and emits debounced status via postMessage.
4237
4418
  */
4238
4419
  merge(nullifyObs$, cirrusConnected$, signalingStatus$, streamToggle$)
4239
- .pipe(debounceTime(0), tapLog('STATUS'), takeUntilDestroyed(this.destroyRef))
4420
+ .pipe(debounceTime(0), tapLog('STATUS=>'), takeUntilDestroyed(this.destroyRef))
4240
4421
  .subscribe((status) => this.sendMessageToIndex(status));
4241
4422
  }
4242
4423
  /**
@@ -4258,12 +4439,12 @@ class UnrealStatusComponent {
4258
4439
  // Fails silently if the iframe's content window cannot be accessed
4259
4440
  }
4260
4441
  }
4261
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealStatusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4262
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: UnrealStatusComponent, isStandalone: true, selector: "app-unreal-status", ngImport: i0, template: "@if (messagePercents$() !== null) {\n <div class=\"status-box\">\n <div class=\"status-box__message\">\n <src-loading\n [size]=\"30\"\n [backgroundStrokeColor]=\"'#E5E7EB'\"\n [progressStrokeColor]=\"'#1F2937'\"\n />\n {{ messagePercents$() }}%\n </div>\n </div>\n}\n\n@if (isDevMode) {\n <app-video-stats />\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-box{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:101;display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--src-color-bg-default);border:none;border-radius:var(--src-border-rounded-full, 9999px);box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814;pointer-events:none}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--src-color-text-default, #1f2937);font-size:16px;font-weight:400;line-height:24px}.status-box__message .status-box__message_text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.status-box__message src-loading{margin-right:12px}\n"], dependencies: [{ kind: "component", type: VideoStatsComponent, selector: "app-video-stats" }, { kind: "component", type: SourceLoadingComponent, selector: "src-loading", inputs: ["size", "progress", "lineCap", "backgroundStrokeColor", "progressStrokeColor", "strokeWidth", "data-testid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4442
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealStatusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4443
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.2", type: UnrealStatusComponent, isStandalone: true, selector: "app-unreal-status", ngImport: i0, template: "@if (showLoader()) {\n <div class=\"status-box\">\n <div class=\"status-box__message\">\n <src-loading\n [size]=\"30\"\n [backgroundStrokeColor]=\"'#E5E7EB'\"\n [progressStrokeColor]=\"'#1F2937'\"\n />\n {{ messagePercents() }}%\n </div>\n </div>\n}\n\n@if (isDevMode) {\n <app-video-stats />\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-box{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:101;display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--src-color-bg-default);border:none;border-radius:var(--src-border-rounded-full, 9999px);box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814;pointer-events:none}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--src-color-text-default, #1f2937);font-size:16px;font-weight:400;line-height:24px}.status-box__message .status-box__message_text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.status-box__message src-loading{margin-right:12px}\n"], dependencies: [{ kind: "component", type: VideoStatsComponent, selector: "app-video-stats" }, { kind: "component", type: SourceLoadingComponent, selector: "src-loading", inputs: ["size", "progress", "lineCap", "backgroundStrokeColor", "progressStrokeColor", "strokeWidth", "data-testid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4263
4444
  }
4264
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealStatusComponent, decorators: [{
4445
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealStatusComponent, decorators: [{
4265
4446
  type: Component,
4266
- args: [{ selector: 'app-unreal-status', changeDetection: ChangeDetectionStrategy.OnPush, imports: [VideoStatsComponent, SourceLoadingComponent], template: "@if (messagePercents$() !== null) {\n <div class=\"status-box\">\n <div class=\"status-box__message\">\n <src-loading\n [size]=\"30\"\n [backgroundStrokeColor]=\"'#E5E7EB'\"\n [progressStrokeColor]=\"'#1F2937'\"\n />\n {{ messagePercents$() }}%\n </div>\n </div>\n}\n\n@if (isDevMode) {\n <app-video-stats />\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-box{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:101;display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--src-color-bg-default);border:none;border-radius:var(--src-border-rounded-full, 9999px);box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814;pointer-events:none}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--src-color-text-default, #1f2937);font-size:16px;font-weight:400;line-height:24px}.status-box__message .status-box__message_text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.status-box__message src-loading{margin-right:12px}\n"] }]
4447
+ args: [{ selector: 'app-unreal-status', changeDetection: ChangeDetectionStrategy.OnPush, imports: [VideoStatsComponent, SourceLoadingComponent], template: "@if (showLoader()) {\n <div class=\"status-box\">\n <div class=\"status-box__message\">\n <src-loading\n [size]=\"30\"\n [backgroundStrokeColor]=\"'#E5E7EB'\"\n [progressStrokeColor]=\"'#1F2937'\"\n />\n {{ messagePercents() }}%\n </div>\n </div>\n}\n\n@if (isDevMode) {\n <app-video-stats />\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-box{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:101;display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--src-color-bg-default);border:none;border-radius:var(--src-border-rounded-full, 9999px);box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814;pointer-events:none}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--src-color-text-default, #1f2937);font-size:16px;font-weight:400;line-height:24px}.status-box__message .status-box__message_text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.status-box__message src-loading{margin-right:12px}\n"] }]
4267
4448
  }] });
4268
4449
 
4269
4450
  class UnrealSceneComponent {
@@ -4351,8 +4532,7 @@ class UnrealSceneComponent {
4351
4532
  }
4352
4533
  ngOnDestroy() {
4353
4534
  this.commandsSender.destroy();
4354
- this.store.dispatch(destroyConnectionsAndResetState());
4355
- this.store.dispatch(resetUnrealStateAction());
4535
+ this.store.dispatch(destroyUnrealScene());
4356
4536
  }
4357
4537
  adaptVideo(size) {
4358
4538
  const video = this.videoService.video;
@@ -4447,10 +4627,10 @@ class UnrealSceneComponent {
4447
4627
  makeEven(value) {
4448
4628
  return Math.floor((value + 1) / 2) * 2;
4449
4629
  }
4450
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealSceneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4451
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.2.1", type: UnrealSceneComponent, isStandalone: true, selector: "app-unreal-scene", inputs: { isStudio: { classPropertyName: "isStudio", publicName: "isStudio", isSignal: true, isRequired: false, transformFunction: null }, useContainerAsSizeProvider: { classPropertyName: "useContainerAsSizeProvider", publicName: "useContainerAsSizeProvider", isSignal: true, isRequired: false, transformFunction: null }, studioResolutionSize: { classPropertyName: "studioResolutionSize", publicName: "studioResolutionSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changeMouseOverScene: "changeMouseOverScene" }, host: { listeners: { "mouseover": "onMouseOver()", "mouseout": "onMouseOut()" } }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], ngImport: i0, template: "<div class=\"frame unreal-container\">\n <div #container [class.lightModeCursor]=\"lightMode()\" id=\"player\"></div>\n\n <app-unreal-status />\n <app-freeze-frame />\n <app-clickable-overlay />\n <app-afk-restart-screen-locker />\n\n <ng-content select=\"app-video-locker\"></ng-content>\n <ng-content select=\"app-pdf\"></ng-content>\n <ng-content select=\"app-show-case\"></ng-content>\n <ng-content select=\"app-low-bandwidth-detector\"></ng-content>\n</div>\n", styles: ["#player{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center;overflow:hidden}.unreal-container{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff}.unreal-container.select{cursor:url('data:image/svg+xml,<svg width=\"24\" height=\"24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M15.15 21.375a1.423 1.423 0 0 1-1.15.063 1.487 1.487 0 0 1-.85-.788l-3-6.45-2.325 3.25c-.283.4-.658.525-1.125.375-.467-.15-.7-.466-.7-.95V4.05c0-.417.188-.717.563-.9.375-.183.729-.142 1.062.125l10.1 7.95c.383.284.496.65.337 1.1-.158.45-.479.675-.962.675h-4.2l2.975 6.375c.183.384.204.767.063 1.15a1.487 1.487 0 0 1-.788.85Z\" fill=\"%23000\"/><path d=\"m12.697 20.861.002.005c.237.495.617.852 1.128 1.04.515.191 1.039.16 1.539-.08a1.987 1.987 0 0 0 1.04-1.128 1.922 1.922 0 0 0-.079-1.536v-.003L13.684 13.5H17.1c.32 0 .628-.075.89-.26.263-.184.438-.447.544-.749.106-.3.135-.617.04-.925a1.458 1.458 0 0 0-.545-.738L7.936 2.883a1.51 1.51 0 0 0-.768-.336 1.471 1.471 0 0 0-.825.154 1.495 1.495 0 0 0-.626.547 1.492 1.492 0 0 0-.217.802v12.825c0 .324.08.634.272.897.193.261.467.43.775.53.31.099.632.12.942.016.31-.103.555-.313.743-.578h.001l1.825-2.552 2.639 5.673Z\" stroke=\"%23fff\" stroke-opacity=\".7\"/></svg>') 6 2,auto}.unreal-container.orbit{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.998 21.6c-1.966 0-3.737-.53-5.312-1.588-1.575-1.058-2.738-2.437-3.488-4.137a.852.852 0 0 1 .013-.713.867.867 0 0 1 .512-.487.86.86 0 0 1 .688.037c.225.109.387.28.487.513.617 1.35 1.559 2.45 2.825 3.3 1.267.85 2.692 1.275 4.275 1.275 1.384 0 2.646-.33 3.788-.988a7.985 7.985 0 0 0 2.762-2.612h-.85a.873.873 0 0 1-.64-.257.863.863 0 0 1-.26-.638.88.88 0 0 1 .26-.643.864.864 0 0 1 .64-.262h3c.255 0 .47.086.642.259a.87.87 0 0 1 .258.64v3a.874.874 0 0 1-.257.642.863.863 0 0 1-.637.259.88.88 0 0 1-.643-.259.865.865 0 0 1-.263-.641v-.725a9.95 9.95 0 0 1-3.35 2.925c-1.35.733-2.833 1.1-4.45 1.1Zm0-17.4c-1.383 0-2.645.33-3.787.987A7.985 7.985 0 0 0 5.448 7.8h.85c.255 0 .47.086.642.257a.863.863 0 0 1 .258.638.88.88 0 0 1-.258.642.864.864 0 0 1-.642.263h-3a.87.87 0 0 1-.64-.259.87.87 0 0 1-.26-.641v-3c0-.255.086-.469.258-.641a.863.863 0 0 1 .637-.26.88.88 0 0 1 .643.26.864.864 0 0 1 .262.64v.726A9.95 9.95 0 0 1 7.548 3.5c1.35-.733 2.834-1.1 4.45-1.1 2 0 3.788.542 5.363 1.625 1.575 1.083 2.736 2.49 3.482 4.22a.724.724 0 0 1-.045.646.926.926 0 0 1-.525.437.87.87 0 0 1-.687-.04.977.977 0 0 1-.488-.513c-.616-1.35-1.558-2.45-2.825-3.3-1.266-.85-2.691-1.275-4.275-1.275Zm0 10.8a2.893 2.893 0 0 1-2.125-.875A2.893 2.893 0 0 1 8.998 12c0-.833.292-1.542.875-2.125A2.893 2.893 0 0 1 11.998 9c.834 0 1.542.292 2.125.875.584.583.875 1.292.875 2.125s-.291 1.542-.875 2.125a2.893 2.893 0 0 1-2.125.875Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"m2.739 16.072.002.005c.789 1.788 2.014 3.24 3.666 4.35 1.66 1.116 3.53 1.673 5.591 1.673 1.696 0 3.262-.386 4.69-1.16a10.54 10.54 0 0 0 2.714-2.097c.069.167.171.32.308.454.272.268.613.403.994.403.38 0 .722-.135.992-.406s.402-.614.402-.994v-3c0-.381-.134-.724-.405-.995a1.37 1.37 0 0 0-.995-.405h-3c-.383 0-.727.137-.997.411a1.38 1.38 0 0 0-.403.994c0 .38.135.723.407.992.238.237.532.369.858.397a7.552 7.552 0 0 1-2.027 1.685c-1.062.613-2.237.92-3.538.92-1.488 0-2.815-.397-3.996-1.19-1.19-.798-2.07-1.826-2.647-3.087-.147-.34-.393-.6-.727-.76a1.36 1.36 0 0 0-1.073-.058c-.37.133-.64.397-.8.754a1.351 1.351 0 0 0-.016 1.114Zm4.553-8.37a1.36 1.36 0 0 0-.858-.396A7.552 7.552 0 0 1 8.46 5.62c1.062-.613 2.237-.921 3.537-.921 1.489 0 2.816.398 3.997 1.19 1.19.8 2.07 1.827 2.646 3.088.148.34.394.6.728.76a1.37 1.37 0 0 0 1.068.063c.347-.12.62-.346.799-.668.187-.338.215-.71.072-1.072l-.003-.007-.003-.007c-.784-1.818-2.006-3.298-3.658-4.434C15.982 2.47 14.094 1.9 11.998 1.9c-1.695 0-3.261.385-4.688 1.16a10.536 10.536 0 0 0-2.715 2.096 1.364 1.364 0 0 0-.308-.453 1.38 1.38 0 0 0-.994-.403c-.38 0-.722.135-.992.406s-.403.614-.403.994v3c0 .38.135.724.406.995.27.27.614.405.994.405h3c.384 0 .727-.137.998-.412a1.38 1.38 0 0 0 .402-.993c0-.38-.135-.723-.406-.992ZM9.52 14.48a3.393 3.393 0 0 0 2.478 1.02c.965 0 1.8-.342 2.479-1.02a3.392 3.392 0 0 0 1.021-2.48c0-.963-.343-1.8-1.021-2.478A3.392 3.392 0 0 0 11.998 8.5c-.964 0-1.8.343-2.478 1.021A3.392 3.392 0 0 0 8.498 12c0 .964.344 1.8 1.022 2.479Z\"/></svg>') 12 12,auto}.unreal-container.pan{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.997 10.075a1.01 1.01 0 0 1-.738-.313 1.008 1.008 0 0 1-.312-.737V6.15l-.95.95a.998.998 0 0 1-.763.312 1.09 1.09 0 0 1-.763-.337 1.06 1.06 0 0 1-.324-.775c0-.3.108-.558.324-.775l2.776-2.775c.117-.116.237-.2.362-.25a1.04 1.04 0 0 1 .775 0c.125.05.246.134.363.25l2.8 2.8c.216.217.32.47.313.762a1.096 1.096 0 0 1-.338.763 1.058 1.058 0 0 1-.775.325c-.3 0-.558-.108-.775-.325l-.925-.925v2.875c0 .283-.104.53-.312.737a1.01 1.01 0 0 1-.738.313Zm0 11.5a1.04 1.04 0 0 1-.388-.075 1.103 1.103 0 0 1-.362-.25l-2.8-2.8a1.002 1.002 0 0 1-.313-.762c.009-.292.121-.546.337-.763.217-.217.475-.325.775-.325.3 0 .559.108.775.325l.926.925v-2.875c0-.283.104-.529.312-.737a1.01 1.01 0 0 1 .738-.313c.283 0 .529.104.738.313.208.208.312.454.312.737v2.875l.95-.95a.998.998 0 0 1 .762-.312c.292.008.546.12.763.337.216.217.324.475.324.775 0 .3-.108.559-.324.775l-2.775 2.775c-.117.116-.238.2-.363.25a1.04 1.04 0 0 1-.387.075Zm4.925-6.05a1.058 1.058 0 0 1-.326-.774c0-.3.109-.559.326-.776l.925-.925H14.97c-.283 0-.528-.104-.736-.312a1.01 1.01 0 0 1-.313-.738c0-.283.104-.53.313-.738.208-.208.453-.312.737-.312h2.875l-.95-.95a.998.998 0 0 1-.313-.762 1.09 1.09 0 0 1 .338-.764 1.06 1.06 0 0 1 .775-.324c.3 0 .558.108.775.324l2.774 2.776c.117.117.2.238.251.363a1.04 1.04 0 0 1 0 .775c-.05.124-.134.245-.25.362l-2.8 2.8c-.217.216-.471.32-.762.313a1.096 1.096 0 0 1-.763-.337Zm-11.4 0L2.746 12.75a1.103 1.103 0 0 1-.25-.362 1.04 1.04 0 0 1 0-.775c.05-.125.133-.246.25-.363l2.8-2.8c.216-.216.47-.32.762-.313.292.01.546.121.763.337.217.217.325.476.325.776 0 .3-.108.558-.325.775l-.925.925h2.875c.283 0 .529.104.737.312.208.209.313.455.313.738s-.105.53-.313.738a1.008 1.008 0 0 1-.737.312H6.147l.95.95c.217.217.32.471.312.762a1.09 1.09 0 0 1-.337.764 1.06 1.06 0 0 1-.775.324c-.3 0-.559-.108-.776-.324Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M9.22 7.912c.436.013.821-.15 1.13-.458l.097-.097v1.668c0 .422.16.793.458 1.09.298.299.67.46 1.092.46.422 0 .793-.161 1.091-.46.298-.297.459-.668.459-1.09V7.357l.071.072c.309.308.693.471 1.13.471.435 0 .82-.163 1.128-.471.301-.303.47-.676.483-1.102v-.001a1.501 1.501 0 0 0-.46-1.13l-2.799-2.8a1.6 1.6 0 0 0-.528-.36l-.003-.001a1.54 1.54 0 0 0-1.145 0l-.002.001a1.6 1.6 0 0 0-.529.36L8.118 5.172l-.001.001a1.56 1.56 0 0 0-.47 1.128c0 .435.163.82.47 1.128.303.303.676.472 1.103.484Zm0 0 .014-.5-.015.5Zm-1.311 6.865a1.497 1.497 0 0 0-.458-1.13l-.097-.097h1.668c.421 0 .793-.16 1.09-.458.298-.299.46-.67.46-1.092a1.51 1.51 0 0 0-.46-1.091 1.507 1.507 0 0 0-1.09-.459H7.354l.071-.072c.309-.308.472-.692.472-1.128 0-.436-.163-.82-.472-1.13a1.596 1.596 0 0 0-1.101-.482h-.002a1.501 1.501 0 0 0-1.129.459l-2.8 2.8a1.6 1.6 0 0 0-.36.528l-.001.003a1.54 1.54 0 0 0 0 1.144v.003c.081.2.208.375.362.529l2.774 2.775c.309.308.693.47 1.129.47.435 0 .82-.162 1.128-.47.303-.302.472-.676.484-1.102Zm0 0-.5-.014.5.015v-.001Zm3.038-8.627-.5.5.5.207V6.15Zm.475 15.814.002.001a1.54 1.54 0 0 0 1.145 0h.002c.2-.081.375-.208.53-.362l2.775-2.774a1.56 1.56 0 0 0 .47-1.129c0-.435-.162-.82-.47-1.128a1.589 1.589 0 0 0-1.103-.484 1.497 1.497 0 0 0-1.13.458l-.096.097v-1.668c0-.421-.161-.793-.459-1.09a1.509 1.509 0 0 0-1.091-.46c-.422 0-.794.161-1.092.46a1.507 1.507 0 0 0-.458 1.09v1.668l-.072-.072a1.558 1.558 0 0 0-1.129-.471c-.436 0-.82.163-1.128.471l-.001.001c-.301.302-.47.675-.483 1.101v.001c-.012.437.151.821.46 1.13l2.8 2.8c.153.153.328.28.528.36Zm5.146-6.085c.303.302.676.47 1.102.484a1.5 1.5 0 0 0 1.13-.46l2.8-2.8a1.6 1.6 0 0 0 .36-.528l.002-.003a1.54 1.54 0 0 0 0-1.144l-.001-.003a1.6 1.6 0 0 0-.361-.529l-2.774-2.775h-.001a1.56 1.56 0 0 0-1.128-.47c-.436 0-.82.162-1.128.47h-.001a1.59 1.59 0 0 0-.484 1.102c-.012.437.15.822.459 1.13l.097.097h-1.668c-.422 0-.793.16-1.09.459-.299.298-.46.67-.46 1.091 0 .422.161.793.46 1.092.297.297.668.458 1.09.458h1.668l-.072.072a1.558 1.558 0 0 0-.472 1.129c0 .435.164.82.472 1.128ZM6.147 13.05l.5.5.207-.5h-.707Z\"/></svg>') 12 12,auto}.unreal-container.dolly{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><mask id=\"a\" width=\"24\" height=\"24\" x=\"0\" y=\"0\" maskUnits=\"userSpaceOnUse\" style=\"mask-type:alpha\"><path fill=\"%23127392\" d=\"M0 0h24v24H0z\"/></mask><g mask=\"url(%23a)\"><path fill=\"%23000\" d=\"M12.002 20.575a1.026 1.026 0 0 1-.75-.325l-2.55-2.55a.933.933 0 0 1-.287-.713c.008-.275.113-.513.313-.713.2-.2.441-.3.725-.3.283 0 .524.1.724.3l.776.776V6.95l-.8.8c-.2.2-.438.296-.712.288a1.018 1.018 0 0 1-.713-.312c-.2-.2-.3-.442-.3-.726s.1-.525.3-.725l2.524-2.525a1.026 1.026 0 0 1 1.5 0l2.55 2.55c.2.2.296.438.289.713a1.023 1.023 0 0 1-.314.713c-.2.2-.441.3-.724.3a.988.988 0 0 1-.725-.3l-.775-.776v10.1l.8-.8c.2-.2.437-.296.712-.288.275.008.512.112.712.312.2.2.3.442.3.726s-.1.525-.3.725l-2.524 2.525a1.026 1.026 0 0 1-.75.325Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M13.553 8.153c.28.245.62.373 1 .373.415 0 .784-.153 1.078-.447.287-.287.446-.644.46-1.05v-.002a1.432 1.432 0 0 0-.435-1.08l-2.55-2.55-.354.353.354-.354a1.526 1.526 0 0 0-2.207 0L8.374 5.921A1.487 1.487 0 0 0 7.928 7c0 .416.152.785.446 1.08.288.287.645.446 1.052.457l4.127-.384Zm0 0v7.693m0-7.693a1.667 1.667 0 0 1-.079-.074l-3.021.075v7.693a1.482 1.482 0 0 0-1-.373c-.416 0-.785.153-1.079.447a1.522 1.522 0 0 0-.46 1.05v.002c-.011.417.14.786.435 1.08l2.55 2.55a1.527 1.527 0 0 0 2.207 0l2.525-2.524c.294-.294.446-.663.446-1.079 0-.416-.152-.785-.446-1.08a1.518 1.518 0 0 0-1.052-.457m0 0-.014.5.014-.5Zm0 0a1.424 1.424 0 0 0-1.027.383m0 0a1.583 1.583 0 0 0-.053.05l.354.354m-.3-.404v.104l.3.3m0 0-.3-.3v.6l.3-.3Z\"/></g></svg>') 12 12,auto}.unreal-container.disabled:before{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}#playerUI{position:absolute;z-index:10;width:100%}#statsContainer{display:none;text-align:left;background-color:#000c}#stats{padding:6px;font-weight:700;font-size:14px;color:#0f0}canvas{position:absolute;image-rendering:crisp-edges}#overlay{position:absolute;top:0;right:2%;z-index:100;padding:4px;border-top-width:0;-webkit-border-bottom-right-radius:5px;-moz-border-radius-bottomright:5px;border-bottom-right-radius:5px;-webkit-border-bottom-left-radius:5px;-moz-border-radius-bottomleft:5px;border-bottom-left-radius:5px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none}.overlay{font-weight:lighter;font-family:var(--src-font-family-body)}#overlayButton:hover{cursor:pointer}#overlayButton{font-size:40px;text-align:right}#overlaySettings{display:none;width:300px;padding:4px}#videoMessageOverlay{position:absolute;z-index:20;width:100%;margin:auto;font-size:1.8em;font-family:var(--src-font-family-body)}#playButton{display:inline-block;height:auto}img#playButton{width:10%;max-width:241px}#UIInteraction{position:fixed}#UIInteractionButtonBoundary{padding:2px}#UIInteractionButton{cursor:pointer}#hiddenInput{position:absolute;left:-10%;width:0;opacity:0}#editTextButton{position:absolute;width:40px;height:40px}\n"], dependencies: [{ kind: "component", type: AfkRestartScreenLockerComponent, selector: "app-afk-restart-screen-locker" }, { kind: "component", type: ClickableOverlayComponent, selector: "app-clickable-overlay" }, { kind: "component", type: UnrealStatusComponent, selector: "app-unreal-status" }, { kind: "component", type: FreezeFrameComponent, selector: "app-freeze-frame" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4630
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealSceneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4631
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.2.2", type: UnrealSceneComponent, isStandalone: true, selector: "app-unreal-scene", inputs: { isStudio: { classPropertyName: "isStudio", publicName: "isStudio", isSignal: true, isRequired: false, transformFunction: null }, useContainerAsSizeProvider: { classPropertyName: "useContainerAsSizeProvider", publicName: "useContainerAsSizeProvider", isSignal: true, isRequired: false, transformFunction: null }, studioResolutionSize: { classPropertyName: "studioResolutionSize", publicName: "studioResolutionSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changeMouseOverScene: "changeMouseOverScene" }, host: { listeners: { "mouseover": "onMouseOver()", "mouseout": "onMouseOut()" } }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], ngImport: i0, template: "<div class=\"frame unreal-container\">\n <div #container [class.lightModeCursor]=\"lightMode()\" id=\"player\"></div>\n\n <app-unreal-status />\n <app-freeze-frame />\n <app-clickable-overlay />\n <app-afk-restart-screen-locker />\n\n <ng-content select=\"app-video-locker\"></ng-content>\n <ng-content select=\"app-pdf\"></ng-content>\n <ng-content select=\"app-show-case\"></ng-content>\n <ng-content select=\"app-low-bandwidth-detector\"></ng-content>\n</div>\n", styles: ["#player{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center;overflow:hidden}.unreal-container{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff}.unreal-container.select{cursor:url('data:image/svg+xml,<svg width=\"24\" height=\"24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M15.15 21.375a1.423 1.423 0 0 1-1.15.063 1.487 1.487 0 0 1-.85-.788l-3-6.45-2.325 3.25c-.283.4-.658.525-1.125.375-.467-.15-.7-.466-.7-.95V4.05c0-.417.188-.717.563-.9.375-.183.729-.142 1.062.125l10.1 7.95c.383.284.496.65.337 1.1-.158.45-.479.675-.962.675h-4.2l2.975 6.375c.183.384.204.767.063 1.15a1.487 1.487 0 0 1-.788.85Z\" fill=\"%23000\"/><path d=\"m12.697 20.861.002.005c.237.495.617.852 1.128 1.04.515.191 1.039.16 1.539-.08a1.987 1.987 0 0 0 1.04-1.128 1.922 1.922 0 0 0-.079-1.536v-.003L13.684 13.5H17.1c.32 0 .628-.075.89-.26.263-.184.438-.447.544-.749.106-.3.135-.617.04-.925a1.458 1.458 0 0 0-.545-.738L7.936 2.883a1.51 1.51 0 0 0-.768-.336 1.471 1.471 0 0 0-.825.154 1.495 1.495 0 0 0-.626.547 1.492 1.492 0 0 0-.217.802v12.825c0 .324.08.634.272.897.193.261.467.43.775.53.31.099.632.12.942.016.31-.103.555-.313.743-.578h.001l1.825-2.552 2.639 5.673Z\" stroke=\"%23fff\" stroke-opacity=\".7\"/></svg>') 6 2,auto}.unreal-container.orbit{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.998 21.6c-1.966 0-3.737-.53-5.312-1.588-1.575-1.058-2.738-2.437-3.488-4.137a.852.852 0 0 1 .013-.713.867.867 0 0 1 .512-.487.86.86 0 0 1 .688.037c.225.109.387.28.487.513.617 1.35 1.559 2.45 2.825 3.3 1.267.85 2.692 1.275 4.275 1.275 1.384 0 2.646-.33 3.788-.988a7.985 7.985 0 0 0 2.762-2.612h-.85a.873.873 0 0 1-.64-.257.863.863 0 0 1-.26-.638.88.88 0 0 1 .26-.643.864.864 0 0 1 .64-.262h3c.255 0 .47.086.642.259a.87.87 0 0 1 .258.64v3a.874.874 0 0 1-.257.642.863.863 0 0 1-.637.259.88.88 0 0 1-.643-.259.865.865 0 0 1-.263-.641v-.725a9.95 9.95 0 0 1-3.35 2.925c-1.35.733-2.833 1.1-4.45 1.1Zm0-17.4c-1.383 0-2.645.33-3.787.987A7.985 7.985 0 0 0 5.448 7.8h.85c.255 0 .47.086.642.257a.863.863 0 0 1 .258.638.88.88 0 0 1-.258.642.864.864 0 0 1-.642.263h-3a.87.87 0 0 1-.64-.259.87.87 0 0 1-.26-.641v-3c0-.255.086-.469.258-.641a.863.863 0 0 1 .637-.26.88.88 0 0 1 .643.26.864.864 0 0 1 .262.64v.726A9.95 9.95 0 0 1 7.548 3.5c1.35-.733 2.834-1.1 4.45-1.1 2 0 3.788.542 5.363 1.625 1.575 1.083 2.736 2.49 3.482 4.22a.724.724 0 0 1-.045.646.926.926 0 0 1-.525.437.87.87 0 0 1-.687-.04.977.977 0 0 1-.488-.513c-.616-1.35-1.558-2.45-2.825-3.3-1.266-.85-2.691-1.275-4.275-1.275Zm0 10.8a2.893 2.893 0 0 1-2.125-.875A2.893 2.893 0 0 1 8.998 12c0-.833.292-1.542.875-2.125A2.893 2.893 0 0 1 11.998 9c.834 0 1.542.292 2.125.875.584.583.875 1.292.875 2.125s-.291 1.542-.875 2.125a2.893 2.893 0 0 1-2.125.875Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"m2.739 16.072.002.005c.789 1.788 2.014 3.24 3.666 4.35 1.66 1.116 3.53 1.673 5.591 1.673 1.696 0 3.262-.386 4.69-1.16a10.54 10.54 0 0 0 2.714-2.097c.069.167.171.32.308.454.272.268.613.403.994.403.38 0 .722-.135.992-.406s.402-.614.402-.994v-3c0-.381-.134-.724-.405-.995a1.37 1.37 0 0 0-.995-.405h-3c-.383 0-.727.137-.997.411a1.38 1.38 0 0 0-.403.994c0 .38.135.723.407.992.238.237.532.369.858.397a7.552 7.552 0 0 1-2.027 1.685c-1.062.613-2.237.92-3.538.92-1.488 0-2.815-.397-3.996-1.19-1.19-.798-2.07-1.826-2.647-3.087-.147-.34-.393-.6-.727-.76a1.36 1.36 0 0 0-1.073-.058c-.37.133-.64.397-.8.754a1.351 1.351 0 0 0-.016 1.114Zm4.553-8.37a1.36 1.36 0 0 0-.858-.396A7.552 7.552 0 0 1 8.46 5.62c1.062-.613 2.237-.921 3.537-.921 1.489 0 2.816.398 3.997 1.19 1.19.8 2.07 1.827 2.646 3.088.148.34.394.6.728.76a1.37 1.37 0 0 0 1.068.063c.347-.12.62-.346.799-.668.187-.338.215-.71.072-1.072l-.003-.007-.003-.007c-.784-1.818-2.006-3.298-3.658-4.434C15.982 2.47 14.094 1.9 11.998 1.9c-1.695 0-3.261.385-4.688 1.16a10.536 10.536 0 0 0-2.715 2.096 1.364 1.364 0 0 0-.308-.453 1.38 1.38 0 0 0-.994-.403c-.38 0-.722.135-.992.406s-.403.614-.403.994v3c0 .38.135.724.406.995.27.27.614.405.994.405h3c.384 0 .727-.137.998-.412a1.38 1.38 0 0 0 .402-.993c0-.38-.135-.723-.406-.992ZM9.52 14.48a3.393 3.393 0 0 0 2.478 1.02c.965 0 1.8-.342 2.479-1.02a3.392 3.392 0 0 0 1.021-2.48c0-.963-.343-1.8-1.021-2.478A3.392 3.392 0 0 0 11.998 8.5c-.964 0-1.8.343-2.478 1.021A3.392 3.392 0 0 0 8.498 12c0 .964.344 1.8 1.022 2.479Z\"/></svg>') 12 12,auto}.unreal-container.pan{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.997 10.075a1.01 1.01 0 0 1-.738-.313 1.008 1.008 0 0 1-.312-.737V6.15l-.95.95a.998.998 0 0 1-.763.312 1.09 1.09 0 0 1-.763-.337 1.06 1.06 0 0 1-.324-.775c0-.3.108-.558.324-.775l2.776-2.775c.117-.116.237-.2.362-.25a1.04 1.04 0 0 1 .775 0c.125.05.246.134.363.25l2.8 2.8c.216.217.32.47.313.762a1.096 1.096 0 0 1-.338.763 1.058 1.058 0 0 1-.775.325c-.3 0-.558-.108-.775-.325l-.925-.925v2.875c0 .283-.104.53-.312.737a1.01 1.01 0 0 1-.738.313Zm0 11.5a1.04 1.04 0 0 1-.388-.075 1.103 1.103 0 0 1-.362-.25l-2.8-2.8a1.002 1.002 0 0 1-.313-.762c.009-.292.121-.546.337-.763.217-.217.475-.325.775-.325.3 0 .559.108.775.325l.926.925v-2.875c0-.283.104-.529.312-.737a1.01 1.01 0 0 1 .738-.313c.283 0 .529.104.738.313.208.208.312.454.312.737v2.875l.95-.95a.998.998 0 0 1 .762-.312c.292.008.546.12.763.337.216.217.324.475.324.775 0 .3-.108.559-.324.775l-2.775 2.775c-.117.116-.238.2-.363.25a1.04 1.04 0 0 1-.387.075Zm4.925-6.05a1.058 1.058 0 0 1-.326-.774c0-.3.109-.559.326-.776l.925-.925H14.97c-.283 0-.528-.104-.736-.312a1.01 1.01 0 0 1-.313-.738c0-.283.104-.53.313-.738.208-.208.453-.312.737-.312h2.875l-.95-.95a.998.998 0 0 1-.313-.762 1.09 1.09 0 0 1 .338-.764 1.06 1.06 0 0 1 .775-.324c.3 0 .558.108.775.324l2.774 2.776c.117.117.2.238.251.363a1.04 1.04 0 0 1 0 .775c-.05.124-.134.245-.25.362l-2.8 2.8c-.217.216-.471.32-.762.313a1.096 1.096 0 0 1-.763-.337Zm-11.4 0L2.746 12.75a1.103 1.103 0 0 1-.25-.362 1.04 1.04 0 0 1 0-.775c.05-.125.133-.246.25-.363l2.8-2.8c.216-.216.47-.32.762-.313.292.01.546.121.763.337.217.217.325.476.325.776 0 .3-.108.558-.325.775l-.925.925h2.875c.283 0 .529.104.737.312.208.209.313.455.313.738s-.105.53-.313.738a1.008 1.008 0 0 1-.737.312H6.147l.95.95c.217.217.32.471.312.762a1.09 1.09 0 0 1-.337.764 1.06 1.06 0 0 1-.775.324c-.3 0-.559-.108-.776-.324Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M9.22 7.912c.436.013.821-.15 1.13-.458l.097-.097v1.668c0 .422.16.793.458 1.09.298.299.67.46 1.092.46.422 0 .793-.161 1.091-.46.298-.297.459-.668.459-1.09V7.357l.071.072c.309.308.693.471 1.13.471.435 0 .82-.163 1.128-.471.301-.303.47-.676.483-1.102v-.001a1.501 1.501 0 0 0-.46-1.13l-2.799-2.8a1.6 1.6 0 0 0-.528-.36l-.003-.001a1.54 1.54 0 0 0-1.145 0l-.002.001a1.6 1.6 0 0 0-.529.36L8.118 5.172l-.001.001a1.56 1.56 0 0 0-.47 1.128c0 .435.163.82.47 1.128.303.303.676.472 1.103.484Zm0 0 .014-.5-.015.5Zm-1.311 6.865a1.497 1.497 0 0 0-.458-1.13l-.097-.097h1.668c.421 0 .793-.16 1.09-.458.298-.299.46-.67.46-1.092a1.51 1.51 0 0 0-.46-1.091 1.507 1.507 0 0 0-1.09-.459H7.354l.071-.072c.309-.308.472-.692.472-1.128 0-.436-.163-.82-.472-1.13a1.596 1.596 0 0 0-1.101-.482h-.002a1.501 1.501 0 0 0-1.129.459l-2.8 2.8a1.6 1.6 0 0 0-.36.528l-.001.003a1.54 1.54 0 0 0 0 1.144v.003c.081.2.208.375.362.529l2.774 2.775c.309.308.693.47 1.129.47.435 0 .82-.162 1.128-.47.303-.302.472-.676.484-1.102Zm0 0-.5-.014.5.015v-.001Zm3.038-8.627-.5.5.5.207V6.15Zm.475 15.814.002.001a1.54 1.54 0 0 0 1.145 0h.002c.2-.081.375-.208.53-.362l2.775-2.774a1.56 1.56 0 0 0 .47-1.129c0-.435-.162-.82-.47-1.128a1.589 1.589 0 0 0-1.103-.484 1.497 1.497 0 0 0-1.13.458l-.096.097v-1.668c0-.421-.161-.793-.459-1.09a1.509 1.509 0 0 0-1.091-.46c-.422 0-.794.161-1.092.46a1.507 1.507 0 0 0-.458 1.09v1.668l-.072-.072a1.558 1.558 0 0 0-1.129-.471c-.436 0-.82.163-1.128.471l-.001.001c-.301.302-.47.675-.483 1.101v.001c-.012.437.151.821.46 1.13l2.8 2.8c.153.153.328.28.528.36Zm5.146-6.085c.303.302.676.47 1.102.484a1.5 1.5 0 0 0 1.13-.46l2.8-2.8a1.6 1.6 0 0 0 .36-.528l.002-.003a1.54 1.54 0 0 0 0-1.144l-.001-.003a1.6 1.6 0 0 0-.361-.529l-2.774-2.775h-.001a1.56 1.56 0 0 0-1.128-.47c-.436 0-.82.162-1.128.47h-.001a1.59 1.59 0 0 0-.484 1.102c-.012.437.15.822.459 1.13l.097.097h-1.668c-.422 0-.793.16-1.09.459-.299.298-.46.67-.46 1.091 0 .422.161.793.46 1.092.297.297.668.458 1.09.458h1.668l-.072.072a1.558 1.558 0 0 0-.472 1.129c0 .435.164.82.472 1.128ZM6.147 13.05l.5.5.207-.5h-.707Z\"/></svg>') 12 12,auto}.unreal-container.dolly{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><mask id=\"a\" width=\"24\" height=\"24\" x=\"0\" y=\"0\" maskUnits=\"userSpaceOnUse\" style=\"mask-type:alpha\"><path fill=\"%23127392\" d=\"M0 0h24v24H0z\"/></mask><g mask=\"url(%23a)\"><path fill=\"%23000\" d=\"M12.002 20.575a1.026 1.026 0 0 1-.75-.325l-2.55-2.55a.933.933 0 0 1-.287-.713c.008-.275.113-.513.313-.713.2-.2.441-.3.725-.3.283 0 .524.1.724.3l.776.776V6.95l-.8.8c-.2.2-.438.296-.712.288a1.018 1.018 0 0 1-.713-.312c-.2-.2-.3-.442-.3-.726s.1-.525.3-.725l2.524-2.525a1.026 1.026 0 0 1 1.5 0l2.55 2.55c.2.2.296.438.289.713a1.023 1.023 0 0 1-.314.713c-.2.2-.441.3-.724.3a.988.988 0 0 1-.725-.3l-.775-.776v10.1l.8-.8c.2-.2.437-.296.712-.288.275.008.512.112.712.312.2.2.3.442.3.726s-.1.525-.3.725l-2.524 2.525a1.026 1.026 0 0 1-.75.325Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M13.553 8.153c.28.245.62.373 1 .373.415 0 .784-.153 1.078-.447.287-.287.446-.644.46-1.05v-.002a1.432 1.432 0 0 0-.435-1.08l-2.55-2.55-.354.353.354-.354a1.526 1.526 0 0 0-2.207 0L8.374 5.921A1.487 1.487 0 0 0 7.928 7c0 .416.152.785.446 1.08.288.287.645.446 1.052.457l4.127-.384Zm0 0v7.693m0-7.693a1.667 1.667 0 0 1-.079-.074l-3.021.075v7.693a1.482 1.482 0 0 0-1-.373c-.416 0-.785.153-1.079.447a1.522 1.522 0 0 0-.46 1.05v.002c-.011.417.14.786.435 1.08l2.55 2.55a1.527 1.527 0 0 0 2.207 0l2.525-2.524c.294-.294.446-.663.446-1.079 0-.416-.152-.785-.446-1.08a1.518 1.518 0 0 0-1.052-.457m0 0-.014.5.014-.5Zm0 0a1.424 1.424 0 0 0-1.027.383m0 0a1.583 1.583 0 0 0-.053.05l.354.354m-.3-.404v.104l.3.3m0 0-.3-.3v.6l.3-.3Z\"/></g></svg>') 12 12,auto}.unreal-container.disabled:before{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}#playerUI{position:absolute;z-index:10;width:100%}#statsContainer{display:none;text-align:left;background-color:#000c}#stats{padding:6px;font-weight:700;font-size:14px;color:#0f0}canvas{position:absolute;image-rendering:crisp-edges}#overlay{position:absolute;top:0;right:2%;z-index:100;padding:4px;border-top-width:0;-webkit-border-bottom-right-radius:5px;-moz-border-radius-bottomright:5px;border-bottom-right-radius:5px;-webkit-border-bottom-left-radius:5px;-moz-border-radius-bottomleft:5px;border-bottom-left-radius:5px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none}.overlay{font-weight:lighter;font-family:var(--src-font-family-body)}#overlayButton:hover{cursor:pointer}#overlayButton{font-size:40px;text-align:right}#overlaySettings{display:none;width:300px;padding:4px}#videoMessageOverlay{position:absolute;z-index:20;width:100%;margin:auto;font-size:1.8em;font-family:var(--src-font-family-body)}#playButton{display:inline-block;height:auto}img#playButton{width:10%;max-width:241px}#UIInteraction{position:fixed}#UIInteractionButtonBoundary{padding:2px}#UIInteractionButton{cursor:pointer}#hiddenInput{position:absolute;left:-10%;width:0;opacity:0}#editTextButton{position:absolute;width:40px;height:40px}\n"], dependencies: [{ kind: "component", type: AfkRestartScreenLockerComponent, selector: "app-afk-restart-screen-locker" }, { kind: "component", type: ClickableOverlayComponent, selector: "app-clickable-overlay" }, { kind: "component", type: UnrealStatusComponent, selector: "app-unreal-status" }, { kind: "component", type: FreezeFrameComponent, selector: "app-freeze-frame" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4452
4632
  }
4453
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UnrealSceneComponent, decorators: [{
4633
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: UnrealSceneComponent, decorators: [{
4454
4634
  type: Component,
4455
4635
  args: [{ selector: 'app-unreal-scene', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
4456
4636
  AfkRestartScreenLockerComponent,
@@ -4503,6 +4683,7 @@ class VideoLockerComponent {
4503
4683
  .subscribe((src) => this.createVideo(src));
4504
4684
  }
4505
4685
  createVideo(src) {
4686
+ const matchAbsolutePath = !!src.match(/.*\.(mp4|WebM)(\?.*)?$/i);
4506
4687
  // Select the parent layout where the video will be inserted
4507
4688
  const videoContainer = this.videoElement.nativeElement;
4508
4689
  videoContainer.innerHTML = ''; // This will remove all existing content within the layout
@@ -4511,13 +4692,20 @@ class VideoLockerComponent {
4511
4692
  videoElement.autoplay = true;
4512
4693
  videoElement.muted = true;
4513
4694
  videoElement.loop = true;
4514
- videoElement.poster = `${src}/cover.webp`;
4695
+ if (!matchAbsolutePath) {
4696
+ videoElement.poster = `${src}/cover.webp`;
4697
+ }
4515
4698
  videoElement.style.position = 'absolute';
4516
4699
  videoElement.style.width = '100%';
4517
4700
  videoElement.style.height = '100%';
4518
4701
  videoElement.style.objectFit = 'cover';
4519
4702
  const sourceElement = document.createElement('source');
4520
- sourceElement.src = `${src}/video.mp4`;
4703
+ if (matchAbsolutePath) {
4704
+ sourceElement.src = src;
4705
+ }
4706
+ else {
4707
+ sourceElement.src = `${src}/video.mp4`;
4708
+ }
4521
4709
  sourceElement.type = 'video/mp4';
4522
4710
  // Append the source element to the video element
4523
4711
  videoElement.appendChild(sourceElement);
@@ -4526,10 +4714,10 @@ class VideoLockerComponent {
4526
4714
  // Append the video element to the layout
4527
4715
  videoContainer.appendChild(videoElement);
4528
4716
  }
4529
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: VideoLockerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4530
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: VideoLockerComponent, isStandalone: true, selector: "app-video-locker", viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoPlayerCover"], descendants: true }], ngImport: i0, template: "@if (imageIntroSrc$ | async; as url) {\n <div class=\"backImage\">\n <img [ngSrc]=\"url\" fill alt=\"Loading...\" />\n </div>\n} @else if (videoIntroSrc$ | async) {\n <div #videoPlayerCover class=\"backVideo\"></div>\n}\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4717
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: VideoLockerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4718
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.2", type: VideoLockerComponent, isStandalone: true, selector: "app-video-locker", viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoPlayerCover"], descendants: true }], ngImport: i0, template: "@if (imageIntroSrc$ | async; as url) {\n <div class=\"backImage\">\n <img [ngSrc]=\"url\" fill alt=\"Loading...\" />\n </div>\n} @else if (videoIntroSrc$ | async) {\n <div #videoPlayerCover class=\"backVideo\"></div>\n}\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4531
4719
  }
4532
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: VideoLockerComponent, decorators: [{
4720
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: VideoLockerComponent, decorators: [{
4533
4721
  type: Component,
4534
4722
  args: [{ selector: 'app-video-locker', imports: [AsyncPipe, NgOptimizedImage], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (imageIntroSrc$ | async; as url) {\n <div class=\"backImage\">\n <img [ngSrc]=\"url\" fill alt=\"Loading...\" />\n </div>\n} @else if (videoIntroSrc$ | async) {\n <div #videoPlayerCover class=\"backVideo\"></div>\n}\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"] }]
4535
4723
  }], propDecorators: { videoElement: [{
@@ -4551,15 +4739,18 @@ class WebrtcErrorModalComponent {
4551
4739
  .pipe(first())
4552
4740
  .subscribe((isConnected) => {
4553
4741
  if (!isConnected) {
4554
- this.store.dispatch(destroyConnectionsAndResetState());
4742
+ this.store.dispatch(disconnectStream({
4743
+ reason: DisconnectReason.DropConnection,
4744
+ message: 'Modal Drop Connection',
4745
+ }));
4555
4746
  }
4556
4747
  this.close();
4557
4748
  });
4558
4749
  }
4559
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: WebrtcErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4560
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.1", type: WebrtcErrorModalComponent, isStandalone: true, selector: "app-webrtc-error-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"src-modal\">\n <header class=\"src-modal__header\">\n <div [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n WebRTC error\n </div>\n </header>\n <section class=\"src-modal__body\">\n <div style=\"text-align: left\">\n An internet connection type (WebRTC) appears to be blocked either by your\n browser settings or your current network. If WebRTC is blocked on your\n browser you may be able to adjust these settings yourself based on\n instructions here:\n <a href=\"https://myownconference.com/blog/en/webrtc/\" target=\"blank\"\n >https://myownconference.com/blog/en/webrtc/</a\n >\n <br /><br />\n Trying a different web browser may help confirm this as well. If WebRTC is\n blocked by your network, try switching to a different network if possible\n or contact your network administrator.<br /><br />\n WebRTC is common, safe and increasingly utilised method for streaming real\n time 3D experiences via a web browser. It typically consumes no more\n bandwidth than streaming an HD video.\n </div>\n </section>\n <footer class=\"src-modal__footer\">\n <src-button\n (onClick)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n [colorScheme]=\"'primary'\"\n >\n Ok\n </src-button>\n </footer>\n</div>\n", styles: [".src-modal{--modalBodyPadding: 20px 8px 20px 20px;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto}.src-modal ::ng-deep .ng-scroll-content{--_scrollbar-content-width: initial;--_viewport-padding-right: 12px}.src-modal__body{width:100%}.src-modal__body p{color:var(--src-color-bg-default, #1f2937);font-size:14px;font-style:normal;font-weight:400;line-height:24px;margin-top:0}.src-modal__body a{word-wrap:break-word;white-space:normal}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "colorScheme", "size", "state", "customClass", "hasDisclosure", "isFullWidth", "isPressed", "isDisabled", "isLoading", "iconButton", "srcButtonConfig", "formID", "data-testid"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4750
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: WebrtcErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4751
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.2", type: WebrtcErrorModalComponent, isStandalone: true, selector: "app-webrtc-error-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"src-modal\">\n <header class=\"src-modal__header\">\n <div [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n WebRTC error\n </div>\n </header>\n <section class=\"src-modal__body\">\n <div style=\"text-align: left\">\n An internet connection type (WebRTC) appears to be blocked either by your\n browser settings or your current network. If WebRTC is blocked on your\n browser you may be able to adjust these settings yourself based on\n instructions here:\n <a href=\"https://myownconference.com/blog/en/webrtc/\" target=\"blank\"\n >https://myownconference.com/blog/en/webrtc/</a\n >\n <br /><br />\n Trying a different web browser may help confirm this as well. If WebRTC is\n blocked by your network, try switching to a different network if possible\n or contact your network administrator.<br /><br />\n WebRTC is common, safe and increasingly utilised method for streaming real\n time 3D experiences via a web browser. It typically consumes no more\n bandwidth than streaming an HD video.\n </div>\n </section>\n <footer class=\"src-modal__footer\">\n <src-button\n (onClick)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n [colorScheme]=\"'primary'\"\n >\n Ok\n </src-button>\n </footer>\n</div>\n", styles: [".src-modal{--modalBodyPadding: 20px 8px 20px 20px;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto}.src-modal ::ng-deep .ng-scroll-content{--_scrollbar-content-width: initial;--_viewport-padding-right: 12px}.src-modal__body{width:100%}.src-modal__body p{color:var(--src-color-bg-default, #1f2937);font-size:14px;font-style:normal;font-weight:400;line-height:24px;margin-top:0}.src-modal__body a{word-wrap:break-word;white-space:normal}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "colorScheme", "size", "state", "customClass", "hasDisclosure", "isFullWidth", "isPressed", "isDisabled", "isLoading", "iconButton", "srcButtonConfig", "formID", "data-testid"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4561
4752
  }
4562
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: WebrtcErrorModalComponent, decorators: [{
4753
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: WebrtcErrorModalComponent, decorators: [{
4563
4754
  type: Component,
4564
4755
  args: [{ selector: 'app-webrtc-error-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceButtonComponent], template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"src-modal\">\n <header class=\"src-modal__header\">\n <div [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n WebRTC error\n </div>\n </header>\n <section class=\"src-modal__body\">\n <div style=\"text-align: left\">\n An internet connection type (WebRTC) appears to be blocked either by your\n browser settings or your current network. If WebRTC is blocked on your\n browser you may be able to adjust these settings yourself based on\n instructions here:\n <a href=\"https://myownconference.com/blog/en/webrtc/\" target=\"blank\"\n >https://myownconference.com/blog/en/webrtc/</a\n >\n <br /><br />\n Trying a different web browser may help confirm this as well. If WebRTC is\n blocked by your network, try switching to a different network if possible\n or contact your network administrator.<br /><br />\n WebRTC is common, safe and increasingly utilised method for streaming real\n time 3D experiences via a web browser. It typically consumes no more\n bandwidth than streaming an HD video.\n </div>\n </section>\n <footer class=\"src-modal__footer\">\n <src-button\n (onClick)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n [colorScheme]=\"'primary'\"\n >\n Ok\n </src-button>\n </footer>\n</div>\n", styles: [".src-modal{--modalBodyPadding: 20px 8px 20px 20px;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto}.src-modal ::ng-deep .ng-scroll-content{--_scrollbar-content-width: initial;--_viewport-padding-right: 12px}.src-modal__body{width:100%}.src-modal__body p{color:var(--src-color-bg-default, #1f2937);font-size:14px;font-style:normal;font-weight:400;line-height:24px;margin-top:0}.src-modal__body a{word-wrap:break-word;white-space:normal}\n"] }]
4565
4756
  }] });
@@ -4603,10 +4794,10 @@ class FilterSettingsComponent {
4603
4794
  panelOpen: this.settings.panelOpen,
4604
4795
  });
4605
4796
  }
4606
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: FilterSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4607
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: FilterSettingsComponent, isStandalone: true, selector: "app-filter-settings", ngImport: i0, template: "<div class=\"settings-container\">\n <button (click)=\"togglePanel()\" class=\"gear-button\">LBM</button>\n @if (settings.panelOpen) {\n <div class=\"settings-panel\">\n <h3>LBM Trigger Settings</h3>\n <h5>Data Flow Monitor</h5>\n <form>\n <label>\n Monitoring delay:\n <input\n [(ngModel)]=\"settings.monitoringDelayTime\"\n [placeholder]=\"defaultFilterModel.monitoringDelayTime\"\n type=\"number\"\n name=\"monitoringDelayTime\"\n />\n </label>\n\n <label>\n Minimum Bitrate (kbit/s):\n <input\n [(ngModel)]=\"settings.minimumBitrate\"\n [placeholder]=\"defaultFilterModel.minimumBitrate\"\n type=\"number\"\n name=\"minimumBitrate\"\n />\n </label>\n <label>\n Yellow Flag (%):\n <input\n [(ngModel)]=\"settings.yellowFlag\"\n [placeholder]=\"defaultFilterModel.yellowFlag\"\n type=\"number\"\n name=\"yellowFlag\"\n />\n </label>\n <label>\n Red Flag (%):\n <input\n [(ngModel)]=\"settings.redFlag\"\n [placeholder]=\"defaultFilterModel.redFlag\"\n type=\"number\"\n name=\"redFlag\"\n />\n </label>\n <label>\n Minimum FPS:\n <input\n [(ngModel)]=\"settings.minimumFps\"\n [placeholder]=\"defaultFilterModel.minimumFps\"\n type=\"number\"\n name=\"minimumFps\"\n />\n </label>\n <hr />\n <h5>Kalman Filter</h5>\n <label>\n Initial Bitrate Estimate (kbit/s):\n <input\n [(ngModel)]=\"settings.initialBitrateEstimate\"\n [placeholder]=\"defaultFilterModel.initialBitrateEstimate\"\n type=\"number\"\n name=\"initialBitrateEstimate\"\n />\n </label>\n <label>\n Initial Error Covariance:\n <input\n [(ngModel)]=\"settings.initialErrorCovariance\"\n [placeholder]=\"defaultFilterModel.initialErrorCovariance\"\n type=\"number\"\n name=\"initialErrorCovariance\"\n />\n </label>\n <label>\n Process Noise (Q):\n <input\n [(ngModel)]=\"settings.processNoise\"\n [placeholder]=\"defaultFilterModel.processNoise\"\n type=\"number\"\n name=\"processNoise\"\n />\n </label>\n <label>\n Measurement Noise (R):\n <input\n [(ngModel)]=\"settings.measurementNoise\"\n [placeholder]=\"defaultFilterModel.measurementNoise\"\n type=\"number\"\n name=\"measurementNoise\"\n />\n </label>\n </form>\n <br />\n <br />\n <div class=\"apply-button-container\">\n <button (click)=\"reset()\">Reset</button>\n <button (click)=\"saveSettings()\">Apply</button>\n </div>\n </div>\n }\n</div>\n", styles: [".settings-container{position:absolute;top:65px;right:10px;z-index:1000}.gear-button{right:0;width:50px;position:absolute;cursor:pointer;font-size:10px;background:none;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#e6f419;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}.settings-panel{font-size:13px;background:#fff;border:1px solid #ccc;padding:15px;margin-top:25px;border-radius:4px;box-shadow:0 2px 8px #0003}.settings-panel h3{margin-top:0}.settings-panel form{display:flex;flex-direction:column}.settings-panel label{margin-bottom:10px;display:flex;flex-direction:column}.settings-panel input{padding:4px;margin-top:4px;border:1px solid #ccc;border-radius:2px}.apply-button-container{position:absolute;bottom:15px;right:15px}.apply-button-container button{padding:6px 12px;border:none;background-color:#1976d2;color:#fff;border-radius:4px;cursor:pointer;margin:2px}.apply-button-container button:hover{background-color:#1565c0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4797
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: FilterSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4798
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.2", type: FilterSettingsComponent, isStandalone: true, selector: "app-filter-settings", ngImport: i0, template: "<div class=\"settings-container\">\n <button (click)=\"togglePanel()\" class=\"gear-button\">LBM</button>\n @if (settings.panelOpen) {\n <div class=\"settings-panel\">\n <h3>LBM Trigger Settings</h3>\n <h5>Data Flow Monitor</h5>\n <form>\n <label>\n Monitoring delay:\n <input\n [(ngModel)]=\"settings.monitoringDelayTime\"\n [placeholder]=\"defaultFilterModel.monitoringDelayTime\"\n type=\"number\"\n name=\"monitoringDelayTime\"\n />\n </label>\n\n <label>\n Minimum Bitrate (kbit/s):\n <input\n [(ngModel)]=\"settings.minimumBitrate\"\n [placeholder]=\"defaultFilterModel.minimumBitrate\"\n type=\"number\"\n name=\"minimumBitrate\"\n />\n </label>\n <label>\n Yellow Flag (%):\n <input\n [(ngModel)]=\"settings.yellowFlag\"\n [placeholder]=\"defaultFilterModel.yellowFlag\"\n type=\"number\"\n name=\"yellowFlag\"\n />\n </label>\n <label>\n Red Flag (%):\n <input\n [(ngModel)]=\"settings.redFlag\"\n [placeholder]=\"defaultFilterModel.redFlag\"\n type=\"number\"\n name=\"redFlag\"\n />\n </label>\n <label>\n Minimum FPS:\n <input\n [(ngModel)]=\"settings.minimumFps\"\n [placeholder]=\"defaultFilterModel.minimumFps\"\n type=\"number\"\n name=\"minimumFps\"\n />\n </label>\n <hr />\n <h5>Kalman Filter</h5>\n <label>\n Initial Bitrate Estimate (kbit/s):\n <input\n [(ngModel)]=\"settings.initialBitrateEstimate\"\n [placeholder]=\"defaultFilterModel.initialBitrateEstimate\"\n type=\"number\"\n name=\"initialBitrateEstimate\"\n />\n </label>\n <label>\n Initial Error Covariance:\n <input\n [(ngModel)]=\"settings.initialErrorCovariance\"\n [placeholder]=\"defaultFilterModel.initialErrorCovariance\"\n type=\"number\"\n name=\"initialErrorCovariance\"\n />\n </label>\n <label>\n Process Noise (Q):\n <input\n [(ngModel)]=\"settings.processNoise\"\n [placeholder]=\"defaultFilterModel.processNoise\"\n type=\"number\"\n name=\"processNoise\"\n />\n </label>\n <label>\n Measurement Noise (R):\n <input\n [(ngModel)]=\"settings.measurementNoise\"\n [placeholder]=\"defaultFilterModel.measurementNoise\"\n type=\"number\"\n name=\"measurementNoise\"\n />\n </label>\n </form>\n <br />\n <br />\n <div class=\"apply-button-container\">\n <button (click)=\"reset()\">Reset</button>\n <button (click)=\"saveSettings()\">Apply</button>\n </div>\n </div>\n }\n</div>\n", styles: [".settings-container{position:absolute;top:65px;right:10px;z-index:1000}.gear-button{right:0;width:50px;position:absolute;cursor:pointer;font-size:10px;background:none;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#e6f419;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}.settings-panel{font-size:13px;background:#fff;border:1px solid #ccc;padding:15px;margin-top:25px;border-radius:4px;box-shadow:0 2px 8px #0003}.settings-panel h3{margin-top:0}.settings-panel form{display:flex;flex-direction:column}.settings-panel label{margin-bottom:10px;display:flex;flex-direction:column}.settings-panel input{padding:4px;margin-top:4px;border:1px solid #ccc;border-radius:2px}.apply-button-container{position:absolute;bottom:15px;right:15px}.apply-button-container button{padding:6px 12px;border:none;background-color:#1976d2;color:#fff;border-radius:4px;cursor:pointer;margin:2px}.apply-button-container button:hover{background-color:#1565c0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4608
4799
  }
4609
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: FilterSettingsComponent, decorators: [{
4800
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: FilterSettingsComponent, decorators: [{
4610
4801
  type: Component,
4611
4802
  args: [{ selector: 'app-filter-settings', imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"settings-container\">\n <button (click)=\"togglePanel()\" class=\"gear-button\">LBM</button>\n @if (settings.panelOpen) {\n <div class=\"settings-panel\">\n <h3>LBM Trigger Settings</h3>\n <h5>Data Flow Monitor</h5>\n <form>\n <label>\n Monitoring delay:\n <input\n [(ngModel)]=\"settings.monitoringDelayTime\"\n [placeholder]=\"defaultFilterModel.monitoringDelayTime\"\n type=\"number\"\n name=\"monitoringDelayTime\"\n />\n </label>\n\n <label>\n Minimum Bitrate (kbit/s):\n <input\n [(ngModel)]=\"settings.minimumBitrate\"\n [placeholder]=\"defaultFilterModel.minimumBitrate\"\n type=\"number\"\n name=\"minimumBitrate\"\n />\n </label>\n <label>\n Yellow Flag (%):\n <input\n [(ngModel)]=\"settings.yellowFlag\"\n [placeholder]=\"defaultFilterModel.yellowFlag\"\n type=\"number\"\n name=\"yellowFlag\"\n />\n </label>\n <label>\n Red Flag (%):\n <input\n [(ngModel)]=\"settings.redFlag\"\n [placeholder]=\"defaultFilterModel.redFlag\"\n type=\"number\"\n name=\"redFlag\"\n />\n </label>\n <label>\n Minimum FPS:\n <input\n [(ngModel)]=\"settings.minimumFps\"\n [placeholder]=\"defaultFilterModel.minimumFps\"\n type=\"number\"\n name=\"minimumFps\"\n />\n </label>\n <hr />\n <h5>Kalman Filter</h5>\n <label>\n Initial Bitrate Estimate (kbit/s):\n <input\n [(ngModel)]=\"settings.initialBitrateEstimate\"\n [placeholder]=\"defaultFilterModel.initialBitrateEstimate\"\n type=\"number\"\n name=\"initialBitrateEstimate\"\n />\n </label>\n <label>\n Initial Error Covariance:\n <input\n [(ngModel)]=\"settings.initialErrorCovariance\"\n [placeholder]=\"defaultFilterModel.initialErrorCovariance\"\n type=\"number\"\n name=\"initialErrorCovariance\"\n />\n </label>\n <label>\n Process Noise (Q):\n <input\n [(ngModel)]=\"settings.processNoise\"\n [placeholder]=\"defaultFilterModel.processNoise\"\n type=\"number\"\n name=\"processNoise\"\n />\n </label>\n <label>\n Measurement Noise (R):\n <input\n [(ngModel)]=\"settings.measurementNoise\"\n [placeholder]=\"defaultFilterModel.measurementNoise\"\n type=\"number\"\n name=\"measurementNoise\"\n />\n </label>\n </form>\n <br />\n <br />\n <div class=\"apply-button-container\">\n <button (click)=\"reset()\">Reset</button>\n <button (click)=\"saveSettings()\">Apply</button>\n </div>\n </div>\n }\n</div>\n", styles: [".settings-container{position:absolute;top:65px;right:10px;z-index:1000}.gear-button{right:0;width:50px;position:absolute;cursor:pointer;font-size:10px;background:none;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#e6f419;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}.settings-panel{font-size:13px;background:#fff;border:1px solid #ccc;padding:15px;margin-top:25px;border-radius:4px;box-shadow:0 2px 8px #0003}.settings-panel h3{margin-top:0}.settings-panel form{display:flex;flex-direction:column}.settings-panel label{margin-bottom:10px;display:flex;flex-direction:column}.settings-panel input{padding:4px;margin-top:4px;border:1px solid #ccc;border-radius:2px}.apply-button-container{position:absolute;bottom:15px;right:15px}.apply-button-container button{padding:6px 12px;border:none;background-color:#1976d2;color:#fff;border-radius:4px;cursor:pointer;margin:2px}.apply-button-container button:hover{background-color:#1565c0}\n"] }]
4612
4803
  }] });
@@ -4711,10 +4902,10 @@ class LowBandwidthDetectorComponent {
4711
4902
  this.store.dispatch(changeLowBandwidth({ lowBandwidth: false }));
4712
4903
  });
4713
4904
  }
4714
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: LowBandwidthDetectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4715
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: LowBandwidthDetectorComponent, isStandalone: true, selector: "app-low-bandwidth-detector", ngImport: i0, template: "@if (isReducedQuality() || isLowBandwidth()) {\n <div\n [class.expanded]=\"isIndicatorExpanded() && isLowBandwidth()\"\n class=\"lbm-indicator freeze-loader\"\n >\n <div (click)=\"toggleIndicator(true)\" class=\"lbm-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 20 20\">\n <path\n fill=\"#85888E\"\n d=\"M7.189 3.605c-.73.145-1.438.35-2.126.614A14.412 14.412 0 0 0 .896 6.666a1.08 1.08 0 0 0-.375.844c0 .34.118.629.354.865s.524.36.865.375c.34.014.65-.09.927-.313a11.89 11.89 0 0 1 3.385-1.916 10.94 10.94 0 0 1 1.235-.375l-.098-2.541ZM7.385 8.708a9.107 9.107 0 0 0-2.906 1.48c-.264.194-.402.464-.416.812-.014.347.104.646.354.896.236.236.524.364.864.385.34.02.664-.073.97-.281.422-.29.878-.53 1.368-.721a2.545 2.545 0 0 1-.166-.814l-.068-1.757ZM12.386 11.267c.094-.25.15-.52.161-.802l.068-1.755a9.019 9.019 0 0 1 2.927 1.52c.264.193.4.46.406.801.007.34-.114.635-.364.885a1.213 1.213 0 0 1-.865.365A1.614 1.614 0 0 1 13.77 12a6.574 6.574 0 0 0-1.385-.733ZM12.713 6.146l.098-2.542c.73.146 1.438.351 2.127.615 1.541.59 2.93 1.406 4.166 2.447.25.223.379.5.386.834.007.333-.115.625-.365.875a1.253 1.253 0 0 1-.864.375c-.34.014-.65-.09-.927-.313a11.892 11.892 0 0 0-3.386-1.916 10.94 10.94 0 0 0-1.235-.375ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n />\n <path\n fill=\"#fff\"\n fill-rule=\"evenodd\"\n d=\"M10 .833c-.91 0-1.637.756-1.602 1.665l.304 7.92a1.299 1.299 0 0 0 2.596 0l.305-7.92A1.604 1.604 0 0 0 10 .833ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n\n <div\n [class.lbm-message--open]=\"isLowBandwidth() && isIndicatorExpanded()\"\n class=\"lbm-message\"\n >\n <p class=\"lbm-description\">\n Fluid Interactivity Modes were disabled due to an unstable connection.\n\n <button\n (click)=\"openLBMDialog()\"\n [attr.data-testid]=\"'learn-more-lbm'\"\n type=\"button\"\n class=\"lbm-learn-more\"\n >\n Learn more\n </button>\n </p>\n <button\n (click)=\"toggleIndicator(false)\"\n [attr.data-testid]=\"'close-lbm-indicator'\"\n type=\"button\"\n class=\"lbm-close\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M10.0001 11.2289L6.68618 14.5428C6.51951 14.7095 6.31818 14.7895 6.08218 14.7828C5.84618 14.7755 5.64485 14.6885 5.47818 14.5218C5.31151 14.3552 5.22818 14.1505 5.22818 13.9078C5.22818 13.6645 5.31151 13.4595 5.47818 13.2928L8.77107 9.99992L5.45718 6.68603C5.29051 6.51936 5.21051 6.3147 5.21718 6.07203C5.22451 5.8287 5.31151 5.6237 5.47818 5.45703C5.64485 5.29036 5.84951 5.20703 6.09218 5.20703C6.33551 5.20703 6.54051 5.29036 6.70718 5.45703L10.0001 8.77092L13.314 5.45703C13.4806 5.29036 13.6853 5.20703 13.928 5.20703C14.1713 5.20703 14.3763 5.29036 14.543 5.45703C14.7096 5.6237 14.793 5.8287 14.793 6.07203C14.793 6.3147 14.7096 6.51936 14.543 6.68603L11.2291 9.99992L14.543 13.3138C14.7096 13.4805 14.793 13.6818 14.793 13.9178C14.793 14.1538 14.7096 14.3552 14.543 14.5218C14.3763 14.6885 14.1713 14.7718 13.928 14.7718C13.6853 14.7718 13.4806 14.6885 13.314 14.5218L10.0001 11.2289Z\"\n fill=\"white\"\n />\n </svg>\n </button>\n </div>\n </div>\n}\n\n@if (devModeService.isDevMode) {\n <app-filter-settings />\n}\n", styles: [".freeze-loader,.lbm-indicator{--lbmPositionTop: 18px;--lbmPositionLeft: 18px;--lbmPositionRight: 18px;--lbmIndicatorSize: 36px;--lbmIndicatorBorderRadius: 8px;--lbmIndicatorCloseSize: 20px;--lbmPadding: 10px;--lbmTextSize: 13px;--lbmLineHeight: 20px;--lbmTextColor: #ffffff;--lbmBackgroundColor: rgba(17, 24, 39, .7);position:absolute;top:var(--lbmPositionTop);left:var(--lbmPositionLeft);z-index:6;display:flex;justify-content:center;align-items:flex-start;min-width:var(--lbmIndicatorSize);min-height:var(--lbmIndicatorSize);overflow:hidden}.freeze-loader.expanded,.lbm-indicator.expanded{--lbmPositionTop: 8px;--lbmPositionLeft: 8px;--lbmPositionRight: 8px;width:auto;max-width:calc(100% - var(--lbmPositionLeft) - var(--lbmPositionRight));padding:var(--lbmPadding);background:var(--lbmBackgroundColor);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:var(--lbmIndicatorBorderRadius);gap:12px}.freeze-loader .lbm-icon,.lbm-indicator .lbm-icon{display:flex;width:var(--lbmIndicatorSize);height:var(--lbmIndicatorSize);padding:8px;color:#fff;background-color:var(--lbmBackgroundColor);border-radius:50%;flex-shrink:0;cursor:pointer}.freeze-loader .lbm-icon svg,.freeze-loader .lbm-icon img,.lbm-indicator .lbm-icon svg,.lbm-indicator .lbm-icon img{width:100%;height:100%}.freeze-loader .lbm-close,.lbm-indicator .lbm-close{position:absolute;top:0;right:0;width:var(--lbmIndicatorCloseSize);height:var(--lbmIndicatorCloseSize);background-color:transparent;border:none;padding:0;cursor:pointer}.freeze-loader .lbm-message,.lbm-indicator .lbm-message{position:relative;opacity:0;height:0;max-width:0;padding-right:24px;pointer-events:none;visibility:hidden;animation:closeMessage .3s forwards}.freeze-loader .lbm-message--open,.lbm-indicator .lbm-message--open{position:relative;visibility:visible;display:flex;align-items:center;align-self:center;pointer-events:all;gap:12px;max-width:fit-content;height:auto;opacity:1;animation:openMessage .3s forwards}.freeze-loader .lbm-description,.lbm-indicator .lbm-description{margin:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor)}.freeze-loader .lbm-learn-more,.lbm-indicator .lbm-learn-more{display:inline-flex;padding:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor);-webkit-text-decoration-line:underline;text-decoration-line:underline;background-color:transparent;border:none;cursor:pointer}.freeze-loader .lbm-learn-more:hover,.lbm-indicator .lbm-learn-more:hover{text-decoration:none}@keyframes openMessage{0%{opacity:0;max-width:0;height:0}10%{height:auto;max-width:fit-content}to{opacity:1;height:auto;max-width:fit-content}}@keyframes closeMessage{0%{opacity:1;max-width:fit-content}to{opacity:0;max-width:0}}\n"], dependencies: [{ kind: "component", type: FilterSettingsComponent, selector: "app-filter-settings" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4905
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: LowBandwidthDetectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4906
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.2", type: LowBandwidthDetectorComponent, isStandalone: true, selector: "app-low-bandwidth-detector", ngImport: i0, template: "@if (isReducedQuality() || isLowBandwidth()) {\n <div\n [class.expanded]=\"isIndicatorExpanded() && isLowBandwidth()\"\n class=\"lbm-indicator freeze-loader\"\n >\n <div (click)=\"toggleIndicator(true)\" class=\"lbm-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 20 20\">\n <path\n fill=\"#85888E\"\n d=\"M7.189 3.605c-.73.145-1.438.35-2.126.614A14.412 14.412 0 0 0 .896 6.666a1.08 1.08 0 0 0-.375.844c0 .34.118.629.354.865s.524.36.865.375c.34.014.65-.09.927-.313a11.89 11.89 0 0 1 3.385-1.916 10.94 10.94 0 0 1 1.235-.375l-.098-2.541ZM7.385 8.708a9.107 9.107 0 0 0-2.906 1.48c-.264.194-.402.464-.416.812-.014.347.104.646.354.896.236.236.524.364.864.385.34.02.664-.073.97-.281.422-.29.878-.53 1.368-.721a2.545 2.545 0 0 1-.166-.814l-.068-1.757ZM12.386 11.267c.094-.25.15-.52.161-.802l.068-1.755a9.019 9.019 0 0 1 2.927 1.52c.264.193.4.46.406.801.007.34-.114.635-.364.885a1.213 1.213 0 0 1-.865.365A1.614 1.614 0 0 1 13.77 12a6.574 6.574 0 0 0-1.385-.733ZM12.713 6.146l.098-2.542c.73.146 1.438.351 2.127.615 1.541.59 2.93 1.406 4.166 2.447.25.223.379.5.386.834.007.333-.115.625-.365.875a1.253 1.253 0 0 1-.864.375c-.34.014-.65-.09-.927-.313a11.892 11.892 0 0 0-3.386-1.916 10.94 10.94 0 0 0-1.235-.375ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n />\n <path\n fill=\"#fff\"\n fill-rule=\"evenodd\"\n d=\"M10 .833c-.91 0-1.637.756-1.602 1.665l.304 7.92a1.299 1.299 0 0 0 2.596 0l.305-7.92A1.604 1.604 0 0 0 10 .833ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n\n <div\n [class.lbm-message--open]=\"isLowBandwidth() && isIndicatorExpanded()\"\n class=\"lbm-message\"\n >\n <p class=\"lbm-description\">\n Fluid Interactivity Modes were disabled due to an unstable connection.\n\n <button\n (click)=\"openLBMDialog()\"\n [attr.data-testid]=\"'learn-more-lbm'\"\n type=\"button\"\n class=\"lbm-learn-more\"\n >\n Learn more\n </button>\n </p>\n <button\n (click)=\"toggleIndicator(false)\"\n [attr.data-testid]=\"'close-lbm-indicator'\"\n type=\"button\"\n class=\"lbm-close\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M10.0001 11.2289L6.68618 14.5428C6.51951 14.7095 6.31818 14.7895 6.08218 14.7828C5.84618 14.7755 5.64485 14.6885 5.47818 14.5218C5.31151 14.3552 5.22818 14.1505 5.22818 13.9078C5.22818 13.6645 5.31151 13.4595 5.47818 13.2928L8.77107 9.99992L5.45718 6.68603C5.29051 6.51936 5.21051 6.3147 5.21718 6.07203C5.22451 5.8287 5.31151 5.6237 5.47818 5.45703C5.64485 5.29036 5.84951 5.20703 6.09218 5.20703C6.33551 5.20703 6.54051 5.29036 6.70718 5.45703L10.0001 8.77092L13.314 5.45703C13.4806 5.29036 13.6853 5.20703 13.928 5.20703C14.1713 5.20703 14.3763 5.29036 14.543 5.45703C14.7096 5.6237 14.793 5.8287 14.793 6.07203C14.793 6.3147 14.7096 6.51936 14.543 6.68603L11.2291 9.99992L14.543 13.3138C14.7096 13.4805 14.793 13.6818 14.793 13.9178C14.793 14.1538 14.7096 14.3552 14.543 14.5218C14.3763 14.6885 14.1713 14.7718 13.928 14.7718C13.6853 14.7718 13.4806 14.6885 13.314 14.5218L10.0001 11.2289Z\"\n fill=\"white\"\n />\n </svg>\n </button>\n </div>\n </div>\n}\n\n@if (devModeService.isDevMode) {\n <app-filter-settings />\n}\n", styles: [".freeze-loader,.lbm-indicator{--lbmPositionTop: 18px;--lbmPositionLeft: 18px;--lbmPositionRight: 18px;--lbmIndicatorSize: 36px;--lbmIndicatorBorderRadius: 8px;--lbmIndicatorCloseSize: 20px;--lbmPadding: 10px;--lbmTextSize: 13px;--lbmLineHeight: 20px;--lbmTextColor: #ffffff;--lbmBackgroundColor: rgba(17, 24, 39, .7);position:absolute;top:var(--lbmPositionTop);left:var(--lbmPositionLeft);z-index:6;display:flex;justify-content:center;align-items:flex-start;min-width:var(--lbmIndicatorSize);min-height:var(--lbmIndicatorSize);overflow:hidden}.freeze-loader.expanded,.lbm-indicator.expanded{--lbmPositionTop: 8px;--lbmPositionLeft: 8px;--lbmPositionRight: 8px;width:auto;max-width:calc(100% - var(--lbmPositionLeft) - var(--lbmPositionRight));padding:var(--lbmPadding);background:var(--lbmBackgroundColor);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:var(--lbmIndicatorBorderRadius);gap:12px}.freeze-loader .lbm-icon,.lbm-indicator .lbm-icon{display:flex;width:var(--lbmIndicatorSize);height:var(--lbmIndicatorSize);padding:8px;color:#fff;background-color:var(--lbmBackgroundColor);border-radius:50%;flex-shrink:0;cursor:pointer}.freeze-loader .lbm-icon svg,.freeze-loader .lbm-icon img,.lbm-indicator .lbm-icon svg,.lbm-indicator .lbm-icon img{width:100%;height:100%}.freeze-loader .lbm-close,.lbm-indicator .lbm-close{position:absolute;top:0;right:0;width:var(--lbmIndicatorCloseSize);height:var(--lbmIndicatorCloseSize);background-color:transparent;border:none;padding:0;cursor:pointer}.freeze-loader .lbm-message,.lbm-indicator .lbm-message{position:relative;opacity:0;height:0;max-width:0;padding-right:24px;pointer-events:none;visibility:hidden;animation:closeMessage .3s forwards}.freeze-loader .lbm-message--open,.lbm-indicator .lbm-message--open{position:relative;visibility:visible;display:flex;align-items:center;align-self:center;pointer-events:all;gap:12px;max-width:fit-content;height:auto;opacity:1;animation:openMessage .3s forwards}.freeze-loader .lbm-description,.lbm-indicator .lbm-description{margin:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor)}.freeze-loader .lbm-learn-more,.lbm-indicator .lbm-learn-more{display:inline-flex;padding:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor);-webkit-text-decoration-line:underline;text-decoration-line:underline;background-color:transparent;border:none;cursor:pointer}.freeze-loader .lbm-learn-more:hover,.lbm-indicator .lbm-learn-more:hover{text-decoration:none}@keyframes openMessage{0%{opacity:0;max-width:0;height:0}10%{height:auto;max-width:fit-content}to{opacity:1;height:auto;max-width:fit-content}}@keyframes closeMessage{0%{opacity:1;max-width:fit-content}to{opacity:0;max-width:0}}\n"], dependencies: [{ kind: "component", type: FilterSettingsComponent, selector: "app-filter-settings" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4716
4907
  }
4717
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: LowBandwidthDetectorComponent, decorators: [{
4908
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.2", ngImport: i0, type: LowBandwidthDetectorComponent, decorators: [{
4718
4909
  type: Component,
4719
4910
  args: [{ selector: 'app-low-bandwidth-detector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FilterSettingsComponent], template: "@if (isReducedQuality() || isLowBandwidth()) {\n <div\n [class.expanded]=\"isIndicatorExpanded() && isLowBandwidth()\"\n class=\"lbm-indicator freeze-loader\"\n >\n <div (click)=\"toggleIndicator(true)\" class=\"lbm-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 20 20\">\n <path\n fill=\"#85888E\"\n d=\"M7.189 3.605c-.73.145-1.438.35-2.126.614A14.412 14.412 0 0 0 .896 6.666a1.08 1.08 0 0 0-.375.844c0 .34.118.629.354.865s.524.36.865.375c.34.014.65-.09.927-.313a11.89 11.89 0 0 1 3.385-1.916 10.94 10.94 0 0 1 1.235-.375l-.098-2.541ZM7.385 8.708a9.107 9.107 0 0 0-2.906 1.48c-.264.194-.402.464-.416.812-.014.347.104.646.354.896.236.236.524.364.864.385.34.02.664-.073.97-.281.422-.29.878-.53 1.368-.721a2.545 2.545 0 0 1-.166-.814l-.068-1.757ZM12.386 11.267c.094-.25.15-.52.161-.802l.068-1.755a9.019 9.019 0 0 1 2.927 1.52c.264.193.4.46.406.801.007.34-.114.635-.364.885a1.213 1.213 0 0 1-.865.365A1.614 1.614 0 0 1 13.77 12a6.574 6.574 0 0 0-1.385-.733ZM12.713 6.146l.098-2.542c.73.146 1.438.351 2.127.615 1.541.59 2.93 1.406 4.166 2.447.25.223.379.5.386.834.007.333-.115.625-.365.875a1.253 1.253 0 0 1-.864.375c-.34.014-.65-.09-.927-.313a11.892 11.892 0 0 0-3.386-1.916 10.94 10.94 0 0 0-1.235-.375ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n />\n <path\n fill=\"#fff\"\n fill-rule=\"evenodd\"\n d=\"M10 .833c-.91 0-1.637.756-1.602 1.665l.304 7.92a1.299 1.299 0 0 0 2.596 0l.305-7.92A1.604 1.604 0 0 0 10 .833ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n\n <div\n [class.lbm-message--open]=\"isLowBandwidth() && isIndicatorExpanded()\"\n class=\"lbm-message\"\n >\n <p class=\"lbm-description\">\n Fluid Interactivity Modes were disabled due to an unstable connection.\n\n <button\n (click)=\"openLBMDialog()\"\n [attr.data-testid]=\"'learn-more-lbm'\"\n type=\"button\"\n class=\"lbm-learn-more\"\n >\n Learn more\n </button>\n </p>\n <button\n (click)=\"toggleIndicator(false)\"\n [attr.data-testid]=\"'close-lbm-indicator'\"\n type=\"button\"\n class=\"lbm-close\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M10.0001 11.2289L6.68618 14.5428C6.51951 14.7095 6.31818 14.7895 6.08218 14.7828C5.84618 14.7755 5.64485 14.6885 5.47818 14.5218C5.31151 14.3552 5.22818 14.1505 5.22818 13.9078C5.22818 13.6645 5.31151 13.4595 5.47818 13.2928L8.77107 9.99992L5.45718 6.68603C5.29051 6.51936 5.21051 6.3147 5.21718 6.07203C5.22451 5.8287 5.31151 5.6237 5.47818 5.45703C5.64485 5.29036 5.84951 5.20703 6.09218 5.20703C6.33551 5.20703 6.54051 5.29036 6.70718 5.45703L10.0001 8.77092L13.314 5.45703C13.4806 5.29036 13.6853 5.20703 13.928 5.20703C14.1713 5.20703 14.3763 5.29036 14.543 5.45703C14.7096 5.6237 14.793 5.8287 14.793 6.07203C14.793 6.3147 14.7096 6.51936 14.543 6.68603L11.2291 9.99992L14.543 13.3138C14.7096 13.4805 14.793 13.6818 14.793 13.9178C14.793 14.1538 14.7096 14.3552 14.543 14.5218C14.3763 14.6885 14.1713 14.7718 13.928 14.7718C13.6853 14.7718 13.4806 14.6885 13.314 14.5218L10.0001 11.2289Z\"\n fill=\"white\"\n />\n </svg>\n </button>\n </div>\n </div>\n}\n\n@if (devModeService.isDevMode) {\n <app-filter-settings />\n}\n", styles: [".freeze-loader,.lbm-indicator{--lbmPositionTop: 18px;--lbmPositionLeft: 18px;--lbmPositionRight: 18px;--lbmIndicatorSize: 36px;--lbmIndicatorBorderRadius: 8px;--lbmIndicatorCloseSize: 20px;--lbmPadding: 10px;--lbmTextSize: 13px;--lbmLineHeight: 20px;--lbmTextColor: #ffffff;--lbmBackgroundColor: rgba(17, 24, 39, .7);position:absolute;top:var(--lbmPositionTop);left:var(--lbmPositionLeft);z-index:6;display:flex;justify-content:center;align-items:flex-start;min-width:var(--lbmIndicatorSize);min-height:var(--lbmIndicatorSize);overflow:hidden}.freeze-loader.expanded,.lbm-indicator.expanded{--lbmPositionTop: 8px;--lbmPositionLeft: 8px;--lbmPositionRight: 8px;width:auto;max-width:calc(100% - var(--lbmPositionLeft) - var(--lbmPositionRight));padding:var(--lbmPadding);background:var(--lbmBackgroundColor);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:var(--lbmIndicatorBorderRadius);gap:12px}.freeze-loader .lbm-icon,.lbm-indicator .lbm-icon{display:flex;width:var(--lbmIndicatorSize);height:var(--lbmIndicatorSize);padding:8px;color:#fff;background-color:var(--lbmBackgroundColor);border-radius:50%;flex-shrink:0;cursor:pointer}.freeze-loader .lbm-icon svg,.freeze-loader .lbm-icon img,.lbm-indicator .lbm-icon svg,.lbm-indicator .lbm-icon img{width:100%;height:100%}.freeze-loader .lbm-close,.lbm-indicator .lbm-close{position:absolute;top:0;right:0;width:var(--lbmIndicatorCloseSize);height:var(--lbmIndicatorCloseSize);background-color:transparent;border:none;padding:0;cursor:pointer}.freeze-loader .lbm-message,.lbm-indicator .lbm-message{position:relative;opacity:0;height:0;max-width:0;padding-right:24px;pointer-events:none;visibility:hidden;animation:closeMessage .3s forwards}.freeze-loader .lbm-message--open,.lbm-indicator .lbm-message--open{position:relative;visibility:visible;display:flex;align-items:center;align-self:center;pointer-events:all;gap:12px;max-width:fit-content;height:auto;opacity:1;animation:openMessage .3s forwards}.freeze-loader .lbm-description,.lbm-indicator .lbm-description{margin:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor)}.freeze-loader .lbm-learn-more,.lbm-indicator .lbm-learn-more{display:inline-flex;padding:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor);-webkit-text-decoration-line:underline;text-decoration-line:underline;background-color:transparent;border:none;cursor:pointer}.freeze-loader .lbm-learn-more:hover,.lbm-indicator .lbm-learn-more:hover{text-decoration:none}@keyframes openMessage{0%{opacity:0;max-width:0;height:0}10%{height:auto;max-width:fit-content}to{opacity:1;height:auto;max-width:fit-content}}@keyframes closeMessage{0%{opacity:1;max-width:fit-content}to{opacity:0;max-width:0}}\n"] }]
4720
4911
  }] });
@@ -4723,5 +4914,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImpor
4723
4914
  * Generated bundle index. Do not edit.
4724
4915
  */
4725
4916
 
4726
- export { AFKService, AfkRestartScreenLockerComponent, AggregatorService, AnswerHandler, CONSOLE_COMMAND_DISABLE_MESSAGES, CONSOLE_COMMAND_ENABLE_MESSAGES, CONSOLE_COMMAND_PIXEL_QUALITY, ClickableOverlayComponent, CommandTelemetryService, ConfigHandler, ConsoleExtensionsService, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DataFlowMonitor, DevModeService, DisconnectReason, EControlSchemeType, EMessageType, EToClientMessageType, FULL_HD_HEIGHT, FULL_HD_WIDTH, FilterSettingsComponent, FreezeFrameComponent, FreezeFrameService, IceCandidateHandler, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, KalmanFilter1D, LatencyTimings, LowBandwidthDetectorComponent, LowBandwidthModalComponent, MINIMAL_FPS, MouseButton, MouseButtonsMask, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, PingHandler, PlayerCountHandler, RegionsPingService, ResetTelemetry, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamStatusTelemetryService, SubService, TelemetryStart, TelemetryStop, UNREAL_CONFIG, UnrealCommunicatorService, UnrealEffects, UnrealInternalSignalEvents, UnrealSceneComponent, UnrealStatusMessage, VideoLockerComponent, VideoRecorder, VideoService, VideoStatsComponent, WSCloseCode_NORMAL_AFK_TIMEOUT, WSCloseCode_NORMAL_CIRRUS_CLOSED, WSCloseCode_NORMAL_CLOSURE, WSCloseCode_NORMAL_MANUAL_DISCONNECT, WSCloseCodes, WS_OPEN_STATE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, alignProductsToPlaneCommand, changeLowBandwidth, changeStatusMainVideoOnScene, changeStreamResolutionAction, changeStreamResolutionSuccessAction, clampAndKeepMaxPercents, clampPanToProductsCommand, commandCompleted, commandStarted, decodeData, destroyConnectionsAndResetState, destroyRemoteConnections, dispatchResize, floatToSmoothPercents, forceResizeUnrealVideo, fromResizeObserver, fromSignal, fromUnrealCallBackSignal, getActiveUrl, getApplyCameraPresetCommand, getApplyZoomCommand, getCameraBoxCommand, getCameraRecenterCommand, getCameraSettingsCommand, getChangeGizmoTypeCommand, getChangeResolutionCommand, getClickSnapCommand, getControlSensitivityCommand, getDebugModeCommand, getDragCommand, getDragSequenceCommand, getDropCommand, getEnableComposureCommand, getEnableControlsCommand, getEnableSceneStateCallbackCommand, getEnableSpinnerModeCommand, getEnableTeleportCommand, getExecuteConsoleCommand, getFitToObjectsCommand, getFreezeFrameCommand, getFreezeFrameNative, getImageFromVideoStream, getInitSequenceByObjectNameCommand, getJumpToSequenceCommand, getLoadLevelCommand, getLoadProductCommand, getLoadSceneStateCommand, getLoopBackCommand, getMoveSelectedCommand, getPauseSequenceCommand, getPlaySequenceCommand, getRequestCameraPresetCommand, getResetControlClampsCommand, getRotateSelectedCommand, getRtcErrorMessage, getSelectProductByObjectNameCommand, getSetCameraControlClampsCommand, getSetControlCameraModeCommand, getSetFpsCommand, getSetMaterialCommand, getSetPawnMovementModeCommand, getSetSettingsSequenceCommand, getStopSequenceCommand, getTakeRenderCommand, getTakeSpinnerRenderCommand, getTakeSpinnerRenderPreviewCommand, getUnLoadAllProductsCommand, getUnLoadProductByObjectNameCommand, getUnfreezeFrameNative, getUnselectAllProductsCommand, getWeatherCommand, initSignalling, initialState, mapQpToQuality, observeCommandResponse, provideAngularUnrealModule, removeExileCommands, resetAfk, resetAfkAction, resetConfig, resetIntroSrc, resetUnrealState, resetUnrealStateAction, resetWarnTimeout, selectCommandProgress, selectCommandsInProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsFreezeFrameLoading, selectIsVideoPlayingAndDataChannelConnected, selectLastCommandInProgress, selectLoaderCommands, selectMatchMakerUrls, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectWarnTimeout, selectWsUrl, sendSignal, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setDataChannelConnected, setErrorMessage, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setIsFreezeLoaderPercents, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMatchMakerUrls, setMatchUrls, setMaxFps, setProductsLocationCommand, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setViewportReady, showUnrealErrorMessage, smoothTransition, trackMixpanelEvent, unLoadAllLevelsCommand, unrealFeature, unrealReducer, updateCirrusInfo };
4917
+ export { AFKService, AfkRestartScreenLockerComponent, AggregatorService, AnswerHandler, CONSOLE_COMMAND_DISABLE_MESSAGES, CONSOLE_COMMAND_ENABLE_MESSAGES, CONSOLE_COMMAND_PIXEL_QUALITY, ClickableOverlayComponent, CommandTelemetryService, ConfigHandler, ConsoleExtensionsService, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DataFlowMonitor, DevModeService, DisconnectReason, EControlSchemeType, EMessageType, EToClientMessageType, FULL_HD_HEIGHT, FULL_HD_WIDTH, FilterSettingsComponent, FreezeFrameComponent, FreezeFrameService, IceCandidateHandler, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, KalmanFilter1D, LatencyTimings, LowBandwidthDetectorComponent, LowBandwidthModalComponent, MINIMAL_FPS, MouseButton, MouseButtonsMask, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PingHandler, PlayerCountHandler, RegionsPingService, ResetTelemetry, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamStatusTelemetryService, SubService, TelemetryStart, TelemetryStop, UNREAL_CONFIG, UnrealCommunicatorService, UnrealEffects, UnrealInternalSignalEvents, UnrealSceneComponent, UnrealStatusMessage, VideoLockerComponent, VideoRecorder, VideoService, VideoStatsComponent, WSCloseCode_CIRRUS_ABNORMAL_CLOSURE, WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR, WSCloseCode_CIRRUS_PLAYER_DISCONNECTED, WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER, WSCloseCode_FORCE_CIRRUS_CLOSE, WSCloseCode_NORMAL_AFK_TIMEOUT, WSCloseCode_NORMAL_CLOSURE, WSCloseCode_NORMAL_MANUAL_DISCONNECT, WSCloseCodes, WS_OPEN_STATE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, alignProductsToPlaneCommand, changeLowBandwidth, changeStatusMainVideoOnScene, changeStreamResolutionAction, changeStreamResolutionSuccessAction, clampAndKeepMaxPercents, clampPanToProductsCommand, commandCompleted, commandStarted, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, floatToSmoothPercents, forceResizeUnrealVideo, fromResizeObserver, fromSignal, fromUnrealCallBackSignal, getActiveUrl, getApplyCameraPresetCommand, getApplyZoomCommand, getCameraBoxCommand, getCameraRecenterCommand, getCameraSettingsCommand, getChangeGizmoTypeCommand, getChangeResolutionCommand, getClickSnapCommand, getControlSensitivityCommand, getDebugModeCommand, getDragCommand, getDragSequenceCommand, getDropCommand, getEnableComposureCommand, getEnableControlsCommand, getEnableSceneStateCallbackCommand, getEnableSpinnerModeCommand, getEnableTeleportCommand, getExecuteConsoleCommand, getFitToObjectsCommand, getFreezeFrameCommand, getFreezeFrameNative, getImageFromVideoStream, getInitSequenceByObjectNameCommand, getJumpToSequenceCommand, getLoadLevelCommand, getLoadProductCommand, getLoadSceneStateCommand, getLoopBackCommand, getMoveSelectedCommand, getPauseSequenceCommand, getPlaySequenceCommand, getRequestCameraPresetCommand, getResetControlClampsCommand, getRotateSelectedCommand, getRtcErrorMessage, getSelectProductByObjectNameCommand, getSetCameraControlClampsCommand, getSetControlCameraModeCommand, getSetFpsCommand, getSetMaterialCommand, getSetPawnMovementModeCommand, getSetSettingsSequenceCommand, getStopSequenceCommand, getTakeRenderCommand, getTakeSpinnerRenderCommand, getTakeSpinnerRenderPreviewCommand, getUnLoadAllProductsCommand, getUnLoadProductByObjectNameCommand, getUnfreezeFrameNative, getUnselectAllProductsCommand, getWeatherCommand, initSignalling, initialState, isLoaderScreenVisible, mapQpToQuality, observeCommandResponse, provideAngularUnrealModule, removeExileCommands, resetAfk, resetAfkAction, resetConfig, resetIntroSrc, resetWarnTimeout, selectClientAndViewIds, selectCommandProgress, selectCommandsInProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsFreezeFrameLoading, selectIsVideoPlayingAndDataChannelConnected, selectLastCommandInProgress, selectLoaderCommands, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectWarnTimeout, sendSignal, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setDataChannelConnected, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMatchUrls, setMaxFps, setProductsLocationCommand, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setViewportNotReady, setViewportReady, showPopupWithoutAutoStart, showUnrealErrorMessage, smoothTransition, startStream, trackMixpanelEvent, unLoadAllLevelsCommand, unrealFeature, unrealReducer, updateCirrusInfo };
4727
4918
  //# sourceMappingURL=3dsource-angular-unreal-module.mjs.map