Skip to content

Commit

Permalink
feat(mux-player, mux-player-react): Add extra-source-params/extraSour…
Browse files Browse the repository at this point in the history
…ceParams attr/prop for advanced usage.

Mux Player `extraPlaylistParams` (prop) / `extra-playlist-params` (attr)
allows users to specify any arbitrary search/query params for the
resultant `src` URL (based on, e.g. `playbackId` and other specific
props/attrs used to construct the URL).
  • Loading branch information
cjpillsbury authored Nov 28, 2023
1 parent f7200e7 commit a5ad6ed
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 6 deletions.
6 changes: 6 additions & 0 deletions examples/nextjs-with-typescript/pages/MuxPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,12 @@ function MuxPlayerPage({ location }: Props) {
maxResolution={state.maxResolution}
minResolution={state.minResolution}
renditionOrder={state.renditionOrder}
// To test/apply extra playlist params to resultant src URL (CJP)
// extraSourceParams={{
// foo: 'str',
// bar: true,
// baz: 1,
// }}
preload={state.preload}
streamType={state.streamType}
targetLiveWindow={state.targetLiveWindow}
Expand Down
3 changes: 3 additions & 0 deletions packages/mux-player-react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type MuxMediaPropTypes = {
disableCookies: boolean;
// metadata: Partial<Options["data"]>;
metadata: { [k: string]: any };
extraSourceParams: Record<string, any>;
beaconCollectionDomain: string;
customDomain: string;
playbackId: string;
Expand Down Expand Up @@ -180,10 +181,12 @@ const usePlayer = (
playbackRates,
currentTime,
themeProps,
extraSourceParams,
...remainingProps
} = props;
useObjectPropEffect('playbackRates', playbackRates, ref);
useObjectPropEffect('metadata', metadata, ref);
useObjectPropEffect('extraSourceParams', extraSourceParams, ref);
useObjectPropEffect('themeProps', themeProps, ref);
useObjectPropEffect('tokens', tokens, ref);
useObjectPropEffect('playbackId', playbackId, ref);
Expand Down
29 changes: 28 additions & 1 deletion packages/mux-player/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const PlayerAttributes = {
THEME: 'theme',
DEFAULT_STREAM_TYPE: 'default-stream-type',
TARGET_LIVE_WINDOW: 'target-live-window',
EXTRA_SOURCE_PARAMS: 'extra-source-params',
NO_VOLUME_PREF: 'no-volume-pref',
};

Expand Down Expand Up @@ -153,8 +154,10 @@ function getProps(el: MuxPlayerElement, state?: any): MuxTemplateProps {
customDomain: el.getAttribute(MuxVideoAttributes.CUSTOM_DOMAIN) ?? undefined,
title: el.getAttribute(PlayerAttributes.TITLE),
novolumepref: el.hasAttribute(PlayerAttributes.NO_VOLUME_PREF),
extraPlaylistParams: { redundant_streams: true },
...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)
extraSourceParams: el.extraSourceParams,
};

return props;
Expand Down Expand Up @@ -219,6 +222,8 @@ const initialState = {
isDialogOpen: false,
};

const DEFAULT_EXTRA_PLAYLIST_PARAMS = { redundant_streams: true };

export interface MuxPlayerElementEventMap extends HTMLVideoElementEventMap {
cuepointchange: CustomEvent<{ time: number; value: any }>;
cuepointschange: CustomEvent<Array<{ time: number; value: any }>>;
Expand Down Expand Up @@ -1221,6 +1226,28 @@ class MuxPlayerElement extends VideoApiElement implements MuxPlayerElement {
}
}

get extraSourceParams() {
if (!this.hasAttribute(PlayerAttributes.EXTRA_SOURCE_PARAMS)) {
return DEFAULT_EXTRA_PLAYLIST_PARAMS;
}

return [...new URLSearchParams(this.getAttribute(PlayerAttributes.EXTRA_SOURCE_PARAMS) as string).entries()].reduce(
(paramsObj, [k, v]) => {
paramsObj[k] = v;
return paramsObj;
},
{} as Record<string, any>
);
}

set extraSourceParams(value: Record<string, any>) {
if (value == null) {
this.removeAttribute(PlayerAttributes.EXTRA_SOURCE_PARAMS);
} else {
this.setAttribute(PlayerAttributes.EXTRA_SOURCE_PARAMS, new URLSearchParams(value).toString());
}
}

/**
* Get Mux asset custom domain.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/mux-player/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type MuxTemplateProps = Partial<MuxPlayerProps> & {
maxResolution?: MaxResolutionValue;
minResolution?: MinResolutionValue;
renditionOrder?: RenditionOrderValue;
extraPlaylistParams?: Record<string, any>;
extraSourceParams?: Record<string, any>;
tokens: {
playback?: string;
thumbnail?: string;
Expand Down
43 changes: 43 additions & 0 deletions packages/mux-player/test/player.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,49 @@ describe('<mux-player>', () => {
assert.equal(player.maxResolution, '720p');
});

it('should apply extra-playlist-params as arbitrary search params on src', async function () {
const player = await fixture(`<mux-player
stream-type="on-demand"
extra-source-params="foo=str&bar=true&baz=1"
playback-id="r4rOE02cc95tbe3I00302nlrHfT023Q3IedFJW029w018KxZA"
></mux-player>`);
const muxVideo = player.media;

// NOTE: While you may use any value for the setter, the current impl will convert all values to string equivalents (CJP)
const expectedExtraPlaylistParams = { foo: 'str', bar: 'true', baz: '1' };
assert.deepEqual(
player.extraSourceParams,
expectedExtraPlaylistParams,
'should reflect value when set via attribute'
);
const actualSrcUrl = new URL(muxVideo.src);
const expectedSrcUrl = new URL(
'https://stream.mux.com/r4rOE02cc95tbe3I00302nlrHfT023Q3IedFJW029w018KxZA.m3u8?foo=str&bar=true&baz=1'
);
assert.equal(actualSrcUrl.searchParams.size, expectedSrcUrl.searchParams.size);
expectedSrcUrl.searchParams.forEach((value, key) => {
assert.equal(actualSrcUrl.searchParams.get(key), value);
});

player.removeAttribute('extra-source-params');
assert.deepEqual(
player.extraSourceParams,
{ redundant_streams: true },
'should reset to default params when attribute is removed'
);

player.extraSourceParams = {
foo: 'str',
bar: true,
baz: 1,
};
assert.deepEqual(
player.extraSourceParams,
expectedExtraPlaylistParams,
'should reflect value when set via property'
);
});

describe('buffered behaviors', function () {
it('should have an empty TimeRanges value by default', async function () {
const playerEl = await fixture('<mux-player stream-type="on-demand"></mux-player>');
Expand Down
6 changes: 3 additions & 3 deletions packages/playback-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ type MuxVideoURLProps = Partial<{
storyboard: string;
thumbnail: string;
}>;
extraPlaylistParams: Record<string, any>;
extraSourceParams: Record<string, any>;
}>;

export const toMuxVideoURL = ({
Expand All @@ -249,7 +249,7 @@ export const toMuxVideoURL = ({
minResolution,
renditionOrder,
tokens: { playback: token } = {},
extraPlaylistParams = {},
extraSourceParams = {},
}: MuxVideoURLProps = {}) => {
if (!playbackIdWithParams) return undefined;
const [playbackId, queryPart = ''] = toPlaybackIdParts(playbackIdWithParams);
Expand Down Expand Up @@ -284,7 +284,7 @@ export const toMuxVideoURL = ({
if (renditionOrder) {
url.searchParams.set('rendition_order', renditionOrder);
}
Object.entries(extraPlaylistParams).forEach(([k, v]) => {
Object.entries(extraSourceParams).forEach(([k, v]) => {
if (v == undefined) return;
url.searchParams.set(k, v);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/playback-core/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ describe('playback core', function () {
toMuxVideoURL({
playbackId: '123?redundant_streams=true',
minResolution: '720p',
extraPlaylistParams: {
extraSourceParams: {
extra_extra: 'readallaboutit',
},
}),
Expand Down

5 comments on commit a5ad6ed

@vercel
Copy link

@vercel vercel bot commented on a5ad6ed Nov 28, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

elements-demo-create-react-app – ./examples/create-react-app-with-typescript

elements-demo-create-react-app-git-main-mux.vercel.app
elements-demo-create-react-app-mux.vercel.app
elements-demo-create-react-app.vercel.app

@vercel
Copy link

@vercel vercel bot commented on a5ad6ed Nov 28, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

elements-demo-vanilla – ./examples/vanilla-ts-esm

elements-demo-vanilla-mux.vercel.app
elements-demo-vanilla.vercel.app
elements-demo-vanilla-git-main-mux.vercel.app

@vercel
Copy link

@vercel vercel bot commented on a5ad6ed Nov 28, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

elements-demo-vue – ./examples/vue-with-typescript

elements-demo-vue.vercel.app
elements-demo-vue-mux.vercel.app
elements-demo-vue-git-main-mux.vercel.app

@vercel
Copy link

@vercel vercel bot commented on a5ad6ed Nov 28, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

elements-demo-nextjs – ./examples/nextjs-with-typescript

elements-demo-nextjs.vercel.app
elements-demo-nextjs-git-main-mux.vercel.app
elements-demo-nextjs-mux.vercel.app

@vercel
Copy link

@vercel vercel bot commented on a5ad6ed Nov 28, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

elements-demo-svelte-kit – ./examples/svelte-kit

elements-demo-svelte-kit-git-main-mux.vercel.app
elements-demo-svelte-kit-mux.vercel.app
elements-demo-svelte-kit.vercel.app

Please sign in to comment.