Skip to content

Commit

Permalink
feat: add setVolume command and replace subscribeMuted by subscribeVo…
Browse files Browse the repository at this point in the history
  • Loading branch information
difosfor committed Feb 13, 2024
1 parent 964fdbc commit f9747df
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 31 deletions.
20 changes: 13 additions & 7 deletions src/AbstractPlayerBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
Qualities,
StreamPhase,
UserFeedback,
Volume,
} from './util/schema';
import {
validateBoolean,
Expand Down Expand Up @@ -119,6 +120,9 @@ export abstract class AbstractPlayerBridge extends LiveryBridge {
if (name === 'setMuted') {
return this.setMuted(validateBoolean(arg));
}
if (name === 'setVolume') {
return this.setVolume(validateNumber(arg));
}
if (name === 'submitUserFeedback') {
return this.submitUserFeedback(validateUserFeedback(arg));
}
Expand All @@ -137,9 +141,6 @@ export abstract class AbstractPlayerBridge extends LiveryBridge {
if (name === 'subscribeMode') {
return this.subscribeMode(listener);
}
if (name === 'subscribeMuted') {
return this.subscribeMuted(listener);
}
if (name === 'subscribeOrientation') {
return this.subscribeOrientation(listener);
}
Expand All @@ -155,6 +156,9 @@ export abstract class AbstractPlayerBridge extends LiveryBridge {
if (name === 'subscribeStreamPhase') {
return this.subscribeStreamPhase(listener);
}
if (name === 'subscribeVolume') {
return this.subscribeVolume(listener);
}

return super.handleCommand(name, arg, listener);
}
Expand Down Expand Up @@ -269,6 +273,8 @@ export abstract class AbstractPlayerBridge extends LiveryBridge {

protected abstract setMuted(muted: boolean): void;

protected abstract setVolume(volume: number): void;

protected abstract submitUserFeedback(value: UserFeedback): void;

protected abstract subscribeConfig(
Expand All @@ -287,15 +293,15 @@ export abstract class AbstractPlayerBridge extends LiveryBridge {
listener: (mode: PlaybackMode) => void,
): PlaybackMode;

protected abstract subscribeMuted(
listener: (value: boolean) => void,
): boolean;

protected abstract subscribePlaybackState(
listener: (playbackState: PlaybackState) => void,
): PlaybackState;

protected abstract subscribeQualities(
listener: (qualities?: Qualities) => void,
): Qualities | undefined;

protected abstract subscribeVolume(
listener: (volume: Volume) => void,
): Volume;
}
38 changes: 26 additions & 12 deletions src/InteractiveBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
Qualities,
StreamPhase,
UserFeedback,
Volume,
} from './util/schema';
import {
validateBoolean,
Expand All @@ -28,6 +29,7 @@ import {
validateStreamPhase,
validateString,
validateStringOrUndefined,
validateVolume,
} from './util/schema';

/**
Expand Down Expand Up @@ -148,7 +150,7 @@ export class InteractiveBridge extends LiveryBridge {
* Attempt to start or resume playback.
*
* Can fail if not allowed by the browser, e.g: when not called directly from a click event listener.
* In that case it can fall back to muted playback, changing {@link subscribeMuted} to true.
* In that case it can fall back to muted playback, changing {@link subscribeVolume}.muted to true.
* Or if that also fails then {@link subscribePaused} will remain true.
*/
play() {
Expand Down Expand Up @@ -254,12 +256,24 @@ export class InteractiveBridge extends LiveryBridge {
*
* Unmuting can fail if not allowed by the browser, e.g: when not called directly from a click event listener.
* The specified state is kept track of by the player though and respected on reload when possible.
* Look at {@link subscribeMuted} state to track actual unmuting.
* Look at {@link subscribeVolume}.muted state to track actual unmuting.
*/
setMuted(muted: boolean) {
return this.sendCommand('setMuted', muted);
}

/**
* Change `volume` to specified value.
*
* When a player starts unmuted at volume `0` and this is changed to a higher volume later,
* that can be disallowed by the browser, e.g: when not called directly from a click event listener.
* In that case the player will fall back to changing {@link subscribeVolume}.muted to `true`
* to allow the volume change to persist.
*/
setVolume(volume: number) {
return this.sendCommand('setVolume', volume);
}

/**
* Submit user feedback.
*
Expand Down Expand Up @@ -325,16 +339,6 @@ export class InteractiveBridge extends LiveryBridge {
).then((mode) => validatePlaybackMode(mode));
}

/**
* Returns promise of current player muted state
* and calls back `listener` with any subsequent muted changes.
*/
subscribeMuted(listener: (muted: boolean) => void) {
return this.sendCommand('subscribeMuted', undefined, (value) =>
listener(validateBoolean(value)),
).then(validateBoolean);
}

/**
* Returns promise of current player window orientation (`'landscape' \| 'portrait'`)
* and calls back `listener` with any subsequent orientations.
Expand Down Expand Up @@ -442,6 +446,16 @@ export class InteractiveBridge extends LiveryBridge {
).then(validateStreamPhase);
}

/**
* Returns promise of current player volume state
* and calls back `listener` with any subsequent volume changes.
*/
subscribeVolume(listener: (volume: Volume) => void) {
return this.sendCommand('subscribeVolume', undefined, (value) =>
listener(validateVolume(value)),
).then(validateVolume);
}

/**
* Unregister custom command by name.
*
Expand Down
31 changes: 23 additions & 8 deletions src/MockPlayerBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
PlaybackState,
Qualities,
UserFeedback,
Volume,
} from './util/schema';

const buildQuality = (index: number) => ({
Expand Down Expand Up @@ -53,8 +54,6 @@ export class MockPlayerBridge extends AbstractPlayerBridge {

private muted = true;

private mutedListeners: ((value: boolean) => void)[] = [];

private playbackMode: PlaybackMode = 'LIVE';

private playbackState: PlaybackState = 'PLAYING';
Expand All @@ -69,6 +68,10 @@ export class MockPlayerBridge extends AbstractPlayerBridge {

private qualitiesListeners: ((value?: Qualities) => void)[] = [];

private volume = 1;

private volumeListeners: ((value: Volume) => void)[] = [];

private zeroTimestamp = Date.now();

constructor(target?: ConstructorParameters<typeof AbstractPlayerBridge>[0]) {
Expand Down Expand Up @@ -176,7 +179,19 @@ export class MockPlayerBridge extends AbstractPlayerBridge {
return;
}
this.muted = muted;
this.mutedListeners.forEach((listener) => listener(this.muted));
this.volumeListeners.forEach((listener) =>
listener({ muted: this.muted, volume: this.volume }),
);
}

protected setVolume(volume: number) {
if (volume === this.volume) {
return;
}
this.volume = volume;
this.volumeListeners.forEach((listener) =>
listener({ muted: this.muted, volume: this.volume }),
);
}

protected submitUserFeedback(value: UserFeedback) {
Expand Down Expand Up @@ -235,11 +250,6 @@ export class MockPlayerBridge extends AbstractPlayerBridge {
return this.playbackMode;
}

protected subscribeMuted(listener: (value: boolean) => void) {
this.mutedListeners.push(listener);
return this.muted;
}

protected subscribePlaybackState(
listener: (playbackState: PlaybackState) => void,
) {
Expand All @@ -252,6 +262,11 @@ export class MockPlayerBridge extends AbstractPlayerBridge {
return this.qualities;
}

protected subscribeVolume(listener: (value: Volume) => void) {
this.volumeListeners.push(listener);
return { muted: this.muted, volume: this.volume };
}

private setPlaybackState(playbackState: PlaybackState) {
if (playbackState === this.playbackState) {
return;
Expand Down
17 changes: 13 additions & 4 deletions src/livery-bridge-interactive/LiveryBridgeInteractive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const BRIDGE_GET_NAMES = [
'setControlsDisabled',
'setDisplay',
'setMuted',
'setVolume',
'submitUserFeedback',
] as const;
const BRIDGE_SUBSCRIBE_NAMES = [
Expand All @@ -40,7 +41,6 @@ const BRIDGE_SUBSCRIBE_NAMES = [
'subscribeError',
'subscribeFullscreen',
'subscribeMode',
'subscribeMuted',
'subscribeOrientation',
'subscribePaused',
'subscribePlaybackState',
Expand All @@ -49,6 +49,7 @@ const BRIDGE_SUBSCRIBE_NAMES = [
'subscribeQuality',
'subscribeStalled',
'subscribeStreamPhase',
'subscribeVolume',
] as const;

type BridgeGetName = (typeof BRIDGE_GET_NAMES)[number];
Expand Down Expand Up @@ -279,6 +280,7 @@ export class LiveryBridgeInteractive extends LitElement {
</option>
<option value="setDisplay">setDisplay</option>
<option value="setMuted">setMuted</option>
<option value="setVolume">setVolume</option>
<option value="submitUserFeedback">
submitUserFeedback
</option>
Expand Down Expand Up @@ -337,7 +339,6 @@ export class LiveryBridgeInteractive extends LitElement {
subscribeFullscreen
</option>
<option value="subscribeMode">subscribeMode</option>
<option value="subscribeMuted">subscribeMuted</option>
<option value="subscribeOrientation">
subscribeOrientation
</option>
Expand All @@ -354,6 +355,7 @@ export class LiveryBridgeInteractive extends LitElement {
<option value="subscribeStreamPhase">
subscribeStreamPhase
</option>
<option value="subscribeVolume">subscribeVolume</option>
</select>
<button type="submit">Send</button>
</form>
Expand Down Expand Up @@ -456,6 +458,7 @@ export class LiveryBridgeInteractive extends LitElement {

switch (methodName) {
case 'seek':
case 'setVolume':
case 'selectQuality': {
const inputElement = this.renderRoot.querySelector(
'#getCommandNameInput',
Expand All @@ -467,6 +470,10 @@ export class LiveryBridgeInteractive extends LitElement {

inputElement.setAttribute('style', 'display: inline-block');
inputElement.setAttribute('type', 'number');
inputElement.setAttribute(
'step',
methodName === 'setVolume' ? 'any' : '1',
);
inputElement.value = '';
break;
}
Expand All @@ -478,6 +485,7 @@ export class LiveryBridgeInteractive extends LitElement {
if (inputElement) {
inputElement.setAttribute('style', 'display: none');
inputElement.setAttribute('type', 'text');
inputElement.removeAttribute('step');
}
}
}
Expand Down Expand Up @@ -567,9 +575,10 @@ export class LiveryBridgeInteractive extends LitElement {

switch (methodName) {
case 'seek':
case 'selectQuality': {
case 'selectQuality':
case 'setVolume': {
const inputValue = getInputValue('Input');
this.interactiveBridge[methodName](parseInt(inputValue, 10)).then(
this.interactiveBridge[methodName](parseFloat(inputValue)).then(
setText,
setText,
);
Expand Down
9 changes: 9 additions & 0 deletions src/util/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,15 @@ export type StreamPhase = z.infer<typeof zStreamPhase>;

export const validateStreamPhase = createValidate(zStreamPhase);

const zVolume = z.object({
muted: zBoolean,
volume: zNumber,
});

export type Volume = z.infer<typeof zVolume>;

export const validateVolume = createValidate(zVolume);

const zConfig = z.object({
/** Registry of controls that should be shown to the user. */
controls: z.object({
Expand Down

0 comments on commit f9747df

Please sign in to comment.