Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/core/src/dom/media/castable/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <Base extends CastableMediaSuperclass>(
export const CastableMediaMixin = <Base extends CastableMediaHostConstructor>(
superclass: Base
): MixinReturn<Base, CastableMediaProps> => {
): MixinReturn<Base, CastableMedia> => {
class CastableMedia extends superclass {
#castOptions = getDefaultCastOptions();
#castCustomData: Record<string, unknown> | null | undefined;
Expand Down Expand Up @@ -222,5 +222,5 @@ export const CastableMediaMixin = <Base extends CastableMediaSuperclass>(
}
}

return CastableMedia as unknown as MixinReturn<Base, CastableMediaProps>;
return CastableMedia as unknown as MixinReturn<Base, CastableMedia>;
};
28 changes: 19 additions & 9 deletions packages/core/src/dom/media/castable/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown> | 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;
Expand Down Expand Up @@ -32,20 +46,16 @@ export interface CastableMediaBase extends EventTarget {
querySelector<K extends keyof HTMLElementTagNameMap>(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<string, unknown> | 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;
}
12 changes: 10 additions & 2 deletions packages/core/src/dom/media/dash/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<dashjs.MediaPlayerClass, HTMLVideoElement>
implements MediaEngineHost<dashjs.MediaPlayerClass, HTMLVideoElement>, DashMediaProps
{
#engine: dashjs.MediaPlayerClass;
#src = '';
#src = dashMediaDefaultProps.src;

constructor() {
super();
Expand Down
32 changes: 25 additions & 7 deletions packages/core/src/dom/media/hls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any>;
debug: boolean;
preload: PreloadType;
}

export const hlsMediaDefaultProps: HlsMediaProps = {
src: '',
type: undefined,
preferPlayback: 'mse',
config: {},
debug: false,
preload: 'metadata',
};
Comment thread
mihar-22 marked this conversation as resolved.

export class HlsMedia extends HTMLVideoElementHost implements HlsMediaProps {
#delegate: HlsJsMedia | NativeHlsMedia | null = null;
#src = '';
#type: SourceType | undefined;
#preferPlayback: PlaybackType | undefined = 'mse';
#config: Record<string, any> = {};
#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<void> | null;
#prevEngineProps?: Record<string, any> | null;

Expand Down
22 changes: 18 additions & 4 deletions packages/core/src/dom/media/mux/index.ts
Original file line number Diff line number Diff line change
@@ -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';
}
29 changes: 20 additions & 9 deletions packages/core/src/dom/media/mux/mux-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<HlsMedia['engine'], HTMLMediaElement> {
readonly debug: boolean;
attach(target: HTMLMediaElement): void;
detach(): void;
load(): void;
}

export interface MuxDataMediaProps {
MuxDataSdk: MuxDataSdk | undefined;
beaconCollectionDomain: string | undefined;
Expand All @@ -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<HlsMedia['engine'], HTMLMediaElement> {
readonly debug: boolean;
attach(target: HTMLMediaElement): void;
detach(): void;
load(): void;
}

export const MuxDataMediaMixin: Mixin<MuxDataMediaHost, MuxDataMediaProps> = (BaseClass) => {
class MuxDataMedia extends BaseClass {
#MuxDataSdk: MuxDataSdk | undefined = Mux;
Expand Down
16 changes: 13 additions & 3 deletions packages/core/src/dom/media/native-hls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 2 additions & 4 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
26 changes: 15 additions & 11 deletions packages/react/src/media/dash-video/index.tsx
Original file line number Diff line number Diff line change
@@ -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<VideoHTMLAttributes<HTMLVideoElement>> &
InferClassProps<typeof DashMedia>;
export interface DashVideoProps
extends Omit<VideoHTMLAttributes<HTMLVideoElement>, keyof DashMediaProps>,
Partial<DashMediaProps> {
children?: ReactNode;
}

export const DashVideo = forwardRef<HTMLVideoElement, DashVideoProps>(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 (
<video ref={composedRef} {...mediaProps(mediaApi, DashMedia, props)}>
<video ref={composedRef} {...htmlProps}>
{children}
</video>
);
Expand Down
25 changes: 15 additions & 10 deletions packages/react/src/media/hls-video/index.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
'use client';

import { HlsMedia } from '@videojs/core/dom/media/hls';
import type { InferClassProps } from '@videojs/utils/types';
import type { PropsWithChildren, VideoHTMLAttributes } from 'react';
import type { HlsMediaProps } from '@videojs/core/dom/media/hls';
import { HlsMedia, hlsMediaDefaultProps } from '@videojs/core/dom/media/hls';
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 HlsVideoProps = PropsWithChildren<VideoHTMLAttributes<HTMLVideoElement>> & InferClassProps<typeof HlsMedia>;
export interface HlsVideoProps
extends Omit<VideoHTMLAttributes<HTMLVideoElement>, keyof HlsMediaProps>,
Partial<HlsMediaProps> {
children?: ReactNode;
}

export const HlsVideo = forwardRef<HTMLVideoElement, HlsVideoProps>(function HlsVideo({ children, ...props }, ref) {
const mediaApi = useMediaInstance(HlsMedia);

const composedRef = useComposedRefs(attachMediaElement(mediaApi), ref);
const media = useMediaInstance(HlsMedia);
const attachRef = useAttachMedia(media);
const composedRef = useComposedRefs(attachRef, ref);
const htmlProps = useSyncProps(media, props, hlsMediaDefaultProps);

return (
<video ref={composedRef} {...mediaProps(mediaApi, HlsMedia, props)}>
<video ref={composedRef} {...htmlProps}>
{children}
</video>
);
Expand Down
34 changes: 21 additions & 13 deletions packages/react/src/media/mux-audio/index.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
import { MuxAudioMedia } from '@videojs/core/dom/media/mux';
import type { InferClassProps } from '@videojs/utils/types';
import type { AudioHTMLAttributes, PropsWithChildren } from 'react';
'use client';

import type { MuxMediaProps } from '@videojs/core/dom/media/mux';
import { MuxAudioMedia, muxMediaDefaultProps } from '@videojs/core/dom/media/mux';
import type { AudioHTMLAttributes, ReactNode } 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 MuxAudioProps = PropsWithChildren<AudioHTMLAttributes<HTMLAudioElement>> &
InferClassProps<typeof MuxAudioMedia>;

export const MuxAudio = forwardRef<HTMLAudioElement, MuxAudioProps>(({ children, ...props }, ref) => {
const mediaApi = useMediaInstance(MuxAudioMedia);
export interface MuxAudioProps
extends Omit<AudioHTMLAttributes<HTMLAudioElement>, keyof MuxMediaProps>,
Partial<MuxMediaProps> {
children?: ReactNode;
}

const composedRef = useComposedRefs(attachMediaElement(mediaApi), ref);
export const MuxAudio = forwardRef<HTMLAudioElement, MuxAudioProps>(function MuxAudio({ children, ...props }, ref) {
const media = useMediaInstance(MuxAudioMedia);
const attachRef = useAttachMedia(media);
const composedRef = useComposedRefs(attachRef, ref);
const htmlProps = useSyncProps(media, props, muxMediaDefaultProps);

return (
<audio ref={composedRef} {...mediaProps(mediaApi, MuxAudioMedia, props)}>
<audio ref={composedRef} {...htmlProps}>
{children}
</audio>
);
});

export default MuxAudio;
export namespace MuxAudio {
export type Props = MuxAudioProps;
}
Loading
Loading