@3dsource/angular-unreal-module 0.0.170 → 0.0.172
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,16 +1,16 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, output, ChangeDetectionStrategy, Component, InjectionToken, inject, DestroyRef, Injectable, signal, effect, untracked, computed, ElementRef, viewChild, afterNextRender, HostListener, SecurityContext, Pipe, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
|
|
3
|
-
import {
|
|
4
|
-
import { createAction, props, createReducer, on, createFeature,
|
|
5
|
-
import { whereNot, Falsy, Logger, clampf, lerp, getCanvasCached, getSnapshot, fitIntoRectangle, generateUuid, Truthy, tapLog, Semaphore, COLOR_CODES, where, KeyboardNumericCode, InvertedKeyMap, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB } from '@3dsource/utils';
|
|
6
|
-
import { filter, map as map$1, distinctUntilChanged, takeWhile, takeUntil, catchError, tap, timeout, finalize, first, switchMap as switchMap$1, withLatestFrom, exhaustMap, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1 } from 'rxjs/operators';
|
|
2
|
+
import { input, output, ChangeDetectionStrategy, Component, InjectionToken, inject, DestroyRef, Injectable, signal, effect, untracked, computed, ElementRef, viewChild, afterNextRender, HostListener, TemplateRef, Directive, SecurityContext, Pipe, contentChild, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
|
|
3
|
+
import { NgTemplateOutlet, NgOptimizedImage, JsonPipe } from '@angular/common';
|
|
4
|
+
import { createAction, props, createReducer, on, createFeature, createSelector, Store, provideState } from '@ngrx/store';
|
|
7
5
|
import { Actions, ofType, createEffect, provideEffects } from '@ngrx/effects';
|
|
6
|
+
import { Subject, skip, share, timer, of, take, defer, switchMap, from, throwError, Observable, EMPTY, retry, merge, timeout as timeout$1, repeat, BehaviorSubject, interval, map as map$1, takeUntil as takeUntil$1, fromEvent, tap as tap$1, throttleTime, delay, concat, debounceTime, mergeMap, NEVER, ignoreElements, startWith, scan, timestamp, pairwise, concatMap, animationFrameScheduler, shareReplay, firstValueFrom, asyncScheduler, filter as filter$1, withLatestFrom as withLatestFrom$1, Subscription, bufferTime, combineLatestWith } from 'rxjs';
|
|
7
|
+
import { whereNot, lerp, clampf, Falsy, generateUuid, Truthy, Logger, tapLog, Semaphore, COLOR_CODES, getCanvasCached, getSnapshot, fitIntoRectangle, where, KeyboardNumericCode, InvertedKeyMap, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB } from '@3dsource/utils';
|
|
8
|
+
import { filter, takeUntil, catchError, tap, map, timeout, finalize, first, switchMap as switchMap$1, distinctUntilChanged, withLatestFrom, exhaustMap, takeWhile, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1 } from 'rxjs/operators';
|
|
8
9
|
import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
|
|
9
10
|
import { HttpClient } from '@angular/common/http';
|
|
10
11
|
import { ScrollStrategyOptions } from '@angular/cdk/overlay';
|
|
11
12
|
import { MetaBoxCommand, EPayloadVersion } from '@3dsource/types-unreal';
|
|
12
13
|
import { toSignal, toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
13
|
-
import { NgOptimizedImage, JsonPipe } from '@angular/common';
|
|
14
14
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
15
15
|
|
|
16
16
|
class UnrealButtonComponent {
|
|
@@ -34,14 +34,246 @@ class UnrealButtonComponent {
|
|
|
34
34
|
event.preventDefault();
|
|
35
35
|
this.clicked.emit(event);
|
|
36
36
|
}
|
|
37
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
38
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
37
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
38
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: UnrealButtonComponent, isStandalone: true, selector: "app-unreal-button", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, weight: { classPropertyName: "weight", publicName: "weight", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, isDisabled: { classPropertyName: "isDisabled", publicName: "isDisabled", isSignal: true, isRequired: false, transformFunction: null }, testID: { classPropertyName: "testID", publicName: "data-testid", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<button\n (click)=\"handleClick($event)\"\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n [class.unreal-button--primary]=\"weight() === 'primary'\"\n [class.unreal-button--lg]=\"size() === 'lg'\"\n [class.unreal-button--full-width]=\"fullWidth()\"\n [disabled]=\"isDisabled()\"\n [type]=\"type()\"\n class=\"unreal-button\"\n>\n <span><ng-content></ng-content></span>\n</button>\n", styles: [".unreal-button{--unrealButtonHeight: 36px;--unrealButtonPaddings: 0 12px;--unrealButtonBorderRadius: 6px;--unrealButtonFontSize: var(--src-font-size-sm, 14px);--unrealButtonLineHeight: var(--src-font-line-sm, 20px);--unrealButtonWidth: auto;--unrealButtonBgColor: transparent;--unrealButtonBorderColor: var(--src-border-button-basic, #e5e7eb);--unrealButtonBorder: 1px solid var(--unrealButtonBorderColor);--unrealButtonFontColor: var(--src-text-ui-secondary-main, #111827);--unrealButtonFontWeight: var(--src-font-weight-medium, 500);--unrealButtonFocusRingSize: 4px;--unrealButtonFocusRingColor: var( --src-shadow-light, rgba(148, 163, 184, .16) );--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 1px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16));position:relative;display:flex;justify-content:center;align-items:center;width:var(--unrealButtonWidth);height:var(--unrealButtonHeight);padding:var(--unrealButtonPaddings);font-weight:var(--unrealButtonFontWeight);font-size:var(--unrealButtonFontSize);font-style:normal;line-height:var(--unrealButtonLineHeight);text-align:center;color:var(--unrealButtonFontColor);letter-spacing:0;white-space:nowrap;border:none;background-color:var(--unrealButtonBgColor);border-radius:var(--unrealButtonBorderRadius);box-shadow:var(--unrealButtonBoxShadow);cursor:pointer}.unreal-button:after{content:\"\";position:absolute;inset:0;border-radius:var(--unrealButtonBorderRadius);border:var(--unrealButtonBorder)}.unreal-button>span{display:flex;justify-content:center;align-items:center;gap:var(--src-gap-md, 6px);width:auto;font-size:inherit;line-height:inherit;color:inherit;font-weight:inherit}.unreal-button:disabled{--unrealButtonBgColor: var( --src-ui-accent-disabled, rgba(148, 163, 184, .24) );--unrealButtonFontColor: var(--src-text-ui-secondary-disabled, #9ca3af);--unrealButtonBoxShadow: none;--unrealButtonBorder: none;--unrealButtonBorderColor: transparent;cursor:default;pointer-events:none}.unreal-button:focus-visible{--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af);outline:none;box-shadow:0 0 0 var(--unrealButtonFocusRingSize) var(--unrealButtonFocusRingColor)}@media(pointer:fine){.unreal-button:hover{--unrealButtonBgColor: var( --src-ui-secondary-default-hover, rgba(148, 163, 184, .08) );--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af)}}.unreal-button--primary{--unrealButtonBgColor: var(--unreal-accent, var(--src-ui-accent-default, #017bff));--unrealButtonBorder: none;--unrealButtonFontColor: var(--unreal-accent-text, var(--src-text-ui-primary-main, #fff));--unrealButtonFocusRingColor: var( --src-shadow-accent-light, rgba(1, 123, 255, .16) )}@media(pointer:fine){.unreal-button--primary:hover{--unrealButtonBgColor: var(--unreal-accent-hover, var(--src-ui-accent-default-hover, #016fe6));--unrealButtonFontColor: var(--unreal-accent-text, var(--src-text-ui-primary-main, #fff))}}.unreal-button--lg{--unrealButtonHeight: 44px;--unrealButtonPaddings: 0 16px;--unrealButtonBorderRadius: 8px;--unrealButtonFontSize: var(--src-font-size-base, 16px);--unrealButtonLineHeight: var(--src-font-line-base, 24px);--unrealButtonFocusRingSize: 6px;--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 2px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16))}.unreal-button--lg>span{gap:var(--src-gap-md, 8px)}.unreal-button--full-width{--unrealButtonWidth: 100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
39
39
|
}
|
|
40
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
40
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealButtonComponent, decorators: [{
|
|
41
41
|
type: Component,
|
|
42
|
-
args: [{ selector: 'app-unreal-button', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n (click)=\"handleClick($event)\"\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n [class.unreal-button--primary]=\"weight() === 'primary'\"\n [class.unreal-button--lg]=\"size() === 'lg'\"\n [class.unreal-button--full-width]=\"fullWidth()\"\n [disabled]=\"isDisabled()\"\n [type]=\"type()\"\n class=\"unreal-button\"\n>\n <span><ng-content></ng-content></span>\n</button>\n", styles: [".unreal-button{--unrealButtonHeight: 36px;--unrealButtonPaddings: 0 12px;--unrealButtonBorderRadius: 6px;--unrealButtonFontSize: var(--src-font-size-sm, 14px);--unrealButtonLineHeight: var(--src-font-line-sm, 20px);--unrealButtonWidth: auto;--unrealButtonBgColor: transparent;--unrealButtonBorderColor: var(--src-border-button-basic, #e5e7eb);--unrealButtonBorder: 1px solid var(--unrealButtonBorderColor);--unrealButtonFontColor: var(--src-text-ui-secondary-main, #111827);--unrealButtonFontWeight: var(--src-font-weight-medium, 500);--unrealButtonFocusRingSize: 4px;--unrealButtonFocusRingColor: var( --src-shadow-light, rgba(148, 163, 184, .16) );--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 1px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16));position:relative;display:flex;justify-content:center;align-items:center;width:var(--unrealButtonWidth);height:var(--unrealButtonHeight);padding:var(--unrealButtonPaddings);font-weight:var(--unrealButtonFontWeight);font-size:var(--unrealButtonFontSize);font-style:normal;line-height:var(--unrealButtonLineHeight);text-align:center;color:var(--unrealButtonFontColor);letter-spacing:0;white-space:nowrap;border:none;background-color:var(--unrealButtonBgColor);border-radius:var(--unrealButtonBorderRadius);box-shadow:var(--unrealButtonBoxShadow);cursor:pointer}.unreal-button:after{content:\"\";position:absolute;inset:0;border-radius:var(--unrealButtonBorderRadius);border:var(--unrealButtonBorder)}.unreal-button>span{display:flex;justify-content:center;align-items:center;gap:var(--src-gap-md, 6px);width:auto;font-size:inherit;line-height:inherit;color:inherit;font-weight:inherit}.unreal-button:disabled{--unrealButtonBgColor: var( --src-ui-accent-disabled, rgba(148, 163, 184, .24) );--unrealButtonFontColor: var(--src-text-ui-secondary-disabled, #9ca3af);--unrealButtonBoxShadow: none;--unrealButtonBorder: none;--unrealButtonBorderColor: transparent;cursor:default;pointer-events:none}.unreal-button:focus-visible{--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af);outline:none;box-shadow:0 0 0 var(--unrealButtonFocusRingSize) var(--unrealButtonFocusRingColor)}@media(pointer:fine){.unreal-button:hover{--unrealButtonBgColor: var( --src-ui-secondary-default-hover, rgba(148, 163, 184, .08) );--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af)}}.unreal-button--primary{--unrealButtonBgColor: var(--src-ui-accent-default, #017bff);--unrealButtonBorder: none;--unrealButtonFontColor: var(--src-text-ui-primary-main, #fff);--unrealButtonFocusRingColor: var( --src-shadow-accent-light, rgba(1, 123, 255, .16) )}@media(pointer:fine){.unreal-button--primary:hover{--unrealButtonBgColor: var(--src-ui-accent-default-hover, #016fe6);--unrealButtonFontColor: var(--src-text-ui-primary-main, #fff)}}.unreal-button--lg{--unrealButtonHeight: 44px;--unrealButtonPaddings: 0 16px;--unrealButtonBorderRadius: 8px;--unrealButtonFontSize: var(--src-font-size-base, 16px);--unrealButtonLineHeight: var(--src-font-line-base, 24px);--unrealButtonFocusRingSize: 6px;--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 2px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16))}.unreal-button--lg>span{gap:var(--src-gap-md, 8px)}.unreal-button--full-width{--unrealButtonWidth: 100%}\n"] }]
|
|
42
|
+
args: [{ selector: 'app-unreal-button', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n (click)=\"handleClick($event)\"\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n [class.unreal-button--primary]=\"weight() === 'primary'\"\n [class.unreal-button--lg]=\"size() === 'lg'\"\n [class.unreal-button--full-width]=\"fullWidth()\"\n [disabled]=\"isDisabled()\"\n [type]=\"type()\"\n class=\"unreal-button\"\n>\n <span><ng-content></ng-content></span>\n</button>\n", styles: [".unreal-button{--unrealButtonHeight: 36px;--unrealButtonPaddings: 0 12px;--unrealButtonBorderRadius: 6px;--unrealButtonFontSize: var(--src-font-size-sm, 14px);--unrealButtonLineHeight: var(--src-font-line-sm, 20px);--unrealButtonWidth: auto;--unrealButtonBgColor: transparent;--unrealButtonBorderColor: var(--src-border-button-basic, #e5e7eb);--unrealButtonBorder: 1px solid var(--unrealButtonBorderColor);--unrealButtonFontColor: var(--src-text-ui-secondary-main, #111827);--unrealButtonFontWeight: var(--src-font-weight-medium, 500);--unrealButtonFocusRingSize: 4px;--unrealButtonFocusRingColor: var( --src-shadow-light, rgba(148, 163, 184, .16) );--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 1px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16));position:relative;display:flex;justify-content:center;align-items:center;width:var(--unrealButtonWidth);height:var(--unrealButtonHeight);padding:var(--unrealButtonPaddings);font-weight:var(--unrealButtonFontWeight);font-size:var(--unrealButtonFontSize);font-style:normal;line-height:var(--unrealButtonLineHeight);text-align:center;color:var(--unrealButtonFontColor);letter-spacing:0;white-space:nowrap;border:none;background-color:var(--unrealButtonBgColor);border-radius:var(--unrealButtonBorderRadius);box-shadow:var(--unrealButtonBoxShadow);cursor:pointer}.unreal-button:after{content:\"\";position:absolute;inset:0;border-radius:var(--unrealButtonBorderRadius);border:var(--unrealButtonBorder)}.unreal-button>span{display:flex;justify-content:center;align-items:center;gap:var(--src-gap-md, 6px);width:auto;font-size:inherit;line-height:inherit;color:inherit;font-weight:inherit}.unreal-button:disabled{--unrealButtonBgColor: var( --src-ui-accent-disabled, rgba(148, 163, 184, .24) );--unrealButtonFontColor: var(--src-text-ui-secondary-disabled, #9ca3af);--unrealButtonBoxShadow: none;--unrealButtonBorder: none;--unrealButtonBorderColor: transparent;cursor:default;pointer-events:none}.unreal-button:focus-visible{--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af);outline:none;box-shadow:0 0 0 var(--unrealButtonFocusRingSize) var(--unrealButtonFocusRingColor)}@media(pointer:fine){.unreal-button:hover{--unrealButtonBgColor: var( --src-ui-secondary-default-hover, rgba(148, 163, 184, .08) );--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af)}}.unreal-button--primary{--unrealButtonBgColor: var(--unreal-accent, var(--src-ui-accent-default, #017bff));--unrealButtonBorder: none;--unrealButtonFontColor: var(--unreal-accent-text, var(--src-text-ui-primary-main, #fff));--unrealButtonFocusRingColor: var( --src-shadow-accent-light, rgba(1, 123, 255, .16) )}@media(pointer:fine){.unreal-button--primary:hover{--unrealButtonBgColor: var(--unreal-accent-hover, var(--src-ui-accent-default-hover, #016fe6));--unrealButtonFontColor: var(--unreal-accent-text, var(--src-text-ui-primary-main, #fff))}}.unreal-button--lg{--unrealButtonHeight: 44px;--unrealButtonPaddings: 0 16px;--unrealButtonBorderRadius: 8px;--unrealButtonFontSize: var(--src-font-size-base, 16px);--unrealButtonLineHeight: var(--src-font-line-base, 24px);--unrealButtonFocusRingSize: 6px;--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 2px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16))}.unreal-button--lg>span{gap:var(--src-gap-md, 8px)}.unreal-button--full-width{--unrealButtonWidth: 100%}\n"] }]
|
|
43
43
|
}], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], weight: [{ type: i0.Input, args: [{ isSignal: true, alias: "weight", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], isDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDisabled", required: false }] }], testID: [{ type: i0.Input, args: [{ isSignal: true, alias: "data-testid", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }] } });
|
|
44
44
|
|
|
45
|
+
function AnswerHandler(msg) {
|
|
46
|
+
this.onWebRtcAnswer$.next(msg);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function ConfigHandler(msg) {
|
|
50
|
+
this.onConfig$.next(msg);
|
|
51
|
+
//DO NOT REMOVE. Possible required in future for SFU
|
|
52
|
+
/*this.send({
|
|
53
|
+
type: OrchestrationMessageTypes.listStreamers,
|
|
54
|
+
correlationId: this.correlationId,
|
|
55
|
+
});*/
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function IceCandidateHandler(msg) {
|
|
59
|
+
this.onWebRtcIce$.next(msg.candidate);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const scoped = (templateString) => `[UNREAL] ${templateString[0]}`;
|
|
63
|
+
const unrealCommandCallback = createAction(scoped `unreal command callback`, props());
|
|
64
|
+
const commandStarted = createAction(scoped `command started`, props());
|
|
65
|
+
const commandCompleted = createAction(scoped `command completed`, props());
|
|
66
|
+
const trackMixpanelEvent = createAction(scoped `track Mixpanel event`, props());
|
|
67
|
+
const trackTelemetryData = createAction(scoped `track telemetry data`, props());
|
|
68
|
+
const setMaxFps = createAction(scoped `change fps`, props());
|
|
69
|
+
const destroyRemoteConnections = createAction(scoped `destroyRemoteConnections`, props());
|
|
70
|
+
const destroyUnrealScene = createAction(scoped `destroyUnrealScene`);
|
|
71
|
+
const setCirrusConnected = createAction(scoped `cirrusConnected`);
|
|
72
|
+
const setCirrusDisconnected = createAction(scoped `cirrusDisconnected`);
|
|
73
|
+
const changeStatusMainVideoOnScene = createAction(scoped `change status main video on scene`, props());
|
|
74
|
+
const setAwsInstance = createAction(scoped `set aws instance`, props());
|
|
75
|
+
const setStatusMessage = createAction(scoped `set status message`, props());
|
|
76
|
+
const setStatusPercentSignallingServer = createAction(scoped `change status percent signalling server`, props());
|
|
77
|
+
const setFreezeFrame = createAction(scoped `set freeze frame`, props());
|
|
78
|
+
const setStreamClientCompanyId = createAction(scoped `set stream client company id`, props());
|
|
79
|
+
const setStreamViewId = createAction(scoped `set stream view id`, props());
|
|
80
|
+
const setIntroImageSrc = createAction(scoped `set intro image src`, props());
|
|
81
|
+
const setLoadingImageSrc = createAction(scoped `set loading image src`, props());
|
|
82
|
+
const setIntroVideoSrc = createAction(scoped `set intro video src`, props());
|
|
83
|
+
const resetIntroSrc = createAction(scoped `reset intro src`);
|
|
84
|
+
const setFreezeFrameFromVideo = createAction(scoped `set freeze frame from video`, props());
|
|
85
|
+
const setEstablishingConnection = createAction(scoped `set establishing connection`, props());
|
|
86
|
+
const dataChannelConnected = createAction(scoped `data channel connected`, props());
|
|
87
|
+
const dataChannelReady = createAction(scoped `dataChannelReady`);
|
|
88
|
+
const setConfig = createAction(scoped `set config`, props());
|
|
89
|
+
const disconnectStream = createAction(scoped `disconnectStream`, props());
|
|
90
|
+
const dropConnection = createAction(scoped `dropConnection`);
|
|
91
|
+
const setViewportReady = createAction(scoped `set viewport ready`);
|
|
92
|
+
const setViewportNotReady = createAction(scoped `set viewport not ready`);
|
|
93
|
+
const changeStreamResolutionAction = createAction(scoped `change stream resolution`, props());
|
|
94
|
+
const setSignalingName = createAction(scoped `set aws instanceName`, props());
|
|
95
|
+
const setOrchestrationParameters = createAction(scoped `set orchestration parameters`, props());
|
|
96
|
+
/**
|
|
97
|
+
* Dispatched when the orchestration server signals that the reserved
|
|
98
|
+
* instance is ready to serve the stream (`instanceReady` WS message).
|
|
99
|
+
* Carries the stream-machine coordinates — `originInstanceUri` is used to
|
|
100
|
+
* open the second (stream-machine) signalling socket. Also consumed by
|
|
101
|
+
* telemetry as a phase marker.
|
|
102
|
+
*/
|
|
103
|
+
const instanceReady = createAction(scoped `instance ready`, props());
|
|
104
|
+
/**
|
|
105
|
+
* Dispatched when `RegionsPingService.getFastest()` begins — right
|
|
106
|
+
* before the region-list HTTP request goes out. Bookends the entire
|
|
107
|
+
* region-discovery flow together with {@link regionResolved}.
|
|
108
|
+
* Observability-only — no state change.
|
|
109
|
+
*/
|
|
110
|
+
const regionPingFetchStart = createAction(scoped `region ping fetch start`);
|
|
111
|
+
/**
|
|
112
|
+
* Dispatched once `RegionsPingService.getProviders()` returns the region
|
|
113
|
+
* list — i.e., the fetch step is done and the actual per-region ping
|
|
114
|
+
* sweep is about to start. Splits the region-discovery waterfall into
|
|
115
|
+
* two legs: config fetch vs. ping sweep. Observability-only.
|
|
116
|
+
*/
|
|
117
|
+
const regionPingFetchEnd = createAction(scoped `region ping fetch end`);
|
|
118
|
+
/**
|
|
119
|
+
* Dispatched once the closest region has been resolved by the
|
|
120
|
+
* regions-ping service. Carries every sampled ping RTT to the winning
|
|
121
|
+
* region (one entry per probe — `checkLatency` issues `ping_count`
|
|
122
|
+
* sequential pings per region) so consumers can inspect variance /
|
|
123
|
+
* jitter, not just the mean. Observability-only — no state change.
|
|
124
|
+
*/
|
|
125
|
+
const regionResolved = createAction(scoped `region resolved`, props());
|
|
126
|
+
/**
|
|
127
|
+
* Dispatched around the HTTP polling roundup inside `getAwsInstance`
|
|
128
|
+
* (old orchestration flow). Only fires when polling is actually used
|
|
129
|
+
* (i.e., signaling URL is not a direct `ws://` / `wss://` endpoint).
|
|
130
|
+
* Observability-only — no state change.
|
|
131
|
+
*/
|
|
132
|
+
const pollingStarted = createAction(scoped `polling started`);
|
|
133
|
+
const pollingEnded = createAction(scoped `polling ended`);
|
|
134
|
+
/**
|
|
135
|
+
* Dispatched once for every `retry()` attempt inside the signalling
|
|
136
|
+
* connect pipeline. Two kinds:
|
|
137
|
+
*
|
|
138
|
+
* - `'connect'` — outer retry around the whole connect pipeline
|
|
139
|
+
* (region ping → AWS instance → WebSocket). Fires when the WS
|
|
140
|
+
* errors (e.g. "WebSocket closed before open") and the entire
|
|
141
|
+
* pipeline is re-subscribed. See `SIGNALLING.md` — every one of
|
|
142
|
+
* these currently triggers a fresh region-ping storm.
|
|
143
|
+
* - `'orchestration'` — inner retry inside `getAwsInstance`,
|
|
144
|
+
* advancing `urlGen` to the next candidate signalling URL when
|
|
145
|
+
* polling fails or returns an error payload.
|
|
146
|
+
*
|
|
147
|
+
* Observability-only — no state change. Surfaces in the viewer's
|
|
148
|
+
* Anomalies table as `signaling_retry` rows so a session that
|
|
149
|
+
* needed N tries shows N entries with their reasons.
|
|
150
|
+
*/
|
|
151
|
+
const signalingRetryAttempted = createAction(scoped `signaling retry attempted`, props());
|
|
152
|
+
/**
|
|
153
|
+
* Dispatched when the client has just sent an SDP answer back to Cirrus
|
|
154
|
+
* after receiving the server-initiated offer. Observability-only.
|
|
155
|
+
*/
|
|
156
|
+
const answerSent = createAction(scoped `answer sent`);
|
|
157
|
+
const setOrchestrationMessage = createAction(scoped `set orchestration message`, props());
|
|
158
|
+
const setOrchestrationProgress = createAction(scoped `set orchestration progress`, props());
|
|
159
|
+
const setOrchestrationContext = createAction(scoped `set orchestration params`, props());
|
|
160
|
+
const updateCirrusInfo = createAction(scoped `set unrealInfo`, props());
|
|
161
|
+
const setLoopBackCommandIsCompleted = createAction(scoped `set loopBack command is completed`);
|
|
162
|
+
const setAfkTimerVisible = createAction(scoped `set afk timer visible`);
|
|
163
|
+
const setAfkTimerHide = createAction(scoped `set afk timer hide`);
|
|
164
|
+
const setAfkTimerCountdown = createAction(scoped `set afk timer count down`, props());
|
|
165
|
+
const showUnrealErrorMessage = createAction(scoped `show unreal error message`, props());
|
|
166
|
+
const initSignalling = createAction(scoped `init signalling`);
|
|
167
|
+
/**
|
|
168
|
+
* Dispatched when a prefetched connection parked on `window.__stream` by the
|
|
169
|
+
* `stream-prefetch.js` head-script is adopted instead of opening a fresh
|
|
170
|
+
* `connectToSignaling`. Marks cirrus connected and records the adopted machine
|
|
171
|
+
* in one transaction; the data channel / app-init chain then proceeds via
|
|
172
|
+
* `dataChannelConnected` dispatched from `WebRtcPlayerService`.
|
|
173
|
+
*
|
|
174
|
+
* Deliberately does NOT touch `streamClientCompanyId` / `streamViewId`: the host
|
|
175
|
+
* app (e.g. metabox) sets those authoritatively from its own data before
|
|
176
|
+
* `initSignalling`, and the prefetch script's ids are connection-internal only.
|
|
177
|
+
*/
|
|
178
|
+
const streamAdopted = createAction(scoped `stream adopted`, props());
|
|
179
|
+
const startStream = createAction(scoped `startStream`, props());
|
|
180
|
+
const resetConfig = createAction(scoped `reset config`);
|
|
181
|
+
const resetAfk = createAction(scoped `reset afk action`);
|
|
182
|
+
const resetWarnTimeout = createAction(scoped `reset config warn timeout`);
|
|
183
|
+
/**
|
|
184
|
+
* Fan-in for "cancel an in-flight signalling attempt". Dispatched by the
|
|
185
|
+
* abortEstablishingConnection$ effect whenever any of these source actions
|
|
186
|
+
* fire: `setCirrusDisconnected` (cirrus dropped), `dropConnection` (user
|
|
187
|
+
* called drop), `destroyUnrealScene` (component teardown). Consumers
|
|
188
|
+
* (`SignallingService.abort$`, `setDataChannelTimeoutCheck$.abort$`) listen
|
|
189
|
+
* to this one action instead of the trio so the "abort" intent has a single
|
|
190
|
+
* named entry in Redux DevTools and one subscription site to update.
|
|
191
|
+
*/
|
|
192
|
+
const abortEstablishingConnection = createAction(scoped `abortEstablishingConnection`);
|
|
193
|
+
const setUnrealPlaywrightConfig = createAction(scoped `set unreal playwright config`);
|
|
194
|
+
const saveAnalyticsEvent = createAction(scoped `save analytics event`, props());
|
|
195
|
+
/** Action to trigger WebRTC peer reconnection (keeps signaling connection) */
|
|
196
|
+
const reconnectPeer = createAction(scoped `reconnect peer`, props());
|
|
197
|
+
/** Action to reset the data channel state before reconnection */
|
|
198
|
+
const resetDataChannelForReconnect = createAction(scoped `reset data channel for reconnect`);
|
|
199
|
+
/** Action dispatched when peer reconnection succeeds */
|
|
200
|
+
const reconnectPeerSuccess = createAction(scoped `reconnect peer success`);
|
|
201
|
+
/** Action dispatched when all peer reconnection attempts are exhausted */
|
|
202
|
+
const reconnectPeerFailed = createAction(scoped `reconnect peer failed`, props());
|
|
203
|
+
/** Action dispatched when ICE connection state changes to failed/disconnected */
|
|
204
|
+
const iceConnectionFailed = createAction(scoped `ice connection failed`, props());
|
|
205
|
+
const forceResizeViewport = createAction(scoped `force resize viewport`);
|
|
206
|
+
const videoAdaptedToContainer = createAction(scoped `video adapted to container`);
|
|
207
|
+
const videoStreamInitialized = createAction(scoped `video stream initialized`);
|
|
208
|
+
const dropDC = createAction(scoped `drop data channel`);
|
|
209
|
+
const dropPC = createAction(scoped `drop peer connection`);
|
|
210
|
+
const showLoadingProgress = createAction(scoped `show loading progress`, props());
|
|
211
|
+
const showResumeStreamPopup = createAction(scoped `show resume stream popup`, props());
|
|
212
|
+
const showUserInactivityTimeoutPopup = createAction(scoped `show user inactivity timeout popup`, props());
|
|
213
|
+
const setUserInactivityTimeout = createAction(scoped `set user inactivity timeout`, props());
|
|
214
|
+
const resetUserInactivityTimeout = createAction(scoped `reset user inactivity timeout`);
|
|
215
|
+
|
|
216
|
+
const WSCloseCode_NORMAL_CLOSURE = 3000;
|
|
217
|
+
const WSCloseCode_NORMAL_AFK_TIMEOUT = 3001;
|
|
218
|
+
const WSCloseCode_NORMAL_MANUAL_DISCONNECT = 3002;
|
|
219
|
+
const WSCloseCode_FORCE_CIRRUS_CLOSE = 3003;
|
|
220
|
+
const WSCloseCode_UNKNOWN = 1000;
|
|
221
|
+
const WSCloseCode_CIRRUS_PLAYER_DISCONNECTED = 1001;
|
|
222
|
+
const WSCloseCode_CIRRUS_ABNORMAL_CLOSURE = 1006;
|
|
223
|
+
const WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR = 1013;
|
|
224
|
+
const WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER = 1011;
|
|
225
|
+
const WSCloseCodes = [
|
|
226
|
+
WSCloseCode_NORMAL_CLOSURE,
|
|
227
|
+
WSCloseCode_NORMAL_AFK_TIMEOUT,
|
|
228
|
+
WSCloseCode_NORMAL_MANUAL_DISCONNECT,
|
|
229
|
+
WSCloseCode_FORCE_CIRRUS_CLOSE,
|
|
230
|
+
WSCloseCode_UNKNOWN,
|
|
231
|
+
WSCloseCode_CIRRUS_PLAYER_DISCONNECTED,
|
|
232
|
+
WSCloseCode_CIRRUS_ABNORMAL_CLOSURE,
|
|
233
|
+
WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR,
|
|
234
|
+
WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER,
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
const DisconnectReason = {
|
|
238
|
+
Afk: 'Afk',
|
|
239
|
+
None: 'None',
|
|
240
|
+
Destroy: 'Destroy',
|
|
241
|
+
DataChannelClosed: 'DataChannelClosed',
|
|
242
|
+
DataChannelTimeout: 'DataChannelTimeout',
|
|
243
|
+
WebRTCError: 'WebRTCError',
|
|
244
|
+
WebSocketError: 'WebSocketError',
|
|
245
|
+
WebSocketClose: 'WebSocketClose',
|
|
246
|
+
DropConnection: 'DropConnection',
|
|
247
|
+
OrchestrationPlayerDisconnected: 'OrchestrationPlayerDisconnected',
|
|
248
|
+
OrchestrationStreamerDisconnected: 'OrchestrationStreamerDisconnected',
|
|
249
|
+
IceConnectionFailed: 'IceConnectionFailed',
|
|
250
|
+
ReconnectFailed: 'ReconnectFailed',
|
|
251
|
+
NewConnectionAttempt: 'NewConnectionAttempt',
|
|
252
|
+
AbortEstablishingConnection: 'AbortEstablishingConnection',
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/*
|
|
256
|
+
export interface RequestReservation extends InstanceMessageBase {
|
|
257
|
+
type: 'requestReservation';
|
|
258
|
+
}
|
|
259
|
+
*/
|
|
260
|
+
const OrchestrationMessageTypes = {
|
|
261
|
+
error: 'error',
|
|
262
|
+
offer: 'offer',
|
|
263
|
+
answer: 'answer',
|
|
264
|
+
ssInfo: 'ssInfo',
|
|
265
|
+
config: 'config',
|
|
266
|
+
playerCount: 'playerCount',
|
|
267
|
+
iceCandidate: 'iceCandidate',
|
|
268
|
+
streamerList: 'streamerList',
|
|
269
|
+
requestStream: 'requestStream',
|
|
270
|
+
listStreamers: 'listStreamers',
|
|
271
|
+
instanceReady: 'instanceReady',
|
|
272
|
+
p2pEstablished: 'p2pEstablished',
|
|
273
|
+
instanceReserved: 'instanceReserved',
|
|
274
|
+
interruptClientStream: 'interruptClientStream',
|
|
275
|
+
};
|
|
276
|
+
|
|
45
277
|
const SpecialKeyCodes = {
|
|
46
278
|
BackSpace: 8,
|
|
47
279
|
Shift: 16,
|
|
@@ -140,67 +372,6 @@ const InputOptions = {
|
|
|
140
372
|
suppressBrowserKeys: true,
|
|
141
373
|
};
|
|
142
374
|
|
|
143
|
-
const WSCloseCode_NORMAL_CLOSURE = 3000;
|
|
144
|
-
const WSCloseCode_NORMAL_AFK_TIMEOUT = 3001;
|
|
145
|
-
const WSCloseCode_NORMAL_MANUAL_DISCONNECT = 3002;
|
|
146
|
-
const WSCloseCode_FORCE_CIRRUS_CLOSE = 3003;
|
|
147
|
-
const WSCloseCode_UNKNOWN = 1000;
|
|
148
|
-
const WSCloseCode_CIRRUS_PLAYER_DISCONNECTED = 1001;
|
|
149
|
-
const WSCloseCode_CIRRUS_ABNORMAL_CLOSURE = 1006;
|
|
150
|
-
const WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR = 1013;
|
|
151
|
-
const WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER = 1011;
|
|
152
|
-
const WSCloseCodes = [
|
|
153
|
-
WSCloseCode_NORMAL_CLOSURE,
|
|
154
|
-
WSCloseCode_NORMAL_AFK_TIMEOUT,
|
|
155
|
-
WSCloseCode_NORMAL_MANUAL_DISCONNECT,
|
|
156
|
-
WSCloseCode_FORCE_CIRRUS_CLOSE,
|
|
157
|
-
WSCloseCode_UNKNOWN,
|
|
158
|
-
WSCloseCode_CIRRUS_PLAYER_DISCONNECTED,
|
|
159
|
-
WSCloseCode_CIRRUS_ABNORMAL_CLOSURE,
|
|
160
|
-
WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR,
|
|
161
|
-
WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER,
|
|
162
|
-
];
|
|
163
|
-
|
|
164
|
-
const DisconnectReason = {
|
|
165
|
-
Afk: 'Afk',
|
|
166
|
-
None: 'None',
|
|
167
|
-
Destroy: 'Destroy',
|
|
168
|
-
DataChannelClosed: 'DataChannelClosed',
|
|
169
|
-
DataChannelTimeout: 'DataChannelTimeout',
|
|
170
|
-
WebRTCError: 'WebRTCError',
|
|
171
|
-
WebSocketError: 'WebSocketError',
|
|
172
|
-
WebSocketClose: 'WebSocketClose',
|
|
173
|
-
DropConnection: 'DropConnection',
|
|
174
|
-
OrchestrationPlayerDisconnected: 'OrchestrationPlayerDisconnected',
|
|
175
|
-
OrchestrationStreamerDisconnected: 'OrchestrationStreamerDisconnected',
|
|
176
|
-
IceConnectionFailed: 'IceConnectionFailed',
|
|
177
|
-
ReconnectFailed: 'ReconnectFailed',
|
|
178
|
-
NewConnectionAttempt: 'NewConnectionAttempt',
|
|
179
|
-
AbortEstablishingConnection: 'AbortEstablishingConnection',
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
/*
|
|
183
|
-
export interface RequestReservation extends InstanceMessageBase {
|
|
184
|
-
type: 'requestReservation';
|
|
185
|
-
}
|
|
186
|
-
*/
|
|
187
|
-
const OrchestrationMessageTypes = {
|
|
188
|
-
error: 'error',
|
|
189
|
-
offer: 'offer',
|
|
190
|
-
answer: 'answer',
|
|
191
|
-
ssInfo: 'ssInfo',
|
|
192
|
-
config: 'config',
|
|
193
|
-
playerCount: 'playerCount',
|
|
194
|
-
iceCandidate: 'iceCandidate',
|
|
195
|
-
streamerList: 'streamerList',
|
|
196
|
-
requestStream: 'requestStream',
|
|
197
|
-
listStreamers: 'listStreamers',
|
|
198
|
-
instanceReady: 'instanceReady',
|
|
199
|
-
p2pEstablished: 'p2pEstablished',
|
|
200
|
-
instanceReserved: 'instanceReserved',
|
|
201
|
-
interruptClientStream: 'interruptClientStream',
|
|
202
|
-
};
|
|
203
|
-
|
|
204
375
|
/** Disconnect reasons that should trigger remote connection cleanup. */
|
|
205
376
|
const DESTROY_DISCONNECT_REASONS = [
|
|
206
377
|
DisconnectReason.Afk,
|
|
@@ -300,160 +471,6 @@ const UnrealStatusMessage = {
|
|
|
300
471
|
STARTING_YOUR_SESSION: 'Starting your session',
|
|
301
472
|
};
|
|
302
473
|
|
|
303
|
-
const scoped = (templateString) => `[UNREAL] ${templateString[0]}`;
|
|
304
|
-
const unrealCommandCallback = createAction(scoped `unreal command callback`, props());
|
|
305
|
-
const commandStarted = createAction(scoped `command started`, props());
|
|
306
|
-
const commandCompleted = createAction(scoped `command completed`, props());
|
|
307
|
-
const trackMixpanelEvent = createAction(scoped `track Mixpanel event`, props());
|
|
308
|
-
const trackTelemetryData = createAction(scoped `track telemetry data`, props());
|
|
309
|
-
const setMaxFps = createAction(scoped `change fps`, props());
|
|
310
|
-
const destroyRemoteConnections = createAction(scoped `destroyRemoteConnections`, props());
|
|
311
|
-
const destroyUnrealScene = createAction(scoped `destroyUnrealScene`);
|
|
312
|
-
const setCirrusConnected = createAction(scoped `cirrusConnected`);
|
|
313
|
-
const setCirrusDisconnected = createAction(scoped `cirrusDisconnected`);
|
|
314
|
-
const changeStatusMainVideoOnScene = createAction(scoped `change status main video on scene`, props());
|
|
315
|
-
const setAwsInstance = createAction(scoped `set aws instance`, props());
|
|
316
|
-
const setStatusMessage = createAction(scoped `set status message`, props());
|
|
317
|
-
const setStatusPercentSignallingServer = createAction(scoped `change status percent signalling server`, props());
|
|
318
|
-
const setFreezeFrame = createAction(scoped `set freeze frame`, props());
|
|
319
|
-
const setStreamClientCompanyId = createAction(scoped `set stream client company id`, props());
|
|
320
|
-
const setStreamViewId = createAction(scoped `set stream view id`, props());
|
|
321
|
-
const setIntroImageSrc = createAction(scoped `set intro image src`, props());
|
|
322
|
-
const setLoadingImageSrc = createAction(scoped `set loading image src`, props());
|
|
323
|
-
const setIntroVideoSrc = createAction(scoped `set intro video src`, props());
|
|
324
|
-
const resetIntroSrc = createAction(scoped `reset intro src`);
|
|
325
|
-
const setFreezeFrameFromVideo = createAction(scoped `set freeze frame from video`, props());
|
|
326
|
-
const setEstablishingConnection = createAction(scoped `set establishing connection`, props());
|
|
327
|
-
const dataChannelConnected = createAction(scoped `data channel connected`, props());
|
|
328
|
-
const dataChannelReady = createAction(scoped `dataChannelReady`);
|
|
329
|
-
const setConfig = createAction(scoped `set config`, props());
|
|
330
|
-
const disconnectStream = createAction(scoped `disconnectStream`, props());
|
|
331
|
-
const dropConnection = createAction(scoped `dropConnection`);
|
|
332
|
-
const setViewportReady = createAction(scoped `set viewport ready`);
|
|
333
|
-
const setViewportNotReady = createAction(scoped `set viewport not ready`);
|
|
334
|
-
const changeStreamResolutionAction = createAction(scoped `change stream resolution`, props());
|
|
335
|
-
const setSignalingName = createAction(scoped `set aws instanceName`, props());
|
|
336
|
-
const setOrchestrationParameters = createAction(scoped `set orchestration parameters`, props());
|
|
337
|
-
/**
|
|
338
|
-
* Dispatched when the orchestration server signals that the reserved
|
|
339
|
-
* instance is ready to serve the stream (`instanceReady` WS message).
|
|
340
|
-
* Carries the stream-machine coordinates — `originInstanceUri` is used to
|
|
341
|
-
* open the second (stream-machine) signalling socket. Also consumed by
|
|
342
|
-
* telemetry as a phase marker.
|
|
343
|
-
*/
|
|
344
|
-
const instanceReady = createAction(scoped `instance ready`, props());
|
|
345
|
-
/**
|
|
346
|
-
* Dispatched when `RegionsPingService.getFastest()` begins — right
|
|
347
|
-
* before the region-list HTTP request goes out. Bookends the entire
|
|
348
|
-
* region-discovery flow together with {@link regionResolved}.
|
|
349
|
-
* Observability-only — no state change.
|
|
350
|
-
*/
|
|
351
|
-
const regionPingFetchStart = createAction(scoped `region ping fetch start`);
|
|
352
|
-
/**
|
|
353
|
-
* Dispatched once `RegionsPingService.getProviders()` returns the region
|
|
354
|
-
* list — i.e., the fetch step is done and the actual per-region ping
|
|
355
|
-
* sweep is about to start. Splits the region-discovery waterfall into
|
|
356
|
-
* two legs: config fetch vs. ping sweep. Observability-only.
|
|
357
|
-
*/
|
|
358
|
-
const regionPingFetchEnd = createAction(scoped `region ping fetch end`);
|
|
359
|
-
/**
|
|
360
|
-
* Dispatched once the closest region has been resolved by the
|
|
361
|
-
* regions-ping service. Carries every sampled ping RTT to the winning
|
|
362
|
-
* region (one entry per probe — `checkLatency` issues `ping_count`
|
|
363
|
-
* sequential pings per region) so consumers can inspect variance /
|
|
364
|
-
* jitter, not just the mean. Observability-only — no state change.
|
|
365
|
-
*/
|
|
366
|
-
const regionResolved = createAction(scoped `region resolved`, props());
|
|
367
|
-
/**
|
|
368
|
-
* Dispatched around the HTTP polling roundup inside `getAwsInstance`
|
|
369
|
-
* (old orchestration flow). Only fires when polling is actually used
|
|
370
|
-
* (i.e., signaling URL is not a direct `ws://` / `wss://` endpoint).
|
|
371
|
-
* Observability-only — no state change.
|
|
372
|
-
*/
|
|
373
|
-
const pollingStarted = createAction(scoped `polling started`);
|
|
374
|
-
const pollingEnded = createAction(scoped `polling ended`);
|
|
375
|
-
/**
|
|
376
|
-
* Dispatched once for every `retry()` attempt inside the signalling
|
|
377
|
-
* connect pipeline. Two kinds:
|
|
378
|
-
*
|
|
379
|
-
* - `'connect'` — outer retry around the whole connect pipeline
|
|
380
|
-
* (region ping → AWS instance → WebSocket). Fires when the WS
|
|
381
|
-
* errors (e.g. "WebSocket closed before open") and the entire
|
|
382
|
-
* pipeline is re-subscribed. See `SIGNALLING.md` — every one of
|
|
383
|
-
* these currently triggers a fresh region-ping storm.
|
|
384
|
-
* - `'orchestration'` — inner retry inside `getAwsInstance`,
|
|
385
|
-
* advancing `urlGen` to the next candidate signalling URL when
|
|
386
|
-
* polling fails or returns an error payload.
|
|
387
|
-
*
|
|
388
|
-
* Observability-only — no state change. Surfaces in the viewer's
|
|
389
|
-
* Anomalies table as `signaling_retry` rows so a session that
|
|
390
|
-
* needed N tries shows N entries with their reasons.
|
|
391
|
-
*/
|
|
392
|
-
const signalingRetryAttempted = createAction(scoped `signaling retry attempted`, props());
|
|
393
|
-
/**
|
|
394
|
-
* Dispatched when the client has just sent an SDP answer back to Cirrus
|
|
395
|
-
* after receiving the server-initiated offer. Observability-only.
|
|
396
|
-
*/
|
|
397
|
-
const answerSent = createAction(scoped `answer sent`);
|
|
398
|
-
const setOrchestrationMessage = createAction(scoped `set orchestration message`, props());
|
|
399
|
-
const setOrchestrationProgress = createAction(scoped `set orchestration progress`, props());
|
|
400
|
-
const setOrchestrationContext = createAction(scoped `set orchestration params`, props());
|
|
401
|
-
const updateCirrusInfo = createAction(scoped `set unrealInfo`, props());
|
|
402
|
-
const setLoopBackCommandIsCompleted = createAction(scoped `set loopBack command is completed`);
|
|
403
|
-
const setAfkTimerVisible = createAction(scoped `set afk timer visible`);
|
|
404
|
-
const setAfkTimerHide = createAction(scoped `set afk timer hide`);
|
|
405
|
-
const setAfkTimerCountdown = createAction(scoped `set afk timer count down`, props());
|
|
406
|
-
const showUnrealErrorMessage = createAction(scoped `show unreal error message`, props());
|
|
407
|
-
const initSignalling = createAction(scoped `init signalling`);
|
|
408
|
-
/**
|
|
409
|
-
* Dispatched when a prefetched connection parked on `window.__stream` by the
|
|
410
|
-
* `stream-prefetch.js` head-script is adopted instead of opening a fresh
|
|
411
|
-
* `connectToSignaling`. Marks cirrus connected and records the adopted machine
|
|
412
|
-
* in one transaction; the data channel / app-init chain then proceeds via
|
|
413
|
-
* `dataChannelConnected` dispatched from `WebRtcPlayerService`.
|
|
414
|
-
*
|
|
415
|
-
* Deliberately does NOT touch `streamClientCompanyId` / `streamViewId`: the host
|
|
416
|
-
* app (e.g. metabox) sets those authoritatively from its own data before
|
|
417
|
-
* `initSignalling`, and the prefetch script's ids are connection-internal only.
|
|
418
|
-
*/
|
|
419
|
-
const streamAdopted = createAction(scoped `stream adopted`, props());
|
|
420
|
-
const startStream = createAction(scoped `startStream`, props());
|
|
421
|
-
const resetConfig = createAction(scoped `reset config`);
|
|
422
|
-
const resetAfk = createAction(scoped `reset afk action`);
|
|
423
|
-
const resetWarnTimeout = createAction(scoped `reset config warn timeout`);
|
|
424
|
-
/**
|
|
425
|
-
* Fan-in for "cancel an in-flight signalling attempt". Dispatched by the
|
|
426
|
-
* abortEstablishingConnection$ effect whenever any of these source actions
|
|
427
|
-
* fire: `setCirrusDisconnected` (cirrus dropped), `dropConnection` (user
|
|
428
|
-
* called drop), `destroyUnrealScene` (component teardown). Consumers
|
|
429
|
-
* (`SignallingService.abort$`, `setDataChannelTimeoutCheck$.abort$`) listen
|
|
430
|
-
* to this one action instead of the trio so the "abort" intent has a single
|
|
431
|
-
* named entry in Redux DevTools and one subscription site to update.
|
|
432
|
-
*/
|
|
433
|
-
const abortEstablishingConnection = createAction(scoped `abortEstablishingConnection`);
|
|
434
|
-
const setUnrealPlaywrightConfig = createAction(scoped `set unreal playwright config`);
|
|
435
|
-
const saveAnalyticsEvent = createAction(scoped `save analytics event`, props());
|
|
436
|
-
/** Action to trigger WebRTC peer reconnection (keeps signaling connection) */
|
|
437
|
-
const reconnectPeer = createAction(scoped `reconnect peer`, props());
|
|
438
|
-
/** Action to reset the data channel state before reconnection */
|
|
439
|
-
const resetDataChannelForReconnect = createAction(scoped `reset data channel for reconnect`);
|
|
440
|
-
/** Action dispatched when peer reconnection succeeds */
|
|
441
|
-
const reconnectPeerSuccess = createAction(scoped `reconnect peer success`);
|
|
442
|
-
/** Action dispatched when all peer reconnection attempts are exhausted */
|
|
443
|
-
const reconnectPeerFailed = createAction(scoped `reconnect peer failed`, props());
|
|
444
|
-
/** Action dispatched when ICE connection state changes to failed/disconnected */
|
|
445
|
-
const iceConnectionFailed = createAction(scoped `ice connection failed`, props());
|
|
446
|
-
const forceResizeViewport = createAction(scoped `force resize viewport`);
|
|
447
|
-
const videoAdaptedToContainer = createAction(scoped `video adapted to container`);
|
|
448
|
-
const videoStreamInitialized = createAction(scoped `video stream initialized`);
|
|
449
|
-
const dropDC = createAction(scoped `drop data channel`);
|
|
450
|
-
const dropPC = createAction(scoped `drop peer connection`);
|
|
451
|
-
const showLoadingProgress = createAction(scoped `show loading progress`, props());
|
|
452
|
-
const showResumeStreamPopup = createAction(scoped `show resume stream popup`, props());
|
|
453
|
-
const showUserInactivityTimeoutPopup = createAction(scoped `show user inactivity timeout popup`, props());
|
|
454
|
-
const setUserInactivityTimeout = createAction(scoped `set user inactivity timeout`, props());
|
|
455
|
-
const resetUserInactivityTimeout = createAction(scoped `reset user inactivity timeout`);
|
|
456
|
-
|
|
457
474
|
const COMMAND_EXILE_TIMEOUT_MS = 10_000;
|
|
458
475
|
function exileExpiredCommands(commandsInProgress, idToRemove, now) {
|
|
459
476
|
let remaining = commandsInProgress.filter(whereNot({ id: idToRemove }));
|
|
@@ -772,437 +789,18 @@ const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScen
|
|
|
772
789
|
],
|
|
773
790
|
})), on(commandCompleted, (state, { id, now }) => {
|
|
774
791
|
const { commandsInProgress, exiled } = exileExpiredCommands(state.commandsInProgress, id, now);
|
|
775
|
-
return {
|
|
776
|
-
...state,
|
|
777
|
-
commandsInProgress,
|
|
778
|
-
// +1 for the just-completed command, +exiled for stragglers we GC'd.
|
|
779
|
-
totalCommandsCompleted: state.totalCommandsCompleted + 1 + exiled,
|
|
780
|
-
};
|
|
781
|
-
}));
|
|
782
|
-
|
|
783
|
-
const unrealFeature = createFeature({
|
|
784
|
-
name: 'unrealFeature',
|
|
785
|
-
reducer: unrealReducer,
|
|
786
|
-
});
|
|
787
|
-
|
|
788
|
-
class SubService {
|
|
789
|
-
constructor() {
|
|
790
|
-
this.destroyRef = inject(DestroyRef);
|
|
791
|
-
this.destroy$ = new Subject();
|
|
792
|
-
this.actions$ = inject(Actions);
|
|
793
|
-
this.store = inject(Store);
|
|
794
|
-
this.disconnect$ = this.store
|
|
795
|
-
.select(unrealFeature.selectCirrusConnected)
|
|
796
|
-
.pipe(skip(1), filter(Falsy), share());
|
|
797
|
-
this.destroyRef.onDestroy(() => {
|
|
798
|
-
this.destroy$.next();
|
|
799
|
-
this.destroy$.complete();
|
|
800
|
-
});
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
function AnswerHandler(msg) {
|
|
805
|
-
this.onWebRtcAnswer$.next(msg);
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
function ConfigHandler(msg) {
|
|
809
|
-
this.onConfig$.next(msg);
|
|
810
|
-
//DO NOT REMOVE. Possible required in future for SFU
|
|
811
|
-
/*this.send({
|
|
812
|
-
type: OrchestrationMessageTypes.listStreamers,
|
|
813
|
-
correlationId: this.correlationId,
|
|
814
|
-
});*/
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
function IceCandidateHandler(msg) {
|
|
818
|
-
this.onWebRtcIce$.next(msg.candidate);
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
function InstanceReadyHandler(msg) {
|
|
822
|
-
const payload = msg?.payload;
|
|
823
|
-
if (!payload) {
|
|
824
|
-
Logger.warn('instanceReady received without payload — ignoring');
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
if (!payload.originInstanceUri) {
|
|
828
|
-
Logger.warn('instanceReady without originInstanceUri — ignoring');
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
// Emit for observability (telemetry) and store the stream-machine coords.
|
|
832
|
-
this.store.dispatch(instanceReady(payload));
|
|
833
|
-
// This is the answer `requestCirrusViaSocket` is waiting for: the Cirrus
|
|
834
|
-
// address (`originInstanceUri`) plus the connection id to hand it. The socket
|
|
835
|
-
// itself is opened by the shared `connectToCirrus`, not here.
|
|
836
|
-
this.onInstanceReady$.next(payload);
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
function InstanceReservedHandler(msg) {
|
|
840
|
-
this.store.dispatch(setOrchestrationParameters({
|
|
841
|
-
instanceName: msg.payload.instanceId,
|
|
842
|
-
streamRequestId: msg.payload.streamRequestId,
|
|
843
|
-
eta: msg.payload.eta ?? 150,
|
|
844
|
-
}));
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
function PlayerCountHandler(msg) {
|
|
848
|
-
Logger.log(msg);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
function SSInfoHandler(msg) {
|
|
852
|
-
this.store.dispatch(updateCirrusInfo({ ssData: msg.data }));
|
|
853
|
-
return null;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
const dropCodes = [
|
|
857
|
-
WSCloseCode_UNKNOWN,
|
|
858
|
-
WSCloseCode_CIRRUS_PLAYER_DISCONNECTED,
|
|
859
|
-
WSCloseCode_CIRRUS_ABNORMAL_CLOSURE,
|
|
860
|
-
WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER,
|
|
861
|
-
WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR,
|
|
862
|
-
];
|
|
863
|
-
function OnCloseHandler(e) {
|
|
864
|
-
const message = `WebSocket closed: ${JSON.stringify(e.code)} - ${e.reason}`;
|
|
865
|
-
Logger.warn(message);
|
|
866
|
-
this.store.dispatch(setCirrusDisconnected());
|
|
867
|
-
if (dropCodes.includes(e.code)) {
|
|
868
|
-
this.store.dispatch(dropConnection());
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
function OnErrorHandler(e) {
|
|
873
|
-
Logger.warn(`WS error: ${JSON.stringify(e)}`);
|
|
874
|
-
this.store.dispatch(disconnectStream({
|
|
875
|
-
reason: DisconnectReason.WebSocketError,
|
|
876
|
-
message: 'WebSocket Error',
|
|
877
|
-
}));
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
function OnMessageHandler(e) {
|
|
881
|
-
const handleData = (data) => {
|
|
882
|
-
//Logger.colored(...COLOR_CODES.FROM_SIGNALING, data);
|
|
883
|
-
const msg = JSON.parse(data);
|
|
884
|
-
this.handleMessage(msg);
|
|
885
|
-
};
|
|
886
|
-
if (typeof e.data === 'string') {
|
|
887
|
-
try {
|
|
888
|
-
handleData(e.data);
|
|
889
|
-
}
|
|
890
|
-
catch (err) {
|
|
891
|
-
Logger.warn('OnMessageHandler: failed to parse string message', err);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
else if (e.data instanceof Blob) {
|
|
895
|
-
e.data
|
|
896
|
-
.text()
|
|
897
|
-
.then(handleData)
|
|
898
|
-
.catch((err) => Logger.warn('OnMessageHandler: failed to parse Blob message', err));
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
/**
|
|
903
|
-
* Open handler of the CIRRUS socket only (`addWsHandlers`), so
|
|
904
|
-
* `setCirrusConnected` now means what it says. It used to also send
|
|
905
|
-
* `requestStream` here, which made the flag fire on the orchestration control
|
|
906
|
-
* socket — a whole provisioning wait (`eta`, ~150 s) before any Cirrus existed.
|
|
907
|
-
* `requestStream` belongs to the control socket and is sent by
|
|
908
|
-
* `requestCirrusViaSocket`.
|
|
909
|
-
*/
|
|
910
|
-
function OnOpenHandler() {
|
|
911
|
-
this.store.dispatch(setCirrusConnected());
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
/**
|
|
915
|
-
* Keeps the max of numbers seen so far; resets to 0 whenever `reset$` emits.
|
|
916
|
-
*/
|
|
917
|
-
const keepMaxUntilReset = (reset$) => (source$) => merge(source$, reset$.pipe(map(() => null))).pipe(scan((max, value) => (value === null ? 0 : Math.max(max, value)), 0));
|
|
918
|
-
|
|
919
|
-
// Reused across every inbound data-channel message — instantiating a
|
|
920
|
-
// TextDecoder per call adds needless GC churn on a hot path.
|
|
921
|
-
const utf16Decoder = new TextDecoder('utf-16');
|
|
922
|
-
function decodeData(anyData) {
|
|
923
|
-
try {
|
|
924
|
-
return utf16Decoder.decode(anyData);
|
|
925
|
-
}
|
|
926
|
-
catch {
|
|
927
|
-
return '';
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
const dispatchResize = () => {
|
|
932
|
-
window.dispatchEvent(new Event('resize'));
|
|
933
|
-
};
|
|
934
|
-
|
|
935
|
-
const errorCodes = {
|
|
936
|
-
code404: 'Your asset is being synced on Metabox servers and is not yet available in Metabox Studio or Metabox Configurator. Please check again later after syncing is complete. ',
|
|
937
|
-
};
|
|
938
|
-
function getRtcErrorMessage(code) {
|
|
939
|
-
if (code === null) {
|
|
940
|
-
return undefined;
|
|
941
|
-
}
|
|
942
|
-
return errorCodes[`code${code}`];
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
const JUMP_THRESHOLD = 0.95;
|
|
946
|
-
const MAX_TRANSITION_DURATION_MS = 200;
|
|
947
|
-
/**
|
|
948
|
-
* Maps a 0..1 stream directly to a 0..100 integer percent stream.
|
|
949
|
-
* No animation — emits whatever the source emits, rounded and clamped.
|
|
950
|
-
*/
|
|
951
|
-
const floatToPercents = () => (source) => source.pipe(map$1((value) => clampf(0, 100, Math.round(value * 100))));
|
|
952
|
-
/**
|
|
953
|
-
* Maps a 0..1 progress stream to a smoothed 0..100 integer percent stream.
|
|
954
|
-
* Animates between consecutive values over up to 200ms; jumps directly when
|
|
955
|
-
* the delta exceeds 0.95 (treated as a reset, not a real progression).
|
|
956
|
-
*/
|
|
957
|
-
const floatToSmoothPercents = () => (source) => source.pipe(timestamp(), startWith({ value: 0, timestamp: 0 }), pairwise(), concatMap(([prev, curr]) => {
|
|
958
|
-
const isJump = Math.abs(curr.value - prev.value) > JUMP_THRESHOLD;
|
|
959
|
-
const dt = prev.timestamp ? curr.timestamp - prev.timestamp : 0;
|
|
960
|
-
if (isJump || dt === 0) {
|
|
961
|
-
return of(curr.value);
|
|
962
|
-
}
|
|
963
|
-
return smoothTransition(prev.value, curr.value, Math.min(dt, MAX_TRANSITION_DURATION_MS));
|
|
964
|
-
}), floatToPercents());
|
|
965
|
-
/**
|
|
966
|
-
* Smoothly interpolates from `start` to `end` over `duration`ms using the
|
|
967
|
-
* animation frame scheduler. Completes when the transition finishes.
|
|
968
|
-
*
|
|
969
|
-
* NOTE: duplicates `smoothTransition` from `@3dsource/utils`. The utils version
|
|
970
|
-
* is missing `takeWhile(... < 1, true)` and never completes — which would
|
|
971
|
-
* deadlock the `concatMap` above. Keep this local copy until utils is fixed.
|
|
972
|
-
* Not exported: internal to `floatToSmoothPercents`.
|
|
973
|
-
*/
|
|
974
|
-
const smoothTransition = (start, end, duration) => {
|
|
975
|
-
const startTime = animationFrameScheduler.now();
|
|
976
|
-
return interval(0, animationFrameScheduler).pipe(map$1(() => clampf(0, 1, (animationFrameScheduler.now() - startTime) / duration)), distinctUntilChanged(), takeWhile((fraction) => fraction < 1, true), map$1((fraction) => lerp(start, end, fraction)));
|
|
977
|
-
};
|
|
978
|
-
|
|
979
|
-
const getImageFromVideoStream = (takeSizeFrom = 'video', imageOutput, sizes) => {
|
|
980
|
-
try {
|
|
981
|
-
const video = document.getElementById(STREAMING_VIDEO_ID);
|
|
982
|
-
const { canvas, ctx } = getCanvasCached('video');
|
|
983
|
-
canvas.width =
|
|
984
|
-
takeSizeFrom === 'video'
|
|
985
|
-
? sizes?.width || video?.clientWidth || FULL_HD_WIDTH
|
|
986
|
-
: FULL_HD_WIDTH;
|
|
987
|
-
canvas.height =
|
|
988
|
-
takeSizeFrom === 'video'
|
|
989
|
-
? sizes?.height || video?.clientHeight || FULL_HD_HEIGHT
|
|
990
|
-
: FULL_HD_HEIGHT;
|
|
991
|
-
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
992
|
-
return getSnapshot(canvas, canvas.width, imageOutput);
|
|
993
|
-
}
|
|
994
|
-
catch {
|
|
995
|
-
return null;
|
|
996
|
-
}
|
|
997
|
-
};
|
|
998
|
-
|
|
999
|
-
/**
|
|
1000
|
-
* Generator that cycles through `urls` indefinitely. Each call to
|
|
1001
|
-
* `getActiveUrl(urls)` returns a fresh generator with its own counter,
|
|
1002
|
-
* so concurrent generators (e.g., overlapping reconnect attempts) do
|
|
1003
|
-
* not share state. The counter is initialized once per generator
|
|
1004
|
-
* instance — before any `yield` — so each `.next()` call advances
|
|
1005
|
-
* within that single generator.
|
|
1006
|
-
*/
|
|
1007
|
-
function* getActiveUrl(urls) {
|
|
1008
|
-
let index = 0;
|
|
1009
|
-
while (true) {
|
|
1010
|
-
yield urls[index++ % urls.length];
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
class LatencyTimings {
|
|
1015
|
-
constructor() {
|
|
1016
|
-
this.TestStartTimeMs = null;
|
|
1017
|
-
this.UEReceiptTimeMs = null;
|
|
1018
|
-
this.UEEncodeMs = null;
|
|
1019
|
-
this.UECaptureToSendMs = null;
|
|
1020
|
-
this.UETransmissionTimeMs = null;
|
|
1021
|
-
this.BrowserReceiptTimeMs = null;
|
|
1022
|
-
this.FrameDisplayDeltaTimeMs = null;
|
|
1023
|
-
}
|
|
1024
|
-
Reset() {
|
|
1025
|
-
this.TestStartTimeMs = null;
|
|
1026
|
-
this.UEReceiptTimeMs = null;
|
|
1027
|
-
this.UEEncodeMs = null;
|
|
1028
|
-
this.UECaptureToSendMs = null;
|
|
1029
|
-
this.UETransmissionTimeMs = null;
|
|
1030
|
-
this.BrowserReceiptTimeMs = null;
|
|
1031
|
-
this.FrameDisplayDeltaTimeMs = null;
|
|
1032
|
-
}
|
|
1033
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1034
|
-
SetUETimings(UETimings) {
|
|
1035
|
-
this.UEReceiptTimeMs = UETimings.ReceiptTimeMs;
|
|
1036
|
-
this.UEEncodeMs = UETimings.EncodeMs;
|
|
1037
|
-
this.UECaptureToSendMs = UETimings.CaptureToSendMs;
|
|
1038
|
-
this.UETransmissionTimeMs = UETimings.TransmissionTimeMs;
|
|
1039
|
-
this.BrowserReceiptTimeMs = Date.now();
|
|
1040
|
-
}
|
|
1041
|
-
SetFrameDisplayDeltaTime(DeltaTimeMs) {
|
|
1042
|
-
if (this.FrameDisplayDeltaTimeMs == null) {
|
|
1043
|
-
this.FrameDisplayDeltaTimeMs = Math.round(DeltaTimeMs);
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
function qpToQuality(VideoEncoderQP) {
|
|
1049
|
-
const orangeQP = 26;
|
|
1050
|
-
const redQP = 35;
|
|
1051
|
-
let quality = 'lime';
|
|
1052
|
-
if (VideoEncoderQP > redQP) {
|
|
1053
|
-
quality = 'red';
|
|
1054
|
-
}
|
|
1055
|
-
else if (VideoEncoderQP > orangeQP) {
|
|
1056
|
-
quality = 'orange';
|
|
1057
|
-
}
|
|
1058
|
-
return quality;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
/**
|
|
1062
|
-
* Base64-encodes a byte array.
|
|
1063
|
-
*
|
|
1064
|
-
* Builds the intermediate binary string in fixed-size chunks instead of
|
|
1065
|
-
* concatenating one character at a time. The naive `bytes.reduce((s, b) => s +
|
|
1066
|
-
* String.fromCharCode(b), '')` is O(n²) (each `+` re-copies the growing string)
|
|
1067
|
-
* and stalls the main thread for a full-resolution frame; chunking keeps it
|
|
1068
|
-
* O(n). The chunk size stays under the argument-count limit of
|
|
1069
|
-
* `String.fromCharCode.apply`/spread.
|
|
1070
|
-
*
|
|
1071
|
-
* @param bytes - The raw bytes to encode.
|
|
1072
|
-
* @returns The Base64 representation of `bytes`.
|
|
1073
|
-
*/
|
|
1074
|
-
function uint8ToBase64(bytes) {
|
|
1075
|
-
const CHUNK_SIZE = 0x8000;
|
|
1076
|
-
let binary = '';
|
|
1077
|
-
for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
|
|
1078
|
-
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE));
|
|
1079
|
-
}
|
|
1080
|
-
return btoa(binary);
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
function fromResizeObserver(elem) {
|
|
1084
|
-
return new Observable((subscriber) => {
|
|
1085
|
-
const observer = new window.ResizeObserver((entries) => subscriber.next(entries));
|
|
1086
|
-
observer.observe(elem);
|
|
1087
|
-
return () => observer.unobserve(elem);
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
/*
|
|
1092
|
-
* Copyright (c) 2026.
|
|
1093
|
-
* 3DSource.com. Sergii Karanda steve@3dsource.com. All Rights Reserved.
|
|
1094
|
-
*/
|
|
1095
|
-
/**
|
|
1096
|
-
* Rounds a number to the nearest even integer (rounding up for odd values).
|
|
1097
|
-
*
|
|
1098
|
-
* Video encoders (H.264/H.265) require even dimensions because chroma subsampling
|
|
1099
|
-
* operates on 2×2 pixel blocks. Odd dimensions cause encoding artifacts or failures.
|
|
1100
|
-
*
|
|
1101
|
-
* @param value - The number to round to the nearest even integer.
|
|
1102
|
-
* @returns The nearest even integer greater than or equal to the input.
|
|
1103
|
-
*
|
|
1104
|
-
* @example
|
|
1105
|
-
* makeEven(1079) // 1080
|
|
1106
|
-
* makeEven(1080) // 1080
|
|
1107
|
-
* makeEven(1081) // 1082
|
|
1108
|
-
*/
|
|
1109
|
-
function makeEven(value) {
|
|
1110
|
-
return Math.floor((value + 1) / 2) * 2;
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
const FULL_HD_PIXELS = FULL_HD_WIDTH * FULL_HD_HEIGHT;
|
|
1114
|
-
const UHD_PIXELS = FULL_HD_WIDTH * 2 * (FULL_HD_HEIGHT * 2);
|
|
1115
|
-
/**
|
|
1116
|
-
* Calculates the optimal stream resolution for studio mode.
|
|
1117
|
-
*
|
|
1118
|
-
* Fits the studio aspect ratio into the container (scaled by pixelRatio), then
|
|
1119
|
-
* caps the total pixel count to a budget (Full HD or 4K) to avoid overloading
|
|
1120
|
-
* the GPU/encoder while maximizing visual quality.
|
|
1121
|
-
*
|
|
1122
|
-
* The pixel budget approach (instead of per-dimension caps) ensures that
|
|
1123
|
-
* non-standard aspect ratios (portrait, ultra-wide, square) utilize available
|
|
1124
|
-
* pixels efficiently — e.g. a tall 1080×1920 stream uses the same pixel budget
|
|
1125
|
-
* as a wide 1920×1080 stream.
|
|
1126
|
-
*
|
|
1127
|
-
* Resolution is always capped at the studio dimensions — never upscaled beyond
|
|
1128
|
-
* what the Unreal scene was designed for.
|
|
1129
|
-
*
|
|
1130
|
-
* @param params.pixelRatio - Device pixel ratio (`window.devicePixelRatio`).
|
|
1131
|
-
* @param params.containerWidth - CSS width of the video container in pixels.
|
|
1132
|
-
* @param params.containerHeight - CSS height of the video container in pixels.
|
|
1133
|
-
* @param params.studioWidth - Target width of the MetaBox Studio scene.
|
|
1134
|
-
* @param params.studioHeight - Target height of the MetaBox Studio scene.
|
|
1135
|
-
* @param params.maxResolution - When `true`, uses 4K pixel budget (3840×2160);
|
|
1136
|
-
* otherwise Full HD (1920×1080). Controlled by `?maxresolution` query param.
|
|
1137
|
-
* @returns An {@link Area} with the calculated `w`, `h`, position (`x`, `y`),
|
|
1138
|
-
* and `scale` factor relative to the container's device-pixel size.
|
|
1139
|
-
*/
|
|
1140
|
-
function getResizeStudioValues(params) {
|
|
1141
|
-
const { pixelRatio, containerWidth, containerHeight, studioWidth, studioHeight, maxResolution, } = params;
|
|
1142
|
-
const maxSpace = {
|
|
1143
|
-
w: containerWidth * pixelRatio,
|
|
1144
|
-
h: containerHeight * pixelRatio,
|
|
1145
|
-
};
|
|
1146
|
-
const maxPixels = maxResolution ? UHD_PIXELS : FULL_HD_PIXELS;
|
|
1147
|
-
const videoSizePixelPerfect = fitIntoRectangle({ x: 0, y: 0, w: studioWidth, h: studioHeight }, { x: 0, y: 0, ...maxSpace });
|
|
1148
|
-
let w = videoSizePixelPerfect.w;
|
|
1149
|
-
let h = videoSizePixelPerfect.h;
|
|
1150
|
-
if (w * h > maxPixels) {
|
|
1151
|
-
// Derive uniform scale factor `s` from: (w*s) * (h*s) = maxPixels
|
|
1152
|
-
// → w * h * s² = maxPixels → s = √(maxPixels / (w * h)).
|
|
1153
|
-
// Applying the same `s` to both dimensions preserves the aspect ratio.
|
|
1154
|
-
const scaleFactor = Math.sqrt(maxPixels / (w * h));
|
|
1155
|
-
w = w * scaleFactor;
|
|
1156
|
-
h = h * scaleFactor;
|
|
1157
|
-
}
|
|
1158
|
-
const out = fitIntoRectangle(videoSizePixelPerfect, {
|
|
1159
|
-
x: videoSizePixelPerfect.x,
|
|
1160
|
-
y: videoSizePixelPerfect.y,
|
|
1161
|
-
w,
|
|
1162
|
-
h,
|
|
1163
|
-
});
|
|
1164
|
-
out.w = makeEven(out.w);
|
|
1165
|
-
out.h = makeEven(out.h);
|
|
1166
|
-
return out;
|
|
1167
|
-
}
|
|
1168
|
-
/**
|
|
1169
|
-
* Calculates the optimal stream resolution for configurator (non-studio) mode.
|
|
1170
|
-
*
|
|
1171
|
-
* Uses the full container area (scaled by pixelRatio) as the stream resolution,
|
|
1172
|
-
* then caps total pixel count to a budget (Full HD or 4K) to prevent excessive
|
|
1173
|
-
* GPU/encoder load while preserving the container's aspect ratio.
|
|
1174
|
-
*
|
|
1175
|
-
* Unlike {@link getResizeStudioValues}, there is no fixed studio resolution to
|
|
1176
|
-
* fit into — the container itself defines the aspect ratio, and the stream fills
|
|
1177
|
-
* it entirely (no letterboxing).
|
|
1178
|
-
*
|
|
1179
|
-
* @param params.pixelRatio - Device pixel ratio (`window.devicePixelRatio`).
|
|
1180
|
-
* @param params.containerWidth - CSS width of the video container in pixels.
|
|
1181
|
-
* @param params.containerHeight - CSS height of the video container in pixels.
|
|
1182
|
-
* @param params.maxResolution - When `true`, uses 4K pixel budget (3840×2160);
|
|
1183
|
-
* otherwise Full HD (1920×1080). Controlled by `?maxresolution` query param.
|
|
1184
|
-
* @returns An {@link Area} with the calculated `w`, `h`, position (`x`, `y`),
|
|
1185
|
-
* and `scale` factor (1 when no downscale needed, <1 when capped).
|
|
1186
|
-
*/
|
|
1187
|
-
function getResizeValuesUniversal(params) {
|
|
1188
|
-
const { pixelRatio, containerWidth, containerHeight, maxResolution } = params;
|
|
1189
|
-
const maxSpace = {
|
|
1190
|
-
w: containerWidth * pixelRatio,
|
|
1191
|
-
h: containerHeight * pixelRatio,
|
|
1192
|
-
};
|
|
1193
|
-
const maxPixels = maxResolution ? UHD_PIXELS : FULL_HD_PIXELS;
|
|
1194
|
-
let w = maxSpace.w;
|
|
1195
|
-
let h = maxSpace.h;
|
|
1196
|
-
if (w * h > maxPixels) {
|
|
1197
|
-
// Derive uniform scale factor `s` from: (w*s) * (h*s) = maxPixels
|
|
1198
|
-
// → w * h * s² = maxPixels → s = √(maxPixels / (w * h)).
|
|
1199
|
-
// Applying the same `s` to both dimensions preserves the aspect ratio.
|
|
1200
|
-
const scaleFactor = Math.sqrt(maxPixels / (w * h));
|
|
1201
|
-
w = w * scaleFactor;
|
|
1202
|
-
h = h * scaleFactor;
|
|
1203
|
-
}
|
|
1204
|
-
return { w: makeEven(w), h: makeEven(h), x: 0, y: 0, scale: w / maxSpace.w };
|
|
1205
|
-
}
|
|
792
|
+
return {
|
|
793
|
+
...state,
|
|
794
|
+
commandsInProgress,
|
|
795
|
+
// +1 for the just-completed command, +exiled for stragglers we GC'd.
|
|
796
|
+
totalCommandsCompleted: state.totalCommandsCompleted + 1 + exiled,
|
|
797
|
+
};
|
|
798
|
+
}));
|
|
799
|
+
|
|
800
|
+
const unrealFeature = createFeature({
|
|
801
|
+
name: 'unrealFeature',
|
|
802
|
+
reducer: unrealReducer,
|
|
803
|
+
});
|
|
1206
804
|
|
|
1207
805
|
/**
|
|
1208
806
|
* `'metabox'` — the default when `UNREAL_CONFIG` omits `mode` — speaks the full
|
|
@@ -1332,14 +930,30 @@ class WebrtcErrorModalComponent {
|
|
|
1332
930
|
close() {
|
|
1333
931
|
this.#dialogRef.close();
|
|
1334
932
|
}
|
|
1335
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1336
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.
|
|
933
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WebrtcErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
934
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: WebrtcErrorModalComponent, isStandalone: true, selector: "app-webrtc-error-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"unreal-modal\">\n <header class=\"unreal-modal__header\"></header>\n <section class=\"unreal-modal__body\">\n <div [attr.data-testid]=\"'title'\" class=\"unreal-modal__title\">\n WebRTC error\n </div>\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=\"unreal-modal__footer\">\n <app-unreal-button\n (clicked)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n weight=\"primary\"\n >\n Ok\n </app-unreal-button>\n </footer>\n</div>\n", styles: [".unreal-modal{--unrealModalWidth: 422px;--unrealModalMaxWidth: calc(100% - 16px) ;--unrealModalMaxHeight: 80vh;--unrealModalBg: var(--src-surface-container-main, #fff);--unrealModalBoxShadow: 0 16px 36px 4px var(--src-shadow-hard, rgba(71, 85, 105, .24)), 0 1px 4px 0 var(--src-shadow-light, rgba(148, 163, 184, .16));--unrealModalBorderRadius: var(--src-layout-radius-const-rounded-md, 12px);--unrealModalTitleSize: var(--src-font-size-base, 16px);--unrealModalTitleLineHeight: var(--src-font-line-base, 24px);--unrealModalTitleColor: var(--src-text-body-main, #111827);--unrealModalBodyPadding: var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-2xl, 32px) var(--src-layout-padding-const-xl, 24px);--unrealModalTitleBorder: none;--unrealModalFooterBorder: 1px solid var(--src-border-container-light, rgba(148, 163, 184, .16));display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;width:var(--unrealModalWidth);max-width:var(--unrealModalMaxWidth);max-height:var(--unrealModalMaxHeight);margin:auto;background-color:var(--unrealModalBg);border-radius:var(--unrealModalBorderRadius);box-shadow:var(--unrealModalBoxShadow);color:var(--src-text-body-secondary, #4b5563);font-family:var(--src-font-family-header, Inter);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:var(--src-font-line-sm, 20px)}.unreal-modal .unreal-modal__header{position:relative;display:flex;padding:0;width:100%;border-bottom:var(--unrealModalTitleBorder)}.unreal-modal .unreal-modal__body{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:var(--src-layout-gap-const-md, 12px);padding:var(--unrealModalBodyPadding);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--src-border-container-basic, rgba(148, 163, 184, .24)) transparent}.unreal-modal .unreal-modal__body .unreal-modal__title{grid-column:1/-1}.unreal-modal .unreal-modal__body::-webkit-scrollbar{width:4px}.unreal-modal .unreal-modal__body::-webkit-scrollbar-track{background:transparent}.unreal-modal .unreal-modal__body::-webkit-scrollbar-thumb{background-color:var(--src-border-container-basic, rgba(148, 163, 184, .24));border-radius:10px}.unreal-modal .unreal-modal__footer{display:flex;justify-content:flex-end;align-items:center;padding:var(--src-layout-padding-const-md, 12px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px);width:100%;border-top:var(--unrealModalFooterBorder);gap:var(--src-layout-gap-const-sm, 8px)}.unreal-modal .unreal-modal__title{font-size:var(--unrealModalTitleSize);font-weight:600;line-height:var(--unrealModalTitleLineHeight);color:var(--unrealModalTitleColor);word-break:break-word}.unreal-modal .unreal-modal__body{width:100%}.unreal-modal .unreal-modal__body p{color:var(--src-text-body-main, #1f2937);font-size:14px;font-style:normal;font-weight:400;line-height:24px;margin-top:0}.unreal-modal .unreal-modal__body a{word-wrap:break-word;white-space:normal}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1337
935
|
}
|
|
1338
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
936
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WebrtcErrorModalComponent, decorators: [{
|
|
1339
937
|
type: Component,
|
|
1340
938
|
args: [{ selector: 'app-webrtc-error-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealButtonComponent], template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"unreal-modal\">\n <header class=\"unreal-modal__header\"></header>\n <section class=\"unreal-modal__body\">\n <div [attr.data-testid]=\"'title'\" class=\"unreal-modal__title\">\n WebRTC error\n </div>\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=\"unreal-modal__footer\">\n <app-unreal-button\n (clicked)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n weight=\"primary\"\n >\n Ok\n </app-unreal-button>\n </footer>\n</div>\n", styles: [".unreal-modal{--unrealModalWidth: 422px;--unrealModalMaxWidth: calc(100% - 16px) ;--unrealModalMaxHeight: 80vh;--unrealModalBg: var(--src-surface-container-main, #fff);--unrealModalBoxShadow: 0 16px 36px 4px var(--src-shadow-hard, rgba(71, 85, 105, .24)), 0 1px 4px 0 var(--src-shadow-light, rgba(148, 163, 184, .16));--unrealModalBorderRadius: var(--src-layout-radius-const-rounded-md, 12px);--unrealModalTitleSize: var(--src-font-size-base, 16px);--unrealModalTitleLineHeight: var(--src-font-line-base, 24px);--unrealModalTitleColor: var(--src-text-body-main, #111827);--unrealModalBodyPadding: var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-2xl, 32px) var(--src-layout-padding-const-xl, 24px);--unrealModalTitleBorder: none;--unrealModalFooterBorder: 1px solid var(--src-border-container-light, rgba(148, 163, 184, .16));display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;width:var(--unrealModalWidth);max-width:var(--unrealModalMaxWidth);max-height:var(--unrealModalMaxHeight);margin:auto;background-color:var(--unrealModalBg);border-radius:var(--unrealModalBorderRadius);box-shadow:var(--unrealModalBoxShadow);color:var(--src-text-body-secondary, #4b5563);font-family:var(--src-font-family-header, Inter);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:var(--src-font-line-sm, 20px)}.unreal-modal .unreal-modal__header{position:relative;display:flex;padding:0;width:100%;border-bottom:var(--unrealModalTitleBorder)}.unreal-modal .unreal-modal__body{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:var(--src-layout-gap-const-md, 12px);padding:var(--unrealModalBodyPadding);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--src-border-container-basic, rgba(148, 163, 184, .24)) transparent}.unreal-modal .unreal-modal__body .unreal-modal__title{grid-column:1/-1}.unreal-modal .unreal-modal__body::-webkit-scrollbar{width:4px}.unreal-modal .unreal-modal__body::-webkit-scrollbar-track{background:transparent}.unreal-modal .unreal-modal__body::-webkit-scrollbar-thumb{background-color:var(--src-border-container-basic, rgba(148, 163, 184, .24));border-radius:10px}.unreal-modal .unreal-modal__footer{display:flex;justify-content:flex-end;align-items:center;padding:var(--src-layout-padding-const-md, 12px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px);width:100%;border-top:var(--unrealModalFooterBorder);gap:var(--src-layout-gap-const-sm, 8px)}.unreal-modal .unreal-modal__title{font-size:var(--unrealModalTitleSize);font-weight:600;line-height:var(--unrealModalTitleLineHeight);color:var(--unrealModalTitleColor);word-break:break-word}.unreal-modal .unreal-modal__body{width:100%}.unreal-modal .unreal-modal__body p{color:var(--src-text-body-main, #1f2937);font-size:14px;font-style:normal;font-weight:400;line-height:24px;margin-top:0}.unreal-modal .unreal-modal__body a{word-wrap:break-word;white-space:normal}\n"] }]
|
|
1341
939
|
}] });
|
|
1342
940
|
|
|
941
|
+
class SubService {
|
|
942
|
+
constructor() {
|
|
943
|
+
this.destroyRef = inject(DestroyRef);
|
|
944
|
+
this.destroy$ = new Subject();
|
|
945
|
+
this.actions$ = inject(Actions);
|
|
946
|
+
this.store = inject(Store);
|
|
947
|
+
this.disconnect$ = this.store
|
|
948
|
+
.select(unrealFeature.selectCirrusConnected)
|
|
949
|
+
.pipe(skip(1), filter(Falsy), share());
|
|
950
|
+
this.destroyRef.onDestroy(() => {
|
|
951
|
+
this.destroy$.next();
|
|
952
|
+
this.destroy$.complete();
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
1343
957
|
class CommandTelemetryService extends SubService {
|
|
1344
958
|
constructor() {
|
|
1345
959
|
super();
|
|
@@ -1542,10 +1156,10 @@ class CommandTelemetryService extends SubService {
|
|
|
1542
1156
|
this.trackTime(out);
|
|
1543
1157
|
funcToDecorate(out);
|
|
1544
1158
|
}
|
|
1545
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1546
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
1159
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: CommandTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1160
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: CommandTelemetryService, providedIn: 'root' }); }
|
|
1547
1161
|
}
|
|
1548
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1162
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: CommandTelemetryService, decorators: [{
|
|
1549
1163
|
type: Injectable,
|
|
1550
1164
|
args: [{ providedIn: 'root' }]
|
|
1551
1165
|
}], ctorParameters: () => [] });
|
|
@@ -1566,10 +1180,10 @@ class DevModeService {
|
|
|
1566
1180
|
localStorage.removeItem('devMode');
|
|
1567
1181
|
}
|
|
1568
1182
|
}
|
|
1569
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1570
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
1183
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: DevModeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1184
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: DevModeService, providedIn: 'root' }); }
|
|
1571
1185
|
}
|
|
1572
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1186
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: DevModeService, decorators: [{
|
|
1573
1187
|
type: Injectable,
|
|
1574
1188
|
args: [{
|
|
1575
1189
|
providedIn: 'root',
|
|
@@ -1615,7 +1229,7 @@ class RegionsPingService {
|
|
|
1615
1229
|
return of(cached).pipe(tap(() => this.store.dispatch(regionPingFetchEnd())), tap((data) => this.store.dispatch(regionResolved({
|
|
1616
1230
|
region: data.region_code,
|
|
1617
1231
|
pingsMs: data.result,
|
|
1618
|
-
}))), map
|
|
1232
|
+
}))), map((data) => data.region_code));
|
|
1619
1233
|
}
|
|
1620
1234
|
// No regions URL and no cache: there is nothing to ping. Bail out loudly
|
|
1621
1235
|
// instead of letting `HttpClient.get('')` request the current document
|
|
@@ -1638,7 +1252,7 @@ class RegionsPingService {
|
|
|
1638
1252
|
}), tap((data) => this.store.dispatch(regionResolved({
|
|
1639
1253
|
region: data?.region_code ?? '',
|
|
1640
1254
|
pingsMs: data?.result ?? [],
|
|
1641
|
-
}))), map
|
|
1255
|
+
}))), map((data) => data?.region_code));
|
|
1642
1256
|
});
|
|
1643
1257
|
}
|
|
1644
1258
|
/**
|
|
@@ -1888,10 +1502,10 @@ class RegionsPingService {
|
|
|
1888
1502
|
}
|
|
1889
1503
|
}
|
|
1890
1504
|
}
|
|
1891
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1892
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
1505
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RegionsPingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1506
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RegionsPingService, providedIn: 'root' }); }
|
|
1893
1507
|
}
|
|
1894
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1508
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RegionsPingService, decorators: [{
|
|
1895
1509
|
type: Injectable,
|
|
1896
1510
|
args: [{ providedIn: 'root' }]
|
|
1897
1511
|
}], ctorParameters: () => [] });
|
|
@@ -2162,24 +1776,20 @@ class SignallingService extends SubService {
|
|
|
2162
1776
|
this.onConfig$ = new Subject();
|
|
2163
1777
|
this.onWebRtcIce$ = new Subject();
|
|
2164
1778
|
this.onWebRtcAnswer$ = new Subject();
|
|
2165
|
-
// Fed by `InstanceReadyHandler
|
|
2166
|
-
// way `requestCirrusViaPolling` awaits a ready polling response — it is the
|
|
2167
|
-
// socket flow's "here is your Cirrus URL" answer.
|
|
1779
|
+
// Fed by `InstanceReadyHandler` — the socket flow's "here is your Cirrus URL".
|
|
2168
1780
|
this.onInstanceReady$ = new Subject();
|
|
2169
|
-
// Pure signal
|
|
2170
|
-
//
|
|
2171
|
-
// of how many pipelines have `takeUntil(this.abort$)` attached.
|
|
1781
|
+
// Pure signal, no side-effects: `close()` is wired ONCE in init(), so one abort
|
|
1782
|
+
// triggers exactly one close however many pipelines `takeUntil` it.
|
|
2172
1783
|
this.abort$ = this.actions$.pipe(ofType(abortEstablishingConnection));
|
|
2173
|
-
// The CIRRUS signalling socket — the only
|
|
2174
|
-
//
|
|
2175
|
-
// the polled `signallingServer`, the stream machine (`originInstanceUri`), or
|
|
2176
|
-
// a direct dev URL.
|
|
1784
|
+
// The CIRRUS signalling socket — the only one that speaks WebRTC. Same field in
|
|
1785
|
+
// all three flows: polled `signallingServer`, stream machine, or direct dev URL.
|
|
2177
1786
|
this.ws = null;
|
|
2178
|
-
// The orchestration CONTROL socket (socket flow only).
|
|
2179
|
-
// `
|
|
2180
|
-
// `instanceReady` arrives — per the backend contract nothing uses it after the
|
|
2181
|
-
// handover, so it is never left open with dangling handlers.
|
|
1787
|
+
// The orchestration CONTROL socket while we still talk to it (socket flow only).
|
|
1788
|
+
// The only one `sendToOrchestration` can reach — `sendTo` ignores a null socket.
|
|
2182
1789
|
this.controlWs = null;
|
|
1790
|
+
// Handed over: open, only a log-only `onclose` left, waiting for orchestration
|
|
1791
|
+
// to close it. Why we must not: SIGNALLING.md, "Who closes the control socket".
|
|
1792
|
+
this.handedOverControlWs = null;
|
|
2183
1793
|
// `requestStream` resends driven by `OrchestrationErrorHandler` within one
|
|
2184
1794
|
// `requestCirrusViaSocket` wait. Reported as `kind: 'orchestration'` retries.
|
|
2185
1795
|
this.orchestrationRetryCount = 0;
|
|
@@ -2191,22 +1801,18 @@ class SignallingService extends SubService {
|
|
|
2191
1801
|
return;
|
|
2192
1802
|
}
|
|
2193
1803
|
this.setMessageHandlers();
|
|
2194
|
-
// Single owner of the
|
|
2195
|
-
//
|
|
2196
|
-
// `takeUntil(this.abort$)` subscriber fire its own close().
|
|
1804
|
+
// Single owner of the close-on-abort side-effect — must not live as a `tap`
|
|
1805
|
+
// inside `abort$`, or every `takeUntil` subscriber fires its own close().
|
|
2197
1806
|
this.abort$.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
2198
1807
|
this.close({
|
|
2199
1808
|
code: 1000,
|
|
2200
1809
|
reason: DisconnectReason.AbortEstablishingConnection,
|
|
2201
1810
|
});
|
|
2202
1811
|
});
|
|
2203
|
-
//
|
|
2204
|
-
//
|
|
2205
|
-
//
|
|
2206
|
-
//
|
|
2207
|
-
// two routes that both host <app-unreal-scene>) would never send
|
|
2208
|
-
// `p2pEstablished`. `send()` is already a no-op on a closed WS,
|
|
2209
|
-
// so there's no risk of stray messages.
|
|
1812
|
+
// Bind to destroy$, NOT abort$: `init()` runs once in the constructor, and
|
|
1813
|
+
// abort$ fires on every route swap — tearing this down there would leave a
|
|
1814
|
+
// second session unable to send `p2pEstablished`. `send()` no-ops on a closed
|
|
1815
|
+
// WS anyway, so a stray message is not a risk.
|
|
2210
1816
|
this.actions$
|
|
2211
1817
|
.pipe(ofType(dataChannelReady), takeUntil(this.destroy$))
|
|
2212
1818
|
.subscribe(() => {
|
|
@@ -2226,26 +1832,17 @@ class SignallingService extends SubService {
|
|
|
2226
1832
|
return EMPTY;
|
|
2227
1833
|
}
|
|
2228
1834
|
Logger.log('CONNECT TO SIGNALLING');
|
|
2229
|
-
//
|
|
2230
|
-
//
|
|
2231
|
-
// makes them fire on subscribe (lazy), but only ONCE — the retry
|
|
2232
|
-
// below sits INSIDE this defer's projection so re-subscribing the
|
|
2233
|
-
// inner pipeline does not re-trigger these.
|
|
1835
|
+
// These belong to one logical `connectToSignaling`, NOT to one retry attempt:
|
|
1836
|
+
// the retry below sits INSIDE this defer, so re-subscribing does not re-run them.
|
|
2234
1837
|
return defer(() => {
|
|
2235
1838
|
this.correlationId = generateUuid();
|
|
2236
1839
|
this.commandTelemetryService.reset();
|
|
2237
1840
|
this.startEstablishingConnection();
|
|
2238
|
-
// Staged retry:
|
|
2239
|
-
//
|
|
2240
|
-
//
|
|
2241
|
-
//
|
|
2242
|
-
|
|
2243
|
-
// `INNER_RETRY_LIMIT`).
|
|
2244
|
-
// * If the inner retry exhausts, the error propagates to the
|
|
2245
|
-
// OUTER retry below which re-runs `getRegion()` (rare, real
|
|
2246
|
-
// "region down" case). Outer delay > inner so escalation is
|
|
2247
|
-
// bounded.
|
|
2248
|
-
const connectCycle$ = this.getRegion().pipe(tap((region) => this.region.set(region ?? '')), map$1((region) => this.adaptUrlsToRegion(urlList, region)), switchMap((urls) => this.connectAttempt(urls)), takeUntil(this.abort$));
|
|
1841
|
+
// Staged retry: `connectAttempt` owns URL rotation + WS connect and caps
|
|
1842
|
+
// itself at `INNER_RETRY_LIMIT`; only its exhaustion reaches the outer retry,
|
|
1843
|
+
// which re-picks the region (rare, real "region down"). Outer delay > inner,
|
|
1844
|
+
// so escalation is bounded and region discovery runs once per outer cycle.
|
|
1845
|
+
const connectCycle$ = this.getRegion().pipe(tap((region) => this.region.set(region ?? '')), map((region) => this.adaptUrlsToRegion(urlList, region)), switchMap((urls) => this.connectAttempt(urls)), takeUntil(this.abort$));
|
|
2249
1846
|
return connectCycle$.pipe(retry({
|
|
2250
1847
|
delay: (err, attempt) => {
|
|
2251
1848
|
this.store.dispatch(signalingRetryAttempted({
|
|
@@ -2260,41 +1857,32 @@ class SignallingService extends SubService {
|
|
|
2260
1857
|
}).pipe(takeUntil(this.abort$));
|
|
2261
1858
|
}
|
|
2262
1859
|
/**
|
|
2263
|
-
* One inner cycle:
|
|
2264
|
-
* {@link
|
|
2265
|
-
*
|
|
2266
|
-
* urlGen lives in this closure so retries advance through the
|
|
2267
|
-
* pool. Exhaustion bubbles to {@link connectToSignaling}'s outer
|
|
2268
|
-
* retry which re-pings the region.
|
|
1860
|
+
* One inner cycle: {@link requestCirrus} → {@link connectToCirrus}, retried up to
|
|
1861
|
+
* {@link INNER_RETRY_LIMIT}. `urlGen` lives in this closure so retries advance
|
|
1862
|
+
* through the pool; exhaustion bubbles to the outer region-repick retry.
|
|
2269
1863
|
*/
|
|
2270
1864
|
connectAttempt(urls) {
|
|
2271
1865
|
const urlGen = getActiveUrl(urls);
|
|
2272
1866
|
return defer(() => {
|
|
2273
1867
|
// One control socket per ATTEMPT, whatever transport this attempt picks —
|
|
2274
1868
|
// a rotation from a `wss://` orchestration URL to an `http://` one would
|
|
2275
|
-
// otherwise leave the previous control socket behind.
|
|
2276
|
-
//
|
|
2277
|
-
//
|
|
1869
|
+
// otherwise leave the previous control socket behind. This also releases a
|
|
1870
|
+
// handed-over one: rotating abandons that reservation, so orchestration
|
|
1871
|
+
// SHOULD see it close. Deliberately NOT inside the WS-only tier below —
|
|
1872
|
+
// that tier reuses the same machine, so its control socket must survive.
|
|
2278
1873
|
this.teardownControlWs();
|
|
2279
1874
|
return this.requestCirrus(`${urlGen.next().value}`);
|
|
2280
1875
|
}).pipe(tap((awsInstance) => this.store.dispatch(setAwsInstance(awsInstance))), switchMap(({ wsUrl, streamRequestId }) =>
|
|
2281
1876
|
// ── WS-only retry tier ─────────────────────────────────────────────
|
|
2282
|
-
//
|
|
2283
|
-
//
|
|
2284
|
-
//
|
|
2285
|
-
//
|
|
2286
|
-
//
|
|
2287
|
-
// previous reservation is orphaned (server-side leak).
|
|
1877
|
+
// Retries the SAME `wsUrl` `WS_RETRY_PER_INSTANCE` times before letting the
|
|
1878
|
+
// error reach the URL-rotating tier, because rotating re-asks orchestration
|
|
1879
|
+
// and RESERVES A NEW instance, orphaning the previous one (server-side
|
|
1880
|
+
// leak). So transient WS failures are absorbed here; only a genuinely
|
|
1881
|
+
// unusable instance is worth that cost.
|
|
2288
1882
|
//
|
|
2289
|
-
//
|
|
2290
|
-
//
|
|
2291
|
-
//
|
|
2292
|
-
// extra attempts, the instance is genuinely unusable and we
|
|
2293
|
-
// accept the cost of rotating.
|
|
2294
|
-
// `wsUrl` is always a CIRRUS address here, whichever request method
|
|
2295
|
-
// produced it, so the orchestration-issued connection id always rides
|
|
2296
|
-
// on this upgrade URL and Cirrus adopts it instead of generating its
|
|
2297
|
-
// own. Absent id (older backend, direct dev URL) → URL unchanged.
|
|
1883
|
+
// `wsUrl` is always a CIRRUS address, so the orchestration-issued
|
|
1884
|
+
// connection id rides on the upgrade URL and Cirrus adopts it instead of
|
|
1885
|
+
// generating its own. Absent id (older backend, dev URL) → URL unchanged.
|
|
2298
1886
|
this.connectToCirrus(withStreamRequestId(wsUrl ?? '', streamRequestId)).pipe(filter(Truthy), take(1), retry({
|
|
2299
1887
|
count: WS_RETRY_PER_INSTANCE,
|
|
2300
1888
|
delay: (err, attempt) => {
|
|
@@ -2327,14 +1915,11 @@ class SignallingService extends SubService {
|
|
|
2327
1915
|
}));
|
|
2328
1916
|
}
|
|
2329
1917
|
/**
|
|
2330
|
-
* Status message
|
|
2331
|
-
* `
|
|
2332
|
-
*
|
|
2333
|
-
*
|
|
2334
|
-
*
|
|
2335
|
-
* `StreamStatusTelemetryService2` starts a session on that boundary, the
|
|
2336
|
-
* first session was killed 2 ms in as `restart` — losing the only honest
|
|
2337
|
-
* page-load measurement and the prefetch adoption with it.
|
|
1918
|
+
* Status message only — must NOT dispatch `setEstablishingConnection`, which
|
|
1919
|
+
* `UnrealEffects.connectToSignaling$` already emits once per logical attempt for
|
|
1920
|
+
* both branches. Dispatching it here too fired that boundary twice on cold start,
|
|
1921
|
+
* and `StreamStatusTelemetryService2` starts a session on it — so the first
|
|
1922
|
+
* session died 2 ms in as `restart`, taking the page-load measurement with it.
|
|
2338
1923
|
*/
|
|
2339
1924
|
startEstablishingConnection() {
|
|
2340
1925
|
this.showStatusMessage(UnrealStatusMessage.STARTING_YOUR_SESSION);
|
|
@@ -2348,22 +1933,11 @@ class SignallingService extends SubService {
|
|
|
2348
1933
|
});
|
|
2349
1934
|
}
|
|
2350
1935
|
/**
|
|
2351
|
-
* Ask the orchestration for a Cirrus to connect to. All three
|
|
2352
|
-
*
|
|
2353
|
-
*
|
|
2354
|
-
*
|
|
2355
|
-
*
|
|
2356
|
-
*
|
|
2357
|
-
* | Method | Transport | Fails on |
|
|
2358
|
-
* | -------------------------- | -------------------- | ------------------------------ |
|
|
2359
|
-
* | {@link requestCirrusViaPolling} | HTTP poll loop | HTTP 4xx (dead URL) |
|
|
2360
|
-
* | {@link requestCirrusViaSocket} | control WS | socket drop / no `instanceReady` in {@link INSTANCE_READY_TIMEOUT} |
|
|
2361
|
-
* | {@link requestCirrusDirect} | none (dev) | never — nothing is requested |
|
|
2362
|
-
*
|
|
2363
|
-
* Whatever they return goes through the SAME downstream in
|
|
2364
|
-
* {@link connectAttempt}: `setAwsInstance` → `connectToCirrus` → the WS-only
|
|
2365
|
-
* and URL-rotating retry tiers. A failure inside any of them is just an
|
|
2366
|
-
* error on this observable, so those tiers handle it uniformly.
|
|
1936
|
+
* Ask the orchestration for a Cirrus to connect to. All three implementations
|
|
1937
|
+
* answer the same question — which Cirrus, with which `streamRequestId` — and emit
|
|
1938
|
+
* ONE `AwsInstance` whose `wsUrl` is a Cirrus address, so {@link connectAttempt}
|
|
1939
|
+
* treats them uniformly: a failure in any of them is just an error here, which its
|
|
1940
|
+
* retry tiers already handle. Per-transport failure surface: SIGNALLING.md.
|
|
2367
1941
|
*/
|
|
2368
1942
|
requestCirrus(url) {
|
|
2369
1943
|
if (!url.match(/^ws(s?):\/\//i)) {
|
|
@@ -2393,18 +1967,16 @@ class SignallingService extends SubService {
|
|
|
2393
1967
|
* `kind`: nothing was requested.
|
|
2394
1968
|
*/
|
|
2395
1969
|
requestCirrusDirect(url) {
|
|
2396
|
-
return this.waitForClientAndViewIds().pipe(map
|
|
1970
|
+
return this.waitForClientAndViewIds().pipe(map(() => ({
|
|
2397
1971
|
wsUrl: url,
|
|
2398
1972
|
pollingUrl: null,
|
|
2399
1973
|
instanceName: 'Direct Connection',
|
|
2400
1974
|
})));
|
|
2401
1975
|
}
|
|
2402
1976
|
/**
|
|
2403
|
-
* HTTP-orchestration path. Brackets {@link pollLoop} with
|
|
2404
|
-
* `
|
|
2405
|
-
*
|
|
2406
|
-
* last response captured in the closure). Maps the final ready
|
|
2407
|
-
* response to an `AwsInstance`.
|
|
1977
|
+
* HTTP-orchestration path. Brackets {@link pollLoop} with `pollingStarted`/
|
|
1978
|
+
* `pollingEnded` and the `EXT-getSignaling` telemetry span (started in `defer`,
|
|
1979
|
+
* stopped in `finalize` with the last response from the closure).
|
|
2408
1980
|
*/
|
|
2409
1981
|
requestCirrusViaPolling(url) {
|
|
2410
1982
|
return defer(() => {
|
|
@@ -2417,7 +1989,7 @@ class SignallingService extends SubService {
|
|
|
2417
1989
|
}
|
|
2418
1990
|
}), filter(({ clientId, viewId }) => !!clientId && !!viewId), take(1), switchMap(({ clientId, viewId }) => this.pollLoop(`${url}${clientId}/${viewId}`).pipe(tap((data) => {
|
|
2419
1991
|
lastResp = data;
|
|
2420
|
-
}))), map
|
|
1992
|
+
}))), map((data) => ({
|
|
2421
1993
|
wsUrl: httpUrlToWs(`${location.protocol}//${data.signallingServer}`),
|
|
2422
1994
|
pollingUrl: url,
|
|
2423
1995
|
instanceName: data.signallingServer ?? '',
|
|
@@ -2432,19 +2004,14 @@ class SignallingService extends SubService {
|
|
|
2432
2004
|
});
|
|
2433
2005
|
}
|
|
2434
2006
|
/**
|
|
2435
|
-
* WS-orchestration path — the socket
|
|
2436
|
-
*
|
|
2437
|
-
* `
|
|
2438
|
-
* IS the Cirrus address.
|
|
2007
|
+
* WS-orchestration path — the socket twin of {@link requestCirrusViaPolling}.
|
|
2008
|
+
* Opens the control socket, sends `requestStream`, waits for `instanceReady`
|
|
2009
|
+
* whose `originInstanceUri` IS the Cirrus address.
|
|
2439
2010
|
*
|
|
2440
|
-
*
|
|
2441
|
-
*
|
|
2442
|
-
*
|
|
2443
|
-
*
|
|
2444
|
-
* - no `instanceReady` within {@link INSTANCE_READY_TIMEOUT} → error
|
|
2445
|
-
*
|
|
2446
|
-
* The control socket lives exactly as long as this wait — see
|
|
2447
|
-
* {@link awaitInstanceReady}.
|
|
2011
|
+
* Both failure modes — socket drop before `instanceReady`, or no `instanceReady`
|
|
2012
|
+
* within {@link INSTANCE_READY_TIMEOUT} — must surface as a plain error so
|
|
2013
|
+
* `connectAttempt`'s retry tiers see them; without that a dead control socket
|
|
2014
|
+
* hangs the session silently. See {@link awaitInstanceReady}.
|
|
2448
2015
|
*/
|
|
2449
2016
|
requestCirrusViaSocket(url) {
|
|
2450
2017
|
return defer(() => {
|
|
@@ -2460,47 +2027,44 @@ class SignallingService extends SubService {
|
|
|
2460
2027
|
* Send `requestStream` on the freshly opened control socket and resolve with
|
|
2461
2028
|
* the machine coordinates from `instanceReady`.
|
|
2462
2029
|
*
|
|
2463
|
-
*
|
|
2464
|
-
* failures of the request
|
|
2465
|
-
*
|
|
2466
|
-
*
|
|
2467
|
-
*
|
|
2030
|
+
* A race between one answer and two failure signals — until `instanceReady` the
|
|
2031
|
+
* socket's close/error are HARD failures of the request. On success it is handed
|
|
2032
|
+
* over, not closed: {@link handOverControlWs}.
|
|
2033
|
+
*
|
|
2034
|
+
* `on*` properties rather than `fromEvent`: that is how the rest of the file
|
|
2035
|
+
* attaches, and how `teardownSocket` detaches.
|
|
2468
2036
|
*/
|
|
2469
2037
|
awaitInstanceReady(control, orchestrationUrl) {
|
|
2470
|
-
return
|
|
2038
|
+
return defer(() => {
|
|
2039
|
+
// Errors straight into `merge` — no value it could carry, so `error()` rather
|
|
2040
|
+
// than `next()` and there is nothing to map.
|
|
2041
|
+
const failure$ = new Subject();
|
|
2471
2042
|
control.onmessage = OnMessageHandler.bind(this);
|
|
2472
|
-
control.onclose = (e) =>
|
|
2473
|
-
control.onerror = () =>
|
|
2043
|
+
control.onclose = (e) => failure$.error(new Error(`Orchestration socket closed before instanceReady (code ${e.code})`));
|
|
2044
|
+
control.onerror = () => failure$.error(new Error('Orchestration socket error before instanceReady'));
|
|
2474
2045
|
this.controlWs = control;
|
|
2475
2046
|
Logger.log('ORCHESTRATION CONTROL WS OPEN');
|
|
2476
2047
|
this.sendRequestStream();
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
//
|
|
2480
|
-
//
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
// polling URL in the other flow. Shown as `Orchestration:` in the overlay.
|
|
2487
|
-
pollingUrl: orchestrationUrl,
|
|
2488
|
-
instanceName: payload.originInstanceId,
|
|
2489
|
-
streamRequestId: payload.streamRequestId,
|
|
2490
|
-
kind: 'socket',
|
|
2491
|
-
});
|
|
2492
|
-
observer.complete();
|
|
2493
|
-
});
|
|
2494
|
-
// Errored, timed out or aborted before `instanceReady` — same teardown,
|
|
2495
|
-
// the socket must not be left behind either way.
|
|
2496
|
-
return () => {
|
|
2497
|
-
sub.unsubscribe();
|
|
2498
|
-
this.teardownControlWs();
|
|
2499
|
-
};
|
|
2048
|
+
return merge(this.onInstanceReady$.pipe(map((payload) => ({
|
|
2049
|
+
wsUrl: payload.originInstanceUri,
|
|
2050
|
+
// The endpoint that answered — the orchestration URL here, the HTTP
|
|
2051
|
+
// polling URL in the other flow. Shown as `Orchestration:` in the overlay.
|
|
2052
|
+
pollingUrl: orchestrationUrl,
|
|
2053
|
+
instanceName: payload.originInstanceId,
|
|
2054
|
+
streamRequestId: payload.streamRequestId,
|
|
2055
|
+
kind: 'socket',
|
|
2056
|
+
}))), failure$).pipe(first(), tap(() => this.handOverControlWs(control)));
|
|
2500
2057
|
}).pipe(timeout$1({
|
|
2501
2058
|
first: INSTANCE_READY_TIMEOUT,
|
|
2502
2059
|
with: () => throwError(() => new Error(`No instanceReady within ${INSTANCE_READY_TIMEOUT}ms — requesting another machine`)),
|
|
2503
|
-
}), takeUntil(this.abort$)
|
|
2060
|
+
}), takeUntil(this.abort$),
|
|
2061
|
+
// After `takeUntil`, so every ending lands here. Still ours ⇒ the request
|
|
2062
|
+
// failed; after a handover the field is null and we must NOT close.
|
|
2063
|
+
finalize(() => {
|
|
2064
|
+
if (this.controlWs === control) {
|
|
2065
|
+
this.teardownControlWs();
|
|
2066
|
+
}
|
|
2067
|
+
}));
|
|
2504
2068
|
}
|
|
2505
2069
|
/**
|
|
2506
2070
|
* Poll the SAME `url` until ready, or until HTTP 4xx says the URL
|
|
@@ -2513,9 +2077,8 @@ class SignallingService extends SubService {
|
|
|
2513
2077
|
* | HTTP 4xx (except 429) | ❌ Hard | Re-throw → caller rotates URL |
|
|
2514
2078
|
* | HTTP 5xx / 429 / timeout / network | ⚠️ Soft | `EMPTY` → `repeat` same URL |
|
|
2515
2079
|
*
|
|
2516
|
-
* `data.error` is NOT
|
|
2517
|
-
*
|
|
2518
|
-
* are HTTP 4xx and `abort$` upstream.
|
|
2080
|
+
* `data.error` is NOT fatal — the backend reuses it for progress strings like
|
|
2081
|
+
* `"Ready in ~2m 23s"`. Only HTTP 4xx and upstream `abort$` are fatal.
|
|
2519
2082
|
*/
|
|
2520
2083
|
pollLoop(url) {
|
|
2521
2084
|
return defer(() => this.httpClient.get(url)).pipe(timeout$1(POLLING_TIME), catchError((err) => {
|
|
@@ -2550,16 +2113,12 @@ class SignallingService extends SubService {
|
|
|
2550
2113
|
first((data) => !!data.signallingServer));
|
|
2551
2114
|
}
|
|
2552
2115
|
connectToCirrus(wsUrl) {
|
|
2553
|
-
//
|
|
2554
|
-
// re-
|
|
2555
|
-
//
|
|
2556
|
-
// this, the WS-only retry would just re-subscribe the SAME WS
|
|
2557
|
-
// observable, which in production-defer'd `createWebSocket` would
|
|
2558
|
-
// still create a new socket, but in tests would re-emit the same
|
|
2559
|
-
// (already-errored) mock observable.
|
|
2116
|
+
// `defer` so every subscription — including ws-retry re-subscriptions — starts a
|
|
2117
|
+
// FRESH attempt. Without it the retry re-subscribes the SAME observable, which in
|
|
2118
|
+
// tests re-emits the already-errored mock.
|
|
2560
2119
|
return defer(() => {
|
|
2561
|
-
//
|
|
2562
|
-
//
|
|
2120
|
+
// Cirrus only: the control socket belongs to `requestCirrusViaSocket`, which
|
|
2121
|
+
// opened it to GET this `wsUrl`.
|
|
2563
2122
|
this.closeCirrus({
|
|
2564
2123
|
code: 1000,
|
|
2565
2124
|
reason: DisconnectReason.NewConnectionAttempt,
|
|
@@ -2571,9 +2130,9 @@ class SignallingService extends SubService {
|
|
|
2571
2130
|
}));
|
|
2572
2131
|
}
|
|
2573
2132
|
/**
|
|
2574
|
-
* Handlers for the CIRRUS socket — the only
|
|
2575
|
-
*
|
|
2576
|
-
*
|
|
2133
|
+
* Handlers for the CIRRUS socket — the only socket whose close/error are real
|
|
2134
|
+
* stream events (`setCirrusDisconnected` / `dropConnection`). The control socket
|
|
2135
|
+
* outlives {@link awaitInstanceReady} too, but with a log-only `onclose`.
|
|
2577
2136
|
*/
|
|
2578
2137
|
addWsHandlers(ws) {
|
|
2579
2138
|
ws.onmessage = OnMessageHandler.bind(this);
|
|
@@ -2584,12 +2143,10 @@ class SignallingService extends SubService {
|
|
|
2584
2143
|
OnOpenHandler.call(this);
|
|
2585
2144
|
}
|
|
2586
2145
|
/**
|
|
2587
|
-
* Adopt a live
|
|
2588
|
-
*
|
|
2589
|
-
*
|
|
2590
|
-
*
|
|
2591
|
-
* `setCirrusConnected` + re-`sendRequestStream`). Reuses the head-script's
|
|
2592
|
-
* `correlationId` so subsequent correlated messages line up.
|
|
2146
|
+
* Adopt a live socket from the `stream-prefetch.js` head-script. Already OPEN with
|
|
2147
|
+
* `requestStream` sent, so unlike {@link addWsHandlers} this skips
|
|
2148
|
+
* {@link OnOpenHandler} (which would re-`setCirrusConnected`). Reuses the
|
|
2149
|
+
* head-script's `correlationId` so correlated messages line up.
|
|
2593
2150
|
*/
|
|
2594
2151
|
adoptSocket(ws, correlationId) {
|
|
2595
2152
|
this.correlationId = correlationId;
|
|
@@ -2603,14 +2160,14 @@ class SignallingService extends SubService {
|
|
|
2603
2160
|
this.store.dispatch(setStatusMessage({ statusMessage }));
|
|
2604
2161
|
}
|
|
2605
2162
|
/**
|
|
2606
|
-
*
|
|
2607
|
-
*
|
|
2608
|
-
*
|
|
2609
|
-
*
|
|
2163
|
+
* Abort/teardown: closes the Cirrus socket AND the control one, handed over or
|
|
2164
|
+
* not (`abort$` fans in `setCirrusDisconnected` / `dropConnection` /
|
|
2165
|
+
* `destroyUnrealScene`). A new attempt inside a live cycle uses
|
|
2166
|
+
* {@link closeCirrus}, so it does not kill the socket that produced the address
|
|
2167
|
+
* it is about to connect to.
|
|
2610
2168
|
*/
|
|
2611
2169
|
close(data) {
|
|
2612
2170
|
const { code, reason } = data;
|
|
2613
|
-
this.orchestrationRetrySub?.unsubscribe();
|
|
2614
2171
|
this.closeCirrus(data);
|
|
2615
2172
|
this.teardownControlWs(code, reason);
|
|
2616
2173
|
}
|
|
@@ -2621,10 +2178,45 @@ class SignallingService extends SubService {
|
|
|
2621
2178
|
this.teardownSocket(this.ws, code, reason);
|
|
2622
2179
|
this.ws = null;
|
|
2623
2180
|
}
|
|
2181
|
+
/**
|
|
2182
|
+
* No control socket survives this — the live one AND one left open by a handover,
|
|
2183
|
+
* so callers need no extra knowledge about which state it is in.
|
|
2184
|
+
*/
|
|
2624
2185
|
teardownControlWs(code = 1000, reason = DisconnectReason.NewConnectionAttempt) {
|
|
2625
2186
|
this.teardownSocket(this.controlWs, code, reason);
|
|
2187
|
+
this.teardownSocket(this.handedOverControlWs, code, reason);
|
|
2626
2188
|
this.controlWs = null;
|
|
2189
|
+
this.handedOverControlWs = null;
|
|
2190
|
+
this.orchestrationRetryCount = 0;
|
|
2191
|
+
// No control socket ⇒ no armed resend.
|
|
2192
|
+
this.orchestrationRetrySub?.unsubscribe();
|
|
2193
|
+
}
|
|
2194
|
+
/**
|
|
2195
|
+
* Handover, not teardown: strip the handlers, leave the socket OPEN for
|
|
2196
|
+
* orchestration to close (SIGNALLING.md). Moving it out of `controlWs` is what
|
|
2197
|
+
* closes the send path; dropping `onmessage` is what stops a late `error`
|
|
2198
|
+
* (4001/4002) from arming a resend and reserving a second machine.
|
|
2199
|
+
*
|
|
2200
|
+
* Takes the socket explicitly so it is provably the caller's.
|
|
2201
|
+
*/
|
|
2202
|
+
handOverControlWs(ws) {
|
|
2203
|
+
this.orchestrationRetrySub?.unsubscribe();
|
|
2627
2204
|
this.orchestrationRetryCount = 0;
|
|
2205
|
+
ws.onmessage = () => null;
|
|
2206
|
+
ws.onerror = () => null;
|
|
2207
|
+
// Log-only: an expected close that says nothing about the stream, so no store
|
|
2208
|
+
// event. Releasing the reference keeps the field meaning "open".
|
|
2209
|
+
ws.onclose = (e) => {
|
|
2210
|
+
Logger.log(`ORCHESTRATION CONTROL WS CLOSED BY BACKEND (code ${e.code})`);
|
|
2211
|
+
if (this.handedOverControlWs === ws) {
|
|
2212
|
+
this.handedOverControlWs = null;
|
|
2213
|
+
}
|
|
2214
|
+
};
|
|
2215
|
+
if (this.controlWs === ws) {
|
|
2216
|
+
this.controlWs = null;
|
|
2217
|
+
}
|
|
2218
|
+
this.handedOverControlWs = ws;
|
|
2219
|
+
Logger.log('ORCHESTRATION CONTROL WS HANDED OVER — left open for orchestration');
|
|
2628
2220
|
}
|
|
2629
2221
|
teardownSocket(ws, code, reason) {
|
|
2630
2222
|
if (!ws) {
|
|
@@ -2681,16 +2273,13 @@ class SignallingService extends SubService {
|
|
|
2681
2273
|
}
|
|
2682
2274
|
}
|
|
2683
2275
|
/**
|
|
2684
|
-
* One
|
|
2685
|
-
*
|
|
2686
|
-
* split by origin: {@link setOrchestrationHandlers} (control socket) and
|
|
2687
|
-
* {@link setStreamHandlers} (Cirrus).
|
|
2276
|
+
* One map shared by both sockets — `handleMessage` dispatches purely by type, so
|
|
2277
|
+
* registration is split by origin only for readability.
|
|
2688
2278
|
*
|
|
2689
|
-
* Keepalive is deliberately absent: both servers ping at the WS **protocol**
|
|
2690
|
-
*
|
|
2691
|
-
*
|
|
2692
|
-
*
|
|
2693
|
-
* not-yet-updated backend is simply ignored.
|
|
2279
|
+
* Keepalive is deliberately absent: both servers ping at the WS **protocol** level,
|
|
2280
|
+
* which the browser answers itself, so the page must never reply with an
|
|
2281
|
+
* application-level `pong`. An unregistered type is dropped silently, so a `ping`
|
|
2282
|
+
* from a not-yet-updated backend is simply ignored.
|
|
2694
2283
|
*/
|
|
2695
2284
|
setMessageHandlers() {
|
|
2696
2285
|
this.setOrchestrationHandlers();
|
|
@@ -2698,12 +2287,12 @@ class SignallingService extends SubService {
|
|
|
2698
2287
|
}
|
|
2699
2288
|
/**
|
|
2700
2289
|
* Messages from the ORCHESTRATION control socket. Only reachable during the
|
|
2701
|
-
* provisioning wait
|
|
2702
|
-
* `
|
|
2290
|
+
* provisioning wait: the socket outlives `instanceReady`, but `handOverControlWs`
|
|
2291
|
+
* drops its `onmessage`, so nothing routes here afterwards.
|
|
2703
2292
|
*/
|
|
2704
2293
|
setOrchestrationHandlers() {
|
|
2705
|
-
// `instanceReserved` (eta
|
|
2706
|
-
//
|
|
2294
|
+
// `instanceReserved` (eta) + `instanceReady` (the Cirrus address) are the
|
|
2295
|
+
// request/response pair of `requestCirrusViaSocket`.
|
|
2707
2296
|
this.wsMsgHandlers.instanceReserved = InstanceReservedHandler.bind(this);
|
|
2708
2297
|
this.wsMsgHandlers.instanceReady = InstanceReadyHandler.bind(this);
|
|
2709
2298
|
// Orchestration-side refusals (4001 no instances / 4002 limit reached) →
|
|
@@ -2737,9 +2326,9 @@ class SignallingService extends SubService {
|
|
|
2737
2326
|
});
|
|
2738
2327
|
}
|
|
2739
2328
|
/**
|
|
2740
|
-
*
|
|
2741
|
-
*
|
|
2742
|
-
*
|
|
2329
|
+
* Cirrus only — the control socket's state says nothing about the stream
|
|
2330
|
+
* (orchestration closes it once the instance is occupied), so it must stay out
|
|
2331
|
+
* of this gate.
|
|
2743
2332
|
*/
|
|
2744
2333
|
isConnected() {
|
|
2745
2334
|
return !!(this.ws && this.ws.readyState === WS_OPEN_STATE);
|
|
@@ -2748,10 +2337,10 @@ class SignallingService extends SubService {
|
|
|
2748
2337
|
Logger.log('Start Getting Region');
|
|
2749
2338
|
return this.regionsPingService.getFastest().pipe(take(1));
|
|
2750
2339
|
}
|
|
2751
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
2752
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
2340
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SignallingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2341
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SignallingService, providedIn: 'root' }); }
|
|
2753
2342
|
}
|
|
2754
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
2343
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SignallingService, decorators: [{
|
|
2755
2344
|
type: Injectable,
|
|
2756
2345
|
args: [{ providedIn: 'root' }]
|
|
2757
2346
|
}], ctorParameters: () => [] });
|
|
@@ -2779,7 +2368,7 @@ class VideoService extends SubService {
|
|
|
2779
2368
|
this.videoStats$ = this.videoTrack$.pipe(
|
|
2780
2369
|
// If videoTrack$ emits null, switch to EMPTY → the current interval is canceled.
|
|
2781
2370
|
switchMap$1((track) => track
|
|
2782
|
-
? interval(250).pipe(map(() => track.pcClient), switchMap$1((pcClient) => from(this.getStats(pcClient))), filter(Truthy))
|
|
2371
|
+
? interval(250).pipe(map$1(() => track.pcClient), switchMap$1((pcClient) => from(this.getStats(pcClient))), filter(Truthy))
|
|
2783
2372
|
: of(null)), filter(Truthy), share());
|
|
2784
2373
|
this.destroyAbort = new AbortController();
|
|
2785
2374
|
// Teardown is wired ONCE here instead of per `create()`. The per-create
|
|
@@ -3166,10 +2755,10 @@ class VideoService extends SubService {
|
|
|
3166
2755
|
this.armGestureRetry(audio);
|
|
3167
2756
|
}
|
|
3168
2757
|
}
|
|
3169
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
3170
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
2758
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: VideoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2759
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: VideoService, providedIn: 'root' }); }
|
|
3171
2760
|
}
|
|
3172
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
2761
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: VideoService, decorators: [{
|
|
3173
2762
|
type: Injectable,
|
|
3174
2763
|
args: [{ providedIn: 'root' }]
|
|
3175
2764
|
}], ctorParameters: () => [] });
|
|
@@ -3625,25 +3214,25 @@ class WebRtcPlayerService extends SubService {
|
|
|
3625
3214
|
}
|
|
3626
3215
|
handlePeerConnectionEvents(pcClient) {
|
|
3627
3216
|
fromEvent(pcClient, 'signalingstatechange')
|
|
3628
|
-
.pipe(map
|
|
3217
|
+
.pipe(map(() => this.pcClient?.signalingState), takeUntil(merge(this.disconnect$, this.destroy$)))
|
|
3629
3218
|
.subscribe((state) => {
|
|
3630
3219
|
Logger.info('[Signaling state change] |', state, '|');
|
|
3631
3220
|
this.commandTelemetryService.trackStopCommand('EXT-Signaling state change', true, { state, multi: true });
|
|
3632
3221
|
});
|
|
3633
3222
|
fromEvent(pcClient, 'iceconnectionstatechange')
|
|
3634
|
-
.pipe(map
|
|
3223
|
+
.pipe(map(() => this.pcClient?.iceConnectionState), takeUntil(merge(this.disconnect$, this.destroy$)))
|
|
3635
3224
|
.subscribe((state) => {
|
|
3636
3225
|
Logger.info('[Browser ICE connection] |', state, '|');
|
|
3637
3226
|
this.commandTelemetryService.trackStopCommand('EXT-Browser ICE connection', true, { state, multi: true });
|
|
3638
3227
|
});
|
|
3639
3228
|
fromEvent(pcClient, 'icegatheringstatechange')
|
|
3640
|
-
.pipe(map
|
|
3229
|
+
.pipe(map(() => this.pcClient?.iceGatheringState), takeUntil(merge(this.disconnect$, this.destroy$)))
|
|
3641
3230
|
.subscribe((state) => {
|
|
3642
3231
|
Logger.info('[Browser ICE gathering] |', state, '|');
|
|
3643
3232
|
this.commandTelemetryService.trackStopCommand('EXT-Browser ICE gathering', true, { state, multi: true });
|
|
3644
3233
|
});
|
|
3645
3234
|
fromEvent(pcClient, 'connectionstatechange')
|
|
3646
|
-
.pipe(map
|
|
3235
|
+
.pipe(map(() => this.pcClient?.connectionState), takeUntil(merge(this.disconnect$, this.destroy$)))
|
|
3647
3236
|
.subscribe((state) => {
|
|
3648
3237
|
Logger.info('[RTCPeerConnection state] |', state, '|');
|
|
3649
3238
|
// A genuine ICE failure (not a manual dropDC/dropPC, which recover via
|
|
@@ -3660,7 +3249,7 @@ class WebRtcPlayerService extends SubService {
|
|
|
3660
3249
|
}
|
|
3661
3250
|
});
|
|
3662
3251
|
fromEvent(pcClient, 'icecandidate')
|
|
3663
|
-
.pipe(map
|
|
3252
|
+
.pipe(map((e) => e.candidate), filter(Truthy), tap$1((candidate) => Logger.info('[Browser ICE candidate]', '| Type=', candidate.type, '| Protocol=', candidate.protocol, '| Address=', candidate.address, '| Port=', candidate.port, '|')), takeUntil(merge(this.disconnect$, this.destroy$)))
|
|
3664
3253
|
.subscribe((candidate) => this.signallingSrv.send({
|
|
3665
3254
|
type: 'iceCandidate',
|
|
3666
3255
|
candidate,
|
|
@@ -3668,7 +3257,7 @@ class WebRtcPlayerService extends SubService {
|
|
|
3668
3257
|
// When the server creates the DataChannel (server-offer flow),
|
|
3669
3258
|
// we receive it here via the 'datachannel' event
|
|
3670
3259
|
fromEvent(pcClient, 'datachannel')
|
|
3671
|
-
.pipe(tap$1((e) => Logger.info(`[DATACHANNEL] Received remote DC: label="${e.channel.label}" id=${e.channel.id} protocol="${e.channel.protocol}" ordered=${e.channel.ordered}`)), map
|
|
3260
|
+
.pipe(tap$1((e) => Logger.info(`[DATACHANNEL] Received remote DC: label="${e.channel.label}" id=${e.channel.id} protocol="${e.channel.protocol}" ordered=${e.channel.ordered}`)), map((e) => e.channel), takeUntil(merge(this.disconnect$, this.destroy$)))
|
|
3672
3261
|
.subscribe((datachannel) => {
|
|
3673
3262
|
this.dcClient = this.setupDataChannelCallbacks(datachannel);
|
|
3674
3263
|
});
|
|
@@ -3679,10 +3268,10 @@ class WebRtcPlayerService extends SubService {
|
|
|
3679
3268
|
' readyState=' +
|
|
3680
3269
|
e.track.readyState)), share());
|
|
3681
3270
|
onTrack$
|
|
3682
|
-
.pipe(filter((e) => e.track.kind === 'audio'), map
|
|
3271
|
+
.pipe(filter((e) => e.track.kind === 'audio'), map((e) => e.streams[0]))
|
|
3683
3272
|
.subscribe((stream) => this.videoService.attachAudioStream(stream));
|
|
3684
3273
|
onTrack$
|
|
3685
|
-
.pipe(filter((e) => e.track.kind === 'video'), map
|
|
3274
|
+
.pipe(filter((e) => e.track.kind === 'video'), map((e) => e.streams[0]), tap$1(() => Logger.info('Set video source from video track ontrack.')))
|
|
3686
3275
|
.subscribe((stream) => {
|
|
3687
3276
|
if (this.pcClient) {
|
|
3688
3277
|
this.videoService.attachVideoStream(stream, this.pcClient);
|
|
@@ -3783,7 +3372,7 @@ class WebRtcPlayerService extends SubService {
|
|
|
3783
3372
|
`[DATACHANNEL] Message received on "${datachannel.label}" id=${datachannel.id}, byteCode=${view[0]}, size=${view.byteLength}`,
|
|
3784
3373
|
);
|
|
3785
3374
|
}),*/
|
|
3786
|
-
map
|
|
3375
|
+
map((e) => e.data), takeUntil(merge(this.disconnect$, this.destroy$)))
|
|
3787
3376
|
.subscribe((data) => this.onDataChannelMessage$.next(data));
|
|
3788
3377
|
return datachannel;
|
|
3789
3378
|
}
|
|
@@ -3835,10 +3424,10 @@ class WebRtcPlayerService extends SubService {
|
|
|
3835
3424
|
Logger.error("Couldn't create answer", e);
|
|
3836
3425
|
}
|
|
3837
3426
|
}
|
|
3838
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
3839
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
3427
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WebRtcPlayerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3428
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WebRtcPlayerService, providedIn: 'root' }); }
|
|
3840
3429
|
}
|
|
3841
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
3430
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WebRtcPlayerService, decorators: [{
|
|
3842
3431
|
type: Injectable,
|
|
3843
3432
|
args: [{ providedIn: 'root' }]
|
|
3844
3433
|
}], ctorParameters: () => [] });
|
|
@@ -3964,10 +3553,10 @@ class StreamAdoptionService {
|
|
|
3964
3553
|
this.webRtc.adopt(parked.pc, parked.dc, parked.stream, parked.config ?? {}, parked.dcBuffer);
|
|
3965
3554
|
return true;
|
|
3966
3555
|
}
|
|
3967
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
3968
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
3556
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StreamAdoptionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3557
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StreamAdoptionService, providedIn: 'root' }); }
|
|
3969
3558
|
}
|
|
3970
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
3559
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StreamAdoptionService, decorators: [{
|
|
3971
3560
|
type: Injectable,
|
|
3972
3561
|
args: [{ providedIn: 'root' }]
|
|
3973
3562
|
}] });
|
|
@@ -3999,10 +3588,10 @@ class UnrealCallbackService {
|
|
|
3999
3588
|
* ```
|
|
4000
3589
|
*/
|
|
4001
3590
|
fromUnrealCallback(command) {
|
|
4002
|
-
return this.actions$.pipe(ofType(unrealCommandCallback), map
|
|
3591
|
+
return this.actions$.pipe(ofType(unrealCommandCallback), map(({ json }) => 'commandCallback' in json ? json.commandCallback : json), filter((json) => json.command === command),
|
|
4003
3592
|
// Cast is required: after a `command`-based filter TS cannot narrow to a
|
|
4004
3593
|
// type discriminated on `callback`. The unwrapped object IS the full callback.
|
|
4005
|
-
map
|
|
3594
|
+
map((data) => data));
|
|
4006
3595
|
}
|
|
4007
3596
|
/**
|
|
4008
3597
|
* Sends a command and observes the matching response by correlationId.
|
|
@@ -4026,19 +3615,19 @@ class UnrealCallbackService {
|
|
|
4026
3615
|
observeCommandResponse(data, sender, timeOut = 60000, dispatchOnTimeout = true) {
|
|
4027
3616
|
const correlationId = generateUuid();
|
|
4028
3617
|
return defer(() => {
|
|
4029
|
-
const observable = this.actions$.pipe(ofType(unrealCommandCallback), map
|
|
3618
|
+
const observable = this.actions$.pipe(ofType(unrealCommandCallback), map(({ json }) => 'commandCallback' in json ? json.commandCallback : json), filter((json) => json.correlationId === correlationId), timeout$1(timeOut), catchError(() => dispatchOnTimeout
|
|
4030
3619
|
? of({ command: 'timeout', correlationId: 'timeout' })
|
|
4031
3620
|
: of(null)), take(1), tapLog('Command Response Observer'), filter(Truthy),
|
|
4032
3621
|
// Cast: response is matched by correlationId, so it is the callback for K.
|
|
4033
|
-
map
|
|
3622
|
+
map((data) => data));
|
|
4034
3623
|
setTimeout(() => sender({ ...data, correlationId }), 0);
|
|
4035
3624
|
return observable;
|
|
4036
3625
|
});
|
|
4037
3626
|
}
|
|
4038
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
4039
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
3627
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealCallbackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3628
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealCallbackService, providedIn: 'root' }); }
|
|
4040
3629
|
}
|
|
4041
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
3630
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealCallbackService, decorators: [{
|
|
4042
3631
|
type: Injectable,
|
|
4043
3632
|
args: [{ providedIn: 'root' }]
|
|
4044
3633
|
}] });
|
|
@@ -4155,10 +3744,10 @@ class UnrealCommunicatorService extends SubService {
|
|
|
4155
3744
|
requestQualityControl() {
|
|
4156
3745
|
this.sendInputData(new Uint8Array([MessageType.RequestQualityControl]).buffer);
|
|
4157
3746
|
}
|
|
4158
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
4159
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
3747
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealCommunicatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3748
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealCommunicatorService, providedIn: 'root' }); }
|
|
4160
3749
|
}
|
|
4161
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
3750
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealCommunicatorService, decorators: [{
|
|
4162
3751
|
type: Injectable,
|
|
4163
3752
|
args: [{ providedIn: 'root' }]
|
|
4164
3753
|
}], ctorParameters: () => [] });
|
|
@@ -4172,10 +3761,10 @@ class UnrealErrorModalComponent {
|
|
|
4172
3761
|
close() {
|
|
4173
3762
|
this.#dialogRef.close();
|
|
4174
3763
|
}
|
|
4175
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
4176
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.
|
|
3764
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3765
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: UnrealErrorModalComponent, isStandalone: true, selector: "app-unreal-error-modal", ngImport: i0, template: "<div class=\"unreal-modal\">\n <header class=\"unreal-modal__header\">\n <div\n [attr.data-testid]=\"'unreal-error-header-title'\"\n class=\"unreal-modal__title\"\n >\n Warning\n </div>\n </header>\n <div class=\"unreal-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"unreal-modal__footer\">\n <app-unreal-button (clicked)=\"close()\">Ok</app-unreal-button>\n </div>\n</div>\n", styles: [".unreal-modal{--unrealModalWidth: 422px;--unrealModalMaxWidth: calc(100% - 16px) ;--unrealModalMaxHeight: 80vh;--unrealModalBg: var(--src-surface-container-main, #fff);--unrealModalBoxShadow: 0 16px 36px 4px var(--src-shadow-hard, rgba(71, 85, 105, .24)), 0 1px 4px 0 var(--src-shadow-light, rgba(148, 163, 184, .16));--unrealModalBorderRadius: var(--src-layout-radius-const-rounded-md, 12px);--unrealModalTitleSize: var(--src-font-size-base, 16px);--unrealModalTitleLineHeight: var(--src-font-line-base, 24px);--unrealModalTitleColor: var(--src-text-body-main, #111827);--unrealModalBodyPadding: var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-2xl, 32px) var(--src-layout-padding-const-xl, 24px);--unrealModalTitleBorder: none;--unrealModalFooterBorder: 1px solid var(--src-border-container-light, rgba(148, 163, 184, .16));display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;width:var(--unrealModalWidth);max-width:var(--unrealModalMaxWidth);max-height:var(--unrealModalMaxHeight);margin:auto;background-color:var(--unrealModalBg);border-radius:var(--unrealModalBorderRadius);box-shadow:var(--unrealModalBoxShadow);color:var(--src-text-body-secondary, #4b5563);font-family:var(--src-font-family-header, Inter);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:var(--src-font-line-sm, 20px)}.unreal-modal .unreal-modal__header{position:relative;display:flex;padding:0;width:100%;border-bottom:var(--unrealModalTitleBorder)}.unreal-modal .unreal-modal__body{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:var(--src-layout-gap-const-md, 12px);padding:var(--unrealModalBodyPadding);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--src-border-container-basic, rgba(148, 163, 184, .24)) transparent}.unreal-modal .unreal-modal__body .unreal-modal__title{grid-column:1/-1}.unreal-modal .unreal-modal__body::-webkit-scrollbar{width:4px}.unreal-modal .unreal-modal__body::-webkit-scrollbar-track{background:transparent}.unreal-modal .unreal-modal__body::-webkit-scrollbar-thumb{background-color:var(--src-border-container-basic, rgba(148, 163, 184, .24));border-radius:10px}.unreal-modal .unreal-modal__footer{display:flex;justify-content:flex-end;align-items:center;padding:var(--src-layout-padding-const-md, 12px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px);width:100%;border-top:var(--unrealModalFooterBorder);gap:var(--src-layout-gap-const-sm, 8px)}.unreal-modal .unreal-modal__title{font-size:var(--unrealModalTitleSize);font-weight:600;line-height:var(--unrealModalTitleLineHeight);color:var(--unrealModalTitleColor);word-break:break-word}.unreal-modal .unreal-modal__header{padding:16px 24px 0}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4177
3766
|
}
|
|
4178
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
3767
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealErrorModalComponent, decorators: [{
|
|
4179
3768
|
type: Component,
|
|
4180
3769
|
args: [{ selector: 'app-unreal-error-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealButtonComponent], template: "<div class=\"unreal-modal\">\n <header class=\"unreal-modal__header\">\n <div\n [attr.data-testid]=\"'unreal-error-header-title'\"\n class=\"unreal-modal__title\"\n >\n Warning\n </div>\n </header>\n <div class=\"unreal-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"unreal-modal__footer\">\n <app-unreal-button (clicked)=\"close()\">Ok</app-unreal-button>\n </div>\n</div>\n", styles: [".unreal-modal{--unrealModalWidth: 422px;--unrealModalMaxWidth: calc(100% - 16px) ;--unrealModalMaxHeight: 80vh;--unrealModalBg: var(--src-surface-container-main, #fff);--unrealModalBoxShadow: 0 16px 36px 4px var(--src-shadow-hard, rgba(71, 85, 105, .24)), 0 1px 4px 0 var(--src-shadow-light, rgba(148, 163, 184, .16));--unrealModalBorderRadius: var(--src-layout-radius-const-rounded-md, 12px);--unrealModalTitleSize: var(--src-font-size-base, 16px);--unrealModalTitleLineHeight: var(--src-font-line-base, 24px);--unrealModalTitleColor: var(--src-text-body-main, #111827);--unrealModalBodyPadding: var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-2xl, 32px) var(--src-layout-padding-const-xl, 24px);--unrealModalTitleBorder: none;--unrealModalFooterBorder: 1px solid var(--src-border-container-light, rgba(148, 163, 184, .16));display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;width:var(--unrealModalWidth);max-width:var(--unrealModalMaxWidth);max-height:var(--unrealModalMaxHeight);margin:auto;background-color:var(--unrealModalBg);border-radius:var(--unrealModalBorderRadius);box-shadow:var(--unrealModalBoxShadow);color:var(--src-text-body-secondary, #4b5563);font-family:var(--src-font-family-header, Inter);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:var(--src-font-line-sm, 20px)}.unreal-modal .unreal-modal__header{position:relative;display:flex;padding:0;width:100%;border-bottom:var(--unrealModalTitleBorder)}.unreal-modal .unreal-modal__body{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:var(--src-layout-gap-const-md, 12px);padding:var(--unrealModalBodyPadding);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--src-border-container-basic, rgba(148, 163, 184, .24)) transparent}.unreal-modal .unreal-modal__body .unreal-modal__title{grid-column:1/-1}.unreal-modal .unreal-modal__body::-webkit-scrollbar{width:4px}.unreal-modal .unreal-modal__body::-webkit-scrollbar-track{background:transparent}.unreal-modal .unreal-modal__body::-webkit-scrollbar-thumb{background-color:var(--src-border-container-basic, rgba(148, 163, 184, .24));border-radius:10px}.unreal-modal .unreal-modal__footer{display:flex;justify-content:flex-end;align-items:center;padding:var(--src-layout-padding-const-md, 12px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px);width:100%;border-top:var(--unrealModalFooterBorder);gap:var(--src-layout-gap-const-sm, 8px)}.unreal-modal .unreal-modal__title{font-size:var(--unrealModalTitleSize);font-weight:600;line-height:var(--unrealModalTitleLineHeight);color:var(--unrealModalTitleColor);word-break:break-word}.unreal-modal .unreal-modal__header{padding:16px 24px 0}\n"] }]
|
|
4181
3770
|
}] });
|
|
@@ -4203,7 +3792,7 @@ class UnrealEffects {
|
|
|
4203
3792
|
.select(unrealFeature.selectDataChannelConnected)
|
|
4204
3793
|
.pipe(filter(Truthy));
|
|
4205
3794
|
this.destroyConnections$ = createEffect(() => {
|
|
4206
|
-
return this.actions$.pipe(ofType(destroyUnrealScene), map(() => destroyRemoteConnections({ reason: DisconnectReason.Destroy })));
|
|
3795
|
+
return this.actions$.pipe(ofType(destroyUnrealScene), map$1(() => destroyRemoteConnections({ reason: DisconnectReason.Destroy })));
|
|
4207
3796
|
});
|
|
4208
3797
|
// Stage 3 — accurate region re-ping in the background once the stream is up.
|
|
4209
3798
|
// A few seconds after the data channel is ready (so it doesn't compete with
|
|
@@ -4222,10 +3811,10 @@ class UnrealEffects {
|
|
|
4222
3811
|
// subscribe to single intent (and it shows up as a distinct entry in the
|
|
4223
3812
|
// Redux DevTools timeline).
|
|
4224
3813
|
this.abortEstablishingConnection$ = createEffect(() => {
|
|
4225
|
-
return this.actions$.pipe(ofType(setCirrusDisconnected, dropConnection, destroyUnrealScene), map(() => abortEstablishingConnection()));
|
|
3814
|
+
return this.actions$.pipe(ofType(setCirrusDisconnected, dropConnection, destroyUnrealScene), map$1(() => abortEstablishingConnection()));
|
|
4226
3815
|
});
|
|
4227
3816
|
this.resetAfk$ = createEffect(() => {
|
|
4228
|
-
return this.actions$.pipe(ofType(setConfig, setUserInactivityTimeout, resetWarnTimeout, resetConfig), map(() => resetAfk()));
|
|
3817
|
+
return this.actions$.pipe(ofType(setConfig, setUserInactivityTimeout, resetWarnTimeout, resetConfig), map$1(() => resetAfk()));
|
|
4229
3818
|
});
|
|
4230
3819
|
this.iframeMessaging$ = createEffect(() => {
|
|
4231
3820
|
const cirrusConnected$ = this.store
|
|
@@ -4238,16 +3827,16 @@ class UnrealEffects {
|
|
|
4238
3827
|
// Skip initial false; emit null on every viewport state change
|
|
4239
3828
|
const viewportReset$ = this.store
|
|
4240
3829
|
.select(unrealFeature.selectViewportReady)
|
|
4241
|
-
.pipe(skip(1), delay(500), map(() => null));
|
|
3830
|
+
.pipe(skip(1), delay(500), map$1(() => null));
|
|
4242
3831
|
return merge(cirrusStatus$, this.store.select(unrealFeature.selectStatusMessage), viewportReset$).pipe(debounceTime(0), distinctUntilChanged(), tapLog('STATUS=>'), tap((status) => this.postToScreenLocker(status)));
|
|
4243
3832
|
}, { dispatch: false });
|
|
4244
3833
|
this.setSignalingTo100$ = createEffect(() => {
|
|
4245
|
-
return this.actions$.pipe(ofType(setAwsInstance), map(() => setStatusPercentSignallingServer({ percent: 100 })));
|
|
3834
|
+
return this.actions$.pipe(ofType(setAwsInstance), map$1(() => setStatusPercentSignallingServer({ percent: 100 })));
|
|
4246
3835
|
});
|
|
4247
3836
|
this.setOrchestrationParameters$ = createEffect(() => {
|
|
4248
|
-
return this.actions$.pipe(ofType(setOrchestrationParameters), map(({ eta }) => eta), filter((value) => value > 0), switchMap((total) => {
|
|
3837
|
+
return this.actions$.pipe(ofType(setOrchestrationParameters), map$1(({ eta }) => eta), filter((value) => value > 0), switchMap((total) => {
|
|
4249
3838
|
// Emit every second: 0,1,2,...,total (inclusive)
|
|
4250
|
-
return timer(0, 1000).pipe(takeUntil(this.connectionCompleted$), take(total + 1), map((sec) => sec / total), distinctUntilChanged(), map((progressComplete) => setOrchestrationProgress({ progressComplete })));
|
|
3839
|
+
return timer(0, 1000).pipe(takeUntil(this.connectionCompleted$), take(total + 1), map$1((sec) => sec / total), distinctUntilChanged(), map$1((progressComplete) => setOrchestrationProgress({ progressComplete })));
|
|
4251
3840
|
}));
|
|
4252
3841
|
});
|
|
4253
3842
|
this.setDataChannelTimeoutCheck$ = createEffect(() => {
|
|
@@ -4294,7 +3883,7 @@ class UnrealEffects {
|
|
|
4294
3883
|
return of(null);
|
|
4295
3884
|
}),
|
|
4296
3885
|
// map response to the destroy action that ngrx will dispatch
|
|
4297
|
-
map(() => disconnectStream({
|
|
3886
|
+
map$1(() => disconnectStream({
|
|
4298
3887
|
reason: DisconnectReason.DataChannelTimeout,
|
|
4299
3888
|
message: 'Data Channel Timeout',
|
|
4300
3889
|
})));
|
|
@@ -4318,24 +3907,24 @@ class UnrealEffects {
|
|
|
4318
3907
|
* a visible flicker (video flashing to black).
|
|
4319
3908
|
*/
|
|
4320
3909
|
this.forceViewportNotReady$ = createEffect(() => {
|
|
4321
|
-
return this.actions$.pipe(ofType(destroyRemoteConnections), withLatestFrom(this.store.select(unrealFeature.selectViewportReady)), filter(([, viewportReady]) => viewportReady), map(() => setViewportNotReady()));
|
|
3910
|
+
return this.actions$.pipe(ofType(destroyRemoteConnections), withLatestFrom(this.store.select(unrealFeature.selectViewportReady)), filter(([, viewportReady]) => viewportReady), map$1(() => setViewportNotReady()));
|
|
4322
3911
|
});
|
|
4323
3912
|
this.resetCirrusOnDestroy$ = createEffect(() => {
|
|
4324
|
-
return this.actions$.pipe(ofType(destroyRemoteConnections), withLatestFrom(this.store.select(unrealFeature.selectCirrusConnected)), filter(([, cirrusConnected]) => cirrusConnected), map(() => setCirrusDisconnected()));
|
|
3913
|
+
return this.actions$.pipe(ofType(destroyRemoteConnections), withLatestFrom(this.store.select(unrealFeature.selectCirrusConnected)), filter(([, cirrusConnected]) => cirrusConnected), map$1(() => setCirrusDisconnected()));
|
|
4325
3914
|
});
|
|
4326
3915
|
this.destroyRemoteConnections$ = createEffect(() => {
|
|
4327
3916
|
return this.actions$.pipe(ofType(destroyRemoteConnections), tap(({ reason }) => {
|
|
4328
3917
|
this.webRtcPlayerService.releaseRemoteConnections(reason);
|
|
4329
|
-
}), filter(({ reason }) => reason === DisconnectReason.DropConnection), map(() => dropConnection()));
|
|
3918
|
+
}), filter(({ reason }) => reason === DisconnectReason.DropConnection), map$1(() => dropConnection()));
|
|
4330
3919
|
});
|
|
4331
3920
|
this.destroyConnectionAndRestart$ = createEffect(() => {
|
|
4332
|
-
return this.actions$.pipe(ofType(setCirrusDisconnected), withLatestFrom(this.store.select(unrealFeature.selectDisconnectReason)), filter(([, reason]) => reason === DisconnectReason.DataChannelTimeout), map(() => initSignalling()));
|
|
3921
|
+
return this.actions$.pipe(ofType(setCirrusDisconnected), withLatestFrom(this.store.select(unrealFeature.selectDisconnectReason)), filter(([, reason]) => reason === DisconnectReason.DataChannelTimeout), map$1(() => initSignalling()));
|
|
4333
3922
|
});
|
|
4334
3923
|
this.showUnrealError$ = createEffect(() => {
|
|
4335
|
-
return this.actions$.pipe(ofType(showUnrealErrorMessage), map(({ code }) => getRtcErrorMessage(code)), distinctUntilChanged(), filter(Truthy),
|
|
3924
|
+
return this.actions$.pipe(ofType(showUnrealErrorMessage), map$1(({ code }) => getRtcErrorMessage(code)), distinctUntilChanged(), filter(Truthy), exhaustMap((content) => this.dialog.open(UnrealErrorModalComponent, {
|
|
4336
3925
|
scrollStrategy: this.scrollStrategy,
|
|
4337
3926
|
data: { content },
|
|
4338
|
-
}).closed), map(() => showUnrealErrorMessage({ code: null })));
|
|
3927
|
+
}).closed), map$1(() => showUnrealErrorMessage({ code: null })));
|
|
4339
3928
|
});
|
|
4340
3929
|
/**
|
|
4341
3930
|
* `dataChannelReady` is a ONCE-PER-SESSION signal: it starts app init
|
|
@@ -4380,21 +3969,21 @@ class UnrealEffects {
|
|
|
4380
3969
|
// we proceed only after no more commands are started for 50 ms.
|
|
4381
3970
|
return this.actions$.pipe(ofType(commandStarted), debounceTime(50), take(1), switchMap(() => this.unrealCallbackService.observeCommandResponse({ command: MetaBoxCommand.FLoopBackCommand }, (data) => this.commandsSender.sendCommandToUnreal(data))), switchMap(() => this.videoService.videoStats$), filter(({ aggregatedStats: s }) => !!s.framesPerSecond &&
|
|
4382
3971
|
s.framesPerSecond > MINIMAL_FPS &&
|
|
4383
|
-
(s.framesDecoded ?? 0) > 15), take(1), map(() => setLoopBackCommandIsCompleted()));
|
|
3972
|
+
(s.framesDecoded ?? 0) > 15), take(1), map$1(() => setLoopBackCommandIsCompleted()));
|
|
4384
3973
|
}));
|
|
4385
3974
|
});
|
|
4386
3975
|
this.setViewportReadyBySetLoopBackCommandIsCompleted$ = createEffect(() => {
|
|
4387
|
-
return this.actions$.pipe(ofType(setLoopBackCommandIsCompleted, reconnectPeerSuccess), tap(() => this.commandTelemetryService.trackStopCommand('EXT-SetViewportReady', false, { ready: true })), map(() => setViewportReady()));
|
|
3976
|
+
return this.actions$.pipe(ofType(setLoopBackCommandIsCompleted, reconnectPeerSuccess), tap(() => this.commandTelemetryService.trackStopCommand('EXT-SetViewportReady', false, { ready: true })), map$1(() => setViewportReady()));
|
|
4388
3977
|
});
|
|
4389
3978
|
this.startStream$ = createEffect(() => {
|
|
4390
|
-
return this.actions$.pipe(ofType(startStream), map(() => initSignalling()));
|
|
3979
|
+
return this.actions$.pipe(ofType(startStream), map$1(() => initSignalling()));
|
|
4391
3980
|
});
|
|
4392
3981
|
// When the video stream is initialized but autoplay didn't kick in
|
|
4393
3982
|
// (browser blocked it; user gesture pending), the user may need extra
|
|
4394
3983
|
// time before interacting. Refresh the AFK timer so it doesn't tick
|
|
4395
3984
|
// toward an inactivity warning while we're waiting for permission.
|
|
4396
3985
|
this.resetAfkOnPausedVideoInit$ = createEffect(() => {
|
|
4397
|
-
return this.actions$.pipe(ofType(videoStreamInitialized), filter(() => !!this.videoService.video?.paused), map(() => resetAfk()));
|
|
3986
|
+
return this.actions$.pipe(ofType(videoStreamInitialized), filter(() => !!this.videoService.video?.paused), map$1(() => resetAfk()));
|
|
4398
3987
|
});
|
|
4399
3988
|
/**
|
|
4400
3989
|
* Effect: `disconnectReasonHandling$`
|
|
@@ -4417,7 +4006,7 @@ class UnrealEffects {
|
|
|
4417
4006
|
return false;
|
|
4418
4007
|
}
|
|
4419
4008
|
return DESTROY_DISCONNECT_REASONS.includes(reason);
|
|
4420
|
-
}), map(({ reason }) => destroyRemoteConnections({ reason })));
|
|
4009
|
+
}), map$1(({ reason }) => destroyRemoteConnections({ reason })));
|
|
4421
4010
|
});
|
|
4422
4011
|
/**
|
|
4423
4012
|
* Effect: `handleDataChannelClosed$`
|
|
@@ -4431,7 +4020,7 @@ class UnrealEffects {
|
|
|
4431
4020
|
reason === DisconnectReason.IceConnectionFailed) &&
|
|
4432
4021
|
this.canPeerReconnect()), tap(({ reason }) => {
|
|
4433
4022
|
Logger.warn(`[Reconnect] ${reason}, initiating peer reconnection`);
|
|
4434
|
-
}), map(() => reconnectPeer({
|
|
4023
|
+
}), map$1(() => reconnectPeer({
|
|
4435
4024
|
attempt: 1,
|
|
4436
4025
|
maxAttempts: RECONNECT_CONFIG.maxAttempts,
|
|
4437
4026
|
delay: 0,
|
|
@@ -4449,7 +4038,7 @@ class UnrealEffects {
|
|
|
4449
4038
|
*/
|
|
4450
4039
|
this.resetStateForPeerReconnect$ = createEffect(() => {
|
|
4451
4040
|
return this.actions$.pipe(ofType(reconnectPeer), filter(() => RECONNECT_CONFIG.enabled), filter(({ attempt }) => attempt === 1), // Only reset on the first attempt
|
|
4452
|
-
map(() => resetDataChannelForReconnect()));
|
|
4041
|
+
map$1(() => resetDataChannelForReconnect()));
|
|
4453
4042
|
});
|
|
4454
4043
|
/**
|
|
4455
4044
|
* Effect: `executePeerReconnect$`
|
|
@@ -4467,7 +4056,7 @@ class UnrealEffects {
|
|
|
4467
4056
|
return of(this.scheduleRetryOrFail(attempt, maxAttempts, 'Signaling connection lost during peer reconnection'));
|
|
4468
4057
|
}
|
|
4469
4058
|
// Monitor for successful data channel connection
|
|
4470
|
-
return this.actions$.pipe(ofType(dataChannelConnected), take(1), timeout$1(this.dataChannelConnectionTimeout), tap(() => Logger.info('[Reconnect] Peer reconnection successful!')), map(() => reconnectPeerSuccess()), catchError(() => {
|
|
4059
|
+
return this.actions$.pipe(ofType(dataChannelConnected), take(1), timeout$1(this.dataChannelConnectionTimeout), tap(() => Logger.info('[Reconnect] Peer reconnection successful!')), map$1(() => reconnectPeerSuccess()), catchError(() => {
|
|
4471
4060
|
if (attempt < maxAttempts) {
|
|
4472
4061
|
Logger.warn(`[Reconnect] Peer reconnection attempt ${attempt} timed out, scheduling retry`);
|
|
4473
4062
|
}
|
|
@@ -4485,7 +4074,7 @@ class UnrealEffects {
|
|
|
4485
4074
|
* Falls back to full disconnection which may trigger full reconnection.
|
|
4486
4075
|
*/
|
|
4487
4076
|
this.handlePeerReconnectFailed$ = createEffect(() => {
|
|
4488
|
-
return this.actions$.pipe(ofType(reconnectPeerFailed), map(({ reason }) => disconnectStream({
|
|
4077
|
+
return this.actions$.pipe(ofType(reconnectPeerFailed), map$1(({ reason }) => disconnectStream({
|
|
4489
4078
|
reason: DisconnectReason.ReconnectFailed,
|
|
4490
4079
|
message: reason,
|
|
4491
4080
|
})));
|
|
@@ -4521,9 +4110,9 @@ class UnrealEffects {
|
|
|
4521
4110
|
// carries a correlationId resolves a previously started command.
|
|
4522
4111
|
// Skip in Playwright mode (deterministic command IDs are issued directly).
|
|
4523
4112
|
this.listenUnrealCallbackByInitSignalling$ = createEffect(() => {
|
|
4524
|
-
return this.actions$.pipe(ofType(initSignalling), filter(() => !this.unrealInitialConfig?.playwright), switchMap(() => this.actions$.pipe(ofType(unrealCommandCallback), map(({ json }) => 'commandCallback' in json
|
|
4113
|
+
return this.actions$.pipe(ofType(initSignalling), filter(() => !this.unrealInitialConfig?.playwright), switchMap(() => this.actions$.pipe(ofType(unrealCommandCallback), map$1(({ json }) => 'commandCallback' in json
|
|
4525
4114
|
? json.commandCallback?.correlationId
|
|
4526
|
-
: json?.correlationId), filter(Truthy), map((id) => commandCompleted({ id, now: Date.now() })))));
|
|
4115
|
+
: json?.correlationId), filter(Truthy), map$1((id) => commandCompleted({ id, now: Date.now() })))));
|
|
4527
4116
|
});
|
|
4528
4117
|
this.connectToSignaling$ = createEffect(() => {
|
|
4529
4118
|
return this.actions$.pipe(ofType(initSignalling), withLatestFrom(this.store.select(unrealFeature.selectMatchUrls), this.store.select(selectIsAutostart), this.store.select(unrealFeature.selectCirrusConnected)), filter(([, , autoStart, cirrusConnected]) => {
|
|
@@ -4531,7 +4120,7 @@ class UnrealEffects {
|
|
|
4531
4120
|
Logger.warn(`SIGNALING REQUEST can't be called when cirrus connection is already established.`);
|
|
4532
4121
|
}
|
|
4533
4122
|
return autoStart && !cirrusConnected;
|
|
4534
|
-
}), map(([, matchMakerUrls]) => [...matchMakerUrls].filter(Truthy)), filter((urls) => urls.length > 0),
|
|
4123
|
+
}), map$1(([, matchMakerUrls]) => [...matchMakerUrls].filter(Truthy)), filter((urls) => urls.length > 0),
|
|
4535
4124
|
// Ensure a single in-flight request per attempt.
|
|
4536
4125
|
exhaustMap((urls) =>
|
|
4537
4126
|
// If a prefetched `window.__stream` is ready — or still connecting —
|
|
@@ -4548,35 +4137,434 @@ class UnrealEffects {
|
|
|
4548
4137
|
ignoreElements(), startWith(setEstablishingConnection({ value: true })))));
|
|
4549
4138
|
});
|
|
4550
4139
|
}
|
|
4551
|
-
canPeerReconnect() {
|
|
4552
|
-
return RECONNECT_CONFIG.enabled && this.signallingService.isConnected();
|
|
4140
|
+
canPeerReconnect() {
|
|
4141
|
+
return RECONNECT_CONFIG.enabled && this.signallingService.isConnected();
|
|
4142
|
+
}
|
|
4143
|
+
/**
|
|
4144
|
+
* Either schedules the next peer-reconnect attempt or gives up with the
|
|
4145
|
+
* supplied reason. Used in two `executePeerReconnect$` branches that share
|
|
4146
|
+
* the same retry-or-fail decision.
|
|
4147
|
+
*/
|
|
4148
|
+
scheduleRetryOrFail(attempt, maxAttempts, exhaustedReason) {
|
|
4149
|
+
return attempt < maxAttempts
|
|
4150
|
+
? reconnectPeer({ attempt: attempt + 1, maxAttempts })
|
|
4151
|
+
: reconnectPeerFailed({ reason: exhaustedReason });
|
|
4152
|
+
}
|
|
4153
|
+
postToScreenLocker(text) {
|
|
4154
|
+
try {
|
|
4155
|
+
const el = document.getElementById(this.unrealInitialConfig?.screenLockerContainerId ??
|
|
4156
|
+
SCREEN_LOCKER_CONTAINER_ID);
|
|
4157
|
+
el?.contentWindow?.postMessage(text, '*');
|
|
4158
|
+
}
|
|
4159
|
+
catch {
|
|
4160
|
+
// Fails silently if cross-origin
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealEffects, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4164
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealEffects }); }
|
|
4165
|
+
}
|
|
4166
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealEffects, decorators: [{
|
|
4167
|
+
type: Injectable
|
|
4168
|
+
}] });
|
|
4169
|
+
|
|
4170
|
+
function InstanceReadyHandler(msg) {
|
|
4171
|
+
const payload = msg?.payload;
|
|
4172
|
+
if (!payload) {
|
|
4173
|
+
Logger.warn('instanceReady received without payload — ignoring');
|
|
4174
|
+
return;
|
|
4175
|
+
}
|
|
4176
|
+
if (!payload.originInstanceUri) {
|
|
4177
|
+
Logger.warn('instanceReady without originInstanceUri — ignoring');
|
|
4178
|
+
return;
|
|
4179
|
+
}
|
|
4180
|
+
// Emit for observability (telemetry) and store the stream-machine coords.
|
|
4181
|
+
this.store.dispatch(instanceReady(payload));
|
|
4182
|
+
// This is the answer `requestCirrusViaSocket` is waiting for: the Cirrus
|
|
4183
|
+
// address (`originInstanceUri`) plus the connection id to hand it. The socket
|
|
4184
|
+
// itself is opened by the shared `connectToCirrus`, not here.
|
|
4185
|
+
this.onInstanceReady$.next(payload);
|
|
4186
|
+
}
|
|
4187
|
+
|
|
4188
|
+
function InstanceReservedHandler(msg) {
|
|
4189
|
+
this.store.dispatch(setOrchestrationParameters({
|
|
4190
|
+
instanceName: msg.payload.instanceId,
|
|
4191
|
+
streamRequestId: msg.payload.streamRequestId,
|
|
4192
|
+
eta: msg.payload.eta ?? 150,
|
|
4193
|
+
}));
|
|
4194
|
+
}
|
|
4195
|
+
|
|
4196
|
+
function PlayerCountHandler(msg) {
|
|
4197
|
+
Logger.log(msg);
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4200
|
+
function SSInfoHandler(msg) {
|
|
4201
|
+
this.store.dispatch(updateCirrusInfo({ ssData: msg.data }));
|
|
4202
|
+
return null;
|
|
4203
|
+
}
|
|
4204
|
+
|
|
4205
|
+
const dropCodes = [
|
|
4206
|
+
WSCloseCode_UNKNOWN,
|
|
4207
|
+
WSCloseCode_CIRRUS_PLAYER_DISCONNECTED,
|
|
4208
|
+
WSCloseCode_CIRRUS_ABNORMAL_CLOSURE,
|
|
4209
|
+
WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER,
|
|
4210
|
+
WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR,
|
|
4211
|
+
];
|
|
4212
|
+
function OnCloseHandler(e) {
|
|
4213
|
+
const message = `WebSocket closed: ${JSON.stringify(e.code)} - ${e.reason}`;
|
|
4214
|
+
Logger.warn(message);
|
|
4215
|
+
this.store.dispatch(setCirrusDisconnected());
|
|
4216
|
+
if (dropCodes.includes(e.code)) {
|
|
4217
|
+
this.store.dispatch(dropConnection());
|
|
4218
|
+
}
|
|
4219
|
+
}
|
|
4220
|
+
|
|
4221
|
+
function OnErrorHandler(e) {
|
|
4222
|
+
Logger.warn(`WS error: ${JSON.stringify(e)}`);
|
|
4223
|
+
this.store.dispatch(disconnectStream({
|
|
4224
|
+
reason: DisconnectReason.WebSocketError,
|
|
4225
|
+
message: 'WebSocket Error',
|
|
4226
|
+
}));
|
|
4227
|
+
}
|
|
4228
|
+
|
|
4229
|
+
function OnMessageHandler(e) {
|
|
4230
|
+
const handleData = (data) => {
|
|
4231
|
+
//Logger.colored(...COLOR_CODES.FROM_SIGNALING, data);
|
|
4232
|
+
const msg = JSON.parse(data);
|
|
4233
|
+
this.handleMessage(msg);
|
|
4234
|
+
};
|
|
4235
|
+
if (typeof e.data === 'string') {
|
|
4236
|
+
try {
|
|
4237
|
+
handleData(e.data);
|
|
4238
|
+
}
|
|
4239
|
+
catch (err) {
|
|
4240
|
+
Logger.warn('OnMessageHandler: failed to parse string message', err);
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
else if (e.data instanceof Blob) {
|
|
4244
|
+
e.data
|
|
4245
|
+
.text()
|
|
4246
|
+
.then(handleData)
|
|
4247
|
+
.catch((err) => Logger.warn('OnMessageHandler: failed to parse Blob message', err));
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4250
|
+
|
|
4251
|
+
/**
|
|
4252
|
+
* Open handler of the CIRRUS socket only (`addWsHandlers`), so
|
|
4253
|
+
* `setCirrusConnected` now means what it says. It used to also send
|
|
4254
|
+
* `requestStream` here, which made the flag fire on the orchestration control
|
|
4255
|
+
* socket — a whole provisioning wait (`eta`, ~150 s) before any Cirrus existed.
|
|
4256
|
+
* `requestStream` belongs to the control socket and is sent by
|
|
4257
|
+
* `requestCirrusViaSocket`.
|
|
4258
|
+
*/
|
|
4259
|
+
function OnOpenHandler() {
|
|
4260
|
+
this.store.dispatch(setCirrusConnected());
|
|
4261
|
+
}
|
|
4262
|
+
|
|
4263
|
+
/**
|
|
4264
|
+
* Keeps the max of numbers seen so far; resets to 0 whenever `reset$` emits.
|
|
4265
|
+
*/
|
|
4266
|
+
const keepMaxUntilReset = (reset$) => (source$) => merge(source$, reset$.pipe(map$1(() => null))).pipe(scan((max, value) => (value === null ? 0 : Math.max(max, value)), 0));
|
|
4267
|
+
|
|
4268
|
+
// Reused across every inbound data-channel message — instantiating a
|
|
4269
|
+
// TextDecoder per call adds needless GC churn on a hot path.
|
|
4270
|
+
const utf16Decoder = new TextDecoder('utf-16');
|
|
4271
|
+
function decodeData(anyData) {
|
|
4272
|
+
try {
|
|
4273
|
+
return utf16Decoder.decode(anyData);
|
|
4274
|
+
}
|
|
4275
|
+
catch {
|
|
4276
|
+
return '';
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
|
|
4280
|
+
const dispatchResize = () => {
|
|
4281
|
+
window.dispatchEvent(new Event('resize'));
|
|
4282
|
+
};
|
|
4283
|
+
|
|
4284
|
+
const errorCodes = {
|
|
4285
|
+
code404: 'Your asset is being synced on Metabox servers and is not yet available in Metabox Studio or Metabox Configurator. Please check again later after syncing is complete. ',
|
|
4286
|
+
};
|
|
4287
|
+
function getRtcErrorMessage(code) {
|
|
4288
|
+
if (code === null) {
|
|
4289
|
+
return undefined;
|
|
4290
|
+
}
|
|
4291
|
+
return errorCodes[`code${code}`];
|
|
4292
|
+
}
|
|
4293
|
+
|
|
4294
|
+
const JUMP_THRESHOLD = 0.95;
|
|
4295
|
+
const MAX_TRANSITION_DURATION_MS = 200;
|
|
4296
|
+
/**
|
|
4297
|
+
* Maps a 0..1 stream directly to a 0..100 integer percent stream.
|
|
4298
|
+
* No animation — emits whatever the source emits, rounded and clamped.
|
|
4299
|
+
*/
|
|
4300
|
+
const floatToPercents = () => (source) => source.pipe(map((value) => clampf(0, 100, Math.round(value * 100))));
|
|
4301
|
+
/**
|
|
4302
|
+
* Maps a 0..1 progress stream to a smoothed 0..100 integer percent stream.
|
|
4303
|
+
* Animates between consecutive values over up to 200ms; jumps directly when
|
|
4304
|
+
* the delta exceeds 0.95 (treated as a reset, not a real progression).
|
|
4305
|
+
*/
|
|
4306
|
+
const floatToSmoothPercents = () => (source) => source.pipe(timestamp(), startWith({ value: 0, timestamp: 0 }), pairwise(), concatMap(([prev, curr]) => {
|
|
4307
|
+
const isJump = Math.abs(curr.value - prev.value) > JUMP_THRESHOLD;
|
|
4308
|
+
const dt = prev.timestamp ? curr.timestamp - prev.timestamp : 0;
|
|
4309
|
+
if (isJump || dt === 0) {
|
|
4310
|
+
return of(curr.value);
|
|
4311
|
+
}
|
|
4312
|
+
return smoothTransition(prev.value, curr.value, Math.min(dt, MAX_TRANSITION_DURATION_MS));
|
|
4313
|
+
}), floatToPercents());
|
|
4314
|
+
/**
|
|
4315
|
+
* Smoothly interpolates from `start` to `end` over `duration`ms using the
|
|
4316
|
+
* animation frame scheduler. Completes when the transition finishes.
|
|
4317
|
+
*
|
|
4318
|
+
* NOTE: duplicates `smoothTransition` from `@3dsource/utils`. The utils version
|
|
4319
|
+
* is missing `takeWhile(... < 1, true)` and never completes — which would
|
|
4320
|
+
* deadlock the `concatMap` above. Keep this local copy until utils is fixed.
|
|
4321
|
+
* Not exported: internal to `floatToSmoothPercents`.
|
|
4322
|
+
*/
|
|
4323
|
+
const smoothTransition = (start, end, duration) => {
|
|
4324
|
+
const startTime = animationFrameScheduler.now();
|
|
4325
|
+
return interval(0, animationFrameScheduler).pipe(map(() => clampf(0, 1, (animationFrameScheduler.now() - startTime) / duration)), distinctUntilChanged(), takeWhile((fraction) => fraction < 1, true), map((fraction) => lerp(start, end, fraction)));
|
|
4326
|
+
};
|
|
4327
|
+
|
|
4328
|
+
const getImageFromVideoStream = (takeSizeFrom = 'video', imageOutput, sizes) => {
|
|
4329
|
+
try {
|
|
4330
|
+
const video = document.getElementById(STREAMING_VIDEO_ID);
|
|
4331
|
+
const { canvas, ctx } = getCanvasCached('video');
|
|
4332
|
+
canvas.width =
|
|
4333
|
+
takeSizeFrom === 'video'
|
|
4334
|
+
? sizes?.width || video?.clientWidth || FULL_HD_WIDTH
|
|
4335
|
+
: FULL_HD_WIDTH;
|
|
4336
|
+
canvas.height =
|
|
4337
|
+
takeSizeFrom === 'video'
|
|
4338
|
+
? sizes?.height || video?.clientHeight || FULL_HD_HEIGHT
|
|
4339
|
+
: FULL_HD_HEIGHT;
|
|
4340
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
4341
|
+
return getSnapshot(canvas, canvas.width, imageOutput);
|
|
4342
|
+
}
|
|
4343
|
+
catch {
|
|
4344
|
+
return null;
|
|
4345
|
+
}
|
|
4346
|
+
};
|
|
4347
|
+
|
|
4348
|
+
/**
|
|
4349
|
+
* Generator that cycles through `urls` indefinitely. Each call to
|
|
4350
|
+
* `getActiveUrl(urls)` returns a fresh generator with its own counter,
|
|
4351
|
+
* so concurrent generators (e.g., overlapping reconnect attempts) do
|
|
4352
|
+
* not share state. The counter is initialized once per generator
|
|
4353
|
+
* instance — before any `yield` — so each `.next()` call advances
|
|
4354
|
+
* within that single generator.
|
|
4355
|
+
*/
|
|
4356
|
+
function* getActiveUrl(urls) {
|
|
4357
|
+
let index = 0;
|
|
4358
|
+
while (true) {
|
|
4359
|
+
yield urls[index++ % urls.length];
|
|
4553
4360
|
}
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4361
|
+
}
|
|
4362
|
+
|
|
4363
|
+
class LatencyTimings {
|
|
4364
|
+
constructor() {
|
|
4365
|
+
this.TestStartTimeMs = null;
|
|
4366
|
+
this.UEReceiptTimeMs = null;
|
|
4367
|
+
this.UEEncodeMs = null;
|
|
4368
|
+
this.UECaptureToSendMs = null;
|
|
4369
|
+
this.UETransmissionTimeMs = null;
|
|
4370
|
+
this.BrowserReceiptTimeMs = null;
|
|
4371
|
+
this.FrameDisplayDeltaTimeMs = null;
|
|
4563
4372
|
}
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4373
|
+
Reset() {
|
|
4374
|
+
this.TestStartTimeMs = null;
|
|
4375
|
+
this.UEReceiptTimeMs = null;
|
|
4376
|
+
this.UEEncodeMs = null;
|
|
4377
|
+
this.UECaptureToSendMs = null;
|
|
4378
|
+
this.UETransmissionTimeMs = null;
|
|
4379
|
+
this.BrowserReceiptTimeMs = null;
|
|
4380
|
+
this.FrameDisplayDeltaTimeMs = null;
|
|
4381
|
+
}
|
|
4382
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4383
|
+
SetUETimings(UETimings) {
|
|
4384
|
+
this.UEReceiptTimeMs = UETimings.ReceiptTimeMs;
|
|
4385
|
+
this.UEEncodeMs = UETimings.EncodeMs;
|
|
4386
|
+
this.UECaptureToSendMs = UETimings.CaptureToSendMs;
|
|
4387
|
+
this.UETransmissionTimeMs = UETimings.TransmissionTimeMs;
|
|
4388
|
+
this.BrowserReceiptTimeMs = Date.now();
|
|
4389
|
+
}
|
|
4390
|
+
SetFrameDisplayDeltaTime(DeltaTimeMs) {
|
|
4391
|
+
if (this.FrameDisplayDeltaTimeMs == null) {
|
|
4392
|
+
this.FrameDisplayDeltaTimeMs = Math.round(DeltaTimeMs);
|
|
4572
4393
|
}
|
|
4573
4394
|
}
|
|
4574
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: UnrealEffects, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4575
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: UnrealEffects }); }
|
|
4576
4395
|
}
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4396
|
+
|
|
4397
|
+
function qpToQuality(VideoEncoderQP) {
|
|
4398
|
+
const orangeQP = 26;
|
|
4399
|
+
const redQP = 35;
|
|
4400
|
+
let quality = 'lime';
|
|
4401
|
+
if (VideoEncoderQP > redQP) {
|
|
4402
|
+
quality = 'red';
|
|
4403
|
+
}
|
|
4404
|
+
else if (VideoEncoderQP > orangeQP) {
|
|
4405
|
+
quality = 'orange';
|
|
4406
|
+
}
|
|
4407
|
+
return quality;
|
|
4408
|
+
}
|
|
4409
|
+
|
|
4410
|
+
/**
|
|
4411
|
+
* Base64-encodes a byte array.
|
|
4412
|
+
*
|
|
4413
|
+
* Builds the intermediate binary string in fixed-size chunks instead of
|
|
4414
|
+
* concatenating one character at a time. The naive `bytes.reduce((s, b) => s +
|
|
4415
|
+
* String.fromCharCode(b), '')` is O(n²) (each `+` re-copies the growing string)
|
|
4416
|
+
* and stalls the main thread for a full-resolution frame; chunking keeps it
|
|
4417
|
+
* O(n). The chunk size stays under the argument-count limit of
|
|
4418
|
+
* `String.fromCharCode.apply`/spread.
|
|
4419
|
+
*
|
|
4420
|
+
* @param bytes - The raw bytes to encode.
|
|
4421
|
+
* @returns The Base64 representation of `bytes`.
|
|
4422
|
+
*/
|
|
4423
|
+
function uint8ToBase64(bytes) {
|
|
4424
|
+
const CHUNK_SIZE = 0x8000;
|
|
4425
|
+
let binary = '';
|
|
4426
|
+
for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
|
|
4427
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE));
|
|
4428
|
+
}
|
|
4429
|
+
return btoa(binary);
|
|
4430
|
+
}
|
|
4431
|
+
|
|
4432
|
+
function fromResizeObserver(elem) {
|
|
4433
|
+
return new Observable((subscriber) => {
|
|
4434
|
+
const observer = new window.ResizeObserver((entries) => subscriber.next(entries));
|
|
4435
|
+
observer.observe(elem);
|
|
4436
|
+
return () => observer.unobserve(elem);
|
|
4437
|
+
});
|
|
4438
|
+
}
|
|
4439
|
+
|
|
4440
|
+
/*
|
|
4441
|
+
* Copyright (c) 2026.
|
|
4442
|
+
* 3DSource.com. Sergii Karanda steve@3dsource.com. All Rights Reserved.
|
|
4443
|
+
*/
|
|
4444
|
+
/**
|
|
4445
|
+
* Rounds a number to the nearest even integer (rounding up for odd values).
|
|
4446
|
+
*
|
|
4447
|
+
* Video encoders (H.264/H.265) require even dimensions because chroma subsampling
|
|
4448
|
+
* operates on 2×2 pixel blocks. Odd dimensions cause encoding artifacts or failures.
|
|
4449
|
+
*
|
|
4450
|
+
* @param value - The number to round to the nearest even integer.
|
|
4451
|
+
* @returns The nearest even integer greater than or equal to the input.
|
|
4452
|
+
*
|
|
4453
|
+
* @example
|
|
4454
|
+
* makeEven(1079) // 1080
|
|
4455
|
+
* makeEven(1080) // 1080
|
|
4456
|
+
* makeEven(1081) // 1082
|
|
4457
|
+
*/
|
|
4458
|
+
function makeEven(value) {
|
|
4459
|
+
return Math.floor((value + 1) / 2) * 2;
|
|
4460
|
+
}
|
|
4461
|
+
|
|
4462
|
+
const FULL_HD_PIXELS = FULL_HD_WIDTH * FULL_HD_HEIGHT;
|
|
4463
|
+
const UHD_PIXELS = FULL_HD_WIDTH * 2 * (FULL_HD_HEIGHT * 2);
|
|
4464
|
+
/**
|
|
4465
|
+
* Calculates the optimal stream resolution for studio mode.
|
|
4466
|
+
*
|
|
4467
|
+
* Fits the studio aspect ratio into the container (scaled by pixelRatio), then
|
|
4468
|
+
* caps the total pixel count to a budget (Full HD or 4K) to avoid overloading
|
|
4469
|
+
* the GPU/encoder while maximizing visual quality.
|
|
4470
|
+
*
|
|
4471
|
+
* The pixel budget approach (instead of per-dimension caps) ensures that
|
|
4472
|
+
* non-standard aspect ratios (portrait, ultra-wide, square) utilize available
|
|
4473
|
+
* pixels efficiently — e.g. a tall 1080×1920 stream uses the same pixel budget
|
|
4474
|
+
* as a wide 1920×1080 stream.
|
|
4475
|
+
*
|
|
4476
|
+
* Resolution is always capped at the studio dimensions — never upscaled beyond
|
|
4477
|
+
* what the Unreal scene was designed for.
|
|
4478
|
+
*
|
|
4479
|
+
* @param params.pixelRatio - Device pixel ratio (`window.devicePixelRatio`).
|
|
4480
|
+
* @param params.containerWidth - CSS width of the video container in pixels.
|
|
4481
|
+
* @param params.containerHeight - CSS height of the video container in pixels.
|
|
4482
|
+
* @param params.studioWidth - Target width of the MetaBox Studio scene.
|
|
4483
|
+
* @param params.studioHeight - Target height of the MetaBox Studio scene.
|
|
4484
|
+
* @param params.maxResolution - When `true`, uses 4K pixel budget (3840×2160);
|
|
4485
|
+
* otherwise Full HD (1920×1080). Controlled by `?maxresolution` query param.
|
|
4486
|
+
* @returns An {@link Area} with the calculated `w`, `h`, position (`x`, `y`),
|
|
4487
|
+
* and `scale` factor relative to the container's device-pixel size.
|
|
4488
|
+
*/
|
|
4489
|
+
function getResizeStudioValues(params) {
|
|
4490
|
+
const { pixelRatio, containerWidth, containerHeight, studioWidth, studioHeight, maxResolution, } = params;
|
|
4491
|
+
const maxSpace = {
|
|
4492
|
+
w: containerWidth * pixelRatio,
|
|
4493
|
+
h: containerHeight * pixelRatio,
|
|
4494
|
+
};
|
|
4495
|
+
const maxPixels = maxResolution ? UHD_PIXELS : FULL_HD_PIXELS;
|
|
4496
|
+
const videoSizePixelPerfect = fitIntoRectangle({ x: 0, y: 0, w: studioWidth, h: studioHeight }, { x: 0, y: 0, ...maxSpace });
|
|
4497
|
+
let w = videoSizePixelPerfect.w;
|
|
4498
|
+
let h = videoSizePixelPerfect.h;
|
|
4499
|
+
if (w * h > maxPixels) {
|
|
4500
|
+
// Derive uniform scale factor `s` from: (w*s) * (h*s) = maxPixels
|
|
4501
|
+
// → w * h * s² = maxPixels → s = √(maxPixels / (w * h)).
|
|
4502
|
+
// Applying the same `s` to both dimensions preserves the aspect ratio.
|
|
4503
|
+
const scaleFactor = Math.sqrt(maxPixels / (w * h));
|
|
4504
|
+
w = w * scaleFactor;
|
|
4505
|
+
h = h * scaleFactor;
|
|
4506
|
+
}
|
|
4507
|
+
const out = fitIntoRectangle(videoSizePixelPerfect, {
|
|
4508
|
+
x: videoSizePixelPerfect.x,
|
|
4509
|
+
y: videoSizePixelPerfect.y,
|
|
4510
|
+
w,
|
|
4511
|
+
h,
|
|
4512
|
+
});
|
|
4513
|
+
out.w = makeEven(out.w);
|
|
4514
|
+
out.h = makeEven(out.h);
|
|
4515
|
+
return out;
|
|
4516
|
+
}
|
|
4517
|
+
/**
|
|
4518
|
+
* Calculates the optimal stream resolution for configurator (non-studio) mode.
|
|
4519
|
+
*
|
|
4520
|
+
* Uses the full container area (scaled by pixelRatio) as the stream resolution,
|
|
4521
|
+
* then caps total pixel count to a budget (Full HD or 4K) to prevent excessive
|
|
4522
|
+
* GPU/encoder load while preserving the container's aspect ratio.
|
|
4523
|
+
*
|
|
4524
|
+
* Unlike {@link getResizeStudioValues}, there is no fixed studio resolution to
|
|
4525
|
+
* fit into — the container itself defines the aspect ratio, and the stream fills
|
|
4526
|
+
* it entirely (no letterboxing).
|
|
4527
|
+
*
|
|
4528
|
+
* @param params.pixelRatio - Device pixel ratio (`window.devicePixelRatio`).
|
|
4529
|
+
* @param params.containerWidth - CSS width of the video container in pixels.
|
|
4530
|
+
* @param params.containerHeight - CSS height of the video container in pixels.
|
|
4531
|
+
* @param params.maxResolution - When `true`, uses 4K pixel budget (3840×2160);
|
|
4532
|
+
* otherwise Full HD (1920×1080). Controlled by `?maxresolution` query param.
|
|
4533
|
+
* @returns An {@link Area} with the calculated `w`, `h`, position (`x`, `y`),
|
|
4534
|
+
* and `scale` factor (1 when no downscale needed, <1 when capped).
|
|
4535
|
+
*/
|
|
4536
|
+
function getResizeValuesUniversal(params) {
|
|
4537
|
+
const { pixelRatio, containerWidth, containerHeight, maxResolution } = params;
|
|
4538
|
+
const maxSpace = {
|
|
4539
|
+
w: containerWidth * pixelRatio,
|
|
4540
|
+
h: containerHeight * pixelRatio,
|
|
4541
|
+
};
|
|
4542
|
+
const maxPixels = maxResolution ? UHD_PIXELS : FULL_HD_PIXELS;
|
|
4543
|
+
let w = maxSpace.w;
|
|
4544
|
+
let h = maxSpace.h;
|
|
4545
|
+
if (w * h > maxPixels) {
|
|
4546
|
+
// Derive uniform scale factor `s` from: (w*s) * (h*s) = maxPixels
|
|
4547
|
+
// → w * h * s² = maxPixels → s = √(maxPixels / (w * h)).
|
|
4548
|
+
// Applying the same `s` to both dimensions preserves the aspect ratio.
|
|
4549
|
+
const scaleFactor = Math.sqrt(maxPixels / (w * h));
|
|
4550
|
+
w = w * scaleFactor;
|
|
4551
|
+
h = h * scaleFactor;
|
|
4552
|
+
}
|
|
4553
|
+
return { w: makeEven(w), h: makeEven(h), x: 0, y: 0, scale: w / maxSpace.w };
|
|
4554
|
+
}
|
|
4555
|
+
|
|
4556
|
+
/**
|
|
4557
|
+
* Picks the class that carries the decoration of a UI part: the host class when
|
|
4558
|
+
* one is supplied, the built-in one otherwise. An empty string counts as "not
|
|
4559
|
+
* supplied" so a part never ends up with no styling at all.
|
|
4560
|
+
*/
|
|
4561
|
+
function resolveSkin(classes, part, fallback) {
|
|
4562
|
+
const custom = classes[part];
|
|
4563
|
+
if (custom === undefined || custom === '') {
|
|
4564
|
+
return fallback;
|
|
4565
|
+
}
|
|
4566
|
+
return custom;
|
|
4567
|
+
}
|
|
4580
4568
|
|
|
4581
4569
|
class AFKService extends SubService {
|
|
4582
4570
|
constructor() {
|
|
@@ -4692,10 +4680,10 @@ class AFKService extends SubService {
|
|
|
4692
4680
|
}
|
|
4693
4681
|
}, 1000);
|
|
4694
4682
|
}
|
|
4695
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
4696
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
4683
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AFKService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4684
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AFKService, providedIn: 'root' }); }
|
|
4697
4685
|
}
|
|
4698
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
4686
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AFKService, decorators: [{
|
|
4699
4687
|
type: Injectable,
|
|
4700
4688
|
args: [{ providedIn: 'root' }]
|
|
4701
4689
|
}], ctorParameters: () => [] });
|
|
@@ -4745,10 +4733,10 @@ class LoadingProgressService {
|
|
|
4745
4733
|
// `switchMap` cancels the inner subscription on every visibility change
|
|
4746
4734
|
// so stale emissions can't leak through.
|
|
4747
4735
|
#boundedProgress$;
|
|
4748
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
4749
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
4736
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: LoadingProgressService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4737
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: LoadingProgressService, providedIn: 'root' }); }
|
|
4750
4738
|
}
|
|
4751
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
4739
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: LoadingProgressService, decorators: [{
|
|
4752
4740
|
type: Injectable,
|
|
4753
4741
|
args: [{ providedIn: 'root' }]
|
|
4754
4742
|
}] });
|
|
@@ -4834,10 +4822,10 @@ class FreezeFrameService extends SubService {
|
|
|
4834
4822
|
progress: 1,
|
|
4835
4823
|
}));
|
|
4836
4824
|
}
|
|
4837
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
4838
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
4825
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: FreezeFrameService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4826
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: FreezeFrameService, providedIn: 'root' }); }
|
|
4839
4827
|
}
|
|
4840
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
4828
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: FreezeFrameService, decorators: [{
|
|
4841
4829
|
type: Injectable,
|
|
4842
4830
|
args: [{ providedIn: 'root' }]
|
|
4843
4831
|
}], ctorParameters: () => [] });
|
|
@@ -5050,10 +5038,10 @@ class AggregatorService extends SubService {
|
|
|
5050
5038
|
Logger.warn(`unrecognized data received, packet ID ${view[0]}`);
|
|
5051
5039
|
}
|
|
5052
5040
|
}
|
|
5053
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
5054
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
5041
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AggregatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
5042
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AggregatorService, providedIn: 'root' }); }
|
|
5055
5043
|
}
|
|
5056
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
5044
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AggregatorService, decorators: [{
|
|
5057
5045
|
type: Injectable,
|
|
5058
5046
|
args: [{ providedIn: 'root' }]
|
|
5059
5047
|
}], ctorParameters: () => [] });
|
|
@@ -5199,7 +5187,7 @@ class ConsoleExtensionsService extends SubService {
|
|
|
5199
5187
|
// `store.select` replays the current value synchronously, so if it already
|
|
5200
5188
|
// equals `target` this resolves immediately; otherwise it waits for the
|
|
5201
5189
|
// transition or falls back to `false` after `timeoutMs`.
|
|
5202
|
-
return firstValueFrom(this.store.select(unrealFeature.selectDataChannelConnected).pipe(filter((connected) => connected === target), take(1), map(() => true), timeout$1({ first: timeoutMs, with: () => of(false) })));
|
|
5190
|
+
return firstValueFrom(this.store.select(unrealFeature.selectDataChannelConnected).pipe(filter((connected) => connected === target), take(1), map$1(() => true), timeout$1({ first: timeoutMs, with: () => of(false) })));
|
|
5203
5191
|
}
|
|
5204
5192
|
sleep(ms) {
|
|
5205
5193
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -5214,10 +5202,10 @@ class ConsoleExtensionsService extends SubService {
|
|
|
5214
5202
|
Logger.info('emitCommand(command) => send command to Unreal Engine.');
|
|
5215
5203
|
Logger.info('stressTest({iterations}) => hammer reconnect: repeatedly drop DC/PC and assert recovery.');
|
|
5216
5204
|
}
|
|
5217
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
5218
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
5205
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ConsoleExtensionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
5206
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ConsoleExtensionsService, providedIn: 'root' }); }
|
|
5219
5207
|
}
|
|
5220
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
5208
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ConsoleExtensionsService, decorators: [{
|
|
5221
5209
|
type: Injectable,
|
|
5222
5210
|
args: [{ providedIn: 'root' }]
|
|
5223
5211
|
}], ctorParameters: () => [] });
|
|
@@ -5296,8 +5284,8 @@ class InputService extends SubService {
|
|
|
5296
5284
|
if (this.unrealConfig?.playwright) {
|
|
5297
5285
|
return;
|
|
5298
5286
|
}
|
|
5299
|
-
const visibilityHiddenTrigger$ = fromEvent(document, 'visibilitychange').pipe(map
|
|
5300
|
-
const visibilityVisibleTrigger$ = fromEvent(document, 'visibilitychange').pipe(map
|
|
5287
|
+
const visibilityHiddenTrigger$ = fromEvent(document, 'visibilitychange').pipe(map(() => document.visibilityState === 'hidden'), filter(Truthy));
|
|
5288
|
+
const visibilityVisibleTrigger$ = fromEvent(document, 'visibilitychange').pipe(map(() => document.visibilityState === 'visible'), filter(Truthy), startWith(true));
|
|
5301
5289
|
this.deactivatedVideoTrigger$ = merge(this.disconnect$, this.reInit$, visibilityHiddenTrigger$, this.destroy$);
|
|
5302
5290
|
merge(visibilityVisibleTrigger$, this.store.select(unrealFeature.selectViewportReady).pipe(filter(Truthy)))
|
|
5303
5291
|
.pipe(filter(() => !!this.video), takeUntil$1(this.destroy$))
|
|
@@ -5718,10 +5706,10 @@ class InputService extends SubService {
|
|
|
5718
5706
|
this.sendInputData(data.buffer);
|
|
5719
5707
|
});
|
|
5720
5708
|
}
|
|
5721
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
5722
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
5709
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: InputService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
5710
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: InputService, providedIn: 'root' }); }
|
|
5723
5711
|
}
|
|
5724
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
5712
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: InputService, decorators: [{
|
|
5725
5713
|
type: Injectable,
|
|
5726
5714
|
args: [{ providedIn: 'root' }]
|
|
5727
5715
|
}], ctorParameters: () => [] });
|
|
@@ -5751,7 +5739,7 @@ class StreamStatusTelemetryService extends SubService {
|
|
|
5751
5739
|
return this.videoService.videoStats$.pipe(throttleTime(5000, asyncScheduler, {
|
|
5752
5740
|
leading: true,
|
|
5753
5741
|
trailing: true,
|
|
5754
|
-
}), withLatestFrom(this.store.select(unrealFeature.selectAwsInstance), signalingReadyTime$, viewPortReadyTime$), map
|
|
5742
|
+
}), withLatestFrom(this.store.select(unrealFeature.selectAwsInstance), signalingReadyTime$, viewPortReadyTime$), map(([data, signalingServer, signalingReadyTime, viewPortReadyTime,]) => this.mapEventData({
|
|
5755
5743
|
data,
|
|
5756
5744
|
signalingServer,
|
|
5757
5745
|
startTimeUtc,
|
|
@@ -5767,7 +5755,7 @@ class StreamStatusTelemetryService extends SubService {
|
|
|
5767
5755
|
* Used with `withLatestFrom` so events carry a stable duration for the whole session.
|
|
5768
5756
|
*/
|
|
5769
5757
|
readyTimeSince(readyState$, startedAt) {
|
|
5770
|
-
return readyState$.pipe(filter(Boolean), take$1(1), map
|
|
5758
|
+
return readyState$.pipe(filter(Boolean), take$1(1), map(() => Date.now() - startedAt), startWith$1(undefined));
|
|
5771
5759
|
}
|
|
5772
5760
|
mapEventData(params) {
|
|
5773
5761
|
const { data, signalingServer, startTimeUtc, signalingReadyTime, viewPortReadyTime, } = params;
|
|
@@ -5790,10 +5778,10 @@ class StreamStatusTelemetryService extends SubService {
|
|
|
5790
5778
|
VideoEncoderQP,
|
|
5791
5779
|
};
|
|
5792
5780
|
}
|
|
5793
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
5794
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
5781
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StreamStatusTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
5782
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StreamStatusTelemetryService, providedIn: 'root' }); }
|
|
5795
5783
|
}
|
|
5796
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
5784
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StreamStatusTelemetryService, decorators: [{
|
|
5797
5785
|
type: Injectable,
|
|
5798
5786
|
args: [{ providedIn: 'root' }]
|
|
5799
5787
|
}], ctorParameters: () => [] });
|
|
@@ -5918,7 +5906,7 @@ class StreamStatusTelemetryService2 {
|
|
|
5918
5906
|
* `startWith(null)` seed makes `withLatestFrom` on this stream safe even
|
|
5919
5907
|
* before the first video stat arrives (common at data-channel-open).
|
|
5920
5908
|
*/
|
|
5921
|
-
this.selectedPair$ = this.videoService.videoStats$.pipe(map((s) => s.aggregatedStats.selectedPair ?? null), scan((acc, curr) => curr ?? acc, null), startWith(null));
|
|
5909
|
+
this.selectedPair$ = this.videoService.videoStats$.pipe(map$1((s) => s.aggregatedStats.selectedPair ?? null), scan((acc, curr) => curr ?? acc, null), startWith(null));
|
|
5922
5910
|
/**
|
|
5923
5911
|
* Correlation-matched Observable: emits once when the first `loopBack`
|
|
5924
5912
|
* command's corresponding `command completed` action fires. Built from
|
|
@@ -5988,7 +5976,7 @@ class StreamStatusTelemetryService2 {
|
|
|
5988
5976
|
// never ran or its cache expired.
|
|
5989
5977
|
{
|
|
5990
5978
|
id: 'region-resolved',
|
|
5991
|
-
event: this.actions$.pipe(ofType(regionResolved), take(1), map(({ region, pingsMs }) => ({
|
|
5979
|
+
event: this.actions$.pipe(ofType(regionResolved), take(1), map$1(({ region, pingsMs }) => ({
|
|
5992
5980
|
region,
|
|
5993
5981
|
pingsMs: pingsMs.length ? `${pingsMs.join(', ')} ms` : null,
|
|
5994
5982
|
}))),
|
|
@@ -6034,7 +6022,7 @@ class StreamStatusTelemetryService2 {
|
|
|
6034
6022
|
// is shipped under — see {@link SessionState.wireId}.
|
|
6035
6023
|
{
|
|
6036
6024
|
id: 'ssinfo-received',
|
|
6037
|
-
event: this.store.select(unrealFeature.selectSsData).pipe(filter$1(Boolean), take(1), withLatestFrom$1(this.store.select(unrealFeature.selectAwsInstance), this.store.select(selectCirrusConnectionId)), map(([ssData, aws, connectionId]) => ({
|
|
6025
|
+
event: this.store.select(unrealFeature.selectSsData).pipe(filter$1(Boolean), take(1), withLatestFrom$1(this.store.select(unrealFeature.selectAwsInstance), this.store.select(selectCirrusConnectionId)), map$1(([ssData, aws, connectionId]) => ({
|
|
6038
6026
|
client: aws.wsUrl,
|
|
6039
6027
|
instanceName: aws.instanceName,
|
|
6040
6028
|
...ssData,
|
|
@@ -6079,7 +6067,7 @@ class StreamStatusTelemetryService2 {
|
|
|
6079
6067
|
id: 'datachannel-connected',
|
|
6080
6068
|
event: this.store
|
|
6081
6069
|
.select(unrealFeature.selectDataChannelConnected)
|
|
6082
|
-
.pipe(filter$1(Boolean), take(1), map(() => ({
|
|
6070
|
+
.pipe(filter$1(Boolean), take(1), map$1(() => ({
|
|
6083
6071
|
iceCandidateCount: this.session?.iceCandidateCount ?? 0,
|
|
6084
6072
|
handshakeFlow: this.session?.handshakeFlow ?? null,
|
|
6085
6073
|
}))),
|
|
@@ -6111,7 +6099,7 @@ class StreamStatusTelemetryService2 {
|
|
|
6111
6099
|
// fired at Unreal before the viewport was ready.
|
|
6112
6100
|
{
|
|
6113
6101
|
id: 'viewport-ready',
|
|
6114
|
-
event: this.store.select(unrealFeature.selectViewportReady).pipe(filter$1(Boolean), take(1), withLatestFrom$1(this.selectedPair$), map(([, selectedPair]) => ({
|
|
6102
|
+
event: this.store.select(unrealFeature.selectViewportReady).pipe(filter$1(Boolean), take(1), withLatestFrom$1(this.selectedPair$), map$1(([, selectedPair]) => ({
|
|
6115
6103
|
localCandidate: fmtCandidate(selectedPair?.local),
|
|
6116
6104
|
remoteCandidate: fmtCandidate(selectedPair?.remote),
|
|
6117
6105
|
commandCount: this.session?.commandCount ?? 0,
|
|
@@ -6895,10 +6883,10 @@ class StreamStatusTelemetryService2 {
|
|
|
6895
6883
|
emit(event, data) {
|
|
6896
6884
|
this.store.dispatch(trackTelemetryData({ event, data }));
|
|
6897
6885
|
}
|
|
6898
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
6899
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
6886
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StreamStatusTelemetryService2, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
6887
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StreamStatusTelemetryService2, providedIn: 'root' }); }
|
|
6900
6888
|
}
|
|
6901
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
6889
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StreamStatusTelemetryService2, decorators: [{
|
|
6902
6890
|
type: Injectable,
|
|
6903
6891
|
args: [{ providedIn: 'root' }]
|
|
6904
6892
|
}], ctorParameters: () => [] });
|
|
@@ -7034,10 +7022,10 @@ class AnalyticsService extends SubService {
|
|
|
7034
7022
|
Logger.log('Analytics Event:', name, params);
|
|
7035
7023
|
this.store.dispatch(saveAnalyticsEvent({ event: { name, params } }));
|
|
7036
7024
|
}
|
|
7037
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7038
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
7025
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AnalyticsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
7026
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AnalyticsService, providedIn: 'root' }); }
|
|
7039
7027
|
}
|
|
7040
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7028
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AnalyticsService, decorators: [{
|
|
7041
7029
|
type: Injectable,
|
|
7042
7030
|
args: [{ providedIn: 'root' }]
|
|
7043
7031
|
}], ctorParameters: () => [] });
|
|
@@ -7404,10 +7392,10 @@ class FpsMonitorService extends SubService {
|
|
|
7404
7392
|
}
|
|
7405
7393
|
return { fps: fps / len, bitrate: bitrate / len, qp: qp / len };
|
|
7406
7394
|
}
|
|
7407
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7408
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
7395
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: FpsMonitorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
7396
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: FpsMonitorService, providedIn: 'root' }); }
|
|
7409
7397
|
}
|
|
7410
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7398
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: FpsMonitorService, decorators: [{
|
|
7411
7399
|
type: Injectable,
|
|
7412
7400
|
args: [{ providedIn: 'root' }]
|
|
7413
7401
|
}], ctorParameters: () => [] });
|
|
@@ -7421,19 +7409,34 @@ class AfkTimeoutModalComponent {
|
|
|
7421
7409
|
this.afkCountdown = this.#store.selectSignal(unrealFeature.selectAfkCountdown);
|
|
7422
7410
|
this.isShowAfkPopup = computed(() => this.isAfkTimerVisible() && this.showAfkTimeoutPopup(), /* @ts-ignore */
|
|
7423
7411
|
...(ngDevMode ? [{ debugName: "isShowAfkPopup" }] : /* istanbul ignore next */ []));
|
|
7412
|
+
this.uiClasses = input({}, /* @ts-ignore */
|
|
7413
|
+
...(ngDevMode ? [{ debugName: "uiClasses" }] : /* istanbul ignore next */ []));
|
|
7414
|
+
this.contentTemplate = input(null, /* @ts-ignore */
|
|
7415
|
+
...(ngDevMode ? [{ debugName: "contentTemplate" }] : /* istanbul ignore next */ []));
|
|
7416
|
+
this.scrimSkin = computed(() => resolveSkin(this.uiClasses(), 'afkScrim', 'afk-scrim'), /* @ts-ignore */
|
|
7417
|
+
...(ngDevMode ? [{ debugName: "scrimSkin" }] : /* istanbul ignore next */ []));
|
|
7418
|
+
this.cardSkin = computed(() => resolveSkin(this.uiClasses(), 'afkCard', 'resume-box'), /* @ts-ignore */
|
|
7419
|
+
...(ngDevMode ? [{ debugName: "cardSkin" }] : /* istanbul ignore next */ []));
|
|
7420
|
+
this.buttonSkin = computed(() => resolveSkin(this.uiClasses(), 'afkButton', 'afk-button'), /* @ts-ignore */
|
|
7421
|
+
...(ngDevMode ? [{ debugName: "buttonSkin" }] : /* istanbul ignore next */ []));
|
|
7422
|
+
// Created once: identity stays stable, reactivity lives in the signals.
|
|
7423
|
+
this.slotContext = {
|
|
7424
|
+
countdown: this.afkCountdown,
|
|
7425
|
+
reset: () => this.resetAfk(),
|
|
7426
|
+
};
|
|
7424
7427
|
}
|
|
7425
7428
|
#store;
|
|
7426
7429
|
#afkService;
|
|
7427
7430
|
resetAfk() {
|
|
7428
7431
|
this.#afkService.reset();
|
|
7429
7432
|
}
|
|
7430
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7431
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
7433
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AfkTimeoutModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7434
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: AfkTimeoutModalComponent, isStandalone: true, selector: "app-afk-timeout-modal", inputs: { uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null }, contentTemplate: { classPropertyName: "contentTemplate", publicName: "contentTemplate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isShowAfkPopup()) {\n <div\n (click)=\"resetAfk()\"\n [class]=\"scrimSkin()\"\n class=\"afk-anchor clickableState\"\n id=\"videoPlayOverlay\"\n >\n <div [class]=\"cardSkin()\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else {\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"afkCountdown()\" 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 <app-unreal-button\n [fullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n [class]=\"buttonSkin()\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </app-unreal-button>\n }\n </div>\n </div>\n}\n", styles: [".afk-anchor{position:absolute;z-index:30;top:0;width:100%;height:100%}.afk-scrim{font-size:1.8em;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));background-color:var(--unreal-surface-scrim, rgba(100, 100, 100, .7))}.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(--unreal-radius-card, var(--src-border-rounded-parent, 8px));background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));box-shadow:var(--unreal-shadow-card, 0 26px 80px 0 rgba(0, 0, 0, .2), 0 0 1px 0 rgba(0, 0, 0, .2))}.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}.text-number{color:var(--unreal-text-main, #1f2937);text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));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(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--unreal-text-secondary, var(--src-text-body-secondary, #6b7280))}.resume-box__text .resume-box__heading{color:var(--unreal-text-main, var(--src-text-body-main, #1f2937));text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7432
7435
|
}
|
|
7433
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7436
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: AfkTimeoutModalComponent, decorators: [{
|
|
7434
7437
|
type: Component,
|
|
7435
|
-
args: [{ selector: 'app-afk-timeout-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealButtonComponent], template: "@if (isShowAfkPopup()) {\n <div
|
|
7436
|
-
}] });
|
|
7438
|
+
args: [{ selector: 'app-afk-timeout-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealButtonComponent, NgTemplateOutlet], template: "@if (isShowAfkPopup()) {\n <div\n (click)=\"resetAfk()\"\n [class]=\"scrimSkin()\"\n class=\"afk-anchor clickableState\"\n id=\"videoPlayOverlay\"\n >\n <div [class]=\"cardSkin()\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else {\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"afkCountdown()\" 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 <app-unreal-button\n [fullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n [class]=\"buttonSkin()\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </app-unreal-button>\n }\n </div>\n </div>\n}\n", styles: [".afk-anchor{position:absolute;z-index:30;top:0;width:100%;height:100%}.afk-scrim{font-size:1.8em;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));background-color:var(--unreal-surface-scrim, rgba(100, 100, 100, .7))}.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(--unreal-radius-card, var(--src-border-rounded-parent, 8px));background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));box-shadow:var(--unreal-shadow-card, 0 26px 80px 0 rgba(0, 0, 0, .2), 0 0 1px 0 rgba(0, 0, 0, .2))}.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}.text-number{color:var(--unreal-text-main, #1f2937);text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));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(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--unreal-text-secondary, var(--src-text-body-secondary, #6b7280))}.resume-box__text .resume-box__heading{color:var(--unreal-text-main, var(--src-text-body-main, #1f2937));text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"] }]
|
|
7439
|
+
}], propDecorators: { uiClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "uiClasses", required: false }] }], contentTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentTemplate", required: false }] }] } });
|
|
7437
7440
|
|
|
7438
7441
|
class FreezeFrameComponent {
|
|
7439
7442
|
constructor() {
|
|
@@ -7442,10 +7445,10 @@ class FreezeFrameComponent {
|
|
|
7442
7445
|
this.combinedFreeze = this.#store.selectSignal(selectFreezeFrameCombinedDataUrl);
|
|
7443
7446
|
}
|
|
7444
7447
|
#store;
|
|
7445
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7446
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
7448
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: FreezeFrameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7449
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: FreezeFrameComponent, isStandalone: true, selector: "app-freeze-frame", ngImport: i0, template: "@let image = combinedFreeze();\n@let progress = freezeFrameProgressMessageFromVideo();\n\n<div class=\"freeze-images\">\n @if (image) {\n <img [src]=\"image\" class=\"videoStillImage\" alt=\"freezeFrameImage\" />\n }\n\n @if (progress) {\n <div class=\"progress-status\">\n {{ progress }}\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 }); }
|
|
7447
7450
|
}
|
|
7448
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7451
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: FreezeFrameComponent, decorators: [{
|
|
7449
7452
|
type: Component,
|
|
7450
7453
|
args: [{ selector: 'app-freeze-frame', changeDetection: ChangeDetectionStrategy.OnPush, template: "@let image = combinedFreeze();\n@let progress = freezeFrameProgressMessageFromVideo();\n\n<div class=\"freeze-images\">\n @if (image) {\n <img [src]=\"image\" class=\"videoStillImage\" alt=\"freezeFrameImage\" />\n }\n\n @if (progress) {\n <div class=\"progress-status\">\n {{ progress }}\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"] }]
|
|
7451
7454
|
}] });
|
|
@@ -7460,12 +7463,12 @@ class ImageLoadingSrcComponent {
|
|
|
7460
7463
|
loadingImageError() {
|
|
7461
7464
|
this.#store.dispatch(setLoadingImageSrc({ src: '' }));
|
|
7462
7465
|
}
|
|
7463
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7464
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
7466
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ImageLoadingSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7467
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: ImageLoadingSrcComponent, isStandalone: true, selector: "app-image-loading-src", ngImport: i0, template: "@let image = imageLoadingSrc();\n@if (isLoaderScreenVisible()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (image) {\n <img\n (error)=\"loadingImageError()\"\n [ngSrc]=\"image\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\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 .back{background-color:var(--unreal-surface-screen, #fff)}\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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7465
7468
|
}
|
|
7466
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7469
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ImageLoadingSrcComponent, decorators: [{
|
|
7467
7470
|
type: Component,
|
|
7468
|
-
args: [{ selector: 'app-image-loading-src', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgOptimizedImage], template: "@let image = imageLoadingSrc();\n@if (isLoaderScreenVisible()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (image) {\n <img\n (error)=\"loadingImageError()\"\n [ngSrc]=\"image\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\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 .back{background-color
|
|
7471
|
+
args: [{ selector: 'app-image-loading-src', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgOptimizedImage], template: "@let image = imageLoadingSrc();\n@if (isLoaderScreenVisible()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (image) {\n <img\n (error)=\"loadingImageError()\"\n [ngSrc]=\"image\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\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 .back{background-color:var(--unreal-surface-screen, #fff)}\n"] }]
|
|
7469
7472
|
}] });
|
|
7470
7473
|
|
|
7471
7474
|
class StatGraphComponent {
|
|
@@ -7546,10 +7549,10 @@ class StatGraphComponent {
|
|
|
7546
7549
|
}
|
|
7547
7550
|
this.draw();
|
|
7548
7551
|
}
|
|
7549
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7550
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.
|
|
7552
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StatGraphComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7553
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.1", type: StatGraphComponent, isStandalone: true, selector: "app-stat-graph", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, stat: { classPropertyName: "stat", publicName: "stat", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, tickStep: { classPropertyName: "tickStep", publicName: "tickStep", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "resize()" } }, viewQueries: [{ propertyName: "graph", first: true, predicate: ["graph"], descendants: true, isSignal: 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 }); }
|
|
7551
7554
|
}
|
|
7552
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7555
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: StatGraphComponent, decorators: [{
|
|
7553
7556
|
type: Component,
|
|
7554
7557
|
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"] }]
|
|
7555
7558
|
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], stat: [{ type: i0.Input, args: [{ isSignal: true, alias: "stat", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], tickStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "tickStep", required: false }] }], graph: [{ type: i0.ViewChild, args: ['graph', { isSignal: true }] }], resize: [{
|
|
@@ -7557,6 +7560,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
7557
7560
|
args: ['window:resize']
|
|
7558
7561
|
}] } });
|
|
7559
7562
|
|
|
7563
|
+
class UnrealResumeSlotDirective {
|
|
7564
|
+
constructor() {
|
|
7565
|
+
this.template = inject(TemplateRef);
|
|
7566
|
+
}
|
|
7567
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
7568
|
+
return true;
|
|
7569
|
+
}
|
|
7570
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealResumeSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
7571
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.1", type: UnrealResumeSlotDirective, isStandalone: true, selector: "ng-template[unrealResumeSlot]", ngImport: i0 }); }
|
|
7572
|
+
}
|
|
7573
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealResumeSlotDirective, decorators: [{
|
|
7574
|
+
type: Directive,
|
|
7575
|
+
args: [{ selector: 'ng-template[unrealResumeSlot]' }]
|
|
7576
|
+
}] });
|
|
7577
|
+
class UnrealAfkSlotDirective {
|
|
7578
|
+
constructor() {
|
|
7579
|
+
this.template = inject(TemplateRef);
|
|
7580
|
+
}
|
|
7581
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
7582
|
+
return true;
|
|
7583
|
+
}
|
|
7584
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealAfkSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
7585
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.1", type: UnrealAfkSlotDirective, isStandalone: true, selector: "ng-template[unrealAfkSlot]", ngImport: i0 }); }
|
|
7586
|
+
}
|
|
7587
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealAfkSlotDirective, decorators: [{
|
|
7588
|
+
type: Directive,
|
|
7589
|
+
args: [{ selector: 'ng-template[unrealAfkSlot]' }]
|
|
7590
|
+
}] });
|
|
7591
|
+
class UnrealStatusSlotDirective {
|
|
7592
|
+
constructor() {
|
|
7593
|
+
this.template = inject(TemplateRef);
|
|
7594
|
+
}
|
|
7595
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
7596
|
+
return true;
|
|
7597
|
+
}
|
|
7598
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealStatusSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
7599
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.1", type: UnrealStatusSlotDirective, isStandalone: true, selector: "ng-template[unrealStatusSlot]", ngImport: i0 }); }
|
|
7600
|
+
}
|
|
7601
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealStatusSlotDirective, decorators: [{
|
|
7602
|
+
type: Directive,
|
|
7603
|
+
args: [{ selector: 'ng-template[unrealStatusSlot]' }]
|
|
7604
|
+
}] });
|
|
7605
|
+
|
|
7560
7606
|
class UnrealSpinnerComponent {
|
|
7561
7607
|
constructor() {
|
|
7562
7608
|
this.size = input(32, /* @ts-ignore */
|
|
@@ -7575,10 +7621,10 @@ class UnrealSpinnerComponent {
|
|
|
7575
7621
|
this.viewBox = computed(() => `0 0 ${this.size()} ${this.size()}`, /* @ts-ignore */
|
|
7576
7622
|
...(ngDevMode ? [{ debugName: "viewBox" }] : /* istanbul ignore next */ []));
|
|
7577
7623
|
}
|
|
7578
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7579
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
7624
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealSpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7625
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: UnrealSpinnerComponent, isStandalone: true, selector: "app-unreal-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, strokeColor: { classPropertyName: "strokeColor", publicName: "strokeColor", isSignal: true, isRequired: false, transformFunction: null }, testID: { classPropertyName: "testID", publicName: "data-testid", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n class=\"unreal-spinner-container\"\n role=\"status\"\n>\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n [attr.viewBox]=\"viewBox()\"\n [style.--unrealSpinnerSize]=\"size()\"\n class=\"unreal-spinner unreal-spinner--animation\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n style=\"transform: rotate(-90deg)\"\n aria-hidden=\"true\"\n >\n <circle\n [attr.r]=\"radius()\"\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.stroke]=\"strokeColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\"\n stroke-linecap=\"round\"\n ></circle>\n </svg>\n</div>\n", styles: [":host{display:block}.unreal-spinner-container{position:relative;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.unreal-spinner{position:relative;display:inline-block}.unreal-spinner--animation{animation:1.4s linear 0s infinite normal none running unrealSpinnerRotate}.unreal-spinner--animation circle{transform-origin:50% 50%;stroke-dasharray:calc(var(--unrealSpinnerSize) * 2 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:0;animation:1.6s ease-in-out 0s infinite normal none running unrealSpinnerDash}@keyframes unrealSpinnerRotate{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes unrealSpinnerDash{0%{stroke-dasharray:1px,calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:0}50%{stroke-dasharray:calc(var(--unrealSpinnerSize) * 2.5 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:calc(var(--unrealSpinnerSize) * .4 * -1px)}to{stroke-dasharray:calc(var(--unrealSpinnerSize) * 2.5 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:calc(var(--unrealSpinnerSize) * 3 * -1px)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7580
7626
|
}
|
|
7581
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7627
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealSpinnerComponent, decorators: [{
|
|
7582
7628
|
type: Component,
|
|
7583
7629
|
args: [{ selector: 'app-unreal-spinner', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n class=\"unreal-spinner-container\"\n role=\"status\"\n>\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n [attr.viewBox]=\"viewBox()\"\n [style.--unrealSpinnerSize]=\"size()\"\n class=\"unreal-spinner unreal-spinner--animation\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n style=\"transform: rotate(-90deg)\"\n aria-hidden=\"true\"\n >\n <circle\n [attr.r]=\"radius()\"\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.stroke]=\"strokeColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\"\n stroke-linecap=\"round\"\n ></circle>\n </svg>\n</div>\n", styles: [":host{display:block}.unreal-spinner-container{position:relative;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.unreal-spinner{position:relative;display:inline-block}.unreal-spinner--animation{animation:1.4s linear 0s infinite normal none running unrealSpinnerRotate}.unreal-spinner--animation circle{transform-origin:50% 50%;stroke-dasharray:calc(var(--unrealSpinnerSize) * 2 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:0;animation:1.6s ease-in-out 0s infinite normal none running unrealSpinnerDash}@keyframes unrealSpinnerRotate{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes unrealSpinnerDash{0%{stroke-dasharray:1px,calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:0}50%{stroke-dasharray:calc(var(--unrealSpinnerSize) * 2.5 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:calc(var(--unrealSpinnerSize) * .4 * -1px)}to{stroke-dasharray:calc(var(--unrealSpinnerSize) * 2.5 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:calc(var(--unrealSpinnerSize) * 3 * -1px)}}\n"] }]
|
|
7584
7630
|
}], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], strokeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeColor", required: false }] }], testID: [{ type: i0.Input, args: [{ isSignal: true, alias: "data-testid", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
@@ -7593,16 +7639,29 @@ class UnrealStatusComponent {
|
|
|
7593
7639
|
this.showLoadingProgress = this.#store.selectSignal(unrealFeature.selectShowLoadingProgress);
|
|
7594
7640
|
this.isShowLoadingPercentsPopup = computed(() => this.showLoader() && this.showLoadingProgress(), /* @ts-ignore */
|
|
7595
7641
|
...(ngDevMode ? [{ debugName: "isShowLoadingPercentsPopup" }] : /* istanbul ignore next */ []));
|
|
7642
|
+
this.uiClasses = input({}, /* @ts-ignore */
|
|
7643
|
+
...(ngDevMode ? [{ debugName: "uiClasses" }] : /* istanbul ignore next */ []));
|
|
7644
|
+
this.contentTemplate = input(null, /* @ts-ignore */
|
|
7645
|
+
...(ngDevMode ? [{ debugName: "contentTemplate" }] : /* istanbul ignore next */ []));
|
|
7646
|
+
this.cardSkin = computed(() => resolveSkin(this.uiClasses(), 'statusCard', 'status-box'), /* @ts-ignore */
|
|
7647
|
+
...(ngDevMode ? [{ debugName: "cardSkin" }] : /* istanbul ignore next */ []));
|
|
7648
|
+
this.messageSkin = computed(() => resolveSkin(this.uiClasses(), 'statusMessage', 'status-box__message'), /* @ts-ignore */
|
|
7649
|
+
...(ngDevMode ? [{ debugName: "messageSkin" }] : /* istanbul ignore next */ []));
|
|
7650
|
+
// Created once: identity stays stable, reactivity lives in the signals.
|
|
7651
|
+
this.slotContext = {
|
|
7652
|
+
percents: this.smoothPercents,
|
|
7653
|
+
message: computed(() => this.awsInstance()?.message),
|
|
7654
|
+
};
|
|
7596
7655
|
}
|
|
7597
7656
|
#store;
|
|
7598
7657
|
#loadingProgress;
|
|
7599
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7600
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
7658
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealStatusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7659
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: UnrealStatusComponent, isStandalone: true, selector: "app-unreal-status", inputs: { uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null }, contentTemplate: { classPropertyName: "contentTemplate", publicName: "contentTemplate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isShowLoadingPercentsPopup()) {\n @let _awsInstance = awsInstance();\n <div [class]=\"cardSkin()\" class=\"status-anchor\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else if (_awsInstance?.message) {\n <p>{{ _awsInstance?.message }}</p>\n } @else {\n <div [class]=\"messageSkin()\">\n <app-unreal-spinner [size]=\"30\" [strokeColor]=\"'#1F2937'\" />\n {{ smoothPercents() }}%\n </div>\n }\n </div>\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-anchor{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:5;pointer-events:none}.status-box{display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));border:none;border-radius:var(--unreal-radius-pill, var(--src-border-rounded-full, 9999px));box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814}.status-anchor p{padding:10px 15px;margin:0;text-align:center}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--unreal-text-main, var(--src-text-body-main, #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 app-unreal-spinner{margin-right:12px}\n"], dependencies: [{ kind: "component", type: UnrealSpinnerComponent, selector: "app-unreal-spinner", inputs: ["size", "strokeWidth", "strokeColor", "data-testid", "ariaLabel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7601
7660
|
}
|
|
7602
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7661
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealStatusComponent, decorators: [{
|
|
7603
7662
|
type: Component,
|
|
7604
|
-
args: [{ selector: 'app-unreal-status', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealSpinnerComponent], template: "@if (isShowLoadingPercentsPopup()) {\n @let _awsInstance = awsInstance();\n <div class=\"status-
|
|
7605
|
-
}] });
|
|
7663
|
+
args: [{ selector: 'app-unreal-status', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealSpinnerComponent, NgTemplateOutlet], template: "@if (isShowLoadingPercentsPopup()) {\n @let _awsInstance = awsInstance();\n <div [class]=\"cardSkin()\" class=\"status-anchor\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else if (_awsInstance?.message) {\n <p>{{ _awsInstance?.message }}</p>\n } @else {\n <div [class]=\"messageSkin()\">\n <app-unreal-spinner [size]=\"30\" [strokeColor]=\"'#1F2937'\" />\n {{ smoothPercents() }}%\n </div>\n }\n </div>\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-anchor{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:5;pointer-events:none}.status-box{display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));border:none;border-radius:var(--unreal-radius-pill, var(--src-border-rounded-full, 9999px));box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814}.status-anchor p{padding:10px 15px;margin:0;text-align:center}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--unreal-text-main, var(--src-text-body-main, #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 app-unreal-spinner{margin-right:12px}\n"] }]
|
|
7664
|
+
}], propDecorators: { uiClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "uiClasses", required: false }] }], contentTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentTemplate", required: false }] }] } });
|
|
7606
7665
|
|
|
7607
7666
|
class SafePipe {
|
|
7608
7667
|
constructor() {
|
|
@@ -7632,10 +7691,10 @@ class SafePipe {
|
|
|
7632
7691
|
// content instead of disabling framework security checks.
|
|
7633
7692
|
return (this.sanitizer.sanitize(context, value) ?? '');
|
|
7634
7693
|
}
|
|
7635
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7636
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.
|
|
7694
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SafePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
7695
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.1", ngImport: i0, type: SafePipe, isStandalone: true, name: "safe" }); }
|
|
7637
7696
|
}
|
|
7638
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7697
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SafePipe, decorators: [{
|
|
7639
7698
|
type: Pipe,
|
|
7640
7699
|
args: [{
|
|
7641
7700
|
name: 'safe',
|
|
@@ -7748,10 +7807,10 @@ class VideoStatsComponent {
|
|
|
7748
7807
|
elements: this.elementsToShow(),
|
|
7749
7808
|
}));
|
|
7750
7809
|
}
|
|
7751
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7752
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
7810
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: VideoStatsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7811
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: VideoStatsComponent, isStandalone: true, selector: "app-video-stats", ngImport: i0, template: "@if (videoStatus() && 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 [stat]=\"fpsTick()!\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"videoQP()!\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"bitrateTick()!\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList(); track graph.key) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [stat]=\"graph.stat()\"\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]=\"ssData() | safe: 'html'\" class=\"ssInfo\"></div>\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 span{font-weight:700}#aggregatedStats .stat,#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: ["label", "stat", "color", "tickStep"] }, { kind: "pipe", type: JsonPipe, name: "json" }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7753
7812
|
}
|
|
7754
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7813
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: VideoStatsComponent, decorators: [{
|
|
7755
7814
|
type: Component,
|
|
7756
7815
|
args: [{ selector: 'app-video-stats', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StatGraphComponent, JsonPipe, SafePipe], template: "@if (videoStatus() && 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 [stat]=\"fpsTick()!\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"videoQP()!\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"bitrateTick()!\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList(); track graph.key) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [stat]=\"graph.stat()\"\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]=\"ssData() | safe: 'html'\" class=\"ssInfo\"></div>\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 span{font-weight:700}#aggregatedStats .stat,#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"] }]
|
|
7757
7816
|
}], ctorParameters: () => [] });
|
|
@@ -7760,6 +7819,16 @@ class ResumeStreamButtonComponent {
|
|
|
7760
7819
|
#store;
|
|
7761
7820
|
constructor() {
|
|
7762
7821
|
this.#store = inject(Store);
|
|
7822
|
+
this.uiClasses = input({}, /* @ts-ignore */
|
|
7823
|
+
...(ngDevMode ? [{ debugName: "uiClasses" }] : /* istanbul ignore next */ []));
|
|
7824
|
+
this.contentTemplate = input(null, /* @ts-ignore */
|
|
7825
|
+
...(ngDevMode ? [{ debugName: "contentTemplate" }] : /* istanbul ignore next */ []));
|
|
7826
|
+
this.cardSkin = computed(() => resolveSkin(this.uiClasses(), 'resumeCard', 'resume-box'), /* @ts-ignore */
|
|
7827
|
+
...(ngDevMode ? [{ debugName: "cardSkin" }] : /* istanbul ignore next */ []));
|
|
7828
|
+
this.textSkin = computed(() => resolveSkin(this.uiClasses(), 'resumeText', 'resume-box__text'), /* @ts-ignore */
|
|
7829
|
+
...(ngDevMode ? [{ debugName: "textSkin" }] : /* istanbul ignore next */ []));
|
|
7830
|
+
this.buttonSkin = computed(() => resolveSkin(this.uiClasses(), 'resumeButton', 'connect-button'), /* @ts-ignore */
|
|
7831
|
+
...(ngDevMode ? [{ debugName: "buttonSkin" }] : /* istanbul ignore next */ []));
|
|
7763
7832
|
this.streamConfig = this.#store.selectSignal(selectStreamConfig);
|
|
7764
7833
|
this.isSecondStart = signal(this.streamConfig()?.autoStart && location.protocol === 'https:', /* @ts-ignore */
|
|
7765
7834
|
...(ngDevMode ? [{ debugName: "isSecondStart" }] : /* istanbul ignore next */ []));
|
|
@@ -7780,6 +7849,14 @@ class ResumeStreamButtonComponent {
|
|
|
7780
7849
|
}, /* @ts-ignore */
|
|
7781
7850
|
...(ngDevMode ? [{ debugName: "isAfkDisconnect" }] : /* istanbul ignore next */ []));
|
|
7782
7851
|
this.cirrusConnected = this.#store.selectSignal(unrealFeature.selectCirrusConnected);
|
|
7852
|
+
// Created once: the object identity must stay stable, reactivity lives in the
|
|
7853
|
+
// signals it carries. Declared after the signals it references so field
|
|
7854
|
+
// initialisation order holds.
|
|
7855
|
+
this.slotContext = {
|
|
7856
|
+
isSecondStart: this.isSecondStart,
|
|
7857
|
+
isAfkDisconnect: this.isAfkDisconnect,
|
|
7858
|
+
start: () => this.connect(),
|
|
7859
|
+
};
|
|
7783
7860
|
let wasConnected = this.cirrusConnected();
|
|
7784
7861
|
effect(() => {
|
|
7785
7862
|
const connected = this.cirrusConnected();
|
|
@@ -7795,13 +7872,13 @@ class ResumeStreamButtonComponent {
|
|
|
7795
7872
|
disconnect() {
|
|
7796
7873
|
this.isSecondStart.set(true);
|
|
7797
7874
|
}
|
|
7798
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7799
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
7875
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ResumeStreamButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7876
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: ResumeStreamButtonComponent, isStandalone: true, selector: "app-resume-stream-button", inputs: { uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null }, contentTemplate: { classPropertyName: "contentTemplate", publicName: "contentTemplate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isShowResumeButton()) {\n <div [class]=\"cardSkin()\" class=\"resume-anchor\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else {\n @if (isSecondStart()) {\n <div [class]=\"textSkin()\">\n Your stream {{ isAfkDisconnect() ? 'has been' : 'is' }} paused due to\n inactivity\n </div>\n }\n\n <app-unreal-button\n (clicked)=\"connect()\"\n [fullWidth]=\"true\"\n [data-testid]=\"'connect-button'\"\n [class]=\"buttonSkin()\"\n size=\"lg\"\n weight=\"primary\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </app-unreal-button>\n }\n </div>\n}\n", styles: [".resume-anchor{position:absolute;bottom:0;left:50%;transform:translate(-50%,-50%);z-index:6}.resume-box{padding:16px;gap:12px;display:flex;flex-direction:column;align-items:center;border-radius:var(--unreal-radius-card, var(--src-border-rounded-parent, 8px));background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));box-shadow:var(--unreal-shadow-card, 0 26px 80px 0 rgba(0, 0, 0, .2), 0 0 1px 0 rgba(0, 0, 0, .2))}.connect-button{width:100%}.resume-box__text{color:var(--unreal-text-secondary, var(--src-color-grey-500, #6b7280));text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:14px;font-style:normal;font-weight:400;line-height:24px}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7800
7877
|
}
|
|
7801
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7878
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ResumeStreamButtonComponent, decorators: [{
|
|
7802
7879
|
type: Component,
|
|
7803
|
-
args: [{ selector: 'app-resume-stream-button', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealButtonComponent], template: "@if (isShowResumeButton()) {\n <div class=\"resume-
|
|
7804
|
-
}], ctorParameters: () => [] });
|
|
7880
|
+
args: [{ selector: 'app-resume-stream-button', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealButtonComponent, NgTemplateOutlet], template: "@if (isShowResumeButton()) {\n <div [class]=\"cardSkin()\" class=\"resume-anchor\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else {\n @if (isSecondStart()) {\n <div [class]=\"textSkin()\">\n Your stream {{ isAfkDisconnect() ? 'has been' : 'is' }} paused due to\n inactivity\n </div>\n }\n\n <app-unreal-button\n (clicked)=\"connect()\"\n [fullWidth]=\"true\"\n [data-testid]=\"'connect-button'\"\n [class]=\"buttonSkin()\"\n size=\"lg\"\n weight=\"primary\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </app-unreal-button>\n }\n </div>\n}\n", styles: [".resume-anchor{position:absolute;bottom:0;left:50%;transform:translate(-50%,-50%);z-index:6}.resume-box{padding:16px;gap:12px;display:flex;flex-direction:column;align-items:center;border-radius:var(--unreal-radius-card, var(--src-border-rounded-parent, 8px));background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));box-shadow:var(--unreal-shadow-card, 0 26px 80px 0 rgba(0, 0, 0, .2), 0 0 1px 0 rgba(0, 0, 0, .2))}.connect-button{width:100%}.resume-box__text{color:var(--unreal-text-secondary, var(--src-color-grey-500, #6b7280));text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:14px;font-style:normal;font-weight:400;line-height:24px}\n"] }]
|
|
7881
|
+
}], ctorParameters: () => [], propDecorators: { uiClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "uiClasses", required: false }] }], contentTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentTemplate", required: false }] }] } });
|
|
7805
7882
|
|
|
7806
7883
|
class IntroSrcComponent {
|
|
7807
7884
|
#store;
|
|
@@ -7865,10 +7942,10 @@ class IntroSrcComponent {
|
|
|
7865
7942
|
sourceElement.addEventListener('error', () => this.#store.dispatch(resetIntroSrc()));
|
|
7866
7943
|
videoContainer.appendChild(videoElement);
|
|
7867
7944
|
}
|
|
7868
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
7869
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
7945
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: IntroSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7946
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: IntroSrcComponent, isStandalone: true, selector: "app-intro-src", viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoPlayerCover"], descendants: true, isSignal: true }], ngImport: i0, template: "@let image = imageIntroSrc();\n@if (image) {\n <div class=\"backImage\">\n <img (error)=\"loadingImageError()\" [ngSrc]=\"image\" fill alt=\"imageSrc\" />\n </div>\n} @else if (videoIntroSrc()) {\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%;z-index:1}.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%;z-index:1}.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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7870
7947
|
}
|
|
7871
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
7948
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: IntroSrcComponent, decorators: [{
|
|
7872
7949
|
type: Component,
|
|
7873
7950
|
args: [{ selector: 'app-intro-src', imports: [NgOptimizedImage], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let image = imageIntroSrc();\n@if (image) {\n <div class=\"backImage\">\n <img (error)=\"loadingImageError()\" [ngSrc]=\"image\" fill alt=\"imageSrc\" />\n </div>\n} @else if (videoIntroSrc()) {\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%;z-index:1}.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%;z-index:1}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"] }]
|
|
7874
7951
|
}], ctorParameters: () => [], propDecorators: { videoElement: [{ type: i0.ViewChild, args: ['videoPlayerCover', { isSignal: true }] }] } });
|
|
@@ -7904,6 +7981,15 @@ class UnrealSceneComponent {
|
|
|
7904
7981
|
}, /* @ts-ignore */
|
|
7905
7982
|
...(ngDevMode ? [{ debugName: "resolutionSize" }] : /* istanbul ignore next */ []));
|
|
7906
7983
|
this.changeMouseOverScene = output();
|
|
7984
|
+
/** Host classes replacing the built-in decoration of the scene overlays. */
|
|
7985
|
+
this.uiClasses = input({}, /* @ts-ignore */
|
|
7986
|
+
...(ngDevMode ? [{ debugName: "uiClasses" }] : /* istanbul ignore next */ []));
|
|
7987
|
+
this.resumeSlot = contentChild(UnrealResumeSlotDirective, /* @ts-ignore */
|
|
7988
|
+
...(ngDevMode ? [{ debugName: "resumeSlot" }] : /* istanbul ignore next */ []));
|
|
7989
|
+
this.afkSlot = contentChild(UnrealAfkSlotDirective, /* @ts-ignore */
|
|
7990
|
+
...(ngDevMode ? [{ debugName: "afkSlot" }] : /* istanbul ignore next */ []));
|
|
7991
|
+
this.statusSlot = contentChild(UnrealStatusSlotDirective, /* @ts-ignore */
|
|
7992
|
+
...(ngDevMode ? [{ debugName: "statusSlot" }] : /* istanbul ignore next */ []));
|
|
7907
7993
|
this.videoElement = viewChild('videoContainer', /* @ts-ignore */
|
|
7908
7994
|
...(ngDevMode ? [{ debugName: "videoElement" }] : /* istanbul ignore next */ []));
|
|
7909
7995
|
this.resolutionValues$ = toObservable(this.resolutionSize);
|
|
@@ -8001,7 +8087,7 @@ class UnrealSceneComponent {
|
|
|
8001
8087
|
this.height = height;
|
|
8002
8088
|
}));
|
|
8003
8089
|
const resizeRequired$ = merge(this.actions$.pipe(ofType(videoStreamInitialized)), fromEvent(window, 'resize'));
|
|
8004
|
-
const videoStreamSizeChanged$ = this.videoService.videoStats$.pipe(filter(() => !this.isFreezeFrameLoading()), map
|
|
8090
|
+
const videoStreamSizeChanged$ = this.videoService.videoStats$.pipe(filter(() => !this.isFreezeFrameLoading()), map((stats) => ({
|
|
8005
8091
|
w: stats.aggregatedStats.frameWidth || 0,
|
|
8006
8092
|
h: stats.aggregatedStats.frameHeight || 0,
|
|
8007
8093
|
})), distinctUntilChanged((previous, current) => fpIsASameAsB(previous.w, current.w, SAME_SIZE_THRESHOLD) &&
|
|
@@ -8010,8 +8096,8 @@ class UnrealSceneComponent {
|
|
|
8010
8096
|
.pipe(debounceTime(DEBOUNCE_TO_MANY_RESIZE_CALLS), switchMap(() => {
|
|
8011
8097
|
const key = Math.random();
|
|
8012
8098
|
// Poll 3 times to capture late-arriving DOM measurements after layout settles
|
|
8013
|
-
return timer(0, 300).pipe(take(3), map
|
|
8014
|
-
}), map
|
|
8099
|
+
return timer(0, 300).pipe(take(3), map(() => key));
|
|
8100
|
+
}), map((key) => ({ key, ...this.resizeValues })), distinctUntilChanged((previous, current) => fpIsASameAsB(previous.w, current.w, SAME_SIZE_THRESHOLD) &&
|
|
8015
8101
|
fpIsASameAsB(previous.h, current.h, SAME_SIZE_THRESHOLD) &&
|
|
8016
8102
|
previous.key === current.key), tap((size) => this.adaptVideo(size)),
|
|
8017
8103
|
// Key excluded: only dispatch when actual pixel dimensions change
|
|
@@ -8090,10 +8176,10 @@ class UnrealSceneComponent {
|
|
|
8090
8176
|
},
|
|
8091
8177
|
});
|
|
8092
8178
|
}
|
|
8093
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
8094
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: UnrealSceneComponent, isStandalone: true, selector: "app-unreal-scene", inputs: { useContainerAsSizeProvider: { classPropertyName: "useContainerAsSizeProvider", publicName: "useContainerAsSizeProvider", isSignal: true, isRequired: false, transformFunction: null }, isStudio: { classPropertyName: "isStudio", publicName: "isStudio", isSignal: true, isRequired: false, transformFunction: null }, resolutionSize: { classPropertyName: "resolutionSize", publicName: "resolutionSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changeMouseOverScene: "changeMouseOverScene" }, host: { listeners: { "mouseover": "onMouseOver()", "mouseout": "onMouseOut()", "contextmenu": "onContextMenu($event)" } }, viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"frame unreal-container\">\n <div #videoContainer id=\"player\"></div>\n\n <app-intro-src />\n <app-image-loading-src />\n <app-resume-stream-button />\n <app-unreal-status />\n <app-freeze-frame />\n <app-afk-timeout-modal />\n\n <ng-content select=\"app-pdf\"></ng-content>\n <ng-content select=\"app-show-case\"></ng-content>\n\n @if (isDevMode) {\n <app-video-stats />\n }\n</div>\n", styles: [".unreal-container{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff}.unreal-container #player{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center;overflow:hidden}.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>%0A') 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, system-ui, sans-serif)}#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, system-ui, sans-serif)}#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: AfkTimeoutModalComponent, selector: "app-afk-timeout-modal" }, { kind: "component", type: UnrealStatusComponent, selector: "app-unreal-status" }, { kind: "component", type: FreezeFrameComponent, selector: "app-freeze-frame" }, { kind: "component", type: VideoStatsComponent, selector: "app-video-stats" }, { kind: "component", type: ResumeStreamButtonComponent, selector: "app-resume-stream-button" }, { kind: "component", type: ImageLoadingSrcComponent, selector: "app-image-loading-src" }, { kind: "component", type: IntroSrcComponent, selector: "app-intro-src" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
8179
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealSceneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8180
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: UnrealSceneComponent, isStandalone: true, selector: "app-unreal-scene", inputs: { useContainerAsSizeProvider: { classPropertyName: "useContainerAsSizeProvider", publicName: "useContainerAsSizeProvider", isSignal: true, isRequired: false, transformFunction: null }, isStudio: { classPropertyName: "isStudio", publicName: "isStudio", isSignal: true, isRequired: false, transformFunction: null }, resolutionSize: { classPropertyName: "resolutionSize", publicName: "resolutionSize", isSignal: true, isRequired: false, transformFunction: null }, uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changeMouseOverScene: "changeMouseOverScene" }, host: { listeners: { "mouseover": "onMouseOver()", "mouseout": "onMouseOut()", "contextmenu": "onContextMenu($event)" } }, queries: [{ propertyName: "resumeSlot", first: true, predicate: UnrealResumeSlotDirective, descendants: true, isSignal: true }, { propertyName: "afkSlot", first: true, predicate: UnrealAfkSlotDirective, descendants: true, isSignal: true }, { propertyName: "statusSlot", first: true, predicate: UnrealStatusSlotDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"frame unreal-container\">\n <div #videoContainer id=\"player\"></div>\n\n <app-intro-src />\n <app-image-loading-src />\n <app-freeze-frame />\n <app-resume-stream-button\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"resumeSlot()?.template ?? null\"\n />\n <app-unreal-status\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"statusSlot()?.template ?? null\"\n />\n <app-afk-timeout-modal\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"afkSlot()?.template ?? null\"\n />\n\n <ng-content select=\"app-pdf\"></ng-content>\n <ng-content select=\"app-show-case\"></ng-content>\n\n @if (isDevMode) {\n <app-video-stats />\n }\n</div>\n", styles: [".unreal-container{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff}.unreal-container #player{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center;overflow:hidden}.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>%0A') 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, system-ui, sans-serif)}#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, system-ui, sans-serif)}#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: AfkTimeoutModalComponent, selector: "app-afk-timeout-modal", inputs: ["uiClasses", "contentTemplate"] }, { kind: "component", type: UnrealStatusComponent, selector: "app-unreal-status", inputs: ["uiClasses", "contentTemplate"] }, { kind: "component", type: FreezeFrameComponent, selector: "app-freeze-frame" }, { kind: "component", type: VideoStatsComponent, selector: "app-video-stats" }, { kind: "component", type: ResumeStreamButtonComponent, selector: "app-resume-stream-button", inputs: ["uiClasses", "contentTemplate"] }, { kind: "component", type: ImageLoadingSrcComponent, selector: "app-image-loading-src" }, { kind: "component", type: IntroSrcComponent, selector: "app-intro-src" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
8095
8181
|
}
|
|
8096
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
8182
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UnrealSceneComponent, decorators: [{
|
|
8097
8183
|
type: Component,
|
|
8098
8184
|
args: [{ selector: 'app-unreal-scene', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
8099
8185
|
AfkTimeoutModalComponent,
|
|
@@ -8103,8 +8189,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
8103
8189
|
ResumeStreamButtonComponent,
|
|
8104
8190
|
ImageLoadingSrcComponent,
|
|
8105
8191
|
IntroSrcComponent,
|
|
8106
|
-
], template: "<div class=\"frame unreal-container\">\n <div #videoContainer id=\"player\"></div>\n\n <app-intro-src />\n <app-image-loading-src />\n <app-resume-stream-button />\n <app-unreal-status />\n <app-freeze-frame />\n <app-afk-timeout-modal />\n\n <ng-content select=\"app-pdf\"></ng-content>\n <ng-content select=\"app-show-case\"></ng-content>\n\n @if (isDevMode) {\n <app-video-stats />\n }\n</div>\n", styles: [".unreal-container{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff}.unreal-container #player{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center;overflow:hidden}.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>%0A') 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, system-ui, sans-serif)}#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, system-ui, sans-serif)}#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"] }]
|
|
8107
|
-
}], ctorParameters: () => [], propDecorators: { useContainerAsSizeProvider: [{ type: i0.Input, args: [{ isSignal: true, alias: "useContainerAsSizeProvider", required: false }] }], isStudio: [{ type: i0.Input, args: [{ isSignal: true, alias: "isStudio", required: false }] }], resolutionSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "resolutionSize", required: false }] }], changeMouseOverScene: [{ type: i0.Output, args: ["changeMouseOverScene"] }], videoElement: [{ type: i0.ViewChild, args: ['videoContainer', { isSignal: true }] }], onMouseOver: [{
|
|
8192
|
+
], template: "<div class=\"frame unreal-container\">\n <div #videoContainer id=\"player\"></div>\n\n <app-intro-src />\n <app-image-loading-src />\n <app-freeze-frame />\n <app-resume-stream-button\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"resumeSlot()?.template ?? null\"\n />\n <app-unreal-status\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"statusSlot()?.template ?? null\"\n />\n <app-afk-timeout-modal\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"afkSlot()?.template ?? null\"\n />\n\n <ng-content select=\"app-pdf\"></ng-content>\n <ng-content select=\"app-show-case\"></ng-content>\n\n @if (isDevMode) {\n <app-video-stats />\n }\n</div>\n", styles: [".unreal-container{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff}.unreal-container #player{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center;overflow:hidden}.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>%0A') 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, system-ui, sans-serif)}#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, system-ui, sans-serif)}#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"] }]
|
|
8193
|
+
}], ctorParameters: () => [], propDecorators: { useContainerAsSizeProvider: [{ type: i0.Input, args: [{ isSignal: true, alias: "useContainerAsSizeProvider", required: false }] }], isStudio: [{ type: i0.Input, args: [{ isSignal: true, alias: "isStudio", required: false }] }], resolutionSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "resolutionSize", required: false }] }], changeMouseOverScene: [{ type: i0.Output, args: ["changeMouseOverScene"] }], uiClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "uiClasses", required: false }] }], resumeSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => UnrealResumeSlotDirective), { isSignal: true }] }], afkSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => UnrealAfkSlotDirective), { isSignal: true }] }], statusSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => UnrealStatusSlotDirective), { isSignal: true }] }], videoElement: [{ type: i0.ViewChild, args: ['videoContainer', { isSignal: true }] }], onMouseOver: [{
|
|
8108
8194
|
type: HostListener,
|
|
8109
8195
|
args: ['mouseover']
|
|
8110
8196
|
}], onMouseOut: [{
|
|
@@ -8163,5 +8249,5 @@ function provideUnrealModule() {
|
|
|
8163
8249
|
* Generated bundle index. Do not edit.
|
|
8164
8250
|
*/
|
|
8165
8251
|
|
|
8166
|
-
export { AFKService, AUTO_HIGH_RESOLUTION_IDLE_MS, AfkTimeoutModalComponent, AggregatorService, AnalyticsService, AnswerHandler, CommandTelemetryService, ConfigHandler, ConsoleExtensionsService, ControlScheme, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DESTROY_DISCONNECT_REASONS, DevModeService, DisconnectReason, FULL_HD_HEIGHT, FULL_HD_WIDTH, FpsMonitorService, FreezeFrameComponent, FreezeFrameService, INNER_RETRY_LIMIT, INSTANCE_READY_TIMEOUT, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, LatencyTimings, LoadingProgressService, MINIMAL_FPS, MIN_CONTAINER_SIZE, MessageType, MouseButton, MouseButtonsMask, OUTER_RETRY_DELAY, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PlayerCountHandler, RECONNECT_CONFIG, REGION_PING_ACCURATE_SAMPLES, REGION_PING_BACKGROUND_REFRESH_DELAY_MS, REGION_PING_CACHE_KEY, REGION_PING_CACHE_TTL_MS, REGION_PING_WARMUP_DEADLINE_MS, RegionsPingService, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_AUDIO_ID, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamAdoptionService, StreamStatusTelemetryService, StreamStatusTelemetryService2, SubService, ToClientMessage, UNREAL_CONFIG, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealSceneComponent, UnrealStatusMessage, 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, WSCloseCode_UNKNOWN, WSCloseCodes, WS_OPEN_STATE, WS_RETRY_PER_INSTANCE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, answerSent, changeStatusMainVideoOnScene, changeStreamResolutionAction, commandCompleted, commandStarted, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, dropDC, dropPC, floatToPercents, floatToSmoothPercents, forceResizeViewport, fromResizeObserver, getActiveUrl, getImageFromVideoStream, getResizeStudioValues, getResizeValuesUniversal, getRtcErrorMessage, iceConnectionFailed, initSignalling, instanceReady, isMetaboxMode, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, provideUnrealState, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetUserInactivityTimeout, resetWarnTimeout, saveAnalyticsEvent, selectCirrusConnectionId, selectClientAndViewIds, selectCommandProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsFullyConnected, selectIsLoaderScreenVisible, selectIsVideoPlayingAndDataChannelConnected, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectUserInactivityDetected, selectWarnTimeout, setAfkTimerCountdown, setAfkTimerHide, setAfkTimerVisible, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMaxFps, setOrchestrationContext, setOrchestrationMessage, setOrchestrationParameters, setOrchestrationProgress, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setUnrealPlaywrightConfig, setUserInactivityTimeout, setViewportNotReady, setViewportReady, showLoadingProgress, showResumeStreamPopup, showUnrealErrorMessage, showUserInactivityTimeoutPopup, signalingRetryAttempted, startStream, streamAdopted, trackMixpanelEvent, trackTelemetryData, uint8ToBase64, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
|
|
8252
|
+
export { AFKService, AUTO_HIGH_RESOLUTION_IDLE_MS, AfkTimeoutModalComponent, AggregatorService, AnalyticsService, AnswerHandler, CommandTelemetryService, ConfigHandler, ConsoleExtensionsService, ControlScheme, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DESTROY_DISCONNECT_REASONS, DevModeService, DisconnectReason, FULL_HD_HEIGHT, FULL_HD_WIDTH, FpsMonitorService, FreezeFrameComponent, FreezeFrameService, INNER_RETRY_LIMIT, INSTANCE_READY_TIMEOUT, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, LatencyTimings, LoadingProgressService, MINIMAL_FPS, MIN_CONTAINER_SIZE, MessageType, MouseButton, MouseButtonsMask, OUTER_RETRY_DELAY, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PlayerCountHandler, RECONNECT_CONFIG, REGION_PING_ACCURATE_SAMPLES, REGION_PING_BACKGROUND_REFRESH_DELAY_MS, REGION_PING_CACHE_KEY, REGION_PING_CACHE_TTL_MS, REGION_PING_WARMUP_DEADLINE_MS, RegionsPingService, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_AUDIO_ID, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamAdoptionService, StreamStatusTelemetryService, StreamStatusTelemetryService2, SubService, ToClientMessage, UNREAL_CONFIG, UnrealAfkSlotDirective, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealResumeSlotDirective, UnrealSceneComponent, UnrealStatusMessage, UnrealStatusSlotDirective, 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, WSCloseCode_UNKNOWN, WSCloseCodes, WS_OPEN_STATE, WS_RETRY_PER_INSTANCE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, answerSent, changeStatusMainVideoOnScene, changeStreamResolutionAction, commandCompleted, commandStarted, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, dropDC, dropPC, floatToPercents, floatToSmoothPercents, forceResizeViewport, fromResizeObserver, getActiveUrl, getImageFromVideoStream, getResizeStudioValues, getResizeValuesUniversal, getRtcErrorMessage, iceConnectionFailed, initSignalling, instanceReady, isMetaboxMode, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, provideUnrealState, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetUserInactivityTimeout, resetWarnTimeout, resolveSkin, saveAnalyticsEvent, selectCirrusConnectionId, selectClientAndViewIds, selectCommandProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsFullyConnected, selectIsLoaderScreenVisible, selectIsVideoPlayingAndDataChannelConnected, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectUserInactivityDetected, selectWarnTimeout, setAfkTimerCountdown, setAfkTimerHide, setAfkTimerVisible, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMaxFps, setOrchestrationContext, setOrchestrationMessage, setOrchestrationParameters, setOrchestrationProgress, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setUnrealPlaywrightConfig, setUserInactivityTimeout, setViewportNotReady, setViewportReady, showLoadingProgress, showResumeStreamPopup, showUnrealErrorMessage, showUserInactivityTimeoutPopup, signalingRetryAttempted, startStream, streamAdopted, trackMixpanelEvent, trackTelemetryData, uint8ToBase64, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
|
|
8167
8253
|
//# sourceMappingURL=3dsource-angular-unreal-module.mjs.map
|