Skip to content

Commit

Permalink
feat(FEC-13664): Add the ability to configure default alignment of ca…
Browse files Browse the repository at this point in the history
…ptions on the player # (#769)

### Description of the Changes

1. Add `textAlign`: string (left/right/center) property to
`config.text.textStyle`;
2. Remove `align` option form `config.text.textTrackDisplaySetting`;
3. Remove `forceCenter` option from `config.text`
4. upd documentation;
5. handle caption alignment based on `config.text.textStyle.textAlign`
configuration;
6. upd tests;

<img width="1043" alt="image"
src="https://github.com/kaltura/playkit-js/assets/51074448/aa3fca8f-977b-4c2e-a9d7-1c10dbb6887c">


### CheckLists

- [ ] changes have been done against master branch, and PR does not
conflict
- [ ] new unit / functional tests have been added (whenever applicable)
- [ ] test are passing in local environment
- [ ] Travis tests are passing (or test results are not worse than on
master branch :))
- [ ] Docs have been updated

---------

Co-authored-by: Sergey Marchenko <[email protected]>
  • Loading branch information
semarche-kaltura and semarche authored Mar 18, 2024
1 parent f70414c commit cb0ba18
Show file tree
Hide file tree
Showing 19 changed files with 158 additions and 114 deletions.
23 changes: 5 additions & 18 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ var config = {
text: {
enableCEA708Captions: true,
useNativeTextTrack: false,
forceCenter: false,
captionsTextTrack1Label: 'English',
captionsTextTrack1LanguageCode: 'en',
captionsTextTrack2Label: 'Spanish',
Expand Down Expand Up @@ -544,7 +543,6 @@ var config = {
> {
> useNativeTextTrack: boolean,
> enableCEA708Captions: boolean,
> forceCenter: boolean,
> textTrackDisplaySetting: PKTextTrackDisplaySettingObject,
> textStyle: TextStyle,
> captionsTextTrack1Label: string,
Expand All @@ -560,7 +558,6 @@ var config = {
> {
> useNativeTextTrack: false,
> enableCEA708Captions: true,
> forceCenter: false,
> captionsTextTrack1Label: "English",
> captionsTextTrack1LanguageCode: "en",
> captionsTextTrack2Label: "Spanish",
Expand Down Expand Up @@ -592,16 +589,6 @@ var config = {
>
> ##
>
> > ### config.text.forceCenter
> >
> > ##### Type: `Object`
> >
> > ##### Default: `false`
> >
> > ##### Description: set the forceCenter to true will override the position, align and size in textTrackDisplaySetting
>
> ##
>
> > ### config.text.textTrackDisplaySetting
> >
> > ##### Type: `PKTextTrackDisplaySettingObject`
Expand All @@ -616,7 +603,6 @@ var config = {
> {
> line: string | number, // [-16 .. 16]
> lineAlign: string, // ['start', 'center', 'end']
> align: string, // ['start', 'center', 'end', 'left', 'right']
> position: number, //[0 .. 100]
> positionAlign: string, // ['line-left', 'center', 'line-right']
> snapToLines: boolean, // [true, false]
Expand All @@ -633,10 +619,6 @@ var config = {
> >
> > An alignment for the cue box’s line, one of start/center/end alignment
> >
> > ##### align
> >
> > An alignment for all lines of text within the cue box, in the dimension of the writing direction
> >
> > ##### snapToLines
> >
> > is a boolean indicating whether the line is an integer number of lines (using the line dimensions of the first line of the cue), or whether it is a percentage of the dimension of the video. The flag is set to true when lines are counted, and false otherwise.
Expand Down Expand Up @@ -678,6 +660,7 @@ var config = {
> > ```js
> > {
> > fontSize?: '50%' | '75%' | '100%' | '200%' | '300%' | '400%'
> > textAlign?: string, // ['center', 'left', 'right']
> > fontScale?: -2 | -1 | 0 | 2 | 3 | 4
> > fontFamily?: string, // font family available in browser
> > fontColor?: [number, number, number], // RGB
Expand All @@ -693,6 +676,10 @@ var config = {
> > }
> > ```
> >
> > ##### textAlign
> >
> > An alignment for all lines of text within the cue box, in the dimension of the writing direction
> >
> > ##### fontSize
> >
> > Percentage unit relative to the parent element's font
Expand Down
1 change: 0 additions & 1 deletion flow-typed/types/text-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ declare type PKTextConfigObject = {
useNativeTextTrack: boolean,
textTrackDisplaySetting: PKTextTrackDisplaySettingObject,
textStyle: PKTextStyleObject,
forceCenter: boolean,
captionsTextTrack1Label: string,
captionsTextTrack1LanguageCode: string,
captionsTextTrack2Label: string,
Expand Down
1 change: 0 additions & 1 deletion flow-typed/types/text-track-display-setting.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
declare type PKTextTrackDisplaySettingObject = {
line: string | number,
lineAlign: string,
align: string,
position: number,
positionAlign: string,
snapToLines: boolean,
Expand Down
9 changes: 9 additions & 0 deletions src/engines/html5/html5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,15 @@ export default class Html5 extends FakeEventTarget implements IEngine {
}
}

/**
* Get the engine mediaSourceAdapter
* @public
* @returns {IMediaSourceAdapter | null}
*/
public get mediaSourceAdapter(): IMediaSourceAdapter | null {
return this._mediaSourceAdapter;
}

/**
* Get the engine's id
* @public
Expand Down
6 changes: 6 additions & 0 deletions src/engines/html5/media-source/base-media-source-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Error from '../../../error/error';
import { CustomEventType, Html5EventType } from '../../../event/event-type';
import getLogger from '../../../utils/logger';
import Track from '../../../track/track';
import TextStyle from '../../../track/text-style';
import VideoTrack from '../../../track/video-track';
import AudioTrack from '../../../track/audio-track';
import PKTextTrack from '../../../track/text-track';
Expand Down Expand Up @@ -177,6 +178,11 @@ export default class BaseMediaSourceAdapter extends FakeEventTarget implements I
return BaseMediaSourceAdapter._throwNotImplementedError('static canPlayType');
}

// eslint-disable-next-line
public applyTextTrackStyles(sheet: CSSStyleSheet, styles: TextStyle, containerId: string, engineClassName?: string): void {
return BaseMediaSourceAdapter._throwNotImplementedError('applyTextTrackStyles');
}

public load(): Promise<{ tracks: Track[] }> {
return BaseMediaSourceAdapter._throwNotImplementedError('load');
}
Expand Down
1 change: 0 additions & 1 deletion src/player-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const DefaultConfig = {
text: {
enableCEA708Captions: true,
useNativeTextTrack: false,
forceCenter: false,
captionsTextTrack1Label: 'English',
captionsTextTrack1LanguageCode: 'en',
captionsTextTrack2Label: 'Spanish',
Expand Down
70 changes: 36 additions & 34 deletions src/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {EngineProvider} from './engines/engine-provider';
import {ExternalCaptionsHandler} from './track/external-captions-handler';
import {AdBreakType} from './ads/ad-break-type';
import {AdTagType} from './ads/ad-tag-type';
import {ResizeWatcher} from './utils';
import {ResizeWatcher, getSubtitleStyleSheet, resetSubtitleStyleSheet} from './utils';
import {FullscreenController} from './fullscreen/fullscreen-controller';
import {EngineDecorator, EngineDecoratorType} from './engines/engine-decorator';
import {LabelOptions} from './track/label-options';
Expand Down Expand Up @@ -78,13 +78,6 @@ const POSTER_CLASS_NAME: string = 'playkit-poster';
*/
const ENGINE_CLASS_NAME: string = 'playkit-engine';

/**
* The text style class name.
* @type {string}
* @const
*/
const SUBTITLES_STYLE_CLASS_NAME: string = 'playkit-subtitles-style';

/**
* The subtitles class name.
* @type {string}
Expand Down Expand Up @@ -471,7 +464,7 @@ export default class Player extends FakeEventTarget {
public configure(config: any = {}): void {
this._setConfigLogLevel(config);
Utils.Object.mergeDeep(this._config, config);
this._applyTextTrackConfig(config);
this._applyTextTrackConfig();
this._applyABRRestriction(config);
}

Expand Down Expand Up @@ -1473,24 +1466,24 @@ export default class Player extends FakeEventTarget {
* @param {Object} config - new config which configure for checking if it relevant config has changed
* @private
*/
private _applyTextTrackConfig(config: any): void {
if (Utils.Object.hasPropertyPath(config, 'text.textTrackDisplaySetting') || Utils.Object.getPropertyPath(config, 'text.forceCenter')) {
let textDisplaySettings: any = {};
if (Utils.Object.hasPropertyPath(this._config, 'text.textTrackDisplaySetting')) {
textDisplaySettings = Utils.Object.mergeDeep(textDisplaySettings, this._config.text.textTrackDisplaySetting);
}
private _applyTextTrackConfig(): void {
const textTrackDisplaySetting = Utils.Object.getPropertyPath(this._config, 'text.textTrackDisplaySetting');
const textStyle = Utils.Object.getPropertyPath(this._config, 'text.textStyle');
if (textTrackDisplaySetting) {
const textDisplaySettings: any = Utils.Object.mergeDeep({}, textTrackDisplaySetting, {
position: 'auto',
// align - backward compatibility || new caption alignment API || default value
align: textTrackDisplaySetting?.align || textStyle?.textAlign || 'center'
});
// backward compatibility for `text.forceCenter`
if (Utils.Object.getPropertyPath(this._config, 'text.forceCenter')) {
textDisplaySettings = Utils.Object.mergeDeep(textDisplaySettings, {
position: 'auto',
align: 'center',
size: '100'
});
textDisplaySettings.align = 'center';
}
this.setTextDisplaySettings(textDisplaySettings);
}
try {
if (Utils.Object.hasPropertyPath(config, 'text.textStyle')) {
this.textStyle = TextStyle.fromJson(this._config.text.textStyle);
if (textStyle) {
this.textStyle = TextStyle.fromJson(textStyle);
}
} catch (e) {
Player._logger.warn(e);
Expand Down Expand Up @@ -1542,23 +1535,13 @@ export default class Player extends FakeEventTarget {
if (!(style instanceof TextStyle)) {
throw new Error('Style must be instance of TextStyle');
}
let element = Utils.Dom.getElementBySelector(`.${this._playerId}.${SUBTITLES_STYLE_CLASS_NAME}`);
if (!element) {
element = Utils.Dom.createElement('style');
Utils.Dom.addClassName(element, this._playerId);
Utils.Dom.addClassName(element, SUBTITLES_STYLE_CLASS_NAME);
Utils.Dom.appendChild(document.head, element);
}
const sheet = element.sheet;

while (sheet.cssRules.length) {
sheet.deleteRule(0);
}
resetSubtitleStyleSheet(this._playerId);

try {
this._textStyle = style;
if (this._config.text.useNativeTextTrack) {
sheet.insertRule(`#${this._playerId} video.${ENGINE_CLASS_NAME}::cue { ${style.toCSS()} }`, 0);
this._applyCustomSubtitleStyles();
} else if (this._engine) {
this._engine.resetAllCues();
this._externalCaptionsHandler.resetAllCues();
Expand Down Expand Up @@ -1716,6 +1699,22 @@ export default class Player extends FakeEventTarget {
return this._engine.getDrmInfo();
}

private _applyCustomSubtitleStyles(): void {
try {
const containerId = this._el?.parentElement?.id || this._playerId;
if (this._config.text.useNativeTextTrack && !this._config.text.useShakaTextTrackDisplay) {
const sheet = getSubtitleStyleSheet(this._playerId);
ExternalCaptionsHandler.applyNativeTextTrackStyles(sheet, this._textStyle, containerId, ENGINE_CLASS_NAME);
} else if (this._config.text.useShakaTextTrackDisplay) {
resetSubtitleStyleSheet(this._playerId);
const sheet = getSubtitleStyleSheet(this._playerId);
this._engine.mediaSourceAdapter?.applyTextTrackStyles?.(sheet, this._textStyle, containerId);
}
} catch (e) {
Player._logger.error(`Failed to add custom text style: ${e.message}`);
}
}

/**
* Remove the current text track from the player view.
* @returns {void}
Expand Down Expand Up @@ -2564,6 +2563,9 @@ export default class Player extends FakeEventTarget {
* @returns {void}
*/
private _updateTextDisplay(cues: Array<VTTCue>): void {
if (this._config.text.useShakaTextTrackDisplay) {
this._applyCustomSubtitleStyles();
}
if (!this._config.text.useNativeTextTrack && !this._config.text.useShakaTextTrackDisplay) {
processCues(window, cues, this._textDisplayEl, this._textStyle);
}
Expand Down
7 changes: 7 additions & 0 deletions src/track/external-captions-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Error from '../error/error';
import * as Utils from '../utils/util';
import {Parser, StringDecoder} from './text-track-display';
import TextTrack, {getActiveCues} from './text-track';
import TextStyle from './text-style';
import Track from './track';
import {CustomEventType, Html5EventType} from '../event/event-type';
import { FakeEvent } from '../event/fake-event';
Expand Down Expand Up @@ -29,6 +30,12 @@ const SRT_POSTFIX: string = 'srt';
const VTT_POSTFIX: string = 'vtt';

class ExternalCaptionsHandler extends FakeEventTarget {

public static applyNativeTextTrackStyles(sheet: CSSStyleSheet, styles: TextStyle, containerId: string, engineClassName: string): void {
sheet.insertRule(`#${containerId} video.${engineClassName}::-webkit-media-text-track-display { text-align: ${styles.textAlign}!important; }`, 0);
sheet.insertRule(`#${containerId} video.${engineClassName}::cue { ${styles.toCSS()} }`, 0);
}

/**
* The external captions handler class logger.
* @type {any}
Expand Down
25 changes: 24 additions & 1 deletion src/track/text-style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* font size.
* @type {number}
*/
import {FontScaleOptions, FontSizeOptions, PKTextStyleObject} from '../types';
import { FontScaleOptions, FontSizeOptions, PKTextStyleObject, FontAlignmentOptions } from '../types';

const IMPLICIT_SCALE_PERCENTAGE: number = 0.25;

Expand Down Expand Up @@ -130,6 +130,24 @@ class TextStyle {
}
];

/**
* Possible font alignments are left, center, right
*/
public static FontAlignment: { label: string; value: FontAlignmentOptions }[] = [
{
label: 'Left',
value: 'left'
},
{
label: 'Center',
value: 'center'
},
{
label: 'Right',
value: 'right'
}
];

/**
* Creates a CSS RGBA sctring for a given color and opacity values
* @param {TextStyle.StandardColors} color - color value in RGB
Expand All @@ -149,6 +167,7 @@ class TextStyle {
const textStyle = new TextStyle();
textStyle.fontEdge = getValue(setting.fontEdge, textStyle.fontEdge);
textStyle.fontSize = getValue(setting.fontSize, textStyle.fontSize);
textStyle.textAlign = getValue(setting.textAlign, textStyle.textAlign);
textStyle.fontScale = getValue(setting.fontScale, textStyle.fontScale);
textStyle.fontColor = getValue(setting.fontColor, textStyle.fontColor);
textStyle.fontOpacity = getValue(setting.fontOpacity, textStyle.fontOpacity);
Expand All @@ -162,6 +181,7 @@ class TextStyle {
return {
fontEdge: text.fontEdge,
fontSize: text.fontSize,
textAlign: text.textAlign,
fontScale: text.fontScale,
fontColor: text.fontColor,
fontOpacity: text.fontOpacity,
Expand All @@ -180,6 +200,8 @@ class TextStyle {
}
}

public textAlign: FontAlignmentOptions = TextStyle.FontAlignment[1].value;

/**
* Percentage string matching a FontSizes entry
*/
Expand Down Expand Up @@ -254,6 +276,7 @@ class TextStyle {
*/
public toCSS(): string {
const attributes: Array<string> = [];
attributes.push('text-align: ' + this.textAlign);
attributes.push('font-family: ' + this.fontFamily);
attributes.push('color: ' + TextStyle.toRGBA(this.fontColor, this.fontOpacity));
attributes.push('background-color: ' + TextStyle.toRGBA(this.backgroundColor, this.backgroundOpacity));
Expand Down
Loading

0 comments on commit cb0ba18

Please sign in to comment.