bitmovin-player-ui
Version:
Bitmovin Player UI Framework
193 lines (170 loc) • 6.24 kB
text/typescript
import { ContainerConfig, Container } from '../Container';
import { Label, LabelConfig } from '../labels/Label';
import { UIInstanceManager } from '../../UIManager';
import { TvNoiseCanvas } from '../TvNoiseCanvas';
import { ErrorUtils } from '../../utils/ErrorUtils';
import { ErrorEvent, PlayerAPI, PlayerEventBase } from 'bitmovin-player';
import {
isMobileV3PlayerAPI,
MobileV3PlayerAPI,
MobileV3PlayerErrorEvent,
MobileV3PlayerEvent,
MobileV3SourceErrorEvent,
} from '../../utils/MobileV3PlayerAPI';
export interface ErrorMessageTranslator {
(error: ErrorEvent | MobileV3PlayerErrorEvent): string;
}
export interface ErrorMessageMap {
[code: number]: string | ErrorMessageTranslator;
}
/**
* Configuration interface for the {@link ErrorMessageOverlay}.
*
* @category Configs
*/
export interface ErrorMessageOverlayConfig extends ContainerConfig {
/**
* Allows overwriting of the error messages displayed in the overlay for customization and localization.
* This is either a function that receives any {@link ErrorEvent} as parameter and translates error messages,
* or a map of error codes that overwrites specific error messages with a plain string or a function that
* receives the {@link ErrorEvent} as parameter and returns a customized string.
* The translation functions can be used to extract data (e.g. parameters) from the original error message.
*
* Example 1 (catch-all translation function):
* <code>
* errorMessageOverlayConfig = {
* messages: function(error) {
* switch (error.code) {
* // Overwrite error 1000 'Unknown error'
* case 1000:
* return 'Houston, we have a problem'
*
* // Transform error 1201 'The downloaded manifest is invalid' to uppercase
* case 1201:
* var description = ErrorUtils.defaultErrorMessages[error.code];
* return description.toUpperCase();
*
* // Customize error 1207 'The manifest could not be loaded'
* case 1207:
* var statusCode = error.data.statusCode;
* return 'Manifest loading failed with HTTP error ' + statusCode;
* }
* // Return unmodified error message for all other errors
* return error.message;
* }
* };
* </code>
*
* Example 2 (translating specific errors):
* <code>
* errorMessageOverlayConfig = {
* messages: {
* // Overwrite error 1000 'Unknown error'
* 1000: 'Houston, we have a problem',
*
* // Transform error 1201 'Unsupported manifest format' to uppercase
* 1201: function(error) {
* var description = ErrorUtils.defaultErrorMessages[error.code];
* return description.toUpperCase();
* },
*
* // Customize error 1207 'The manifest could not be loaded'
* 1207: function(error) {
* var statusCode = error.data.statusCode;
* return 'Manifest loading failed with HTTP error ' + statusCode;
* }
* }
* };
* </code>
*/
messages?: ErrorMessageMap | ErrorMessageTranslator;
}
/**
* Overlays the player and displays error messages.
*
* @category Components
*/
export class ErrorMessageOverlay extends Container<ErrorMessageOverlayConfig> {
private errorLabel: Label<LabelConfig>;
private tvNoiseBackground: TvNoiseCanvas;
constructor(config: ErrorMessageOverlayConfig = {}) {
super(config);
this.errorLabel = new Label<LabelConfig>({ cssClass: 'ui-errormessage-label' });
this.tvNoiseBackground = new TvNoiseCanvas();
this.config = this.mergeConfig(
config,
{
cssClass: 'ui-errormessage-overlay',
components: [this.tvNoiseBackground, this.errorLabel],
hidden: true,
role: 'status',
},
this.config,
);
}
configure(player: PlayerAPI | MobileV3PlayerAPI, uimanager: UIInstanceManager): void {
super.configure(player, uimanager);
const config = this.getConfig();
const handleErrorMessage = (
event: ErrorEvent | MobileV3SourceErrorEvent | MobileV3PlayerErrorEvent,
message: string,
) => {
const customizedMessage = customizeErrorMessage(uimanager.getConfig().errorMessages || config.messages, event);
if (customizedMessage) {
message = customizedMessage;
}
this.display(message);
};
if (isMobileV3PlayerAPI(player)) {
const errorEventHandler = (event: MobileV3SourceErrorEvent | MobileV3PlayerErrorEvent) => {
const message = ErrorUtils.defaultMobileV3ErrorMessageTranslator(event);
handleErrorMessage(event, message);
};
player.on(MobileV3PlayerEvent.PlayerError, errorEventHandler);
player.on(MobileV3PlayerEvent.SourceError, errorEventHandler);
} else {
player.on(player.exports.PlayerEvent.Error, (event: ErrorEvent) => {
const message = ErrorUtils.defaultWebErrorMessageTranslator(event);
handleErrorMessage(event, message);
});
}
player.on(player.exports.PlayerEvent.SourceLoaded, (event: PlayerEventBase) => {
if (this.isShown()) {
this.clear();
}
});
}
display(errorMessage: string): void {
this.errorLabel.setText(errorMessage);
this.tvNoiseBackground.start();
this.show();
}
private clear(): void {
this.errorLabel.setText('');
// Canvas rendering must be explicitly stopped, else it just continues forever and hogs resources
this.tvNoiseBackground.stop();
this.hide();
}
release(): void {
super.release();
this.clear();
}
}
function customizeErrorMessage(
errorMessages: ErrorMessageTranslator | ErrorMessageMap,
event: ErrorEvent | MobileV3PlayerErrorEvent | MobileV3SourceErrorEvent,
): string | undefined {
if (!errorMessages) {
return undefined;
}
// Process message vocabularies
if (typeof errorMessages === 'function') {
// Translation function for all errors
return errorMessages(event);
}
if (errorMessages[event.code]) {
// It's not a translation function, so it must be a map of strings or translation functions
const customMessage = errorMessages[event.code];
return typeof customMessage === 'string' ? customMessage : customMessage(event);
}
}