diff --git a/packages/core/src/dom/media/castable/index.ts b/packages/core/src/dom/media/castable/index.ts index e3ccf130d..92c15cd10 100644 --- a/packages/core/src/dom/media/castable/index.ts +++ b/packages/core/src/dom/media/castable/index.ts @@ -2,14 +2,14 @@ import type { MixinReturn } from '@videojs/utils/types'; import type { RemotePlaybackLike } from '../predicate'; import { GoogleCastProvider } from './google-cast-provider'; import { RemotePlayback } from './remote-playback'; -import type { CastableMediaProps, CastableMediaSuperclass } from './types'; +import type { CastableMedia, CastableMediaHostConstructor } from './types'; import { getDefaultCastOptions, loadCastFramework, requiresCastFramework } from './utils'; export type { CastableMediaElement } from './types'; -export const CastableMediaMixin = ( +export const CastableMediaMixin = ( superclass: Base -): MixinReturn => { +): MixinReturn => { class CastableMedia extends superclass { #castOptions = getDefaultCastOptions(); #castCustomData: Record | null | undefined; @@ -222,5 +222,5 @@ export const CastableMediaMixin = ( } } - return CastableMedia as unknown as MixinReturn; + return CastableMedia as unknown as MixinReturn; }; diff --git a/packages/core/src/dom/media/castable/types.ts b/packages/core/src/dom/media/castable/types.ts index 9de8c744f..4247357ec 100644 --- a/packages/core/src/dom/media/castable/types.ts +++ b/packages/core/src/dom/media/castable/types.ts @@ -2,7 +2,21 @@ import type { RemotePlaybackLike } from '../predicate'; import type { RemotePlayback } from './remote-playback'; import type { CastOptions } from './utils'; -export interface CastableMediaBase extends EventTarget { +export interface CastableMediaProps { + castReceiver: string | undefined; + castContentType: string | undefined; + castStreamType: string | undefined; + castCustomData: Record | null | undefined; +} + +export const castableMediaDefaultProps: CastableMediaProps = { + castReceiver: undefined, + castContentType: undefined, + castStreamType: undefined, + castCustomData: undefined, +}; + +export interface CastableMediaHost extends EventTarget { readonly target: HTMLMediaElement | null; readonly remote: RemotePlaybackLike | undefined; title: string; @@ -32,20 +46,16 @@ export interface CastableMediaBase extends EventTarget { querySelector(selectors: K): HTMLElementTagNameMap[K] | null; } -export interface CastableMediaProps { +export interface CastableMedia extends CastableMediaProps { readonly remote: RemotePlayback | RemotePlaybackLike | undefined; readonly castOptions: CastOptions; - castReceiver: string | undefined; castSrc: string; - castContentType: string | undefined; - castStreamType: string | undefined; - castCustomData: Record | null | undefined; poster: string; title: string; } -export type CastableMediaElement = CastableMediaBase & CastableMediaProps; +export type CastableMediaElement = CastableMediaHost & CastableMedia; -export interface CastableMediaSuperclass { - new (...args: any[]): CastableMediaBase; +export interface CastableMediaHostConstructor { + new (...args: any[]): CastableMediaHost; } diff --git a/packages/core/src/dom/media/dash/index.ts b/packages/core/src/dom/media/dash/index.ts index dd05f35bf..68dfac5e8 100644 --- a/packages/core/src/dom/media/dash/index.ts +++ b/packages/core/src/dom/media/dash/index.ts @@ -2,12 +2,20 @@ import * as dashjs from 'dashjs'; import type { MediaEngineHost } from '../../../core/media/types'; import { HTMLVideoElementHost } from '../video-host'; +export interface DashMediaProps { + src: string; +} + +export const dashMediaDefaultProps: DashMediaProps = { + src: '', +}; + export class DashMedia extends HTMLVideoElementHost - implements MediaEngineHost + implements MediaEngineHost, DashMediaProps { #engine: dashjs.MediaPlayerClass; - #src = ''; + #src = dashMediaDefaultProps.src; constructor() { super(); diff --git a/packages/core/src/dom/media/hls/index.ts b/packages/core/src/dom/media/hls/index.ts index dfea24387..78710268f 100644 --- a/packages/core/src/dom/media/hls/index.ts +++ b/packages/core/src/dom/media/hls/index.ts @@ -22,14 +22,32 @@ export const SourceTypes = { MP4: 'video/mp4', }; -export class HlsMedia extends HTMLVideoElementHost { +export interface HlsMediaProps { + src: string; + type: SourceType | undefined; + preferPlayback: PlaybackType | undefined; + config: Record; + debug: boolean; + preload: PreloadType; +} + +export const hlsMediaDefaultProps: HlsMediaProps = { + src: '', + type: undefined, + preferPlayback: 'mse', + config: {}, + debug: false, + preload: 'metadata', +}; + +export class HlsMedia extends HTMLVideoElementHost implements HlsMediaProps { #delegate: HlsJsMedia | NativeHlsMedia | null = null; - #src = ''; - #type: SourceType | undefined; - #preferPlayback: PlaybackType | undefined = 'mse'; - #config: Record = {}; - #debug = false; - #preload: PreloadType = 'metadata'; + #src = hlsMediaDefaultProps.src; + #type = hlsMediaDefaultProps.type; + #preferPlayback = hlsMediaDefaultProps.preferPlayback; + #config = { ...hlsMediaDefaultProps.config }; + #debug = hlsMediaDefaultProps.debug; + #preload = hlsMediaDefaultProps.preload; #loadRequested?: Promise | null; #prevEngineProps?: Record | null; diff --git a/packages/core/src/dom/media/mux/index.ts b/packages/core/src/dom/media/mux/index.ts index e42432427..0fbdf8a12 100644 --- a/packages/core/src/dom/media/mux/index.ts +++ b/packages/core/src/dom/media/mux/index.ts @@ -1,14 +1,28 @@ import { CastableMediaMixin } from '../castable'; -import { HlsMedia } from '../hls'; -import { MuxDataMediaMixin } from './mux-data'; +import { type CastableMediaProps, castableMediaDefaultProps } from '../castable/types'; +import { HlsMedia, type HlsMediaProps, hlsMediaDefaultProps } from '../hls'; +import { MuxDataMediaMixin, type MuxDataMediaProps, muxDataMediaDefaultProps } from './mux-data'; -export class MuxVideoMedia extends MuxDataMediaMixin(CastableMediaMixin(HlsMedia)) { +export type { CastableMediaProps, HlsMediaProps, MuxDataMediaProps }; + +export interface MuxMediaProps extends HlsMediaProps, CastableMediaProps, MuxDataMediaProps { + castSrc: string | undefined; +} + +export const muxMediaDefaultProps: MuxMediaProps = { + ...hlsMediaDefaultProps, + ...castableMediaDefaultProps, + ...muxDataMediaDefaultProps, + castSrc: undefined, +}; + +export class MuxVideoMedia extends MuxDataMediaMixin(CastableMediaMixin(HlsMedia)) implements MuxMediaProps { static PLAYER_SOFTWARE_NAME = 'mux-video'; } // TODO: HlsMedia extends HTMLVideoElementHost, we should extend // HTMLAudioElementHost instead but this would require a HlsMediaMixin, // keep it simple for now. -export class MuxAudioMedia extends MuxDataMediaMixin(CastableMediaMixin(HlsMedia)) { +export class MuxAudioMedia extends MuxDataMediaMixin(CastableMediaMixin(HlsMedia)) implements MuxMediaProps { static PLAYER_SOFTWARE_NAME = 'mux-audio'; } diff --git a/packages/core/src/dom/media/mux/mux-data.ts b/packages/core/src/dom/media/mux/mux-data.ts index 69157a597..26dc07d71 100644 --- a/packages/core/src/dom/media/mux/mux-data.ts +++ b/packages/core/src/dom/media/mux/mux-data.ts @@ -5,15 +5,6 @@ import { Hls, type HlsMedia } from '../hls'; import { getPlayerVersion } from './env'; import type { MuxDataOptions, MuxDataSdk } from './types'; -const MUX_VIDEO_DOMAIN = 'mux.com'; - -export interface MuxDataMediaHost extends MediaEngineHost { - readonly debug: boolean; - attach(target: HTMLMediaElement): void; - detach(): void; - load(): void; -} - export interface MuxDataMediaProps { MuxDataSdk: MuxDataSdk | undefined; beaconCollectionDomain: string | undefined; @@ -25,6 +16,26 @@ export interface MuxDataMediaProps { metadata: MuxDataOptions['data'] | undefined; } +export const muxDataMediaDefaultProps: MuxDataMediaProps = { + MuxDataSdk: undefined, + beaconCollectionDomain: undefined, + disableCookies: false, + envKey: undefined, + playerSoftwareName: undefined, + playerSoftwareVersion: undefined, + playerInitTime: undefined, + metadata: undefined, +}; + +const MUX_VIDEO_DOMAIN = 'mux.com'; + +export interface MuxDataMediaHost extends MediaEngineHost { + readonly debug: boolean; + attach(target: HTMLMediaElement): void; + detach(): void; + load(): void; +} + export const MuxDataMediaMixin: Mixin = (BaseClass) => { class MuxDataMedia extends BaseClass { #MuxDataSdk: MuxDataSdk | undefined = Mux; diff --git a/packages/core/src/dom/media/native-hls/index.ts b/packages/core/src/dom/media/native-hls/index.ts index 7513d5409..955b3588c 100644 --- a/packages/core/src/dom/media/native-hls/index.ts +++ b/packages/core/src/dom/media/native-hls/index.ts @@ -3,9 +3,19 @@ import { NativeHlsMediaErrorsMixin } from './errors'; export type PreloadType = '' | 'none' | 'metadata' | 'auto'; -class NativeHlsMediaBase extends HTMLVideoElementHost { - #src = ''; - #preload: PreloadType = 'metadata'; +export interface NativeHlsMediaProps { + src: string; + preload: PreloadType; +} + +export const nativeHlsMediaDefaultProps: NativeHlsMediaProps = { + src: '', + preload: 'metadata', +}; + +class NativeHlsMediaBase extends HTMLVideoElementHost implements NativeHlsMediaProps { + #src = nativeHlsMediaDefaultProps.src; + #preload = nativeHlsMediaDefaultProps.preload; get engine() { return null; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index f75ce4c63..64c5e02b1 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -67,13 +67,11 @@ export { Time } from './ui/time'; export { TimeSlider } from './ui/time-slider'; export { Tooltip, type TooltipContextValue, useTooltipContext } from './ui/tooltip'; export { VolumeSlider } from './ui/volume-slider'; - -// Media utilities -export { attachMediaElement } from './utils/attach-media-element'; -export { mediaProps } from './utils/media-props'; // Utilities export { mergeProps } from './utils/merge-props'; export type { HTMLProps, RenderFunction, RenderProp, UIComponentProps } from './utils/types'; +// Media utilities +export { useAttachMedia } from './utils/use-attach-media'; export { composeRefs, useComposedRefs } from './utils/use-composed-refs'; export { useDestroy } from './utils/use-destroy'; export { useLatestRef } from './utils/use-latest-ref'; diff --git a/packages/react/src/media/dash-video/index.tsx b/packages/react/src/media/dash-video/index.tsx index 73abf2f13..818f58483 100644 --- a/packages/react/src/media/dash-video/index.tsx +++ b/packages/react/src/media/dash-video/index.tsx @@ -1,24 +1,28 @@ 'use client'; -import { DashMedia } from '@videojs/core/dom/media/dash'; -import type { InferClassProps } from '@videojs/utils/types'; -import type { PropsWithChildren, VideoHTMLAttributes } from 'react'; +import type { DashMediaProps } from '@videojs/core/dom/media/dash'; +import { DashMedia, dashMediaDefaultProps } from '@videojs/core/dom/media/dash'; +import type { ReactNode, VideoHTMLAttributes } from 'react'; import { forwardRef } from 'react'; -import { attachMediaElement } from '../../utils/attach-media-element'; -import { mediaProps } from '../../utils/media-props'; +import { useAttachMedia } from '../../utils/use-attach-media'; import { useComposedRefs } from '../../utils/use-composed-refs'; import { useMediaInstance } from '../../utils/use-media-instance'; +import { useSyncProps } from '../../utils/use-sync-props'; -export type DashVideoProps = PropsWithChildren> & - InferClassProps; +export interface DashVideoProps + extends Omit, keyof DashMediaProps>, + Partial { + children?: ReactNode; +} export const DashVideo = forwardRef(function DashVideo({ children, ...props }, ref) { - const mediaApi = useMediaInstance(DashMedia); - - const composedRef = useComposedRefs(attachMediaElement(mediaApi), ref); + const media = useMediaInstance(DashMedia); + const attachRef = useAttachMedia(media); + const composedRef = useComposedRefs(attachRef, ref); + const htmlProps = useSyncProps(media, props, dashMediaDefaultProps); return ( -