@aidenlx/vidstack-react
Version:
UI component library for building high-quality, accessible video and audio experiences on the web.
1,496 lines (1,421 loc) • 277 kB
TypeScript
import { DOMEvent, EventsTarget, InferEventDetail } from 'maverick.js/std';
import * as maverick_js from 'maverick.js';
import { ViewController, MaybeStopEffect, WriteSignal, ReadSignalRecord, Dispose, Scope, Store, State, Component } from 'maverick.js';
import * as media_captions from 'media-captions';
import { VTTCue as VTTCue$1, VTTRegion, CaptionsFileFormat, CaptionsParserFactory, VTTHeaderMetadata } from 'media-captions';
import * as DASH from 'dashjs';
import DASH__default from 'dashjs';
import * as HLS from 'hls.js';
type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';
declare const GROUPED_LOG: unique symbol;
declare class GroupedLog {
readonly logger: Logger;
readonly level: LogLevel;
readonly title: string;
readonly root?: GroupedLog | undefined;
readonly parent?: GroupedLog | undefined;
readonly [GROUPED_LOG] = true;
readonly logs: ({
label?: string;
data: any[];
} | GroupedLog)[];
constructor(logger: Logger, level: LogLevel, title: string, root?: GroupedLog | undefined, parent?: GroupedLog | undefined);
log(...data: any[]): GroupedLog;
labelledLog(label: string, ...data: any[]): GroupedLog;
groupStart(title: string): GroupedLog;
groupEnd(): GroupedLog;
dispatch(): boolean;
}
declare class Logger {
#private;
error(...data: any[]): boolean;
warn(...data: any[]): boolean;
info(...data: any[]): boolean;
debug(...data: any[]): boolean;
errorGroup(title: string): GroupedLog;
warnGroup(title: string): GroupedLog;
infoGroup(title: string): GroupedLog;
debugGroup(title: string): GroupedLog;
setTarget(newTarget: EventTarget | null): void;
dispatch(level: LogLevel, ...data: any[]): boolean;
}
declare const ADD: unique symbol;
declare const REMOVE: unique symbol;
declare const RESET: unique symbol;
declare const SELECT: unique symbol;
declare const READONLY: unique symbol;
declare const SET_READONLY: unique symbol;
declare const ON_RESET: unique symbol;
declare const ON_REMOVE: unique symbol;
declare const ON_USER_SELECT: unique symbol;
/** @internal */
declare const ListSymbol: {
readonly add: typeof ADD;
readonly remove: typeof REMOVE;
readonly reset: typeof RESET;
readonly select: typeof SELECT;
readonly readonly: typeof READONLY;
readonly setReadonly: typeof SET_READONLY;
readonly onReset: typeof ON_RESET;
readonly onRemove: typeof ON_REMOVE;
readonly onUserSelect: typeof ON_USER_SELECT;
};
interface ListItem {
id: string;
}
declare class List<Item extends ListItem, Events extends ListEvents> extends EventsTarget<Events> implements Iterable<Item> {
[index: number]: Item | undefined;
protected items: Item[];
/** @internal */
protected [ListSymbol.readonly]: boolean;
/** @internal */
protected [ListSymbol.onReset]?(trigger?: Event): void;
/** @internal */
protected [ListSymbol.onRemove]?(item: Item, trigger?: Event): void;
get length(): number;
get readonly(): boolean;
/**
* Returns the index of the first occurrence of the given item, or -1 if it is not present.
*/
indexOf(item: Item): number;
/**
* Returns an item matching the given `id`, or `null` if not present.
*/
getById(id: string): Item | null;
/**
* Transform list to an array.
*/
toArray(): Item[];
[Symbol.iterator](): ArrayIterator<Item>;
/** @internal */
[ListSymbol.add](item: Item, trigger?: Event): void;
/** @internal */
[ListSymbol.remove](item: Item, trigger?: Event): void;
/** @internal */
[ListSymbol.reset](trigger?: Event): void;
/** @internal */
[ListSymbol.setReadonly](readonly: boolean, trigger?: Event): void;
}
interface ListEvents<Item extends ListItem = ListItem> {
add: ListAddEvent<Item>;
remove: ListRemoveEvent<Item>;
'readonly-change': ListReadonlyChangeEvent;
}
/**
* Fired when an item has been added to the list.
*
* @detail item
*/
interface ListAddEvent<Item extends ListItem> extends DOMEvent<Item> {
}
/**
* Fired when an item has been removed from the list.
*
* @detail item
*/
interface ListRemoveEvent<Item extends ListItem> extends DOMEvent<Item> {
}
/**
* Fired when the readonly state of the list has changed.
*
* @detail isReadonly
*/
interface ListReadonlyChangeEvent extends DOMEvent<boolean> {
}
interface FullscreenEvents {
'fullscreen-change': FullscreenChangeEvent;
'fullscreen-error': FullscreenErrorEvent;
}
/**
* Fired when an element enters/exits fullscreen. The event detail is a `boolean` indicating
* if fullscreen was entered (`true`) or exited (`false`).
*
* @bubbles
* @composed
* @detail isFullscreen
*/
interface FullscreenChangeEvent extends DOMEvent<boolean> {
}
/**
* Fired when an error occurs either entering or exiting fullscreen. This will generally occur
* if the user has not interacted with the page yet.
*
* @bubbles
* @composed
* @detail error
*/
interface FullscreenErrorEvent extends DOMEvent<unknown> {
}
declare class FullscreenController extends ViewController<{}, {}, FullscreenEvents> implements FullscreenAdapter {
#private;
get active(): boolean;
get supported(): boolean;
protected onConnect(): void;
enter(): Promise<void>;
exit(): Promise<void>;
}
declare function canFullscreen(): boolean;
interface FullscreenAdapter {
/**
* Whether the host element is in fullscreen mode.
*/
readonly active: boolean;
/**
* Whether the native browser fullscreen API is available, or the current provider can
* toggle fullscreen mode. This does not mean that the operation is guaranteed to be successful,
* only that it can be attempted.
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API}
*/
readonly supported: boolean;
/**
* Request to display the current host element in fullscreen.
*
* @throws Error - if fullscreen API is not available.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullscreen}
*/
enter(): Promise<void>;
/**
* Attempt to exit fullscreen on the current host element.
*
* @throws Error - if fullscreen API is not available.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/exitFullscreen}
*/
exit(): Promise<void>;
}
declare global {
interface HTMLElementEventMap extends LoggerEvents {
}
}
interface LoggerEvents {
'vds-log': LogEvent;
}
interface LogEventDetail {
/**
* The log level.
*/
level: LogLevel;
/**
* Data to be logged.
*/
data?: any[];
}
/**
* @bubbles
* @composed
* @detail log
*/
interface LogEvent extends DOMEvent<LogEventDetail> {
}
type ScreenOrientationType =
/**
* Landscape-primary is an orientation where the screen width is greater than the screen height.
* If the device's natural orientation is landscape, then it is in landscape-primary when held
* in that position. If the device's natural orientation is portrait, the user agent sets
* landscape-primary from the two options as shown in the screen orientation values table.
*/
'landscape-primary'
/**
* Landscape-secondary is an orientation where the screen width is greater than the screen
* height. If the device's natural orientation is landscape, it is in landscape-secondary when
* rotated 180º from its natural orientation. If the device's natural orientation is portrait,
* the user agent sets landscape-secondary from the two options as shown in the screen
* orientation values table.
*/
| 'landscape-secondary'
/**
* Portrait-primary is an orientation where the screen width is less than or equal to the screen
* height. If the device's natural orientation is portrait, then it is in portrait-primary when
* held in that position. If the device's natural orientation is landscape, the user agent sets
* portrait-primary from the two options as shown in the screen orientation values table.
*/
| 'portrait-primary'
/**
* Portrait-secondary is an orientation where the screen width is less than or equal to the
* screen height. If the device's natural orientation is portrait, then it is in
* portrait-secondary when rotated 180º from its natural position. If the device's natural
* orientation is landscape, the user agent sets portrait-secondary from the two options as
* shown in the screen orientation values table.
*/
| 'portrait-secondary';
type ScreenOrientationLockType =
/**
* Any is an orientation that means the screen can be locked to any one of portrait-primary,
* portrait-secondary, landscape-primary and landscape-secondary.
*/
'any'
/**
* Landscape is an orientation where the screen width is greater than the screen height and
* depending on platform convention locking the screen to landscape can represent
* landscape-primary, landscape-secondary or both.
*/
| 'landscape'
/**
* Landscape-primary is an orientation where the screen width is greater than the screen height.
* If the device's natural orientation is landscape, then it is in landscape-primary when held
* in that position. If the device's natural orientation is portrait, the user agent sets
* landscape-primary from the two options as shown in the screen orientation values table.
*/
| 'landscape-primary'
/**
* Landscape-secondary is an orientation where the screen width is greater than the screen
* height. If the device's natural orientation is landscape, it is in landscape-secondary when
* rotated 180º from its natural orientation. If the device's natural orientation is portrait,
* the user agent sets landscape-secondary from the two options as shown in the screen
* orientation values table.
*/
| 'landscape-secondary'
/**
* Natural is an orientation that refers to either portrait-primary or landscape-primary
* depending on the device's usual orientation. This orientation is usually provided by the
* underlying operating system.
*/
| 'natural'
/**
* Portrait is an orientation where the screen width is less than or equal to the screen height
* and depending on platform convention locking the screen to portrait can represent
* portrait-primary, portrait-secondary or both.
*/
| 'portrait'
/**
* Portrait-primary is an orientation where the screen width is less than or equal to the screen
* height. If the device's natural orientation is portrait, then it is in portrait-primary when
* held in that position. If the device's natural orientation is landscape, the user agent sets
* portrait-primary from the two options as shown in the screen orientation values table.
*/
| 'portrait-primary'
/**
* Portrait-secondary is an orientation where the screen width is less than or equal to the
* screen height. If the device's natural orientation is portrait, then it is in
* portrait-secondary when rotated 180º from its natural position. If the device's natural
* orientation is landscape, the user agent sets portrait-secondary from the two options as
* shown in the screen orientation values table.
*/
| 'portrait-secondary';
interface ScreenOrientationEvents {
'orientation-change': ScreenOrientationChangeEvent;
}
interface ScreenOrientationChangeEventDetail {
orientation: ScreenOrientationType;
lock?: ScreenOrientationLockType;
}
/**
* Fired when the current screen orientation changes.
*
* @detail orientation
*/
interface ScreenOrientationChangeEvent extends DOMEvent<ScreenOrientationChangeEventDetail> {
}
declare class ScreenOrientationController extends ViewController<{}, {}, ScreenOrientationEvents> {
#private;
/**
* The current screen orientation type.
*
* @signal
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation}
* @see https://w3c.github.io/screen-orientation/#screen-orientation-types-and-locks
*/
get type(): ScreenOrientationType | undefined;
/**
* Whether the screen orientation is currently locked.
*
* @signal
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation}
* @see https://w3c.github.io/screen-orientation/#screen-orientation-types-and-locks
*/
get locked(): boolean;
/**
* Whether the viewport is in a portrait orientation.
*
* @signal
*/
get portrait(): boolean;
/**
* Whether the viewport is in a landscape orientation.
*
* @signal
*/
get landscape(): boolean;
/**
* Whether the native Screen Orientation API is available.
*/
static readonly supported: boolean;
/**
* Whether the native Screen Orientation API is available.
*/
get supported(): boolean;
protected onConnect(): void;
/**
* Locks the orientation of the screen to the desired orientation type using the
* Screen Orientation API.
*
* @param lockType - The screen lock orientation type.
* @throws Error - If screen orientation API is unavailable.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation}
* @see {@link https://w3c.github.io/screen-orientation}
*/
lock(lockType: ScreenOrientationLockType): Promise<void>;
/**
* Unlocks the orientation of the screen to it's default state using the Screen Orientation
* API. This method will throw an error if the API is unavailable.
*
* @throws Error - If screen orientation API is unavailable.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation}
* @see {@link https://w3c.github.io/screen-orientation}
*/
unlock(): Promise<void>;
}
interface MediaRequestEvents {
'media-airplay-request': MediaAirPlayRequestEvent;
'media-audio-track-change-request': MediaAudioTrackChangeRequestEvent;
'media-clip-start-change-request': MediaClipStartChangeRequestEvent;
'media-clip-end-change-request': MediaClipEndChangeRequestEvent;
'media-duration-change-request': MediaDurationChangeRequestEvent;
'media-enter-fullscreen-request': MediaEnterFullscreenRequestEvent;
'media-exit-fullscreen-request': MediaExitFullscreenRequestEvent;
'media-enter-pip-request': MediaEnterPIPRequestEvent;
'media-exit-pip-request': MediaExitPIPRequestEvent;
'media-google-cast-request': MediaGoogleCastRequestEvent;
'media-live-edge-request': MediaLiveEdgeRequestEvent;
'media-loop-request': MediaLoopRequestEvent;
'media-user-loop-change-request': MediaUserLoopChangeRequestEvent;
'media-orientation-lock-request': MediaOrientationLockRequestEvent;
'media-orientation-unlock-request': MediaOrientationUnlockRequestEvent;
'media-mute-request': MediaMuteRequestEvent;
'media-pause-request': MediaPauseRequestEvent;
'media-pause-controls-request': MediaPauseControlsRequestEvent;
'media-play-request': MediaPlayRequestEvent;
'media-quality-change-request': MediaQualityChangeRequestEvent;
'media-rate-change-request': MediaRateChangeRequestEvent;
'media-audio-gain-change-request': MediaAudioGainChangeRequestEvent;
'media-resume-controls-request': MediaResumeControlsRequestEvent;
'media-seek-request': MediaSeekRequestEvent;
'media-seeking-request': MediaSeekingRequestEvent;
'media-start-loading': MediaStartLoadingRequestEvent;
'media-poster-start-loading': MediaPosterStartLoadingRequestEvent;
'media-text-track-change-request': MediaTextTrackChangeRequestEvent;
'media-unmute-request': MediaUnmuteRequestEvent;
'media-volume-change-request': MediaVolumeChangeRequestEvent;
}
/**
* Fired when requesting the AirPlay picker to open.
*
* @bubbles
* @composed
*/
interface MediaAirPlayRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting the media poster to begin loading. This will only take effect if the
* `posterLoad` strategy on the player is set to `custom`.
*
* @bubbles
* @composed
*/
interface MediaPosterStartLoadingRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting to change the `mode` on a text track at the given index in the
* `TextTrackList` on the player.
*
* @bubbles
* @composed
*/
interface MediaTextTrackChangeRequestEvent extends DOMEvent<{
index: number;
mode: TextTrackMode;
}> {
}
/**
* Fired when requesting the media to be muted.
*
* @bubbles
* @composed
*/
interface MediaMuteRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting the media to be unmuted.
*
* @bubbles
* @composed
*/
interface MediaUnmuteRequestEvent extends DOMEvent<void> {
}
/**
* Whether to request fullscreen on the media (i.e., `<media-player>`). The `prefer-media` option
* will first see if the native fullscreen API is available, if not it'll try the media provider.
*/
type MediaFullscreenRequestTarget = 'prefer-media' | 'media' | 'provider';
/**
* Fired when requesting to change the current audio track to the given index in the
* `AudioTrackList` on the player.
*
* @bubbles
* @composed
*/
interface MediaAudioTrackChangeRequestEvent extends DOMEvent<number> {
}
/**
* Fired when requesting to change the clip start time. The event `detail` specifies the new start
* time in seconds.
*
* @bubbles
* @composed
*/
interface MediaClipStartChangeRequestEvent extends DOMEvent<number> {
}
/**
* Fired when requesting to change the clip end time. The event `detail` specifies the new end
* time in seconds.
*
* @bubbles
* @composed
*/
interface MediaClipEndChangeRequestEvent extends DOMEvent<number> {
}
/**
* Fired when requesting to change the length of the media. The event `detail` specifies the
* new length in seconds.
*
* @bubbles
* @composed
*/
interface MediaDurationChangeRequestEvent extends DOMEvent<number> {
}
/**
* Fired when requesting media to enter fullscreen. The event `detail` can specify the
* fullscreen target, which can be the media or provider (defaults to `prefer-media`).
*
* @bubbles
* @composed
*/
interface MediaEnterFullscreenRequestEvent extends DOMEvent<MediaFullscreenRequestTarget> {
}
/**
* Fired when requesting media to exit fullscreen. The event `detail` can specify the fullscreen
* target, which can be the media or provider (defaults to `prefer-media`).
*
* @bubbles
* @composed
*/
interface MediaExitFullscreenRequestEvent extends DOMEvent<MediaFullscreenRequestTarget> {
}
/**
* Fired when requesting media to enter picture-in-picture mode.
*
* @bubbles
* @composed
*/
interface MediaEnterPIPRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting media to exit picture-in-picture mode.
*
* @bubbles
* @composed
*/
interface MediaExitPIPRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting Google Cast.
*
* @bubbles
* @composed
*/
interface MediaGoogleCastRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting media to seek to the live edge (i.e., set the current time to the current
* live time).
*/
interface MediaLiveEdgeRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting media playback to begin/resume.
*
* @bubbles
* @composed
*/
interface MediaPlayRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting to change the current video quality to the given index in the
* `VideoQualityList` on the player.
*
* @bubbles
* @composed
* @detail qualityIndex
*/
interface MediaQualityChangeRequestEvent extends DOMEvent<number> {
}
/**
* Fired when requesting to change the current playback rate.
*
* @bubbles
* @composed
* @detail rate
*/
interface MediaRateChangeRequestEvent extends DOMEvent<number> {
}
/**
* Fired when requesting to change the current audio gain.
*
* @bubbles
* @composed
* @detail gain
*/
interface MediaAudioGainChangeRequestEvent extends DOMEvent<number> {
}
/**
* Fired when requesting media playback to temporarily stop.
*
* @bubbles
* @composed
*/
interface MediaPauseRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting a time change. In other words, moving the play head to a new position.
*
* @bubbles
* @composed
* @detail seekTo
*/
interface MediaSeekRequestEvent extends DOMEvent<number> {
}
/**
* Fired when seeking/scrubbing to a new playback position.
*
* @bubbles
* @composed
* @detail time
*/
interface MediaSeekingRequestEvent extends DOMEvent<number> {
}
/**
* Fired when requesting media to begin loading. This will only take effect if the `load`
* strategy on the player is set to `custom`.
*
* @bubbles
* @composed
*/
interface MediaStartLoadingRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting the media volume to be set to a new level.
*
* @bubbles
* @composed
* @detail volume
*/
interface MediaVolumeChangeRequestEvent extends DOMEvent<number> {
}
/**
* Fired when controls visibility tracking may resume. This is typically called after requesting
* tracking to pause via `media-pause-controls-request`.
*
* @bubbles
* @composed
*/
interface MediaResumeControlsRequestEvent extends DOMEvent<void> {
}
/**
* Fired when controls visibility tracking should pause. This is typically used when a control
* is being actively interacted with, and we don't want the controls to be hidden before
* the interaction is complete (eg: scrubbing, or settings is open).
*
* @bubbles
* @composed
*/
interface MediaPauseControlsRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting the poster _should_ be rendered by the media provider. This should be
* fired if a custom poster is _not_ being used.
*
* @bubbles
* @composed
*/
interface MediaShowPosterRequestEvent extends DOMEvent<void> {
}
/**
* Fired when requesting the poster should _not_ be rendered by the media provider. This
* should be fired if a custom poster element is being used (e.g., `media-poster`).
*
* @bubbles
* @composed
*/
interface MediaHidePosterRequestEvent extends DOMEvent<void> {
}
/**
* Internal event that is fired by a media provider when requesting media playback to restart after
* reaching the end. This event also helps notify the player that media will be looping.
*
* @internal
* @bubbles
* @composed
*/
interface MediaLoopRequestEvent extends DOMEvent<void> {
}
/**
* Fired when the user loop preference changes.
*
* @bubbles
* @composed
*/
interface MediaUserLoopChangeRequestEvent extends DOMEvent<boolean> {
}
/**
* Fired when requesting the screen orientation to be locked to a certain type.
*
* @bubbles
* @composed
*/
interface MediaOrientationLockRequestEvent extends DOMEvent<ScreenOrientationLockType> {
}
/**
* Fired when requesting the screen orientation to be unlocked.
*
* @bubbles
* @composed
*/
interface MediaOrientationUnlockRequestEvent extends DOMEvent<void> {
}
/**
* The current media type.
*/
type MediaType = 'unknown' | 'audio' | 'video';
/**
* The current media stream type.
*/
type MediaStreamType = 'unknown' | 'on-demand' | 'live' | 'live:dvr' | 'll-live' | 'll-live:dvr';
type MediaCrossOrigin = '' | 'anonymous' | 'use-credentials';
type RemotePlaybackType = 'airplay' | 'google-cast' | 'none';
interface RemotePlaybackInfo {
deviceName?: string;
}
/**
* Indicates the current view type which determines how the media will be presented.
*/
type MediaViewType = 'unknown' | 'audio' | 'video';
/**
* Indicates the type of strategy that should be used to initiate the loading process.
*
* @docs {@see https://www.vidstack.io/docs/player/core-concepts/loading#load-strategies}
*/
type MediaLoadingStrategy = 'eager' | 'idle' | 'visible' | 'custom' | 'play';
/**
* Indicates the type of strategy that should be used to initiate the poster loading process.
*
* @docs {@see https://www.vidstack.io/docs/player/core-concepts/loading#load-strategies}
*/
type MediaPosterLoadingStrategy = 'eager' | 'idle' | 'visible' | 'custom';
/**
* A number which represents the general type of error that occurred.
*
* - *Abort Error Code (1):* The fetching of the associated resource was aborted by the user's
* request.
*
* - *Network Error Code (2):* Some kind of network error occurred which prevented the media from
* being successfully fetched, despite having previously been available.
*
* - *Decode Error Code (3):* Despite having previously been determined to be usable, an error
* occurred while trying to decode the media resource, resulting in an error.
*
* - *Invalid Resource Error Code (4):* The associated resource or media provider object (such as
* a `MediaStream`) has been found to be unsuitable.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/MediaError
* @see https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code
*/
type MediaErrorCode = 1 | 2 | 3 | 4;
interface MediaErrorDetail {
message: string;
code?: MediaErrorCode;
error?: Error;
mediaError?: MediaError;
}
declare global {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- It has to be an `interface` so that it can be merged.
interface SymbolConstructor {
readonly observable: symbol;
}
}
/**
Returns a boolean for whether the two given types are equal.
@link https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
@link https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796
Use-cases:
- If you want to make a conditional branch based on the result of a comparison of two types.
@example
```
import type {IsEqual} from 'type-fest';
// This type returns a boolean for whether the given array includes the given item.
// `IsEqual` is used to compare the given array at position 0 and the given item and then return true if they are equal.
type Includes<Value extends readonly any[], Item> =
Value extends readonly [Value[0], ...infer rest]
? IsEqual<Value[0], Item> extends true
? true
: Includes<rest, Item>
: false;
```
@category Type Guard
@category Utilities
*/
type IsEqual<A, B> =
(<G>() => G extends A ? 1 : 2) extends
(<G>() => G extends B ? 1 : 2)
? true
: false;
/**
Filter out keys from an object.
Returns `never` if `Exclude` is strictly equal to `Key`.
Returns `never` if `Key` extends `Exclude`.
Returns `Key` otherwise.
@example
```
type Filtered = Filter<'foo', 'foo'>;
//=> never
```
@example
```
type Filtered = Filter<'bar', string>;
//=> never
```
@example
```
type Filtered = Filter<'bar', 'foo'>;
//=> 'bar'
```
@see {Except}
*/
type Filter<KeyType, ExcludeType> = IsEqual<KeyType, ExcludeType> extends true ? never : (KeyType extends ExcludeType ? never : KeyType);
type ExceptOptions = {
/**
Disallow assigning non-specified properties.
Note that any omitted properties in the resulting type will be present in autocomplete as `undefined`.
@default false
*/
requireExactProps?: boolean;
};
/**
Create a type from an object type without certain keys.
We recommend setting the `requireExactProps` option to `true`.
This type is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). The `Omit` type does not restrict the omitted keys to be keys present on the given type, while `Except` does. The benefits of a stricter type are avoiding typos and allowing the compiler to pick up on rename refactors automatically.
This type was proposed to the TypeScript team, which declined it, saying they prefer that libraries implement stricter versions of the built-in types ([microsoft/TypeScript#30825](https://github.com/microsoft/TypeScript/issues/30825#issuecomment-523668235)).
@example
```
import type {Except} from 'type-fest';
type Foo = {
a: number;
b: string;
};
type FooWithoutA = Except<Foo, 'a'>;
//=> {b: string}
const fooWithoutA: FooWithoutA = {a: 1, b: '2'};
//=> errors: 'a' does not exist in type '{ b: string; }'
type FooWithoutB = Except<Foo, 'b', {requireExactProps: true}>;
//=> {a: number} & Partial<Record<"b", never>>
const fooWithoutB: FooWithoutB = {a: 1, b: '2'};
//=> errors at 'b': Type 'string' is not assignable to type 'undefined'.
```
@category Object
*/
type Except<ObjectType, KeysType extends keyof ObjectType, Options extends ExceptOptions = {requireExactProps: false}> = {
[KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType];
} & (Options['requireExactProps'] extends true
? Partial<Record<KeysType, never>>
: {});
/**
Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability.
@example
```
import type {Simplify} from 'type-fest';
type PositionProps = {
top: number;
left: number;
};
type SizeProps = {
width: number;
height: number;
};
// In your editor, hovering over `Props` will show a flattened object with all the properties.
type Props = Simplify<PositionProps & SizeProps>;
```
Sometimes it is desired to pass a value as a function argument that has a different type. At first inspection it may seem assignable, and then you discover it is not because the `value`'s type definition was defined as an interface. In the following example, `fn` requires an argument of type `Record<string, unknown>`. If the value is defined as a literal, then it is assignable. And if the `value` is defined as type using the `Simplify` utility the value is assignable. But if the `value` is defined as an interface, it is not assignable because the interface is not sealed and elsewhere a non-string property could be added to the interface.
If the type definition must be an interface (perhaps it was defined in a third-party npm package), then the `value` can be defined as `const value: Simplify<SomeInterface> = ...`. Then `value` will be assignable to the `fn` argument. Or the `value` can be cast as `Simplify<SomeInterface>` if you can't re-declare the `value`.
@example
```
import type {Simplify} from 'type-fest';
interface SomeInterface {
foo: number;
bar?: string;
baz: number | undefined;
}
type SomeType = {
foo: number;
bar?: string;
baz: number | undefined;
};
const literal = {foo: 123, bar: 'hello', baz: 456};
const someType: SomeType = literal;
const someInterface: SomeInterface = literal;
function fn(object: Record<string, unknown>): void {}
fn(literal); // Good: literal object type is sealed
fn(someType); // Good: type is sealed
fn(someInterface); // Error: Index signature for type 'string' is missing in type 'someInterface'. Because `interface` can be re-opened
fn(someInterface as Simplify<SomeInterface>); // Good: transform an `interface` into a `type`
```
@link https://github.com/microsoft/TypeScript/issues/15300
@category Object
*/
type Simplify<T> = {[KeyType in keyof T]: T[KeyType]} & {};
/**
Create a type that strips `readonly` from all or some of an object's keys. Inverse of `Readonly<T>`.
This can be used to [store and mutate options within a class](https://github.com/sindresorhus/pageres/blob/4a5d05fca19a5fbd2f53842cbf3eb7b1b63bddd2/source/index.ts#L72), [edit `readonly` objects within tests](https://stackoverflow.com/questions/50703834), [construct a `readonly` object within a function](https://github.com/Microsoft/TypeScript/issues/24509), or to define a single model where the only thing that changes is whether or not some of the keys are writable.
@example
```
import type {Writable} from 'type-fest';
type Foo = {
readonly a: number;
readonly b: readonly string[]; // To show that only the mutability status of the properties, not their values, are affected.
readonly c: boolean;
};
const writableFoo: Writable<Foo> = {a: 1, b: ['2'], c: true};
writableFoo.a = 3;
writableFoo.b[0] = 'new value'; // Will still fail as the value of property "b" is still a readonly type.
writableFoo.b = ['something']; // Will work as the "b" property itself is no longer readonly.
type SomeWritable = Writable<Foo, 'b' | 'c'>;
// type SomeWritable = {
// readonly a: number;
// b: readonly string[]; // It's now writable. The type of the property remains unaffected.
// c: boolean; // It's now writable.
// }
```
@category Object
*/
type Writable<BaseType, Keys extends keyof BaseType = keyof BaseType> =
Simplify<
// Pick just the keys that are not writable from the base type.
Except<BaseType, Keys> &
// Pick the keys that should be writable from the base type and make them writable by removing the `readonly` modifier from the key.
{-readonly [KeyType in keyof Pick<BaseType, Keys>]: Pick<BaseType, Keys>[KeyType]}
>;
/**
Create a type that makes the given keys required. The remaining keys are kept as is. The sister of the `SetOptional` type.
Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are required.
@example
```
import type {SetRequired} from 'type-fest';
type Foo = {
a?: number;
b: string;
c?: boolean;
}
type SomeRequired = SetRequired<Foo, 'b' | 'c'>;
// type SomeRequired = {
// a?: number;
// b: string; // Was already required and still is.
// c: boolean; // Is now required.
// }
```
@category Object
*/
type SetRequired<BaseType, Keys extends keyof BaseType> =
// `extends unknown` is always going to be the case and is used to convert any
// union into a [distributive conditional
// type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
BaseType extends unknown
? Simplify<
// Pick just the keys that are optional from the base type.
Except<BaseType, Keys> &
// Pick the keys that should be required from the base type and make them required.
Required<Pick<BaseType, Keys>>
>
: never;
type MediaSrc = string | AudioSrc | VideoSrc | HLSSrc | DASHSrc | YouTubeSrc | VimeoSrc;
type MediaSrcObject = MediaStream | MediaSource | Blob;
type HTMLMediaSrc = string | MediaSrcObject;
interface Src<T = unknown> {
src: T;
type: string;
}
interface AudioSrc extends AudioSrcMeta {
src: HTMLMediaSrc;
type: AudioMimeType;
}
type AudioMimeType = 'audio/mpeg' | 'audio/ogg' | 'audio/3gp' | 'audio/mp3' | 'audio/webm' | 'audio/flac' | 'audio/object';
interface AudioSrcMeta {
id?: string;
bitrate?: number;
channels?: number;
}
interface VideoSrc extends VideoSrcMeta {
src: HTMLMediaSrc;
type: VideoMimeType;
}
type VideoMimeType = 'video/mp4' | 'video/webm' | 'video/3gp' | 'video/ogg' | 'video/avi' | 'video/mpeg' | 'video/object';
interface VideoSrcMeta {
id?: string;
width?: number;
height?: number;
bitrate?: number;
framerate?: number;
codec?: string;
}
interface HLSSrc {
src: string;
type: HLSMimeType;
}
type HLSMimeType = 'application/vnd.apple.mpegurl' | 'audio/mpegurl' | 'audio/x-mpegurl' | 'application/x-mpegurl' | 'video/x-mpegurl' | 'video/mpegurl' | 'application/mpegurl';
interface DASHSrc {
src: string;
type: DASHMimeType;
}
type DASHMimeType = 'application/dash+xml';
interface YouTubeSrc {
src: string;
type: 'video/youtube';
}
interface VimeoSrc {
src: string;
type: 'video/vimeo';
}
declare function isVideoQualitySrc(src: Src): src is SetRequired<VideoSrc, 'width' | 'height'>;
/**
* A simple facade for dispatching media requests to the nearest media player element.
*
* @docs {@link https://www.vidstack.io/docs/player/core-concepts/state-management#updating}
*
*/
declare class MediaRemoteControl {
#private;
constructor(logger?: Logger | undefined);
/**
* Set the target from which to dispatch media requests events from. The events should bubble
* up from this target to the player element.
*
* @example
* ```ts
* const button = document.querySelector('button');
* remote.setTarget(button);
* ```
*/
setTarget(target: EventTarget | null): void;
/**
* Returns the current player element. This method will attempt to find the player by
* searching up from either the given `target` or default target set via `remote.setTarget`.
*
* @example
* ```ts
* const player = remote.getPlayer();
* ```
*/
getPlayer(target?: EventTarget | null): MediaPlayer | null;
/**
* Set the current player element so the remote can support toggle methods such as
* `togglePaused` as they rely on the current media state.
*/
setPlayer(player: MediaPlayer | null): void;
/**
* Dispatch a request to start the media loading process. This will only work if the media
* player has been initialized with a custom loading strategy `load="custom">`.
*
* @docs {@link https://www.vidstack.io/docs/player/core-concepts/loading#load-strategies}
*/
startLoading(trigger?: Event): void;
/**
* Dispatch a request to start the poster loading process. This will only work if the media
* player has been initialized with a custom poster loading strategy `posterLoad="custom">`.
*
* @docs {@link https://www.vidstack.io/docs/player/core-concepts/loading#load-strategies}
*/
startLoadingPoster(trigger?: Event): void;
/**
* Dispatch a request to connect to AirPlay.
*
* @see {@link https://www.apple.com/au/airplay}
*/
requestAirPlay(trigger?: Event): void;
/**
* Dispatch a request to connect to Google Cast.
*
* @see {@link https://developers.google.com/cast/docs/overview}
*/
requestGoogleCast(trigger?: Event): void;
/**
* Dispatch a request to begin/resume media playback.
*/
play(trigger?: Event): void;
/**
* Dispatch a request to pause media playback.
*/
pause(trigger?: Event): void;
/**
* Dispatch a request to set the media volume to mute (0).
*/
mute(trigger?: Event): void;
/**
* Dispatch a request to unmute the media volume and set it back to it's previous state.
*/
unmute(trigger?: Event): void;
/**
* Dispatch a request to enter fullscreen.
*
* @docs {@link https://www.vidstack.io/docs/player/api/fullscreen#remote-control}
*/
enterFullscreen(target?: MediaFullscreenRequestTarget, trigger?: Event): void;
/**
* Dispatch a request to exit fullscreen.
*
* @docs {@link https://www.vidstack.io/docs/player/api/fullscreen#remote-control}
*/
exitFullscreen(target?: MediaFullscreenRequestTarget, trigger?: Event): void;
/**
* Dispatch a request to lock the screen orientation.
*
* @docs {@link https://www.vidstack.io/docs/player/screen-orientation#remote-control}
*/
lockScreenOrientation(lockType: ScreenOrientationLockType, trigger?: Event): void;
/**
* Dispatch a request to unlock the screen orientation.
*
* @docs {@link https://www.vidstack.io/docs/player/api/screen-orientation#remote-control}
*/
unlockScreenOrientation(trigger?: Event): void;
/**
* Dispatch a request to enter picture-in-picture mode.
*
* @docs {@link https://www.vidstack.io/docs/player/api/picture-in-picture#remote-control}
*/
enterPictureInPicture(trigger?: Event): void;
/**
* Dispatch a request to exit picture-in-picture mode.
*
* @docs {@link https://www.vidstack.io/docs/player/api/picture-in-picture#remote-control}
*/
exitPictureInPicture(trigger?: Event): void;
/**
* Notify the media player that a seeking process is happening and to seek to the given `time`.
*/
seeking(time: number, trigger?: Event): void;
/**
* Notify the media player that a seeking operation has completed and to seek to the given `time`.
* This is generally called after a series of `remote.seeking()` calls.
*/
seek(time: number, trigger?: Event): void;
seekToLiveEdge(trigger?: Event): void;
/**
* Dispatch a request to update the length of the media in seconds.
*
* @example
* ```ts
* remote.changeDuration(100); // 100 seconds
* ```
*/
changeDuration(duration: number, trigger?: Event): void;
/**
* Dispatch a request to update the clip start time. This is the time at which media playback
* should start at.
*
* @example
* ```ts
* remote.changeClipStart(100); // start at 100 seconds
* ```
*/
changeClipStart(startTime: number, trigger?: Event): void;
/**
* Dispatch a request to update the clip end time. This is the time at which media playback
* should end at.
*
* @example
* ```ts
* remote.changeClipEnd(100); // end at 100 seconds
* ```
*/
changeClipEnd(endTime: number, trigger?: Event): void;
/**
* Dispatch a request to update the media volume to the given `volume` level which is a value
* between 0 and 1.
*
* @docs {@link https://www.vidstack.io/docs/player/api/audio-gain#remote-control}
* @example
* ```ts
* remote.changeVolume(0); // 0%
* remote.changeVolume(0.05); // 5%
* remote.changeVolume(0.5); // 50%
* remote.changeVolume(0.75); // 70%
* remote.changeVolume(1); // 100%
* ```
*/
changeVolume(volume: number, trigger?: Event): void;
/**
* Dispatch a request to change the current audio track.
*
* @example
* ```ts
* remote.changeAudioTrack(1); // track at index 1
* ```
*/
changeAudioTrack(index: number, trigger?: Event): void;
/**
* Dispatch a request to change the video quality. The special value `-1` represents auto quality
* selection.
*
* @example
* ```ts
* remote.changeQuality(-1); // auto
* remote.changeQuality(1); // quality at index 1
* ```
*/
changeQuality(index: number, trigger?: Event): void;
/**
* Request auto quality selection.
*/
requestAutoQuality(trigger?: Event): void;
/**
* Dispatch a request to change the mode of the text track at the given index.
*
* @example
* ```ts
* remote.changeTextTrackMode(1, 'showing'); // track at index 1
* ```
*/
changeTextTrackMode(index: number, mode: TextTrackMode, trigger?: Event): void;
/**
* Dispatch a request to change the media playback rate.
*
* @example
* ```ts
* remote.changePlaybackRate(0.5); // Half the normal speed
* remote.changePlaybackRate(1); // Normal speed
* remote.changePlaybackRate(1.5); // 50% faster than normal
* remote.changePlaybackRate(2); // Double the normal speed
* ```
*/
changePlaybackRate(rate: number, trigger?: Event): void;
/**
* Dispatch a request to change the media audio gain.
*
* @example
* ```ts
* remote.changeAudioGain(1); // Disable audio gain
* remote.changeAudioGain(1.5); // 50% louder
* remote.changeAudioGain(2); // 100% louder
* ```
*/
changeAudioGain(gain: number, trigger?: Event): void;
/**
* Dispatch a request to resume idle tracking on controls.
*/
resumeControls(trigger?: Event): void;
/**
* Dispatch a request to pause controls idle tracking. Pausing tracking will result in the
* controls being visible until `remote.resumeControls()` is called. This method
* is generally used when building custom controls and you'd like to prevent the UI from
* disappearing.
*
* @example
* ```ts
* // Prevent controls hiding while menu is being interacted with.
* function onSettingsOpen() {
* remote.pauseControls();
* }
*
* function onSettingsClose() {
* remote.resumeControls();
* }
* ```
*/
pauseControls(trigger?: Event): void;
/**
* Dispatch a request to toggle the media playback state.
*/
togglePaused(trigger?: Event): void;
/**
* Dispatch a request to toggle the controls visibility.
*/
toggleControls(trigger?: Event): void;
/**
* Dispatch a request to toggle the media muted state.
*/
toggleMuted(trigger?: Event): void;
/**
* Dispatch a request to toggle the media fullscreen state.
*
* @docs {@link https://www.vidstack.io/docs/player/api/fullscreen#remote-control}
*/
toggleFullscreen(target?: MediaFullscreenRequestTarget, trigger?: Event): void;
/**
* Dispatch a request to toggle the media picture-in-picture mode.
*
* @docs {@link https://www.vidstack.io/docs/player/api/picture-in-picture#remote-control}
*/
togglePictureInPicture(trigger?: Event): void;
/**
* Show captions.
*/
showCaptions(trigger?: Event): void;
/**
* Turn captions off.
*/
disableCaptions(trigger?: Event): void;
/**
* Dispatch a request to toggle the current captions mode.
*/
toggleCaptions(trigger?: Event): void;
userPrefersLoopChange(prefersLoop: boolean, trigger?: Event): void;
}
type MediaKeyTarget = 'document' | 'player';
interface MediaKeyShortcuts {
[keys: string]: MediaKeyShortcut | undefined;
togglePaused?: MediaKeyShortcut;
toggleMuted?: MediaKeyShortcut;
toggleFullscreen?: MediaKeyShortcut;
togglePictureInPicture?: MediaKeyShortcut;
toggleCaptions?: MediaKeyShortcut;
seekBackward?: MediaKeyShortcut;
seekForward?: MediaKeyShortcut;
speedUp?: MediaKeyShortcut;
slowDown?: MediaKeyShortcut;
volumeUp?: MediaKeyShortcut;
volumeDown?: MediaKeyShortcut;
}
type MediaKeyShortcut = MediaKeysCallback | string | string[] | null;
interface MediaKeysCallback {
keys: string | string[];
/** @deprecated - use `onKeyUp` or `onKeyDown` */
callback?(event: KeyboardEvent, remote: MediaRemoteControl): void;
onKeyUp?(context: {
event: KeyboardEvent;
player: MediaPlayer;
remote: MediaRemoteControl;
}): void;
onKeyDown?(context: {
event: KeyboardEvent;
player: MediaPlayer;
remote: MediaRemoteControl;
}): void;
}
interface SelectListItem extends ListItem {
selected: boolean;
}
declare class SelectList<Item extends SelectListItem, Events extends SelectListEvents<Item>> extends List<Item, Events> {
get selected(): Item | null;
get selectedIndex(): number;
/** @internal */
protected [ListSymbol.onRemove](item: Item, trigger?: Event): void;
/** @internal */
protected [ListSymbol.onUserSelect]?(): void;
/** @internal */
[ListSymbol.add](item: Omit<Item, 'selected'>, trigger?: Event): void;
/** @internal */
[ListSymbol.select](item: Item | undefined, selected: boolean, trigger?: Event): void;
}
interface SelectListEvents<Item extends SelectListItem = SelectListItem> extends ListEvents<Item> {
change: SelectListChangeEvent<Item>;
}
/**
* @detail change
*/
interface SelectListChangeEvent<Item extends SelectListItem> extends DOMEvent<SelectListChangeEventDetail<Item>> {
}
interface SelectListChangeEventDetail<Item extends SelectListItem> {
prev: Item | null;
current: Item | null;
}
interface VideoQualityListEvents {
add: VideoQualityAddEvent;
remove: VideoQualityRemoveEvent;
change: VideoQualityChangeEvent;
'auto-change': VideoQualityAutoChangeEvent;
'readonly-change': ListReadonlyChangeEvent;
}
interface VideoQualityListEvent<T> extends DOMEvent<T> {
target: VideoQualityList;
}
/**
* Fired when a video quality has been added to the list.
*
* @detail newQuality
*/
interface VideoQualityAddEvent extends VideoQualityListEvent<VideoQuality> {
}
/**
* Fired when a video quality has been removed from the list.
*
* @detail removedQuality
*/
interface VideoQualityRemoveEvent extends VideoQualityListEvent<VideoQuality> {
}
/**
* Fired when the selected video quality has changed.
*
* @detail change
*/
interface VideoQualityChangeEvent extends VideoQualityListEvent<VideoQualityChangeEventDetail> {
}
interface VideoQualityChangeEventDetail {
prev: VideoQuality | null;
current: VideoQuality;
}
/**
* Fired when auto quality selection is enabled or disabled.
*/
interface VideoQualityAutoChangeEvent extends VideoQualityListEvent<boolean> {
}
declare const SET_AUTO: unique symbol;
declare const ENABLE_AUTO: unique symbol;
/** @internal */
declare const QualitySymbol: {
readonly setAuto: typeof SET_AUTO;
readonly enableAuto: typeof ENABLE_AUTO;
};
/**
* @see {@link https://vidstack.io/docs/player/core-concepts/video-quality#quality-list}
*/
declare class VideoQualityList extends SelectList<VideoQuality, VideoQualityListEvents> {
#private;
/**
* Configures quality switching:
*
* - `current`: Trigger an immediate quality level switch. This will abort the current fragment
* request if any, flush the whole buffer, and fetch fragment matching with current position
* and requested quality level.
*
* - `next`: Trigger a quality level switch for next fragment. This could eventually flush
* already buffered next fragment.
*
* - `load`: Set quality level for next loaded fragment.
*
* @see {@link https://www.vidstack.io/docs/player/api/video-quality#switch}
* @see {@link https://github.com/video-dev/hls.js/blob/master/docs/API.md#quality-switch-control-api}
*/
switch: 'current' | 'next' | 'load';
/**
* Whether automatic quality selection is enabled.
*/
get auto(): boolean;
/** @internal */
[QualitySymbol.enableAuto]?: (trigger?: Event) => void;
/** @internal */
protected [ListSymbol.onUserSelect](): void;
/** @internal */
protected [ListSymbol.onReset](trigger?: Event): void;
/**
* Request automatic quality selection (if supported). This will be a no-op if the list is
* `readonly` as that already implies auto-selection.
*/
autoSelect(trigger?: Event): void;
getBySrc(src: unknown): VideoQuality | undefined;
/** @internal */
[QualitySymbol.setAuto](auto: boolean, trigger?: Event): void;
}
interface VideoQuality extends SelectListItem {
readonly id: string;
readonly src?: unknown;
readonly width: number;
readonly height: number;
readonly bitrate: number | null;
readonly