@salutejs/jazz-sdk-web-plugins
Version:
Jazz SDK plugins
1,609 lines (1,559 loc) • 159 kB
TypeScript
import { Atom, Signal, Scope } from 'nrgy';
import {
EventError,
EventOk,
SyncResult,
HttpClientFetchError,
HttpClientResponseError,
JazzSdkPlugin,
JazzRoom,
JazzClient,
QueryAtom,
Query,
ConfigFlags,
JazzRoomParticipantId,
LocalAudioOutputDevice,
JazzSdk,
MediaType,
JazzRoomVideoRequest,
JazzRoomVideoQuality,
JazzRoomVideoSource,
LogLevel,
LogEvent,
} from '@salutejs/jazz-sdk-web';
import { Controller } from 'rx-effects';
import { Observable } from 'rxjs';
import { toQuery, toAction } from 'nrgy/rx-effects';
import { observe } from 'nrgy/rxjs';
type ServerRecordingEventStarted = {
type: 'serverRecordStarted';
};
type ServerRecordingEventStopped = {
type: 'serverRecordStopped';
payload: {
reason?: 'DURATION_LIMIT_EXCEEDED' | 'STOP_MANUALLY';
};
};
type ServerRecordingEventUploaded = {
type: 'serverRecordUploaded';
};
type ServerRecordingEventStartError = {
type: 'serverRecordStartError';
payload?: {
error: 'forbidden' | 'alreadyStarted' | 'storageCapacityExceeded';
};
};
type ServerRecordingEventStopError = {
type: 'serverRecordStopError';
payload?: {
error: 'forbidden' | 'notStarted';
};
};
type ServerRecordingEventUploadError = {
type: 'serverRecordUploadedError';
};
type ServerRecordingEventError = {
type: 'serverRecordError';
};
type ServerRecordingEventAutoStartError = {
type: 'serverRecordAutoStartError';
payload?: {
error: 'internalError' | 'hasNoPermission' | 'storageSizeExceeded';
};
};
type ServerRecordingEvents =
| ServerRecordingEventStarted
| ServerRecordingEventStopped
| ServerRecordingEventUploaded
| ServerRecordingEventStartError
| ServerRecordingEventStopError
| ServerRecordingEventUploadError
| ServerRecordingEventError
| ServerRecordingEventAutoStartError;
type RoomServerRecordingStatus =
| 'inactive'
| 'processStarting'
| 'recording'
| 'processStopping';
type RoomServerRecordingSupportedStatus =
| 'supported'
| 'pending'
| 'unsupported';
type BaseServerRecordingRoomService = {
events: Signal<ServerRecordingEvents>;
/**
* Starts server recording
* @throws {ServerRecordingError} When recording fails to start with codes:
* - 'FORBIDDEN'
* - 'VIDEO_RECORD_ALREADY_STARTED'
* - 'VIDEO_RECORD_STORAGE_CAPACITY_EXCEEDED'
* - 'VIDEO_RECORD_START_ERROR'
*/
startServerRecording: () => Promise<void>;
/**
* Stops server recording
* @throws {ServerRecordingError} When recording fails to stop with codes:
* - 'FORBIDDEN'
* - 'VIDEO_RECORD_STOP_ERROR'
* - 'VIDEO_RECORD_NOT_STARTED'
*/
stopServerRecording: () => Promise<void>;
};
type ServerRecordingRoomService = BaseServerRecordingRoomService & {
serverSupportedStatus: Atom<RoomServerRecordingSupportedStatus>;
isReady: Atom<boolean>;
status: Atom<RoomServerRecordingStatus>;
canManageServerRecordList: Atom<boolean>;
canManageServerRecord: Atom<boolean>;
};
declare class ServerRecordingError extends Error {
readonly code: string;
readonly error?: string;
constructor(code: string, message?: string);
}
type ServerRecordingStartErrorEvent = EventError<
| 'FORBIDDEN'
| 'VIDEO_RECORD_ALREADY_STARTED'
| 'VIDEO_RECORD_STORAGE_CAPACITY_EXCEEDED'
| 'VIDEO_RECORD_START_ERROR'
>;
type ServerRecordingStopErrorEvent = EventError<
'FORBIDDEN' | 'VIDEO_RECORD_STOP_ERROR' | 'VIDEO_RECORD_NOT_STARTED'
>;
type ServerRecordingMessages =
| EventOk<'video-record-started'>
| EventOk<'video-record-uploaded'>
| EventOk<
'video-record-stopped', // запись остановлена
{
reason: 'STOP_MANUALLY' | 'DURATION_LIMIT_EXCEEDED';
}
>
| EventError<
'START_ERROR' | 'AUTOSTART_ERROR' | 'RECORD_ERROR' | 'UPLOAD_ERROR', // ошибка загрузки записи
unknown,
'video-record-error'
>;
/**
* Symbols used internally within ts-pattern to construct and discriminate
* Guard, Not, and Select, and AnonymousSelect patterns
*
* Symbols have the advantage of not appearing in auto-complete suggestions in
* user defined patterns, and eliminate the risk of property
* overlap between ts-pattern internals and user defined patterns.
*
* These symbols have to be visible to tsc for type inference to work, but
* users should not import them
* @module
* @private
* @internal
*/
declare const toExclude: unique symbol;
type toExclude = typeof toExclude;
declare const matcher: unique symbol;
type matcher = typeof matcher;
type ValueOf<a> = a extends any[] ? a[number] : a[keyof a];
type Cast<a, b> = a extends b ? a : never;
type BuiltInObjects =
| Function
| Date
| RegExp
| Generator
| {
readonly [Symbol.toStringTag]: string;
}
| any[];
type IsPlainObject<o, excludeUnion = BuiltInObjects> = o extends object
? o extends string | excludeUnion
? false
: true
: false;
type Compute<a extends any> = a extends BuiltInObjects
? a
: {
[k in keyof a]: a[k];
};
type Primitives =
| number
| boolean
| string
| undefined
| null
| symbol
| bigint;
type None = {
type: 'none';
};
type Some<key extends string> = {
type: 'some';
key: key;
};
type SelectionType = None | Some<string>;
type MatcherType =
| 'not'
| 'optional'
| 'or'
| 'and'
| 'array'
| 'select'
| 'default';
type MatcherProtocol<
input,
narrowed,
matcherType extends MatcherType,
selections extends SelectionType,
excluded,
> = {
match: <I>(value: I | input) => MatchResult;
getSelectionKeys?: () => string[];
matcherType?: matcherType;
};
type MatchResult = {
matched: boolean;
selections?: Record<string, any>;
};
/**
* A `Matcher` is an object implementing the match
* protocol. It must define a `symbols.matcher` property
* which returns an object with a `match()` method, taking
* the input value and returning whether the pattern matches
* or not, along with optional selections.
*/
interface Matcher<
input,
narrowed,
matcherType extends MatcherType = 'default',
selections extends SelectionType = None,
excluded = narrowed,
> {
[matcher](): MatcherProtocol<
input,
narrowed,
matcherType,
selections,
excluded
>;
}
type UnknownMatcher = Matcher<unknown, unknown, any, any>;
type OptionalP<input, p> = Matcher<input, p, 'optional'>;
type GuardP<input, narrowed> = Matcher<input, narrowed>;
interface ToExclude<a> {
[toExclude]: a;
}
type UnknownPattern =
| readonly []
| readonly [UnknownPattern, ...UnknownPattern[]]
| {
readonly [k: string]: UnknownPattern;
}
| Set<UnknownPattern>
| Map<unknown, UnknownPattern>
| Primitives
| UnknownMatcher;
/**
* `Pattern<a>` is the generic type for patterns matching a value of type `a`. A pattern can be any (nested) javascript value.
*
* They can also be wildcards, like `P._`, `P.string`, `P.number`,
* or other matchers, like `P.when(predicate)`, `P.not(pattern)`, etc.
*
* [Read documentation for `P.Pattern` on GitHub](https://github.com/gvergnaud/ts-pattern#patterns)
*
* @example
* const pattern: P.Pattern<User> = { name: P.string }
*/
type Pattern<a> =
| Matcher<a, unknown, any, any>
| (a extends Primitives
? a
: unknown extends a
? UnknownPattern
: a extends readonly (infer i)[]
? a extends readonly [any, ...any]
? {
readonly [index in keyof a]: Pattern<a[index]>;
}
: readonly [] | readonly [Pattern<i>, ...Pattern<i>[]]
: a extends Map<infer k, infer v>
? Map<k, Pattern<v>>
: a extends Set<infer v>
? Set<Pattern<v>>
: a extends object
? {
readonly [k in keyof a]?: Pattern<Exclude<a[k], undefined>>;
}
: a);
type OptionalKeys<p> = ValueOf<{
[k in keyof p]: p[k] extends Matcher<any, any, infer matcherType>
? matcherType extends 'optional'
? k
: never
: never;
}>;
type ReduceUnion<tuple extends any[], output = never> = tuple extends readonly [
infer p,
...infer tail,
]
? ReduceUnion<tail, output | InvertPattern<p>>
: output;
type ReduceIntersection<
tuple extends any[],
output = unknown,
> = tuple extends readonly [infer p, ...infer tail]
? ReduceIntersection<tail, output & InvertPattern<p>>
: output;
/**
* ### InvertPattern
* Since patterns have special wildcard values, we need a way
* to transform a pattern into the type of value it represents
*/
type InvertPattern<p> =
p extends Matcher<infer input, infer narrowed, infer matcherType, any>
? {
not: ToExclude<InvertPattern<narrowed>>;
select: InvertPattern<narrowed>;
array: InvertPattern<narrowed>[];
optional: InvertPattern<narrowed> | undefined;
and: ReduceIntersection<Cast<narrowed, any[]>>;
or: ReduceUnion<Cast<narrowed, any[]>>;
default: [narrowed] extends [never] ? input : narrowed;
}[matcherType]
: p extends Primitives
? p
: p extends readonly (infer pp)[]
? p extends readonly [infer p1, infer p2, infer p3, infer p4, infer p5]
? [
InvertPattern<p1>,
InvertPattern<p2>,
InvertPattern<p3>,
InvertPattern<p4>,
InvertPattern<p5>,
]
: p extends readonly [infer p1, infer p2, infer p3, infer p4]
? [
InvertPattern<p1>,
InvertPattern<p2>,
InvertPattern<p3>,
InvertPattern<p4>,
]
: p extends readonly [infer p1, infer p2, infer p3]
? [InvertPattern<p1>, InvertPattern<p2>, InvertPattern<p3>]
: p extends readonly [infer p1, infer p2]
? [InvertPattern<p1>, InvertPattern<p2>]
: p extends readonly [infer p1]
? [InvertPattern<p1>]
: p extends readonly []
? []
: InvertPattern<pp>[]
: p extends Map<infer pk, infer pv>
? Map<pk, InvertPattern<pv>>
: p extends Set<infer pv>
? Set<InvertPattern<pv>>
: IsPlainObject<p> extends true
? OptionalKeys<p> extends infer optKeys
? [optKeys] extends [never]
? {
[k in Exclude<keyof p, optKeys>]: InvertPattern<p[k]>;
}
: Compute<
{
[k in Exclude<keyof p, optKeys>]: InvertPattern<p[k]>;
} & {
[k in Cast<optKeys, keyof p>]?: InvertPattern<p[k]>;
}
>
: never
: p;
type PInfer<p extends Pattern<any>> = InvertPattern<p>;
declare const getVideoStoragePattern: {
usedMb: GuardP<unknown, number>;
availableMb: OptionalP<unknown, GuardP<unknown, number>>;
capacityMb: GuardP<unknown, number>;
};
type StartVideoRecordErrors =
| {
type: 'storageQuotaExceeded';
}
| {
type: 'clientError';
};
type StartVideoRecordResponse = SyncResult<
undefined,
| HttpClientFetchError
| HttpClientResponseError<StartVideoRecordErrors, void, void>
>;
type GetVideoFindRequest = Readonly<{
pageNumber?: number;
pageSize: number;
}>;
type GetVideoFindResponse = {
totalPages: number;
records: VideoRecord[];
pageNumber?: number;
pageSize?: number;
};
type VideoRecord = {
id: number;
title: string;
createdAt: string;
durationSeconds: number;
publicId?: string;
sizeMb?: number;
publicAccess?: boolean;
watchUrl?: string | null;
downloadUrl?: string;
permissions: {
canTogglePublicAccess: boolean;
};
};
type GetVideoResponse = VideoRecord;
type GetVideoStorageResponse = PInfer<typeof getVideoStoragePattern>;
type RoomRecordingServiceClient = Readonly<{
getVideoStorage: () => Promise<GetVideoStorageResponse>;
startVideoRecord: (roomId: string) => Promise<StartVideoRecordResponse>;
stopVideoRecord: (roomId: string) => Promise<void>;
getVideoFind: (request: GetVideoFindRequest) => Promise<GetVideoFindResponse>;
getVideo: (recordId: string) => Promise<GetVideoResponse>;
updateVideoTitle: (recordId: number, title: string) => Promise<void>;
updateVideoPublicAccess: (
recordId: number,
publicAccess: boolean,
) => Promise<void>;
deleteVideo: (recordId: number) => Promise<void>;
getDownloadLink: (recordPublicId: string) => Promise<string>;
getS3DownloadLink: (url: string) => Promise<string | undefined>;
}>;
declare function serverRecordingPlugin(): JazzSdkPlugin;
declare const getServerRecording: (
jazzRoom: JazzRoom,
) => ServerRecordingRoomService;
declare const getServerRecordingClient: (
jazzRoom: JazzRoom | JazzClient,
) => RoomRecordingServiceClient;
type AudioLevelService = {
addMediaStream: (stream: MediaStream) => AudioLevel;
removeMediaStream: (stream: MediaStream) => void;
};
type AudioLevel = {
/**
* @example 50
* @description the min value is 0 and the max value is 100
*/
level: QueryAtom<number>;
isActive: QueryAtom<boolean>;
};
type AudioMixerEventStopAudio = Readonly<{
type: 'stopAudio';
}>;
type AudioMixerEventStoppedAudio = Readonly<{
type: 'stoppedAudio';
}>;
type AudioMixerEventStartAudio = Readonly<{
type: 'startAudio';
}>;
type AudioMixerEventStartedAudio = Readonly<{
type: 'startedAudio';
}>;
type AudioMixerEventAddMediaStream = Readonly<{
type: 'addMediaStream';
payload: {
mediaStream: MediaStream;
audioElement: HTMLAudioElement;
};
}>;
type AudioMixerEventRemoveMediaStream = Readonly<{
type: 'removeMediaStream';
payload: {
mediaStream: MediaStream;
audioElement: HTMLAudioElement;
};
}>;
type AudioMixerEventGainChanged = Readonly<{
type: 'gainChanged';
payload: {
value: number;
};
}>;
type AudioMixerEvent =
| AudioMixerEventStopAudio
| AudioMixerEventStoppedAudio
| AudioMixerEventStartAudio
| AudioMixerEventStartedAudio
| AudioMixerEventAddMediaStream
| AudioMixerEventRemoveMediaStream
| AudioMixerEventGainChanged;
type AudioMixinNode = Controller<{
destinationNode: MediaStreamAudioDestinationNode;
addStream: (stream: MediaStream) => void;
removeStream: (stream: MediaStream) => void;
}>;
type AudioMixer = Controller<{
$isSuspended: Query<boolean>;
isReady: Query<boolean>;
/**
* Creating all the elements for audio capture and playback of external audio streams,
* after that, loopback is immediately launched, if it is enabled.
* Until this function works, the audio will not be played.
*/
startAudio: () => Promise<void>;
/**
* Stops loopback if it was enabled and then clears all audio capture and playback elements.
*/
stopAudio: () => Promise<void>;
addMediaStream: (mediaStreams: MediaStream) => void;
removeMediaStream: (mediaStreams: MediaStream) => void;
removeAllMediaStreams: () => void;
$outputGain: Query<number>;
setOutputGain: (volume: number) => void;
setOutputDevice: (deviceId: string) => Promise<void>;
soundCheck: () => void;
setRemoteInputMixer: (mixer: InputAudioMixerFactory | undefined) => void;
getAudioContext: () => AudioContext;
createMixinNode: () => Promise<AudioMixinNode>;
setAudioSourcesPlayback: (enabled: boolean) => void;
event$: Observable<AudioMixerEvent>;
}>;
type AudioEffectFactory<T extends Controller> = (
audioContext: AudioContext,
) => T;
type AudioSource = Readonly<{
sourceNode: Atom<MediaStreamAudioSourceNode>;
scope: Scope;
element: HTMLAudioElement;
streamId: string;
}>;
type InputAudioMixer = Controller<{
output: AudioNode;
connect: (source: AudioSource) => Controller;
}>;
type InputAudioMixerFactory = AudioEffectFactory<InputAudioMixer>;
type AudioMixerFlags = ConfigFlags<{
loopbackEnabled: boolean;
loopbackMaxChromiumVersion: number;
loopbackRestartOnDisconnect: boolean;
loopbackMungeCandidates: boolean;
loopbackStunServer?: string;
}>;
type AudioTrack = {
participantId: JazzRoomParticipantId;
mediaStream: MediaStream;
};
type AudioMixerParticipantsManager = Controller<{
$mutedParticipants: Query<ReadonlySet<JazzRoomParticipantId>>;
addTracks: (tracks: AudioTrack[]) => void;
removeTracks: (tracks: AudioTrack[]) => void;
removeAllTracks: () => void;
muteParticipants: (
isMuted: boolean,
ids: Array<JazzRoomParticipantId>,
) => void;
}>;
type AudioOutputMixer = {
$isSuspended: Query<boolean>;
event$: Observable<AudioMixerEvent>;
startAudio: () => Promise<void>;
stopAudio: () => Promise<void>;
setOutputDevice: (audioOutput: LocalAudioOutputDevice) => Promise<void>;
outputGain: Query<number>;
setOutputGain: (volume: number) => void;
addMediaStream: (mediaStream: MediaStream) => void;
removeMediaStream: (mediaStream: MediaStream) => void;
muteParticipants: (
isMuted: boolean,
ids: Array<JazzRoomParticipantId>,
) => void;
};
type AudioOutputMixerPluginOptions = Partial<{
flags?: Partial<AudioMixerFlags>;
}>;
type AudioOutputMixerContext = {
audioMixer: AudioMixer;
audioOutputMixer: AudioOutputMixer;
audioMixerParticipantsManager: AudioMixerParticipantsManager;
audioLevel: AudioLevelService;
};
/**
* Позволяет локально управлять звуком участников конференций
*/
declare function audioOutputMixerPlugin(
options?: AudioOutputMixerPluginOptions,
): JazzSdkPlugin;
declare const AUDIO_GAIN_DEFAULT = 1;
declare const MIN_AUDIO_GAIN_VALUE = 0;
declare const MAX_AUDIO_GAIN_VALUE = 10;
/**
* @example
* ```js
* const { getAudioOutputMixer } from '@salutejs/jazz-sdk-web-plugins';
* const audioOutputMixer = getAudioOutputMixer(sdk);
* const initOutputGain = audioOutputMixer.outputGain.get();
* ```
* @example
* ```js
* const { getAudioOutputMixer } from '@salutejs/jazz-sdk-web-plugins';
* const audioOutputMixer = getAudioOutputMixer(sdk);
* audioOutputMixer.setOutputGain(5);
* ```
* @example
* ```js
* const { getAudioOutputMixer } from '@salutejs/jazz-sdk-web-plugins';
* const audioOutputMixer = getAudioOutputMixer(sdk);
* const unsubscribe = handleEvent(
* audioOutputMixer.event$,
* 'gainChanged',
* ({ payload }) => {
* setOutputGain(payload.value);
* },
* );
* ```
*/
declare function getAudioOutputMixer(sdk: JazzSdk): AudioOutputMixer;
type RoomAudioMixerEvent = Readonly<
| {
type: 'muteParticipantsChanged';
payload: {
isMuted: boolean;
participantIds: JazzRoomParticipantId[];
};
}
| {
type: 'muteAllParticipantsChanged';
payload: {
isMuted: boolean;
};
}
>;
type AudioOutputMixerManager = {
mutedParticipants: Query<ReadonlySet<JazzRoomParticipantId>>;
isMutedAll: Query<boolean>;
muteParticipants: (
isMuted: boolean,
participantIds: JazzRoomParticipantId[] | JazzRoomParticipantId,
) => void;
muteAllParticipants: (isMutedAll: boolean) => void;
event$: Observable<RoomAudioMixerEvent>;
};
/**
* @example
* ```js
* const { getAudioOutputMixerManager } from '@salutejs/jazz-sdk-web-plugins'
* const audioOutputMixerManger = getAudioOutputMixerManager(room)
* audioOutputMixerManger.mutedParticipants.get().has(participantId)
* ```
* @example
* ```js
* const { getAudioOutputMixerManager } from '@salutejs/jazz-sdk-web-plugins'
* const audioOutputMixerManger = getAudioOutputMixerManager(room)
*
* const unsubscribe = handleEvent(
* audioOutputMixerManger.event$,
* 'muteParticipantsChanged',
* ({ payload }) => {
* if (payload.participantIds.some((id) => participantId === id)) {
* setIsMuted(payload.isMuted);
* }
* },
* );
* ```
* @example
* ```js
* const { getAudioOutputMixerManager } from '@salutejs/jazz-sdk-web-plugins'
* const audioOutputMixerManger = getAudioOutputMixerManager(room)
* audioOutputMixerManger.muteParticipants(!isMuted, [participantId]);
* ```
*/
declare function getAudioOutputMixerManager(
room: JazzRoom,
): AudioOutputMixerManager;
declare type AllEventTypes = '*';
declare interface BaseEventBusReadonly<EVENTS extends EventLike>
extends BaseEventBusSubscriber<EVENTS> {
name?: string;
}
declare interface BaseEventBusSubscriber<EVENTS extends EventLike> {
/**
* Method for subscribing to bus events.
* In addition to events of the type, you can also specify the * event,
* which will allow you to subscribe to all bus events.
* The method returns a function for unsubscribing the callback
* (this can also be done via the off or removeEventListener methods).
*
* If the onSubscribe lifecycle method is passed,
* it will be called when this event is sent.
*
* If the transport was destroyed, this method will do nothing.
*
* @example
* ```ts
* type Events = { event: string };
* const eventBus = createBaseEventBus<Events>();
*
* const unsubscriber = eventBus.on('event', (event, payload) => console.log(payload));
* unsubscriber();
*
* eventBus.send('event', 'test');
* ```
*/
on<EVENT extends string & keyof EVENTS>(
event: EVENT,
callback: (event: EVENT, payload: EVENTS[EVENT]) => void,
): Unsubscriber;
/**
* unsubscribe from an event.
* If there are no subscribers left for the event, we remove it from the map.
*
* If the onUnsubscribe lifecycle callback is passed,
* it will be called each time this function is called.
*
* If the transport was destroyed, the method does not work.
*
* @example
* ```ts
* type Events = { event: string };
* const eventBus = createBaseEventBus<Events>();
*
* function handler(type: string, payload: string): void {}
*
* eventBus.on('event', handler);
* eventBus.off('event', handler);
* ```
*/
off<EVENT extends string & keyof EVENTS>(
event: EVENT,
callback: (event: EVENT, payload: EVENTS[EVENT]) => void,
): void;
}
declare interface BaseTransportNodeReadonly {
name?: string;
__isRoot: Readonly<false>;
/**
* A property indicating that a class has been destroyed.
* Once resolved, all methods in it stop working and the data is cleared.
*/
isDestroyed: boolean;
/**
* Method to get the root node object referenced by the node.
*/
getTransports: () => TransportRootNodes;
}
declare interface BaseTransportRoot extends DestroyedNode {
name?: string;
__isRoot: Readonly<true>;
}
declare interface DestroyedNode {
isDestroyed: boolean;
destroy(): void;
}
declare type EventLike = Record<string, unknown>;
declare type Namespace = string;
declare interface SubscribeNodeSubscribers<EVENTS extends EventLike> {
on<
EVENTS_KEYS extends keyof EVENTS,
TYPE extends string,
NAMESPACES extends UtilsTypeFilterTypesWithNamespaces<
string & EVENTS_KEYS,
TYPE
>,
EVENT_TYPE extends
| `${NAMESPACES}:${AllEventTypes}`
| AllEventTypes
| (string & EVENTS_KEYS),
NEW_NAMESPACE extends UtilsTypeFilterTypesWithNamespaces<EVENT_TYPE, TYPE>,
CALLBACK_EVENTS extends EVENT_TYPE extends AllEventTypes
? string & EVENTS_KEYS
: EVENT_TYPE extends `${NAMESPACES}:${AllEventTypes}`
? UtilsTypeRemoveNamespaceFromType<string & EVENTS_KEYS, NEW_NAMESPACE>
: EVENT_TYPE,
CALLBACK_PARAMS extends {
[TYPE in CALLBACK_EVENTS]: [event: TYPE, payload: EVENTS[TYPE]];
},
>(
event: EVENT_TYPE,
callback: (...args: CALLBACK_PARAMS[CALLBACK_EVENTS]) => void,
): Unsubscriber;
once<
EVENTS_KEYS extends keyof EVENTS,
TYPE extends string,
NAMESPACES extends UtilsTypeFilterTypesWithNamespaces<
string & EVENTS_KEYS,
TYPE
>,
EVENT_TYPE extends
| `${NAMESPACES}:${AllEventTypes}`
| AllEventTypes
| (string & EVENTS_KEYS),
NEW_NAMESPACE extends UtilsTypeFilterTypesWithNamespaces<EVENT_TYPE, TYPE>,
CALLBACK_EVENTS extends EVENT_TYPE extends AllEventTypes
? string & EVENTS_KEYS
: EVENT_TYPE extends `${NAMESPACES}:${AllEventTypes}`
? UtilsTypeRemoveNamespaceFromType<string & EVENTS_KEYS, NEW_NAMESPACE>
: EVENT_TYPE,
CALLBACK_PARAMS extends {
[TYPE in CALLBACK_EVENTS]: [event: TYPE, payload: EVENTS[TYPE]];
},
>(
event: EVENT_TYPE,
callback: (...args: CALLBACK_PARAMS[CALLBACK_EVENTS]) => void,
): Unsubscriber;
off<
EVENTS_KEYS extends keyof EVENTS,
TYPE extends string,
NAMESPACES extends UtilsTypeFilterTypesWithNamespaces<
string & EVENTS_KEYS,
TYPE
>,
EVENT_TYPE extends
| `${NAMESPACES}:${AllEventTypes}`
| AllEventTypes
| (string & EVENTS_KEYS),
>(
type: EVENT_TYPE,
callback: (...args: any[]) => void,
): void;
}
declare interface SubscribeReadonlyNode<EVENTS extends EventLike>
extends SubscribeReadonlyNodeExtends<EVENTS> {}
declare type SubscribeReadonlyNodeExtends<EVENTS extends EventLike> =
BaseTransportNodeReadonly & SubscribeNodeSubscribers<EVENTS>;
declare type TransportLifecycleEvents<EVENTS extends EventLike> = {
/**
* The transport was cleared. After that,
* it stops functioning and all data in it is cleared.
*/
destroy: undefined;
/**
* Subscribed to some event.
* The object indicates what event was subscribed to and whether it is the first.
*/
subscribe: {
event: string & keyof EVENTS;
mode: 'on' | 'once';
subscriber: Parameters<TransportRootSubscribers<EVENTS>['on']>[1];
subscribersCount: number;
};
/**
* Unsubscribed from some event.
* The object indicates what event was unsubscribed from and whether there are more subscribers.
*/
unsubscribe: {
event: string & keyof EVENTS;
mode: 'on' | 'once';
subscriber: Parameters<TransportRootSubscribers<EVENTS>['off']>[1];
subscribersCount: number;
};
};
declare interface TransportReadonlyNode<EVENTS extends EventLike>
extends TransportReadonlyNodeBase<EVENTS> {
lifecycle: TransportRoot<EVENTS>['lifecycle'];
}
declare type TransportReadonlyNodeBase<EVENTS extends EventLike> =
TransportRootSubscribers<EVENTS> & BaseTransportNodeReadonly;
declare interface TransportRoot<EVENTS extends EventLike>
extends TransportRootBase<EVENTS> {
/**
* Sync mode sending events
*
* @default false
*/
sync?: Readonly<boolean>;
/**
* Method for sending an event to listeners.
* If the transport was destroyed,
* or no one is subscribed to this event, the method will do nothing.
*
* If there are subscribers to *,
* they will listen to all events that were forwarded.
*
* The method works in 2 modes: synchronous and asynchronous (asynchronous mode is enabled by default).
* To change this, you need to pass the 3rd argument.
*
* @example
* ```ts
* type Events = { event: string, event_empty: undefined };
* const transport = createTransport<Events>();
*
* transport.on('event', (event, payload) => console.log(payload));
* transport.on('event_empty', (event, payload) => console.log(payload));
* transport.on('*', (event, payload) => console.log(payload));
*
* transport.send('event', 'test');
* transport.send('event_empty');
* transport.send('event_empty', undefined);
* ```
*/
send<
TYPE extends string & keyof EVENTS,
PARAMETERS extends EVENTS[TYPE] extends undefined
? (payload?: EVENTS[TYPE]) => void
: (payload: EVENTS[TYPE]) => void,
>(
type: TYPE,
...other: Parameters<PARAMETERS>
): void;
/**
* Method for getting a node that has only subscription interfaces (on/once/off).
* Recommended for use in public API services to hide methods
* for direct control of transport state from the outside.
*/
asReadonly(): TransportReadonlyNode<EVENTS>;
}
declare type TransportRootBase<EVENTS extends EventLike> =
TransportRootSubscribers<EVENTS> &
BaseTransportRoot & {
/**
* Transport lifecycle event bus. You can subscribe to 3 events:
* 1) destroy - the transport was cleared. After that, it stops functioning and all data in it is cleared.
* 2) subscribe - subscribed to some event. The object indicates what event was subscribed to and whether it is the first.
* 3) unsubscribe - unsubscribed from some event. The object indicates what event was unsubscribed from and whether there are more subscribers.
*
* When the main transport is destroyed, the lifecycle event bus also dies.
*
* @example
* ```ts
* const transport = createTransport<Events>();
*
* transport.lifecycle.on('destroy', () => console.log('transport is destroy'));
* transport.lifecycle.on('subscribe', ({ event, isFirstSubscribe }) => console.log(`subscribe to event ${event} isFirst=${isFirstSubscribe}`));
* transport.lifecycle.on('unubscribe', ({ event, isHasSubscribers }) => console.log(`unsubscribe from event ${event} isHasSubscribers=${isHasSubscribers}`));
*
* const unsubscriber1 = transport.on('event1', () => {}) // subscribe to event event1 isFirst=true
* const unsubscriber2 = transport.on('event1', () => {}) // subscribe to event event1 isFirst=false
* const unsubscriber3 = transport.on('event2', () => {}) // subscribe to event event2 isFirst=true
*
* unsubscriber3() // unsubscribe from event event2 isHasSubscribers=false
* unsubscriber2() // unsubscribe from event event1 isHasSubscribers=true
* unsubscriber1() // unsubscribe from event event1 isHasSubscribers=false
*
* transport.destroy(); // transport is destroy
* ```
*/
lifecycle: Readonly<
BaseEventBusReadonly<TransportLifecycleEvents<EVENTS>>
>;
};
declare type TransportRootNodes = Record<Namespace, Array<TransportRoot<any>>>;
declare interface TransportRootSubscribers<EVENTS extends EventLike> {
/**
* Method for subscribing to bus events.
* In addition to events of the type, you can also specify the * event,
* which will allow you to subscribe to all bus events.
* The method returns a function for unsubscribing the callback
* (this can also be done via the off or removeEventListener methods).
*
* If the onSubscribe lifecycle method is passed,
* it will be called when this event is sent.
*
* If the transport was destroyed, this method will do nothing.
*
* @example
* ```ts
* type Events = { event: string };
* const transport = createTransport<Events>();
*
* transport.on('event', (event, payload) => console.log(payload));
* const unsubscriber = transport.on('*', (event, payload) => console.log(payload));
* unsubscriber();
*
* transport.send('event', 'test');
* ```
*/
on<
EVENT_TYPE extends string & (keyof EVENTS | AllEventTypes),
EVENT extends EVENT_TYPE extends AllEventTypes
? string & keyof EVENTS
: EVENT_TYPE,
CB extends {
[TYPE in EVENT]: [TYPE, EVENTS[TYPE]];
},
>(
event: EVENT_TYPE,
callback: (...args: CB[EVENT]) => void,
): Unsubscriber;
/**
* A method for one-time subscription to bus events.
* In addition to events of the type, you can also specify an event *,
* which will allow you to subscribe to all bus events.
* The method returns a function for unsubscribing the callback
* (this can also be done via the off or removeEventListener methods).
*
* If the onSubscribe lifecycle method is passed,
* it will be called when this event is sent.
*
* If the transport was destroyed, this method will do nothing.
*
* @example
* ```ts
* type Events = { event: string };
* const transport = createTransport<Events>();
*
* transport.once('event', (event, payload) => console.log(payload));
* const unsubscriber = transport.once('*', (event, payload) => console.log(payload));
* unsubscriber();
*
* transport.send('event', 'test');
* transport.send('event', 'test'); // not call subscribers
* ```
*/
once<
EVENT_TYPE extends string & (keyof EVENTS | AllEventTypes),
EVENT extends EVENT_TYPE extends AllEventTypes
? string & keyof EVENTS
: EVENT_TYPE,
CB extends {
[TYPE in EVENT]: [TYPE, EVENTS[TYPE]];
},
>(
event: EVENT_TYPE,
callback: (...args: CB[EVENT]) => void,
): Unsubscriber;
/**
* unsubscribe from an event.
* If there are no subscribers left for the event, we remove it from the map.
*
* If the onUnsubscribe lifecycle callback is passed,
* it will be called each time this function is called.
*
* If the transport was destroyed, the method does not work.
*
* @example
* ```ts
* type Events = { event: string };
* const transport = createTransport<Events>();
*
* function handler(type: string, payload: string): void {}
*
* transport.on('event', handler);
* transport.off('event', handler);
* ```
*/
off<EVENT_TYPE extends string & (keyof EVENTS | AllEventTypes)>(
event: EVENT_TYPE,
callback: (...args: any[]) => void,
): void;
}
/**
* unsubscribe function to unsubscribe from an event.
*/
declare type Unsubscriber = () => void;
/**
* Utility type for getting namespace from event name (max size 5 namespaces)
*
* @example
*
* UtilsTypeFilterTypesWithNamespaces<'namespace1:event', 'event'> // 'namespace1'
* UtilsTypeFilterTypesWithNamespaces<'namespace1:namespace2:event', 'event'> // 'namespace1:namespace2'
* UtilsTypeFilterTypesWithNamespaces<'namespace1:namespace2:namespace3:event', 'event'> // 'namespace1:namespace2:namespace3'
*/
declare type UtilsTypeFilterTypesWithNamespaces<
STR extends string,
TYPE extends string,
> = STR extends `${infer NAMESPACE_1}:${infer NAMESPACE_2}:${infer NAMESPACE_3}:${infer NAMESPACE_4}:${infer NAMESPACE_5}:${TYPE}`
? `${NAMESPACE_1}:${NAMESPACE_2}:${NAMESPACE_3}:${NAMESPACE_4}:${NAMESPACE_5}`
: STR extends `${infer NAMESPACE_1}:${infer NAMESPACE_2}:${infer NAMESPACE_3}:${infer NAMESPACE_4}:${TYPE}`
? `${NAMESPACE_1}:${NAMESPACE_2}:${NAMESPACE_3}:${NAMESPACE_4}`
: STR extends `${infer NAMESPACE_1}:${infer NAMESPACE_2}:${infer NAMESPACE_3}:${TYPE}`
? `${NAMESPACE_1}:${NAMESPACE_2}:${NAMESPACE_3}`
: STR extends `${infer NAMESPACE_1}:${infer NAMESPACE_2}:${TYPE}`
? `${NAMESPACE_1}:${NAMESPACE_2}`
: STR extends `${infer NAMESPACE}:${TYPE}`
? `${NAMESPACE}`
: never;
/**
* Utility type of extraction from event name type without namespace
*
* @example
* UtilsTypeRemoveNamespaceFromType<'namespace:event', 'namespace'> // 'event'
*/
declare type UtilsTypeRemoveNamespaceFromType<
NAMESPACED_TYPE extends string,
NAMESPACE extends string,
> = NAMESPACED_TYPE extends `${NAMESPACE}:${infer TYPE}` ? TYPE : never;
type VideoElementPoolSettingsVideoSource = Exclude<MediaType, 'audio'>;
type VideoElementPoolSettingsPausedSources = {
[key in VideoElementPoolSettingsVideoSource]?: boolean;
};
type VideoElementPoolSettingsEvents = {
pausedAllVideoChange: {
isPaused: boolean;
};
pauseVideSourcesChange: {
pausedVideoSources: VideoElementPoolSettingsPausedSources;
};
};
/**
* @deprecated please use on/once/off API
*/
type VideoElementPoolSettingsEventsDeprecated =
| {
type: 'pausedAllVideoChange';
payload: {
isPaused: boolean;
};
}
| {
type: 'pauseVideSourcesChange';
payload: {
pausedVideoSources: VideoElementPoolSettingsPausedSources;
};
};
/**
* Сервис для работы с общими настройками
* возможности запускать видео элементы
*
* Применяется как глобально, так и для
* videoElementPoolForRoom
*/
type VideoElementPoolSettingsService = {
eventsSignal: Signal<VideoElementPoolSettingsEventsDeprecated>;
events: TransportReadonlyNode<VideoElementPoolSettingsEvents>;
isPausedAllVideo: Atom<boolean>;
getIsPausedAllVideo: () => boolean;
/**
* Остановка или запуск видео элементов.
*/
setPauseAllVideo: (isPaused: boolean) => void;
pausedVideoSources: Atom<VideoElementPoolSettingsPausedSources>;
getPausedVideoSources: () => VideoElementPoolSettingsPausedSources;
/**
* Остановка или запуск видео элементов нужного типа.
* Влияет на видео элементы в videoElementPoolForRoom
*/
playVideoSources: (
sources:
| VideoElementPoolSettingsVideoSource
| VideoElementPoolSettingsVideoSource[],
) => void;
pauseVideoSources: (
sources:
| VideoElementPoolSettingsVideoSource
| VideoElementPoolSettingsVideoSource[],
) => void;
};
type VideoSizeSettings = {
width: number;
height: number;
};
type Source = Exclude<MediaType, 'audio'>;
type RequestVideoElement = {
/**
* тип используемого видео
*/
source: Source;
/**
* необходимо ли следить за размером видео элемента
* по умолчанию считается, что параметр включен
*
* Если параметр `true`, то если элемент виден, то
* к нему подключается resizeObserver. Как только элемент
* перестает быть видим - происходим отписка.
* Каждый раз, когда меняется размер отправляется запрос
* в плагин displayEndpoints
*
* Если элемент имеет статичные размеры и они точно не изменятся
* то можно выключить это свойство для экономии ресурсов
*
* А также если нужно управлять размером запрашиваемого видео
* в ручном режиме через `setUsageVideoSize`
*
* @default true
*/
watchResize?: boolean;
/**
* игнорирование глобального занижения
* запрашиваемого качества
*
* @default false
*/
ignoreMaxVideoSize?: boolean;
};
type DisplayEndpointsEvents = {
setMaxRequestVideoSizeAllVideos: {
settings: VideoSizeSettings;
};
clearMaxRequestVideoSizeAllVideos: undefined;
};
/**
* @deprecated please use on/once/off API
*/
type DisplayEndpointsEventsDeprecated =
| {
type: 'setMaxRequestVideoSizeAllVideos';
payload: {
settings: VideoSizeSettings;
};
}
| {
type: 'clearMaxRequestVideoSizeAllVideos';
};
/**
* Сервис для взаимодействия с реальными displayEndpoints
*
* В зону ответственности входит отслеживание видимости
* и размеров видео элементов, а также запущены они или нет
* и в зависимости от этих знаний дергает нужные ручки
*
* Элемент будет зарегистрирован в реальных displayEndpoints
* только если он виден на странице и не стоит на паузе
*/
type DisplayEndpointsService = {
events: TransportReadonlyNode<DisplayEndpointsEvents>;
registerElement: (
participantId: JazzRoomParticipantId,
request: RequestVideoElement,
element: HTMLVideoElement,
) => void;
unregisterElement: (element: HTMLVideoElement) => void;
/**
* Метод для ручной активации элемента. Используется если у элемента нет стрима,
* напр. с подписками в Next.
*/
activateElement: (element: HTMLVideoElement) => void;
/**
* метод отправки в displayEndpoints plugin
* обновленных размеров, при этом эти значения не сохраняются
* и если передан watResize=true, то при обновление размера
* видео элемента эти значения будут затерты
*/
setUsageVideoSize: (
element: HTMLVideoElement,
settings?: VideoSizeSettings,
) => void;
/**
* жестко устанавливает размер видео (если он виден)
* и отключает resizeObserver для этого элемента
*
* если передать undefined данная политика отключается
*/
setUsageVideoSizeWithSaving: (
element: HTMLVideoElement,
settings: VideoSizeSettings | undefined,
) => void;
maxVideoSizeAllVideos: Atom<VideoSizeSettings | undefined>;
getMaxVideoSizeAllVideos: () => VideoSizeSettings | undefined;
/**
* Метод установки верхней планки всех запрашиваемых видео
* Нужно для случаев, когда нужно ограничить качество всех видео на странице
*
* Для игнорирования этой настройки можно передать параметр `ignoreMaxVideoSize=true`
* при регистрации видео элемента
*/
setMaxVideoSizeAllVideos: (videoSize: VideoSizeSettings | undefined) => void;
setWatchResize: (element: HTMLVideoElement, isWatch: boolean) => void;
};
type VideoElementPoolFlags = ConfigFlags<{
/**
* If the video in the conference could not start immediately,
* we try to lower and raise it back.
* This helps in cases where packet loss has occurred.
*
* @default true
*/
videoLossEnabled: boolean;
/**
* @default 5_000 ms
*/
videoLossTimeout: number;
/**
* Allow the function of stopping all video elements
* created outside the conference for a separate video stream.
*
* @default false
*/
allVideoPauseForStreamElementsEnabled: boolean;
/**
* When stopping all videos in videoElementPool, do not disable local video elements.
*
* @default true
*/
ignoreLocalVideoForPauseAllVideoEnabled: boolean;
/**
* When stopping a video type in videoElementPool, do not disable the local video element.
*
* @default true
*/
ignoreLocalVideoForPauseVideoSourceEnabled: boolean;
/**
* Allow the video of an element created using getVideoElementForStream
* to stop if the element is out of sight
*
* @default true
*/
stopElementsForStreamIfNotInteractionEnabled: boolean;
/**
* Delayed release of the element.
* It is necessary that when re-rendering the application,
* the deletion and creation of an element with all registrations does not occur.
* If the item is "released" and requested back faster than the release occurs,
* then it will be reused
*
* @default 2_000 ms
*/
delayReleaseVideoElement: number;
/**
* If the video element is not visible, then an unsubscription from displayEndpoints
* will be delayed to avoid "flashing video" during re-renders and other cases
* when the element disappears and appears for a short time.
*
* If rendering occurs, then the minimum time value between
* the flag delayReleaseVideoElement and delayStopRequestVideoIfNotInteraction is taken.
*
* In order for the unsubscription to occur instantly, you need to specify the value 0.
*
* @default 10_000 ms
*/
delayStopRequestVideoIfNotInteraction: number;
/**
* If you need to track the size of the video stream.
* The `getVideoElement` method returns the streamSize property,
* in which once every N seconds (regulated by the `streamSizeCheckInterval` flag)
* the value of the video stream size will be calculated and placed.
*
* @default false
*/
streamSizeCheckEnabled: boolean;
/**
* The interval for checking the size of the video stream.
* If you specify 0, it will be equivalent to passing the `streamSizeCheckEnabled` parameter false.
*
* @default 1_000 ms
*/
streamSizeCheckInterval: number;
/**
* Enabling the feature of limiting the number of requested videos in high quality.
* If a video in full HD is requested for more than the `maxQualitySubscribersLimit` flag,
* its quality is lowered to 640x480.
* When the queue is released, their quality is restored to the originally requested one.
*
* @default true
*/
maxQualitySubscribersLimitEnabled: boolean;
/**
* The maximum number of videos requested in full HD quality.
* It works when the maxQualitySubscribersLimitEnabled flag is enabled.
* If more videos are requested, their quality is lowered to 640x480.
*
* @default 16
*/
maxQualitySubscribersLimit: number;
/**
* the mode of operation of the plugin
*
* if you select `scoped`, then each room will have its own isolated version
*
* if `singleton` is selected, only 1 instance will be created,
* which will be used for all rooms and all video elements will fall into it.
* It may be useful if the plugin is used in child windows of the electron application.
*
* It is `recommended` to use the `scoped` mode
* @default scoped
*/
mode: 'scoped' | 'singleton';
}>;
type RoomVideoElementsVideoEvents = {
/**
* событие любого обновления стрима в комнтае
* то есть (addTrack, removeTrack, trackMuteChange and etc.)
*
* потенциально есть возможность,
* что трек будет unmute, но стрим будет пустой
*/
trackUpdated: {
stream: MediaStream | null | undefined;
isMuted: boolean;
isPaused: boolean;
};
/**
* событие остановки видео элемента
* см `videoElementPoolForRoom.setPauseAllVideo`
* и `videoElementPoolForRoom.playVideoSources`.
*/
elementsPausedChanged: {
stream: MediaStream | null | undefined;
isMuted: boolean;
isPaused: boolean;
};
};
/**
* @deprecated please use on/once/off new API
*/
type RoomVideoElementsVideoEventsDeprecated$1 =
/**
* событие любого обновления стрима в комнтае
* то есть (addTrack, removeTrack, trackMuteChange and etc.)
*
* потенциально есть возможность,
* что трек будет unmute, но стрим будет пустой
*/
| {
type: 'trackUpdated';
payload: {
stream: MediaStream | null | undefined;
isMuted: boolean;
isPaused: boolean;
};
}
/**
* событие остановки видео элемента
* см `videoElementPoolForRoom.setPauseAllVideo`
* и `videoElementPoolForRoom.playVideoSources`.
*/
| {
type: 'elementsPausedChanged';
payload: {
stream: MediaStream | null | undefined;
isMuted: boolean;
isPaused: boolean;
};
};
type VideoElementPoolSettings = VideoElementPoolFlags;
type VideoElementPoolPluginOptions = Partial<VideoElementPoolSettings>;
type VideoElementPoolForRoomEvents = VideoElementPoolSettingsEvents &
DisplayEndpointsEvents;
/**
* @deprecated please use on/once/off new API
*/
type VideoElementPoolForRoomEventsDeprecated =
VideoElementPoolSettingsEventsDeprecated & DisplayEndpointsEventsDeprecated;
type VideoElementPoolForRoomVideoSource = Exclude<MediaType, 'audio'>;
type VideoElementPoolForRoomVideoSizeSettings = {
width: number;
height: number;
};
type VideoElementPoolForRoomElementEvents = RoomVideoElementsVideoEvents;
/**
* @deprecated use on/once/off new API
*/
type VideoElementPoolForRoomElementEventsDeprecated =
RoomVideoElementsVideoEventsDeprecated$1;
type VideoElementPoolForRoomElement = {
/**
* HTMLVideoElement для встройки на страницу
* механиками videoElementPool в него будет
* подставляться актуальный mediaStream
* а также он будет запускаться и останавливаться
*/
videoElement: HTMLVideoElement;
/**
* замьючен ли videoSource
*/
isMuted: Atom<boolean>;
/**
* стоит ли данный videoSource на паузе
* по дефолту состояние повторяет isMuted
*
* но при размьюченном видео может быть false, если:
* 1) все видео элементы стоят на паузе
* см `setPauseAllVideo`
* данное поведение можно игнорировать
* с помощью настройки `ignoreAllVideoPause`
*
* 2) данный videoSource отключен
* см `playVideoSources`
* данное поведение можно игнорировать
* с помощью настройки `ignoreSourcePause`
*/
isPaused: Atom<boolean>;
/**
* mediaStream, который сейчас используется
* для всех видео элементов
*/
stream: Atom<MediaStream | null | undefined>;
/**
* размер видео стрима. Если стрима нет или видео замьючено,
* то вычисления не происходит и значения высоты и ширины
* равны 0
*/
streamSize: Atom<VideoElementPoolStreamSize>;
/**
* videoSource. Используется в качестве атома
* для удобства взаимодействия и сохранения
* единого стиля данных
*/
source: Atom<VideoElementPoolForRoomVideoSource>;
/**
* @deprecated use on/once/off new API
*/
events: Signal<VideoElementPoolForRoomElementEventsDeprecated>;
on: TransportReadonlyNode<VideoElementPoolForRoomElementEvents>['on'];
once: TransportReadonlyNode<VideoElementPoolForRoomElementEvents>['once'];
off: TransportReadonlyNode<VideoElementPoolForRoomElementEvents>['off'];
/**
* установка размера запрашиваемого видео
* при этом происходит остановка отслеживания размера видео элемента
*
* для отмены этого поведения нужно передать undefined
* или это произойдет автоматически≤ если высота или ширина будут равны 0
*/
setVideoSize: (
settings: VideoElementPoolForRoomVideoSizeSettings | undefined,
) => void;
/**
* динамический запуск или остановка отслеживания размера видео элемента
* отслеживание работает только если видео элемент виден на странице
* и он не остановлен (см свойство `isPaused`)
*/
setIsWatchResize: (isWatch: boolean) => void;
/**
* не рекомендуется к использованию
*
* Ручной запуск видео элемента
*
* Метод работает только если данный videoSource
* не стоит на паузе и все видео не стоят на паузе
* или если есть соответсвующие настройки игнорирования
*/
play: () => void;
/**
* не рекомендуется к использованию
*
* ручная остановка видео элемента
*/
pause: () => void;
release: () => void;
};
type VideoElementPoolForRoomRequestVideoElement = {
source: VideoElementPoolForRoomVideoSource;
/**
* игнорирование остановки всех видео элементов
* см `setPauseAllVideo`
*
* @default false
*/
ignoreAllVideoPause?: boolean;
/**
* игнорирование остановки конкретного типа видео
* см `pauseVideoSources`
*
* @default false
*/
ignoreVideoSourcePause?: boolean;
/**
* игнорирование верхней планки запрашиваемого видео
* см `setMaxVideoSizeAllVideos`
*
* @default false
*/
ignoreMaxVideoSize?: boolean;
/**
* нужно ли отслеживать изменение размера видео элемента
* Отслеживание работает только если видео элемент виден
* и не стоит на паузе
*
* @default true
*/
watchResize?: boolean;
/**
* HTMLVideoElement props, которые можно установить сразу
* при инициализации видео элемента
*/
videoElementProps?: Partial<HTMLVideoElement>;
};
/**
* Сервис для работы с видео элементами в комнате
*/
type VideoElementPoolForRoomService = {
/**
* @deprecated use on/once/off new API
*/
events: Signal<VideoElementPoolForRoomEventsDeprecated>;
on: SubscribeReadonlyNode<VideoElementPoolForRoomEvents>['on'];
once: SubscribeReadonlyNode<VideoElementPoolForRoomEvents>['once'];