Skip to content

Commit

Permalink
fix(player/react): server render media sources
Browse files Browse the repository at this point in the history
closes #1260
  • Loading branch information
mihar-22 committed Jul 24, 2024
1 parent cefeafc commit 1efe43d
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 14 deletions.
41 changes: 30 additions & 11 deletions packages/react/src/components/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
useStateContext,
type ReactElementProps,
} from 'maverick.js/react';
import { isString } from 'maverick.js/std';
import { mediaState, type MediaProviderLoader } from 'vidstack';

import { useMediaContext } from '../hooks/use-media-context';
Expand All @@ -22,6 +23,7 @@ const MediaProviderBridge = createReactComponent(MediaProviderInstance);
export interface MediaProviderProps
extends Omit<ReactElementProps<MediaProviderInstance>, 'loaders'> {
loaders?: Array<{ new (): MediaProviderLoader }>;
iframeProps?: React.IframeHTMLAttributes<HTMLIFrameElement>;
mediaProps?: React.HTMLAttributes<HTMLMediaElement>;
children?: React.ReactNode;
ref?: React.Ref<MediaProviderInstance>;
Expand All @@ -39,14 +41,14 @@ export interface MediaProviderProps
* ```
*/
const MediaProvider = React.forwardRef<MediaProviderInstance, MediaProviderProps>(
({ loaders = [], children, mediaProps, ...props }, forwardRef) => {
({ loaders = [], children, iframeProps, mediaProps, ...props }, forwardRef) => {
const reactLoaders = React.useMemo(() => loaders.map((Loader) => new Loader()), loaders);

return (
<MediaProviderBridge {...props} loaders={reactLoaders} ref={forwardRef}>
{(props, instance) => (
<div {...props}>
<MediaOutlet {...mediaProps} provider={instance} />
<MediaOutlet provider={instance} mediaProps={mediaProps} iframeProps={iframeProps} />
{children}
</div>
)}
Expand All @@ -62,15 +64,18 @@ export { MediaProvider };
* MediaOutlet
* -----------------------------------------------------------------------------------------------*/

interface MediaOutletProps extends React.HTMLAttributes<HTMLMediaElement> {
interface MediaOutletProps {
provider: MediaProviderInstance;
mediaProps?: React.HTMLAttributes<HTMLMediaElement>;
iframeProps?: React.IframeHTMLAttributes<HTMLIFrameElement>;
}

function MediaOutlet({ provider, ...props }: MediaOutletProps) {
const { crossOrigin, poster, remotePlaybackInfo, nativeControls, viewType } =
function MediaOutlet({ provider, mediaProps, iframeProps }: MediaOutletProps) {
const { sources, crossOrigin, poster, remotePlaybackInfo, nativeControls, viewType } =
useStateContext(mediaState),
{ loader } = provider.$state,
{ $provider: $$provider, $providerSetup: $$providerSetup } = useMediaContext(),
$sources = useSignal(sources),
$nativeControls = useSignal(nativeControls),
$crossOrigin = useSignal(crossOrigin),
$poster = useSignal(poster),
Expand All @@ -86,7 +91,8 @@ function MediaOutlet({ provider, ...props }: MediaOutletProps) {
isEmbed = isYouTubeEmbed || isVimeoEmbed,
isRemotion = $loader?.name === 'remotion',
isGoogleCast = $loader?.name === 'google-cast',
[googleCastIconPaths, setGoogleCastIconPaths] = React.useState('');
[googleCastIconPaths, setGoogleCastIconPaths] = React.useState(''),
[hasMounted, setHasMounted] = React.useState(false);

React.useEffect(() => {
if (!isGoogleCast || googleCastIconPaths) return;
Expand All @@ -95,6 +101,10 @@ function MediaOutlet({ provider, ...props }: MediaOutletProps) {
});
}, [isGoogleCast]);

React.useEffect(() => {
setHasMounted(true);
}, []);

if (isGoogleCast) {
return (
<div
Expand Down Expand Up @@ -136,7 +146,11 @@ function MediaOutlet({ provider, ...props }: MediaOutletProps) {
React.Fragment,
null,
React.createElement('iframe', {
className: isYouTubeEmbed ? 'vds-youtube' : 'vds-vimeo',
...iframeProps,
className:
(iframeProps?.className ? `${iframeProps.className} ` : '') + isYouTubeEmbed
? 'vds-youtube'
: 'vds-vimeo',
suppressHydrationWarning: true,
tabIndex: !$nativeControls ? -1 : undefined,
'aria-hidden': 'true',
Expand All @@ -151,14 +165,19 @@ function MediaOutlet({ provider, ...props }: MediaOutletProps) {
)
: $mediaType
? React.createElement($mediaType === 'audio' ? 'audio' : 'video', {
...props,
...mediaProps,
controls: $nativeControls ? true : null,
crossOrigin: typeof $crossOrigin === 'boolean' ? '' : $crossOrigin,
poster: $mediaType === 'video' && $nativeControls && $poster ? $poster : null,
preload: 'none',
'aria-hidden': 'true',
suppressHydrationWarning: true,
ref(el: HTMLElement) {
children: !hasMounted
? $sources.map(({ src, type }) =>
isString(src) ? (
<source src={src} type={type !== '?' ? type : undefined} key={src} />
) : null,
)
: null,
ref(el: HTMLMediaElement) {
provider.load(el);
},
})
Expand Down
3 changes: 3 additions & 0 deletions packages/vidstack/src/components/provider/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ export class MediaProvider extends Component<MediaProviderProps, MediaProviderSt

@method
load(target: HTMLElement | null | undefined) {
// Hide underlying provider element from screen readers.
target?.setAttribute('aria-hidden', 'true');

// Use a RAF here to prevent hot reloads resetting provider.
window.cancelAnimationFrame(this._loadRafId);
this._loadRafId = requestAnimationFrame(() => this._runLoader(target));
Expand Down
3 changes: 0 additions & 3 deletions packages/vidstack/src/elements/define/provider-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,6 @@ export class MediaProviderElement extends Host(HTMLElement, MediaProvider) {
const audio =
this._target instanceof HTMLAudioElement ? this._target : document.createElement('audio');

setAttribute(audio, 'preload', 'none');
setAttribute(audio, 'aria-hidden', 'true');

const { controls, crossOrigin } = this._media.$state;
effect(() => {
setAttribute(audio, 'controls', controls());
Expand Down
4 changes: 4 additions & 0 deletions packages/vidstack/styles/player/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@
height: 100%;
}

[data-media-provider] iframe:not([src]) {
display: none;
}

iframe.vds-youtube[data-no-controls] {
height: 1000%;
}
Expand Down

0 comments on commit 1efe43d

Please sign in to comment.