@mux/mux-player 0.1.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +281 -0
  2. package/LICENSE +9 -0
  3. package/README.md +231 -0
  4. package/coverage/lcov-report/base.css +224 -0
  5. package/coverage/lcov-report/block-navigation.js +87 -0
  6. package/coverage/lcov-report/favicon.png +0 -0
  7. package/coverage/lcov-report/index.html +161 -0
  8. package/coverage/lcov-report/prettify.css +1 -0
  9. package/coverage/lcov-report/prettify.js +2 -0
  10. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  11. package/coverage/lcov-report/sorter.js +196 -0
  12. package/coverage/lcov-report/src/dialog.ts.html +247 -0
  13. package/coverage/lcov-report/src/errors.ts.html +574 -0
  14. package/coverage/lcov-report/src/helpers.ts.html +478 -0
  15. package/coverage/lcov-report/src/html.ts.html +580 -0
  16. package/coverage/lcov-report/src/index.html +251 -0
  17. package/coverage/lcov-report/src/index.ts.html +2941 -0
  18. package/coverage/lcov-report/src/logger.ts.html +163 -0
  19. package/coverage/lcov-report/src/media-chrome/dialog.ts.html +661 -0
  20. package/coverage/lcov-report/src/media-chrome/index.html +131 -0
  21. package/coverage/lcov-report/src/media-chrome/time-display.ts.html +295 -0
  22. package/coverage/lcov-report/src/media-theme-mux/icons/airplay.svg.html +109 -0
  23. package/coverage/lcov-report/src/media-theme-mux/icons/captions-off.svg.html +100 -0
  24. package/coverage/lcov-report/src/media-theme-mux/icons/captions-on.svg.html +100 -0
  25. package/coverage/lcov-report/src/media-theme-mux/icons/fullscreen-enter.svg.html +100 -0
  26. package/coverage/lcov-report/src/media-theme-mux/icons/fullscreen-exit.svg.html +100 -0
  27. package/coverage/lcov-report/src/media-theme-mux/icons/index.html +326 -0
  28. package/coverage/lcov-report/src/media-theme-mux/icons/pause.svg.html +100 -0
  29. package/coverage/lcov-report/src/media-theme-mux/icons/pip-enter.svg.html +100 -0
  30. package/coverage/lcov-report/src/media-theme-mux/icons/pip-exit.svg.html +100 -0
  31. package/coverage/lcov-report/src/media-theme-mux/icons/play.svg.html +100 -0
  32. package/coverage/lcov-report/src/media-theme-mux/icons/seek-backward.svg.html +124 -0
  33. package/coverage/lcov-report/src/media-theme-mux/icons/seek-forward.svg.html +124 -0
  34. package/coverage/lcov-report/src/media-theme-mux/icons/volume-high.svg.html +103 -0
  35. package/coverage/lcov-report/src/media-theme-mux/icons/volume-low.svg.html +103 -0
  36. package/coverage/lcov-report/src/media-theme-mux/icons/volume-medium.svg.html +103 -0
  37. package/coverage/lcov-report/src/media-theme-mux/icons/volume-off.svg.html +103 -0
  38. package/coverage/lcov-report/src/media-theme-mux/icons.ts.html +184 -0
  39. package/coverage/lcov-report/src/media-theme-mux/index.html +146 -0
  40. package/coverage/lcov-report/src/media-theme-mux/media-theme-mux.ts.html +1279 -0
  41. package/coverage/lcov-report/src/media-theme-mux/styles.css.html +586 -0
  42. package/coverage/lcov-report/src/styles.css.html +211 -0
  43. package/coverage/lcov-report/src/template.ts.html +463 -0
  44. package/coverage/lcov-report/src/utils.ts.html +385 -0
  45. package/coverage/lcov-report/src/video-api.ts.html +979 -0
  46. package/coverage/lcov.info +4058 -0
  47. package/dist/index.cjs.js +1432 -0
  48. package/dist/index.mjs +709 -0
  49. package/dist/mux-player.js +1478 -0
  50. package/dist/mux-player.mjs +1478 -0
  51. package/dist/tsconfig.tsbuildinfo +1 -0
  52. package/dist/types/dialog.d.ts +6 -0
  53. package/dist/types/errors.d.ts +6 -0
  54. package/dist/types/helpers.d.ts +26 -0
  55. package/dist/types/html.d.ts +18 -0
  56. package/dist/types/index.d.ts +199 -0
  57. package/dist/types/logger.d.ts +5 -0
  58. package/dist/types/media-chrome/dialog.d.ts +12 -0
  59. package/dist/types/media-chrome/time-display.d.ts +9 -0
  60. package/dist/types/media-theme-mux/icons.d.ts +15 -0
  61. package/dist/types/media-theme-mux/media-theme-mux.d.ts +29 -0
  62. package/dist/types/template.d.ts +5 -0
  63. package/dist/types/utils.d.ts +10 -0
  64. package/dist/types/video-api.d.ts +64 -0
  65. package/dist/types-ts3.4/dialog.d.ts +6 -0
  66. package/dist/types-ts3.4/errors.d.ts +6 -0
  67. package/dist/types-ts3.4/helpers.d.ts +26 -0
  68. package/dist/types-ts3.4/html.d.ts +18 -0
  69. package/dist/types-ts3.4/index.d.ts +180 -0
  70. package/dist/types-ts3.4/logger.d.ts +5 -0
  71. package/dist/types-ts3.4/media-chrome/dialog.d.ts +12 -0
  72. package/dist/types-ts3.4/media-chrome/time-display.d.ts +9 -0
  73. package/dist/types-ts3.4/media-theme-mux/icons.d.ts +15 -0
  74. package/dist/types-ts3.4/media-theme-mux/media-theme-mux.d.ts +29 -0
  75. package/dist/types-ts3.4/template.d.ts +5 -0
  76. package/dist/types-ts3.4/utils.d.ts +10 -0
  77. package/dist/types-ts3.4/video-api.d.ts +53 -0
  78. package/lang/en.json +32 -0
  79. package/lang/nl.json +31 -0
  80. package/package.json +107 -0
  81. package/src/dialog.ts +54 -0
  82. package/src/errors.ts +163 -0
  83. package/src/helpers.ts +131 -0
  84. package/src/html.ts +165 -0
  85. package/src/index.ts +952 -0
  86. package/src/logger.ts +26 -0
  87. package/src/media-chrome/dialog.ts +192 -0
  88. package/src/media-chrome/time-display.ts +70 -0
  89. package/src/media-theme-mux/icons/airplay.svg +8 -0
  90. package/src/media-theme-mux/icons/captions-off.svg +5 -0
  91. package/src/media-theme-mux/icons/captions-on.svg +5 -0
  92. package/src/media-theme-mux/icons/fullscreen-enter.svg +5 -0
  93. package/src/media-theme-mux/icons/fullscreen-exit.svg +5 -0
  94. package/src/media-theme-mux/icons/pause.svg +5 -0
  95. package/src/media-theme-mux/icons/pip-enter.svg +5 -0
  96. package/src/media-theme-mux/icons/pip-exit.svg +5 -0
  97. package/src/media-theme-mux/icons/play.svg +5 -0
  98. package/src/media-theme-mux/icons/seek-backward.svg +13 -0
  99. package/src/media-theme-mux/icons/seek-forward.svg +13 -0
  100. package/src/media-theme-mux/icons/volume-high.svg +6 -0
  101. package/src/media-theme-mux/icons/volume-low.svg +6 -0
  102. package/src/media-theme-mux/icons/volume-medium.svg +6 -0
  103. package/src/media-theme-mux/icons/volume-off.svg +6 -0
  104. package/src/media-theme-mux/icons.ts +33 -0
  105. package/src/media-theme-mux/media-theme-mux.ts +398 -0
  106. package/src/media-theme-mux/styles.css +167 -0
  107. package/src/styles.css +42 -0
  108. package/src/template.ts +126 -0
  109. package/src/types.d.ts +52 -0
  110. package/src/utils.ts +100 -0
  111. package/src/video-api.ts +298 -0
  112. package/test/errors.test.js +169 -0
  113. package/test/helpers.test.js +78 -0
  114. package/test/player.test.js +696 -0
  115. package/test/template.test.js +70 -0
  116. package/test/utils.test.js +21 -0
  117. package/test/web-test-runner.config.mjs +29 -0
  118. package/tsconfig.json +21 -0
@@ -0,0 +1,126 @@
1
+ import './media-theme-mux/media-theme-mux';
2
+ import './dialog';
3
+ import { getSrcFromPlaybackId, getPosterURLFromPlaybackId, getStoryboardURLFromPlaybackId } from './helpers';
4
+ import { html } from './html';
5
+ // @ts-ignore
6
+ import cssStr from './styles.css';
7
+ import { i18n, stylePropsToString } from './utils';
8
+
9
+ import type { MuxTemplateProps } from './types';
10
+ import { StreamTypes } from '@mux/playback-core';
11
+
12
+ export const template = (props: MuxTemplateProps) => html`
13
+ <style>
14
+ ${cssStr}
15
+ </style>
16
+ ${content(props)}
17
+ `;
18
+
19
+ export const content = (props: MuxTemplateProps) => html`
20
+ <media-theme-mux
21
+ audio="${props.audio || false}"
22
+ style="${stylePropsToString({
23
+ '--primary-color': props.primaryColor,
24
+ '--secondary-color': props.secondaryColor,
25
+ '--mux-time-range-padding': props.secondaryColor ? '0' : null,
26
+ '--media-range-track-border-radius': props.secondaryColor ? '0' : null,
27
+ }) ?? false}"
28
+ class="size-${props.playerSize}"
29
+ stream-type="${props.streamType}"
30
+ player-size="${props.playerSize}"
31
+ has-captions="${props.hasCaptions}"
32
+ default-hidden-captions="${props.defaultHiddenCaptions}"
33
+ forward-seek-offset="${props.forwardSeekOffset}"
34
+ backward-seek-offset="${props.backwardSeekOffset}"
35
+ >
36
+ <mux-video
37
+ slot="media"
38
+ crossorigin
39
+ playsinline
40
+ autoplay="${props.autoplay ?? false}"
41
+ muted="${props.muted ?? false}"
42
+ loop="${props.loop ?? false}"
43
+ preload="${props.preload ?? false}"
44
+ debug="${props.debug ?? false}"
45
+ prefer-mse="${props.preferMse ?? false}"
46
+ start-time="${props.startTime != null ? props.startTime : false}"
47
+ metadata-video-id="${props.metadataVideoId ?? props.metadata?.video_id ?? false}"
48
+ metadata-video-title="${props.metadataVideoTitle ?? props.metadata?.video_title ?? false}"
49
+ metadata-viewer-user-id="${props.metadataViewerUserId ?? props.metadata?.viewer_user_id ?? false}"
50
+ beacon-collection-domain="${props.beaconCollectionDomain ?? false}"
51
+ player-software-name="${props.playerSoftwareName}"
52
+ player-software-version="${props.playerSoftwareVersion}"
53
+ env-key="${props.envKey ?? false}"
54
+ stream-type="${props.streamType ?? false}"
55
+ custom-domain="${props.customDomain ?? false}"
56
+ src="${!!props.src
57
+ ? props.src
58
+ : props.playbackId
59
+ ? getSrcFromPlaybackId(props.playbackId, { domain: props.customDomain, token: props.tokens.playback })
60
+ : false}"
61
+ poster="${!!props.poster
62
+ ? props.poster
63
+ : props.playbackId && !props.audio
64
+ ? getPosterURLFromPlaybackId(props.playbackId, {
65
+ domain: props.customDomain,
66
+ thumbnailTime: props.thumbnailTime ?? props.startTime,
67
+ token: props.tokens.thumbnail,
68
+ })
69
+ : false}"
70
+ cast-src="${!!props.src
71
+ ? props.src
72
+ : props.playbackId
73
+ ? getSrcFromPlaybackId(props.playbackId, { domain: props.customDomain, token: props.tokens.playback })
74
+ : false}"
75
+ cast-stream-type="${[StreamTypes.LIVE, StreamTypes.LL_LIVE].includes(props.streamType as any) ? 'live' : false}"
76
+ >
77
+ ${props.playbackId &&
78
+ !props.audio &&
79
+ ![StreamTypes.LIVE, StreamTypes.LL_LIVE, StreamTypes.DVR, StreamTypes.LL_DVR].includes(props.streamType as any)
80
+ ? html`<track
81
+ label="thumbnails"
82
+ default
83
+ kind="metadata"
84
+ src="${getStoryboardURLFromPlaybackId(props.playbackId, {
85
+ domain: props.customDomain,
86
+ token: props.tokens.storyboard,
87
+ })}"
88
+ />`
89
+ : html``}
90
+ </mux-video>
91
+ <button
92
+ slot="seek-to-live-button"
93
+ aria-disabled="${props.inLiveWindow}"
94
+ onclick="${function (this: HTMLButtonElement, evt: Event) {
95
+ props.onSeekToLive?.(evt);
96
+ if (props.paused) {
97
+ const playRequestEvt = new CustomEvent('mediaplayrequest', { composed: true, bubbles: true });
98
+ (this as HTMLButtonElement).dispatchEvent(playRequestEvt);
99
+ }
100
+ }}"
101
+ class="mxp-seek-to-live-button"
102
+ >
103
+ Live
104
+ </button>
105
+ <mxp-dialog
106
+ no-auto-hide
107
+ open="${props.isDialogOpen}"
108
+ onclose="${props.onCloseErrorDialog}"
109
+ oninitfocus="${props.onInitFocusDialog}"
110
+ >
111
+ ${props.dialog?.title ? html`<h3>${props.dialog.title}</h3>` : html``}
112
+ <p>
113
+ ${props.dialog?.message}
114
+ ${props.dialog?.linkUrl
115
+ ? html`<a
116
+ href="${props.dialog.linkUrl}"
117
+ target="_blank"
118
+ rel="external noopener"
119
+ aria-label="${props.dialog.linkText ?? ''} ${i18n(`(opens in a new window)`)}"
120
+ >${props.dialog.linkText ?? props.dialog.linkUrl}</a
121
+ >`
122
+ : html``}
123
+ </p>
124
+ </mxp-dialog>
125
+ </media-theme-mux>
126
+ `;
package/src/types.d.ts ADDED
@@ -0,0 +1,52 @@
1
+ import type MuxVideoElement, { MediaError } from '@mux/mux-video';
2
+
3
+ export type MuxPlayerProps = Partial<MuxVideoElement> & {
4
+ preferMse?: boolean;
5
+ };
6
+
7
+ export type MuxTemplateProps = Partial<MuxPlayerProps> & {
8
+ audio: boolean;
9
+ playerSize: string;
10
+ showLoading: boolean;
11
+ hasCaptions: boolean;
12
+ supportsAirPlay: boolean;
13
+ supportsVolume: boolean;
14
+ thumbnailTime: number;
15
+ primaryColor: string;
16
+ secondaryColor: string;
17
+ forwardSeekOffset: number;
18
+ backwardSeekOffset: number;
19
+ isDialogOpen: boolean;
20
+ defaultHiddenCaptions: boolean;
21
+ onCloseErrorDialog: (evt: CustomEvent) => void;
22
+ onInitFocusDialog: (evt: CustomEvent) => void;
23
+ dialog: DialogOptions;
24
+ inLiveWindow: boolean;
25
+ onSeekToLive: (_evt: Event) => void;
26
+ tokens: {
27
+ playback?: string;
28
+ thumbnail?: string;
29
+ storyboard?: string;
30
+ };
31
+ metadataVideoId: string;
32
+ metadataVideoTitle: string;
33
+ metadataViewerUserId: string;
34
+ };
35
+
36
+ export type DialogOptions = {
37
+ title?: string;
38
+ message?: string;
39
+ linkText?: string;
40
+ linkUrl?: string;
41
+ };
42
+
43
+ export type DevlogOptions = {
44
+ message?: string;
45
+ file?: string;
46
+ };
47
+
48
+ export type ErrorEvent = {
49
+ player_error?: MediaError;
50
+ player_error_code?: number;
51
+ player_error_message?: string;
52
+ };
package/src/utils.ts ADDED
@@ -0,0 +1,100 @@
1
+ // @ts-ignore
2
+ import lang from '../lang/en.json';
3
+
4
+ const DEFAULT_LOCALE = 'en';
5
+
6
+ // NL example
7
+ // lang = {
8
+ // "Network Error": "Netwerk Fout",
9
+ // };
10
+ export function i18n(str: string, translate = true): any {
11
+ const message = translate ? lang?.[str] ?? str : str;
12
+ const locale = translate ? lang.code : DEFAULT_LOCALE;
13
+ return new IntlMessageFormat(message, locale);
14
+ }
15
+
16
+ /**
17
+ * Poor man's IntlMessageFormat, enrich if need be.
18
+ * @see https://formatjs.io/docs/intl-messageformat/
19
+ */
20
+ class IntlMessageFormat {
21
+ message: string;
22
+ locale: string;
23
+
24
+ constructor(message: string, locale = lang.code ?? DEFAULT_LOCALE) {
25
+ this.message = message;
26
+ this.locale = locale;
27
+ }
28
+
29
+ format(values: Record<string, any>): string {
30
+ return this.message.replace(/\{(\w+)\}/g, (match, key) => {
31
+ return values[key] ?? '';
32
+ });
33
+ }
34
+
35
+ toString() {
36
+ return this.message;
37
+ }
38
+ }
39
+
40
+ export function stylePropsToString(props: any) {
41
+ let style = '';
42
+ Object.entries(props).forEach(([key, value]) => {
43
+ if (value == null) return;
44
+ style += `${kebabCase(key)}: ${value}; `;
45
+ });
46
+ return style ? style.trim() : undefined;
47
+ }
48
+
49
+ export function kebabCase(name: string) {
50
+ return name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
51
+ }
52
+
53
+ export function camelCase(name: string) {
54
+ return name.replace(/[-_]([a-z])/g, ($0, $1) => $1.toUpperCase());
55
+ }
56
+
57
+ let idCounter = 0;
58
+ export function uniqueId(prefix: string) {
59
+ const id = ++idCounter;
60
+ return `${prefix}${id}`;
61
+ }
62
+
63
+ export function toNumberOrUndefined(val: any) {
64
+ if (val == null) return undefined;
65
+ const num = +val;
66
+ return !Number.isNaN(num) ? num : undefined;
67
+ }
68
+
69
+ export function toQuery(obj: Record<string, any>) {
70
+ const params = toParams(obj).toString();
71
+ return params ? '?' + params : '';
72
+ }
73
+
74
+ export function toParams(obj: Record<string, any>) {
75
+ const params: Record<string, any> = {};
76
+ for (const key in obj) {
77
+ if (obj[key] != null) params[key] = obj[key];
78
+ }
79
+ return new URLSearchParams(params);
80
+ }
81
+
82
+ export function parseJwt(token: string) {
83
+ const base64Url = token.split('.')[1];
84
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
85
+ const jsonPayload = decodeURIComponent(
86
+ atob(base64)
87
+ .split('')
88
+ .map(function (c) {
89
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
90
+ })
91
+ .join('')
92
+ );
93
+ return JSON.parse(jsonPayload);
94
+ }
95
+
96
+ export const containsComposedNode = (rootNode: Node, childNode?: Node | Element | null): boolean => {
97
+ if (!rootNode || !childNode) return false;
98
+ if (rootNode.contains(childNode)) return true;
99
+ return containsComposedNode(rootNode, (childNode.getRootNode() as ShadowRoot).host);
100
+ };
@@ -0,0 +1,298 @@
1
+ import { VideoEvents } from '@mux/mux-video';
2
+ import type MuxVideoElement from '@mux/mux-video';
3
+ import * as logger from './logger';
4
+
5
+ export type CastOptions = {
6
+ receiverApplicationId: string;
7
+ autoJoinPolicy: string;
8
+ androidReceiverCompatible: boolean;
9
+ language: string;
10
+ resumeSavedSession: boolean;
11
+ };
12
+
13
+ export type MuxVideoElementExt = MuxVideoElement & {
14
+ requestCast(options: CastOptions): Promise<undefined>;
15
+ };
16
+
17
+ const AllowedVideoAttributes = {
18
+ AUTOPLAY: 'autoplay',
19
+ CROSSORIGIN: 'crossorigin',
20
+ LOOP: 'loop',
21
+ MUTED: 'muted',
22
+ PLAYSINLINE: 'playsinline',
23
+ SRC: 'src',
24
+ POSTER: 'poster',
25
+ PRELOAD: 'preload',
26
+ };
27
+
28
+ const CustomVideoAttributes = {
29
+ VOLUME: 'volume',
30
+ PLAYBACKRATE: 'playbackrate',
31
+ // This muted attribute also reflects to the muted property while the muted
32
+ // attribute on a native video element reflects only to video.defaultMuted.
33
+ MUTED: 'muted',
34
+ };
35
+
36
+ const AllowedVideoEvents = VideoEvents.filter((type) => type !== 'error');
37
+ const AllowedVideoAttributeNames = Object.values(AllowedVideoAttributes);
38
+ const CustomVideoAttributesNames = Object.values(CustomVideoAttributes);
39
+
40
+ /**
41
+ * Gets called from mux-player when mux-video is rendered and upgraded.
42
+ * We might just merge VideoApiElement in MuxPlayerElement and remove this?
43
+ */
44
+ export function initVideoApi(el: VideoApiElement) {
45
+ el.querySelectorAll(':scope > track').forEach((track) => {
46
+ el.media?.append(track.cloneNode());
47
+ });
48
+
49
+ // The video events are dispatched on the VideoApiElement instance.
50
+ // This makes it possible to add event listeners before the element is upgraded.
51
+ AllowedVideoEvents.forEach((type) => {
52
+ el.media?.addEventListener(type, (evt) => {
53
+ el.dispatchEvent(new Event(evt.type));
54
+ });
55
+ });
56
+ }
57
+
58
+ class VideoApiElement extends HTMLElement {
59
+ static get observedAttributes() {
60
+ return [...AllowedVideoAttributeNames, ...CustomVideoAttributesNames];
61
+ }
62
+
63
+ /**
64
+ * Create a HTMLVideoElement like API with opt-in methods to expose publicly.
65
+ * This class is intentionally not extending MuxVideoElement but composing it
66
+ * to opt in methods and not expose too much. More flexibility in the future.
67
+ */
68
+ constructor() {
69
+ super();
70
+
71
+ this.querySelectorAll(':scope > track').forEach((track) => {
72
+ this.media?.append(track.cloneNode());
73
+ });
74
+
75
+ // Watch for child adds/removes and update the native element if necessary
76
+ /** @type {(mutationList: MutationRecord[]) => void} */
77
+ const mutationCallback = (mutationsList: MutationRecord[]) => {
78
+ for (const mutation of mutationsList) {
79
+ if (mutation.type === 'childList') {
80
+ // Child being removed
81
+ mutation.removedNodes.forEach((node) => {
82
+ const track = this.media?.querySelector(`track[src="${(node as HTMLTrackElement).src}"]`);
83
+ if (track) this.media?.removeChild(track);
84
+ });
85
+
86
+ mutation.addedNodes.forEach((node) => {
87
+ this.media?.append(node.cloneNode());
88
+ });
89
+ }
90
+ }
91
+ };
92
+
93
+ const observer = new MutationObserver(mutationCallback);
94
+ observer.observe(this, { childList: true, subtree: true });
95
+ }
96
+
97
+ attributeChangedCallback(attrName: string, oldValue: string | null, newValue: string) {
98
+ switch (attrName) {
99
+ case CustomVideoAttributes.MUTED: {
100
+ if (this.media) {
101
+ this.media.muted = newValue != null;
102
+ }
103
+ return;
104
+ }
105
+ case CustomVideoAttributes.VOLUME: {
106
+ const val = +newValue;
107
+ if (this.media && !Number.isNaN(val)) {
108
+ this.media.volume = val;
109
+ }
110
+ return;
111
+ }
112
+ case CustomVideoAttributes.PLAYBACKRATE: {
113
+ const val = +newValue;
114
+ if (this.media && !Number.isNaN(val)) {
115
+ this.media.playbackRate = val;
116
+ }
117
+ return;
118
+ }
119
+ }
120
+ }
121
+
122
+ play() {
123
+ return this.media?.play();
124
+ }
125
+
126
+ pause() {
127
+ this.media?.pause();
128
+ }
129
+
130
+ requestCast(options: CastOptions) {
131
+ return this.media?.requestCast(options);
132
+ }
133
+
134
+ get media(): MuxVideoElementExt | null | undefined {
135
+ return this.shadowRoot?.querySelector('mux-video');
136
+ }
137
+
138
+ /**
139
+ * @deprecated please use .media instead
140
+ */
141
+ get video(): MuxVideoElementExt | null | undefined {
142
+ logger.warn('<mux-player>.video is deprecated, please use .media instead');
143
+ return this.media;
144
+ }
145
+
146
+ get paused() {
147
+ return this.media?.paused ?? true;
148
+ }
149
+
150
+ get duration() {
151
+ return this.media?.duration ?? NaN;
152
+ }
153
+
154
+ get ended() {
155
+ return this.media?.ended ?? false;
156
+ }
157
+
158
+ get buffered() {
159
+ return this.media?.buffered;
160
+ }
161
+
162
+ get readyState() {
163
+ return this.media?.readyState ?? 0;
164
+ }
165
+
166
+ get videoWidth() {
167
+ return this.media?.videoWidth;
168
+ }
169
+
170
+ get videoHeight() {
171
+ return this.media?.videoHeight;
172
+ }
173
+
174
+ get currentTime() {
175
+ return this.media?.currentTime ?? 0;
176
+ }
177
+
178
+ set currentTime(val) {
179
+ if (this.media) {
180
+ this.media.currentTime = Number(val);
181
+ }
182
+ }
183
+
184
+ get volume() {
185
+ return this.media?.volume ?? 1;
186
+ }
187
+
188
+ set volume(val) {
189
+ if (this.media) {
190
+ this.media.volume = Number(val);
191
+ }
192
+ }
193
+
194
+ get src() {
195
+ return getVideoAttribute(this, AllowedVideoAttributes.SRC);
196
+ }
197
+
198
+ set src(val) {
199
+ this.setAttribute(AllowedVideoAttributes.SRC, `${val}`);
200
+ }
201
+
202
+ get poster() {
203
+ return getVideoAttribute(this, AllowedVideoAttributes.POSTER) ?? '';
204
+ }
205
+
206
+ set poster(val) {
207
+ this.setAttribute(AllowedVideoAttributes.POSTER, `${val}`);
208
+ }
209
+
210
+ get playbackRate() {
211
+ return this.media?.playbackRate ?? 1;
212
+ }
213
+
214
+ set playbackRate(val) {
215
+ if (this.media) {
216
+ this.media.playbackRate = Number(val);
217
+ }
218
+ }
219
+
220
+ get crossOrigin() {
221
+ return getVideoAttribute(this, AllowedVideoAttributes.CROSSORIGIN);
222
+ }
223
+
224
+ set crossOrigin(val) {
225
+ this.setAttribute(AllowedVideoAttributes.CROSSORIGIN, `${val}`);
226
+ }
227
+
228
+ get autoplay() {
229
+ return getVideoAttribute(this, AllowedVideoAttributes.AUTOPLAY) != null;
230
+ }
231
+
232
+ set autoplay(val) {
233
+ if (val) {
234
+ this.setAttribute(AllowedVideoAttributes.AUTOPLAY, typeof val === 'string' ? val : '');
235
+ } else {
236
+ // Remove boolean attribute if false, 0, '', null, undefined.
237
+ this.removeAttribute(AllowedVideoAttributes.AUTOPLAY);
238
+ }
239
+ }
240
+
241
+ get loop() {
242
+ return getVideoAttribute(this, AllowedVideoAttributes.LOOP) != null;
243
+ }
244
+
245
+ set loop(val) {
246
+ if (val) {
247
+ this.setAttribute(AllowedVideoAttributes.LOOP, '');
248
+ } else {
249
+ // Remove boolean attribute if false, 0, '', null, undefined.
250
+ this.removeAttribute(AllowedVideoAttributes.LOOP);
251
+ }
252
+ }
253
+
254
+ get muted() {
255
+ return getVideoAttribute(this, AllowedVideoAttributes.MUTED) != null;
256
+ }
257
+
258
+ set muted(val) {
259
+ if (val) {
260
+ this.setAttribute(AllowedVideoAttributes.MUTED, '');
261
+ } else {
262
+ // Remove boolean attribute if false, 0, '', null, undefined.
263
+ this.removeAttribute(AllowedVideoAttributes.MUTED);
264
+ }
265
+ }
266
+
267
+ get playsInline() {
268
+ return getVideoAttribute(this, AllowedVideoAttributes.PLAYSINLINE) != null;
269
+ }
270
+
271
+ set playsInline(val) {
272
+ if (val) {
273
+ this.setAttribute(AllowedVideoAttributes.PLAYSINLINE, '');
274
+ } else {
275
+ // Remove boolean attribute if false, 0, '', null, undefined.
276
+ this.removeAttribute(AllowedVideoAttributes.PLAYSINLINE);
277
+ }
278
+ }
279
+
280
+ get preload() {
281
+ return getVideoAttribute(this, AllowedVideoAttributes.PRELOAD);
282
+ }
283
+
284
+ set preload(val) {
285
+ if (val) {
286
+ this.setAttribute(AllowedVideoAttributes.PRELOAD, val);
287
+ } else {
288
+ // Remove boolean attribute if false, 0, '', null, undefined.
289
+ this.removeAttribute(AllowedVideoAttributes.PRELOAD);
290
+ }
291
+ }
292
+ }
293
+
294
+ function getVideoAttribute(el: VideoApiElement, name: string) {
295
+ return el.media ? el.media.getAttribute(name) : el.getAttribute(name);
296
+ }
297
+
298
+ export default VideoApiElement;