Skip to content
Closed
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
54 changes: 54 additions & 0 deletions examples/nextjs-with-typescript/components/renderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,60 @@ export const BooleanRenderer = ({
);
};

export const OptionalBooleanRenderer = ({
name,
value,
label,
onChange,
formatter = DefaultEnumFormatter
}: {
name: string;
value: boolean | undefined;
label?: string;
removeFalse?: boolean;
onChange: (obj: any) => void;
formatter?: (enumValue: boolean) => ReactNode;
}) => {
const labelStr = label ?? toWordsFromKeyName(name);
const values = [true, false];
return (
<div>
<label htmlFor={`${name}-control`}>
{labelStr} (<code>{name}</code>)
</label>
<div>
<input
id={`${name}-none-control`}
type="radio"
onChange={() => {
onChange(toChangeObject(name, undefined))}}
value=""
checked={value === undefined}
/>
<label htmlFor={`${name}-none-control`}>None</label>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: 😎 glad you acknowledged the "3rd state" of "optional booleans" (aka "unset")

{values.map((enumValue, i) => {
return (
<Fragment key={`${name}-${enumValue}`}>
<input
id={`${name}-${enumValue}-control`}
type="radio"
onChange={() => {
const changeValue = enumValue;
console.log("Selecting value:", changeValue, toChangeObject(name, changeValue));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick(blocking): remove the console.log before merging

onChange(toChangeObject(name, changeValue));
}}
value={enumValue.toString()}
checked={value === enumValue}
/>
<label htmlFor={`${name}-${enumValue}-control`}>{formatter(enumValue)}</label>
</Fragment>
);
})}
</div>
</div>
);
};

export const NumberRenderer = ({
name,
value,
Expand Down
5 changes: 5 additions & 0 deletions examples/nextjs-with-typescript/pages/MuxPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,11 @@ function MuxPlayerPage({ location }: Props) {
min={0}
step={1}
/>
<BooleanRenderer
value={state.capLevelToPlayerSize}
name="capLevelToPlayerSize"
onChange={genericOnChange}
></BooleanRenderer>
</div>
</main>
</>
Expand Down
38 changes: 26 additions & 12 deletions examples/nextjs-with-typescript/pages/mux-player.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
// @ts-nocheck
import Link from "next/link";
import Head from "next/head";
import "@mux/mux-player";
import { useState } from "react";
import { useRef, useState } from "react";
import MuxPlayerElement from "@mux/mux-player";
import { OptionalBooleanRenderer } from "../components/renderers";
import { Autoplay, MuxMediaPropTypes } from "../../../packages/playback-core/dist/types/types";

const INITIAL_DEBUG = false;
const INITIAL_MUTED = false;
const INITIAL_AUTOPLAY = false;
const INITIAL_MUTED = true;
const INITIAL_AUTOPLAY: Autoplay = false;
const INITIAL_PLAYBACK_ID = "g65IqSFtWdpGR100c2W8VUHrfIVWTNRen";
const INITIAL_CAP_LEVEL_TO_PLAYER_SIZE : boolean | undefined = undefined;

function MuxPlayerWCPage() {
// const mediaElRef = useRef(null);
const mediaElRef = useRef<MuxPlayerElement>(null);
const [playbackId, setPlaybackId] = useState(INITIAL_PLAYBACK_ID);
const [muted, setMuted] = useState(INITIAL_MUTED);
const [debug, setDebug] = useState(INITIAL_DEBUG);
const [autoplay, setAutoplay] = useState(INITIAL_AUTOPLAY);
const debugObj = debug ? { debug: "" } : {};
const mutedObj = muted ? { muted: "" } : {};
const autoplayObj = autoplay ? { autoplay } : {};
const [autoplay, setAutoplay] = useState<MuxMediaPropTypes["autoplay"]>(INITIAL_AUTOPLAY);
const [capLevelToPlayerSize, setCapLevelToPlayerSize] = useState<boolean | undefined>(INITIAL_CAP_LEVEL_TO_PLAYER_SIZE);
const debugObj : {debug?: boolean}= debug ? { debug: true } : {};
const mutedObj : {muted?: boolean} = muted ? { muted: true } : {};
const autoplayObj : {autoplay?: Autoplay} = autoplay ? { autoplay: autoplay } : {};
return (
<>
<Head>
Expand All @@ -26,11 +29,13 @@ function MuxPlayerWCPage() {

<div>
<mux-player
// style={{ aspectRatio: "16 / 9" }}
ref={mediaElRef}
playback-id={playbackId}
forward-seek-offset={10}
backward-seek-offset={10}
max-auto-resolution="720p"
cap-level-to-player-size={capLevelToPlayerSize ? true : undefined}
disable-cap-level-to-player-size={capLevelToPlayerSize === false ? true : undefined}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick(blocking): should this still exist? I thought we were descoping this for the initial implementation.

// onPlayerReady={() => console.log("ready!")}
{...debugObj}
{...mutedObj}
Expand All @@ -43,13 +48,17 @@ function MuxPlayerWCPage() {
></mux-player>
</div>
<div className="options">
<button onClick={() => {
if (!mediaElRef.current) return;
mediaElRef.current.load();
}}>Reload</button>
<div>
<label htmlFor="autoplay-control">Muted Autoplay</label>
<input
id="autoplay-control"
type="checkbox"
onChange={() => setAutoplay(!autoplay ? "muted" : false)}
checked={autoplay}
checked={!!autoplay}
/>
</div>
<div>
Expand Down Expand Up @@ -78,6 +87,11 @@ function MuxPlayerWCPage() {
defaultValue={playbackId}
/>
</div>
<OptionalBooleanRenderer
value={capLevelToPlayerSize}
name="capLevelToPlayerSize"
onChange={({ capLevelToPlayerSize }) => setCapLevelToPlayerSize(capLevelToPlayerSize)}
/>
</div>
</>
);
Expand Down
27 changes: 25 additions & 2 deletions examples/nextjs-with-typescript/pages/mux-video-react.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import Head from 'next/head';
import { useRef, useState } from "react";
import MuxVideo from "@mux/mux-video/react";
import { EnumRenderer, OptionalBooleanRenderer } from '../components/renderers';
import MuxVideoElement from '@mux/mux-video';

const INITIAL_AUTOPLAY = false;
const INITIAL_MUTED = false;
const INITIAL_CAP_LEVEL_TO_PLAYER_SIZE : boolean | undefined = undefined;
const INITIAL_PREFER_PLAYBACK = undefined;

function MuxVideoPage() {
const mediaElRef = useRef(null);
const mediaElRef = useRef<MuxVideoElement>(null);
const [autoplay, setAutoplay] = useState<"muted" | boolean>(INITIAL_AUTOPLAY);
const [muted, setMuted] = useState(INITIAL_MUTED);
const [preferPlayback, setPreferPlayback] = useState<MuxVideoElement["preferPlayback"]>(INITIAL_PREFER_PLAYBACK);
const [capLevelToPlayerSize, setCapLevelToPlayerSize] = useState<boolean | undefined>(INITIAL_CAP_LEVEL_TO_PLAYER_SIZE);
const [paused, setPaused] = useState<boolean | undefined>(true);

return (
Expand All @@ -32,12 +38,14 @@ function MuxVideoPage() {
// }}
// envKey="mux-data-env-key"
controls
capLevelToPlayerSize={capLevelToPlayerSize === true}
disableCapLevelToPlayerSize={capLevelToPlayerSize === false}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick(blocking): same as above

autoplay={autoplay}
muted={muted}
maxResolution="2160p"
minResolution="540p"
renditionOrder="desc"
preferPlayback="native"
preferPlayback={preferPlayback}
onPlay={() => {
setPaused(false);
}}
Expand All @@ -47,6 +55,10 @@ function MuxVideoPage() {
/>

<div className="options">
<button onClick={() => {
if (!mediaElRef.current) return;
mediaElRef.current.load();
}}>Reload</button>
<div>
<label htmlFor="paused-control">Paused</label>
<input
Expand Down Expand Up @@ -74,6 +86,17 @@ function MuxVideoPage() {
checked={muted}
/>
</div>
<EnumRenderer
value={preferPlayback}
name="preferPlayback"
onChange={({ preferPlayback }) => setPreferPlayback(preferPlayback as MuxVideoElement["preferPlayback"])}
values={['mse', 'native']}
/>
<OptionalBooleanRenderer
value={capLevelToPlayerSize}
name="capLevelToPlayerSize"
onChange={({ capLevelToPlayerSize }) => setCapLevelToPlayerSize(capLevelToPlayerSize)}
/>
</div>
</>
);
Expand Down
10 changes: 8 additions & 2 deletions packages/mux-player-react/src/lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ import useIsIntersecting from './useIsIntersecting';
import type { MuxPlayerProps, MuxPlayerRefAttributes, MuxCSSProperties } from './index';
import type MuxPlayerElement from '@mux/mux-player';

interface MuxPlayerElementReact extends Partial<Omit<MuxPlayerElement, 'style' | 'children'>> {
interface MuxPlayerElementReact
extends Partial<
Omit<MuxPlayerElement, 'style' | 'children' | 'autoplay' | 'capLevelToPlayerSize' | 'dsiableCapLevelToPlayerSize'>
> {
ref: React.MutableRefObject<MuxPlayerElement | null> | null | undefined;
style: React.CSSProperties;
style?: React.CSSProperties;
children?: React.ReactNode;
autoplay?: MuxPlayerProps['autoPlay'];
'cap-level-to-player-size'?: boolean;
'disable-cap-level-to-player-size'?: boolean;
}

declare global {
Expand Down
1 change: 1 addition & 0 deletions packages/mux-player-react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export type MuxPlayerProps = {
theme?: string;
themeProps?: { [k: string]: any };
fullscreenElement?: string;
capLevelToPlayerSize?: boolean;
onAbort?: GenericEventListener<MuxPlayerElementEventMap['abort']>;
onCanPlay?: GenericEventListener<MuxPlayerElementEventMap['canplay']>;
onCanPlayThrough?: GenericEventListener<MuxPlayerElementEventMap['canplaythrough']>;
Expand Down
40 changes: 40 additions & 0 deletions packages/mux-player/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ function getProps(el: MuxPlayerElement, state?: any): MuxTemplateProps {
proudlyDisplayMuxBadge: el.hasAttribute(PlayerAttributes.PROUDLY_DISPLAY_MUX_BADGE),
castReceiver: el.castReceiver,
disablePseudoEnded: el.hasAttribute(PlayerAttributes.DISABLE_PSEUDO_ENDED),
capLevelToPlayerSize: el.capLevelToPlayerSize,
disableCapLevelToPlayerSize: el.disableCapLevelToPlayerSize,
...state,
// NOTE: since the attribute value is used as the "source of truth" for the property getter,
// moving this below the `...state` spread so it resolves to the default value when unset (CJP)
Expand Down Expand Up @@ -754,6 +756,20 @@ class MuxPlayerElement extends VideoApiElement implements IMuxPlayerElement {
}
break;
}
case MuxVideoAttributes.CAP_LEVEL_TO_PLAYER_SIZE: {
if (newValue == null || newValue !== oldValue) {
if (newValue == null) this.capLevelToPlayerSize = undefined;
this.capLevelToPlayerSize = newValue !== 'false';
}
break;
}
case MuxVideoAttributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE: {
if (newValue == null || newValue !== oldValue) {
if (newValue == null) this.disableCapLevelToPlayerSize = undefined;
this.disableCapLevelToPlayerSize = newValue !== 'false';
}
break;
}
}

const shouldClearState = [
Expand Down Expand Up @@ -1883,6 +1899,30 @@ class MuxPlayerElement extends VideoApiElement implements IMuxPlayerElement {
this.setAttribute(PlayerAttributes.PROUDLY_DISPLAY_MUX_BADGE, '');
}
}

get capLevelToPlayerSize(): boolean | undefined {
return this.media?.capLevelToPlayerSize;
}

set capLevelToPlayerSize(val: boolean | undefined) {
if (!this.media) {
logger.error('underlying media element missing when trying to set capLevelToPlayerSize');
return;
}
this.media.capLevelToPlayerSize = val;
}

get disableCapLevelToPlayerSize(): boolean {
return this.media?.disableCapLevelToPlayerSize ?? false;
}

set disableCapLevelToPlayerSize(val: boolean | undefined) {
if (!this.media) {
logger.error('underlying media element missing when trying to set disableCapLevelToPlayerSize');
return;
}
this.media.disableCapLevelToPlayerSize = val;
}
}

export function getVideoAttribute(el: MuxPlayerElement, name: string) {
Expand Down
2 changes: 2 additions & 0 deletions packages/mux-player/src/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ export const content = (props: MuxTemplateProps) => html`
exportparts="video"
disable-pseudo-ended="${props.disablePseudoEnded ?? false}"
max-auto-resolution="${props.maxAutoResolution ?? false}"
cap-level-to-player-size="${props.capLevelToPlayerSize ?? false}"
disable-cap-level-to-player-size="${props.disableCapLevelToPlayerSize ?? false}"
>
${props.storyboard
? html`<track label="thumbnails" default kind="metadata" src="${props.storyboard}" />`
Expand Down
1 change: 1 addition & 0 deletions packages/mux-player/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export type MuxTemplateProps = Partial<MuxPlayerProps> & {
/** Allow playback with ad blocker */
allowAdBlocker?: boolean;
disablePseudoEnded?: boolean;
capLevelToPlayerSize?: boolean;
};

export type DialogOptions = {
Expand Down
50 changes: 50 additions & 0 deletions packages/mux-video/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ export const Attributes = {
LIVE_EDGE_OFFSET: 'live-edge-offset',
TYPE: 'type',
LOGO: 'logo',
CAP_LEVEL_TO_PLAYER_SIZE: 'cap-level-to-player-size',
DISABLE_CAP_LEVEL_TO_PLAYER_SIZE: 'disable-cap-level-to-player-size',
} as const;

const AttributeNameValues = Object.values(Attributes);
Expand Down Expand Up @@ -516,6 +518,45 @@ export class MuxVideoBaseElement extends CustomVideoElement implements IMuxVideo
}
}

/** Keeps track of attribute status */
#capLevelToPlayerSize: boolean | undefined = undefined;
/** Returns capLevelToPlayerSize based on it's attribute and considering
* {@link disableCapLevelToPlayerSize} and {@link _hlsConfig}
*/
get capLevelToPlayerSize(): boolean | undefined {
if (this.disableCapLevelToPlayerSize) {
return false;
}

if (this._hlsConfig?.capLevelToPlayerSize != undefined) {
return this._hlsConfig.capLevelToPlayerSize;
}

return this.#capLevelToPlayerSize;
}

set capLevelToPlayerSize(val: boolean | undefined) {
this.#capLevelToPlayerSize = val;
}

/** If true, capLevelToPlayerSize is treated as set to false */
get disableCapLevelToPlayerSize(): boolean {
return (
this.hasAttribute(Attributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE) &&
this.getAttribute(Attributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE) !== 'false'
);
}

set disableCapLevelToPlayerSize(val: boolean | undefined) {
if (val === this.disableCapLevelToPlayerSize) return;

if (!val) {
this.removeAttribute(Attributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE);
} else {
this.setAttribute(Attributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE, '');
}
}

get drmToken() {
return this.getAttribute(Attributes.DRM_TOKEN) ?? undefined;
}
Expand Down Expand Up @@ -900,6 +941,15 @@ export class MuxVideoBaseElement extends CustomVideoElement implements IMuxVideo
}
break;
}
case Attributes.CAP_LEVEL_TO_PLAYER_SIZE: {
if (newValue == null || newValue !== oldValue) {
if (newValue == null) {
this.capLevelToPlayerSize = undefined;
} else {
this.capLevelToPlayerSize = newValue !== 'false';
}
}
}
}
}

Expand Down
Loading
Loading