@playkit-js/transcript 2.1.4 → 2.1.5-canary.16-1ac3e09

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +5 -5
  3. package/README.md +122 -24
  4. package/dist/1501adfdf5c835667ce7.svg +9 -0
  5. package/dist/301e7a199b2cd06c2edf.svg +9 -0
  6. package/dist/33bce27c0f546e80478c.svg +36 -0
  7. package/dist/6a4867d3d9170cc2a24d.svg +9 -0
  8. package/dist/73bab0af28a1c7aed29f.svg +9 -0
  9. package/dist/84087eb1cff72e5e6bd3.svg +9 -0
  10. package/dist/95d7192dc427afb678d0.svg +9 -0
  11. package/dist/cea5d6a7f050cbd199a1.svg +9 -0
  12. package/dist/d93f06ff32cdfcd016df.svg +9 -0
  13. package/dist/e5496f4c01207db44ffc.svg +9 -0
  14. package/dist/playkit-transcript.js +1 -31
  15. package/dist/playkit-transcript.js.map +1 -1
  16. package/package.json +53 -50
  17. package/src/components/a11y-wrapper/a11y-wrapper.ts +26 -0
  18. package/src/components/a11y-wrapper/index.ts +1 -0
  19. package/src/components/caption/caption.scss +1 -1
  20. package/src/components/caption/caption.tsx +118 -140
  21. package/src/components/caption/index.ts +1 -1
  22. package/src/components/caption-list/captionList.scss +8 -8
  23. package/src/components/caption-list/captionList.tsx +115 -117
  24. package/src/components/caption-list/index.ts +1 -1
  25. package/src/components/close-button/close-button.scss +11 -0
  26. package/src/components/close-button/index.tsx +23 -0
  27. package/src/components/download-print-menu/download-print-menu.scss +49 -48
  28. package/src/components/download-print-menu/download-print-menu.tsx +147 -125
  29. package/src/components/download-print-menu/index.ts +1 -1
  30. package/src/components/icons/index.ts +11 -0
  31. package/src/components/plugin-button/plugin-button.scss +26 -0
  32. package/src/components/plugin-button/plugin-button.tsx +29 -0
  33. package/src/components/popover-menu/index.ts +1 -1
  34. package/src/components/popover-menu/popover-menu.scss +3 -3
  35. package/src/components/popover-menu/popover-menu.tsx +25 -25
  36. package/src/components/search/index.ts +1 -1
  37. package/src/components/search/search.scss +1 -1
  38. package/src/components/search/search.tsx +137 -144
  39. package/src/components/spinner/index.ts +1 -1
  40. package/src/components/spinner/spinner.scss +58 -50
  41. package/src/components/spinner/spinner.tsx +9 -9
  42. package/src/components/transcript/index.ts +1 -1
  43. package/src/components/transcript/transcript.scss +9 -33
  44. package/src/components/transcript/transcript.tsx +333 -454
  45. package/src/global.d.ts +6 -6
  46. package/src/index.ts +13 -1
  47. package/src/transcript-plugin.scss +3 -3
  48. package/src/transcript-plugin.tsx +210 -391
  49. package/src/types/index.ts +3 -0
  50. package/src/types/transcript-config.ts +11 -0
  51. package/src/types/transcript-item-data.ts +29 -0
  52. package/src/types/types-ui.ts +11 -0
  53. package/src/utils/debounce.ts +36 -0
  54. package/src/utils/index.ts +4 -0
  55. package/src/utils/object-utils.ts +34 -0
  56. package/src/utils/popover/popover.scss +30 -0
  57. package/src/utils/popover/popover.tsx +178 -0
  58. package/src/utils/utils.ts +86 -0
  59. package/src/variables.scss +14 -0
  60. package/src/assets/close.svg +0 -10
  61. package/src/utils.ts +0 -192
@@ -1,458 +1,277 @@
1
- import { h } from "preact";
2
- import {
3
- ContribPluginManager,
4
- OnMediaLoad,
5
- OnMediaUnload,
6
- OnPluginSetup,
7
- ContribServices,
8
- ContribPluginData,
9
- ContribPluginConfigs
10
- } from "@playkit-js-contrib/plugin";
11
- import {
12
- getContribLogger,
13
- ObjectUtils
14
- } from "@playkit-js-contrib/common";
15
- import * as styles from './transcript-plugin.scss';
16
- import {
17
- KalturaClient,
18
- KalturaClientException,
19
- KalturaAPIException
20
- } from "kaltura-typescript-client";
21
-
22
- import {
23
- KitchenSinkContentRendererProps,
24
- KitchenSinkItem,
25
- KitchenSinkPositions,
26
- KitchenSinkExpandModes,
27
- downloadContent,
28
- printContent,
29
- UpperBarItem
30
- } from "@playkit-js-contrib/ui";
31
- import { KalturaCaptionAssetFilter } from "kaltura-typescript-client/api/types/KalturaCaptionAssetFilter";
32
- import { CaptionAssetListAction } from "kaltura-typescript-client/api/types/CaptionAssetListAction";
33
- import { KalturaCaptionAsset } from "kaltura-typescript-client/api/types/KalturaCaptionAsset";
34
-
35
- import { Transcript } from "./components/transcript";
36
- import {
37
- getCaptionsByFormat,
38
- CaptionItem,
39
- getConfigValue,
40
- isBoolean,
41
- makePlainText,
42
- CaptionAssetServeAction,
43
- } from "./utils";
44
- import { DownloadPrintMenu } from "./components/download-print-menu";
45
-
46
- const pluginName = `playkit-js-transcript`;
47
-
48
- const logger = getContribLogger({
49
- class: "TranscriptPlugin",
50
- module: "transcript-plugin"
51
- });
52
-
53
- const { get } = ObjectUtils;
54
-
55
- interface TranscriptPluginConfig {
56
- expandOnFirstPlay: boolean;
57
- showTime: boolean;
58
- position: KitchenSinkPositions;
59
- scrollOffset: number; // distance between top border of transcript container and active caption on auto-scroll
60
- searchDebounceTimeout: number; // debounce on search
61
- searchNextPrevDebounceTimeout: number; // debounce on jump between prev/next search result
62
- downloadDisabled: boolean; // disable download menu
63
- printDisabled: boolean; // disable print menu
64
- expandMode: KitchenSinkExpandModes; // over or pushing the player
1
+ import {h} from 'preact';
2
+ import {ObjectUtils} from './utils';
3
+ import {ui} from 'kaltura-player-js';
4
+ import {PluginButton} from './components/plugin-button/plugin-button';
5
+ import {Transcript} from './components/transcript';
6
+ import {getConfigValue, isBoolean, makePlainText, prepareCuePoint} from './utils';
7
+ import {TranscriptConfig, PluginPositions, PluginStates, HighlightedMap, CuePointData, ItemTypes, CuePoint} from './types';
8
+ import {DownloadPrintMenu, downloadContent, printContent} from './components/download-print-menu';
9
+
10
+ const {SidePanelModes, SidePanelPositions, ReservedPresetNames, ReservedPresetAreas} = ui;
11
+ const {get} = ObjectUtils;
12
+ const {Tooltip} = KalturaPlayer.ui.components;
13
+
14
+ interface TimedMetadataEvent {
15
+ payload: {
16
+ cues: Array<CuePoint>;
17
+ };
65
18
  }
66
19
 
67
- export class TranscriptPlugin implements OnMediaLoad, OnPluginSetup, OnMediaUnload {
68
- private _kitchenSinkItem: KitchenSinkItem | null = null;
69
- private _upperBarItem: UpperBarItem | null = null;
20
+ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
21
+ static defaultConfig: TranscriptConfig = {
22
+ expandMode: SidePanelModes.ALONGSIDE,
23
+ expandOnFirstPlay: true,
24
+ showTime: true,
25
+ position: SidePanelPositions.RIGHT,
26
+ scrollOffset: 0,
27
+ searchDebounceTimeout: 250,
28
+ searchNextPrevDebounceTimeout: 100,
29
+ downloadDisabled: false,
30
+ printDisabled: false
31
+ };
32
+ private _activeCaptionMapId: string = '';
33
+ private _activeCuePointsMap: HighlightedMap = {};
34
+ private _captionMap: Map<string, Array<CuePointData>> = new Map();
70
35
  private _isLoading = false;
71
36
  private _hasError = false;
72
- private _entryId = "";
73
- private _captionsList: KalturaCaptionAsset[] = []; // list of captions
74
- private _captions: CaptionItem[] = []; // parsed captions
75
- private _kalturaClient = new KalturaClient();
76
- private _transcriptLanguage = "default";
77
- private _triggeredByKeyboard = false;
78
-
79
- constructor(
80
- private _contribServices: ContribServices,
81
- private _configs: ContribPluginConfigs<TranscriptPluginConfig>,
82
- private _player: KalturaPlayerTypes.Player
83
- ) {}
37
+ private _triggeredByKeyboard = false;
84
38
 
85
- onPluginSetup(): void {
86
- const { playerConfig } = this._configs;
39
+ private _removePopoverIcon: null | Function = null;
87
40
 
88
- this._kalturaClient.setOptions({
89
- clientTag: "playkit-js-transcript",
90
- endpointUrl: playerConfig.provider.env.serviceUrl
91
- });
41
+ private _transcriptPanel = null;
42
+ private _pluginState: PluginStates | null = null;
92
43
 
93
- this._kalturaClient.setDefaultRequestOptions({
94
- ks: playerConfig.provider.ks ? playerConfig.provider.ks : playerConfig.session.ks
95
- });
96
-
97
- this._player.addEventListener(this._player.Event.TIME_UPDATE, this._onTimeUpdate);
98
- this._player.addEventListener(this._player.Event.TEXT_TRACK_CHANGED, this._loadCaptions);
99
- this._player.addEventListener(this._player.Event.RESIZE, this._updateKitchenSink);
44
+ constructor(name: string, player: KalturaPlayerTypes.Player, config: TranscriptConfig) {
45
+ super(name, player, config);
100
46
  }
101
47
 
102
- onRegisterUI(): void {}
103
-
104
- onMediaLoad(): void {
105
- const { playerConfig } = this._configs;
106
- this._entryId = playerConfig.sources.id;
107
- this._getCaptionsList();
48
+ get sidePanelsManager() {
49
+ return this.player.getService('sidePanelsManager') as any;
108
50
  }
109
51
 
110
- onMediaUnload(): void {
111
- this._reset();
52
+ get cuePointManager() {
53
+ return this.player.getService('kalturaCuepoints') as any;
112
54
  }
113
55
 
114
- private _reset(): void {
115
- if (this._kitchenSinkItem) {
116
- this._contribServices.kitchenSinkManager.remove(this._kitchenSinkItem);
117
- this._kitchenSinkItem = null;
118
- }
119
- if (this._upperBarItem) {
120
- this._contribServices.upperBarManager.remove(this._upperBarItem);
121
- this._upperBarItem = null;
122
- }
123
- this._captionsList = [];
124
- this._captions = [];
125
- this._isLoading = false;
126
- this._hasError = false;
127
- this._entryId = "";
128
- this._transcriptLanguage = "default";
56
+ private get _data() {
57
+ return this._captionMap.get(this._activeCaptionMapId) || [];
129
58
  }
130
59
 
131
- private _initKitchensinkAndUpperBarItems(): void {
132
- if (!this._upperBarItem && !this._kitchenSinkItem) {
133
- this._addPopoverIcon();
134
- this._addKitchenSinkItem();
60
+ loadMedia(): void {
61
+ if (!this.cuePointManager || !this.sidePanelsManager) {
62
+ this.logger.warn("kalturaCuepoints or sidePanelsManager haven't registered");
63
+ return;
135
64
  }
65
+ this.onPluginSetup();
66
+ this.cuePointManager.registerTypes([this.cuePointManager.CuepointType.CAPTION]);
136
67
  }
137
68
 
138
- private _addPopoverIcon(): void {
139
- const { downloadDisabled, printDisabled } = this._configs.pluginConfig;
140
- this._upperBarItem = this._contribServices.upperBarManager.add({
141
- label: "Download transcript",
142
- onClick: () => {},
143
- renderItem: () => (
144
- <DownloadPrintMenu
145
- onDownload={this._handleDownload}
146
- onPrint={this._handlePrint}
147
- downloadDisabled={getConfigValue(downloadDisabled, isBoolean, false)}
148
- printDisabled={getConfigValue(printDisabled, isBoolean, false)}
149
- dropdownAriaLabel={`Download or print ${
150
- this._captionsList.length > 1 ? "current " : ""
151
- }transcript`}
152
- printButtonAriaLabel={`Print ${
153
- this._captionsList.length > 1 ? "current " : ""
154
- }transcript`}
155
- downloadButtonAriaLabel={`Download ${
156
- this._captionsList.length > 1 ? "current " : ""
157
- }transcript`}
158
- />
159
- )
160
- });
161
- }
162
-
163
- private _handleIconClick = (event: MouseEvent) => {
164
- this._triggeredByKeyboard = event.x === 0 && event.y === 0;
69
+ onPluginSetup(): void {
70
+ this.eventManager.listen(this.player, this.player.Event.TIMED_METADATA_CHANGE, this._onTimedMetadataChange);
71
+ this.eventManager.listen(this.player, this.player.Event.TIMED_METADATA_ADDED, this._onTimedMetadataAdded);
72
+ this.eventManager.listen(this.player, this.player.Event.TEXT_TRACK_CHANGED, this._handleLanguageChange);
165
73
  }
166
74
 
167
- private _addKitchenSinkItem(): void {
168
- const { expandMode, position, expandOnFirstPlay } = this._configs.pluginConfig;
169
- this._kitchenSinkItem = this._contribServices.kitchenSinkManager.add({
170
- label: "Transcript",
171
- expandMode: expandMode === KitchenSinkExpandModes.OverTheVideo ?
172
- KitchenSinkExpandModes.OverTheVideo :
173
- KitchenSinkExpandModes.AlongSideTheVideo,
174
- renderIcon: () => (
175
- <button
176
- className={styles.pluginButton}
177
- tabIndex={1}
178
- onClick={this._handleIconClick}
179
- >
180
- <div className={styles.transcriptIcon} />
181
- </button>
182
- ),
183
- position: getConfigValue(
184
- position,
185
- position =>
186
- typeof position === "string" &&
187
- (position === KitchenSinkPositions.Bottom ||
188
- position === KitchenSinkPositions.Right),
189
- KitchenSinkPositions.Bottom
190
- ),
191
- renderContent: this._renderKitchenSinkContent
192
- });
75
+ onRegisterUI(): void {}
193
76
 
194
- if (expandOnFirstPlay) {
195
- this._kitchenSinkItem.activate();
77
+ private _handleLanguageChange = () => {
78
+ this._activeCaptionMapId = this._getCaptionMapId();
79
+ if (this._captionMap.has(this._activeCaptionMapId)) {
80
+ this._updateTranscriptPanel();
196
81
  }
197
- }
198
-
199
- private _onTimeUpdate = (): void => {
200
- this._updateKitchenSink();
201
82
  };
202
83
 
203
- private _updateKitchenSink() {
204
- if (this._kitchenSinkItem) {
205
- this._kitchenSinkItem.update();
206
- }
207
- }
208
-
209
- private _initLoading() {
210
- if (!this._isLoading || this._hasError) {
211
- this._isLoading = true;
212
- this._hasError = false;
213
- this._updateKitchenSink();
84
+ private _updateTranscriptPanel() {
85
+ this._isLoading = false;
86
+ if (this._transcriptPanel) {
87
+ this.sidePanelsManager.update(this._transcriptPanel);
214
88
  }
215
89
  }
216
90
 
217
- private _onError = (error?: Error, message?: string, method?: string) => {
218
- let msg: string = message || "Error message not defined";
219
- if (error instanceof KalturaClientException) {
220
- msg = `${msg} (network error etc)`;
221
- } else if (error instanceof KalturaAPIException) {
222
- msg = `${msg} (api exception)`;
223
- }
224
- logger.error(msg, {
225
- method: method || "Method not defined",
226
- data: {
227
- error
91
+ private _onTimedMetadataAdded = ({payload}: TimedMetadataEvent) => {
92
+ const captionData: CuePointData[] = [];
93
+ payload.cues.forEach((cue: CuePoint) => {
94
+ if (cue.metadata.cuePointType === ItemTypes.Caption) {
95
+ captionData.push(prepareCuePoint(cue));
228
96
  }
229
97
  });
230
- this._isLoading = false;
231
- this._hasError = true;
232
- this._updateKitchenSink();
233
- };
234
-
235
- private _loadCaptions = (event?: {}): void => {
236
- if (!this._entryId) {
237
- return;
98
+ if (captionData.length) {
99
+ this._addCaptionData(captionData);
238
100
  }
239
- this._getCaptionsByLang(event);
240
101
  };
241
102
 
242
- private _getCaptionsList = (): void => {
243
- const filter: KalturaCaptionAssetFilter = new KalturaCaptionAssetFilter({ entryIdEqual: this._entryId});
244
- const request = new CaptionAssetListAction({ filter });
245
- this._initLoading();
246
- this._kalturaClient.request(request).then(
247
- data => {
248
- if (data && Array.isArray(data.objects) && data.objects.length > 0) {
249
- // cover both displayOnPlayer=true, as well as non-existence of the attribute
250
- this._captionsList = data.objects.filter(caption => caption.displayOnPlayer !== false)
251
- this._loadCaptions();
252
- } else {
253
- this._onError(undefined, "Data is empty", "_getCaptionsList");
254
- }
255
- },
256
- err => {
257
- this._onError(err, "Failed to fetch captions list", "_getCaptionsList");
258
- }
259
- );
103
+ private _onTimedMetadataChange = ({payload}: TimedMetadataEvent) => {
104
+ const transcriptCuePoints: Array<CuePoint> = payload.cues.filter((cue: CuePoint) => {
105
+ return cue.metadata.cuePointType === ItemTypes.Caption;
106
+ });
107
+ this._activeCuePointsMap = {};
108
+ transcriptCuePoints.forEach(cue => {
109
+ this._activeCuePointsMap[cue.id] = true;
110
+ });
111
+ this._updateTranscriptPanel();
260
112
  };
261
113
 
262
- private _filterCaptionAssetsByProperty = (
263
- list: KalturaCaptionAsset[],
264
- match: string | null,
265
- property: string
266
- ): KalturaCaptionAsset[] => {
267
- return list.filter((kalturaCaptionAsset: KalturaCaptionAsset) => {
268
- return get(kalturaCaptionAsset, property, null) === match;
269
- });
270
- }
114
+ private _addCaptionData = (newData: CuePointData[]) => {
115
+ this._activeCaptionMapId = this._getCaptionMapId();
116
+ this._captionMap.set(this._activeCaptionMapId, newData);
117
+ this._createOrUpdatePlugin();
118
+ };
271
119
 
272
- private _findCaptionAsset = (
273
- event: string | Record<string, any>
274
- ): KalturaCaptionAsset => {
275
- if (typeof event === 'string') {
276
- const filteredByLang = this._filterCaptionAssetsByProperty(this._captionsList, event, 'languageCode');
277
- // take first captions from caption-list when caption language is not defined
278
- return filteredByLang[0] ? filteredByLang[0] : this._captionsList[0];
279
- }
280
- const filteredByLang = this._filterCaptionAssetsByProperty(
281
- this._captionsList,
282
- get(event, 'payload.selectedTextTrack._language', null),
283
- 'languageCode'
284
- );
285
- if (filteredByLang.length === 1) {
286
- return filteredByLang[0];
287
- }
288
- const filteredByLabel = this._filterCaptionAssetsByProperty(
289
- filteredByLang,
290
- get(event, 'payload.selectedTextTrack._label', null),
291
- 'label'
292
- );
293
- if (filteredByLang.length === 1) {
294
- return filteredByLabel[0];
120
+ private _getCaptionMapId = (): string => {
121
+ const allTextTracks = this.player.getTracks(this.player.Track.TEXT) || [];
122
+ const activeTextTrack = allTextTracks.find(track => track.active);
123
+ if (activeTextTrack) {
124
+ const captionMapId = `${activeTextTrack.language}-${activeTextTrack.label}`;
125
+ return captionMapId;
295
126
  }
127
+ return '';
128
+ };
296
129
 
297
- const index: number = get(event, 'payload.selectedTextTrack._id', -1);
298
- const filteredByIndex = this._captionsList[index];
299
- // take first captions from caption-list when caption language is not defined
300
- return filteredByIndex ? filteredByIndex : this._captionsList[0];
130
+ private onClose = () => {
131
+ if (this.sidePanelsManager.isItemActive(this._transcriptPanel)) {
132
+ this._pluginState = PluginStates.CLOSED;
133
+ this.sidePanelsManager.deactivateItem(this._transcriptPanel);
134
+ } else {
135
+ this.sidePanelsManager.activateItem(this._transcriptPanel);
136
+ }
301
137
  };
302
138
 
303
- private _getCaptionsByLang = (
304
- event: string | Record<string, any> = get(this._configs, 'playerConfig.playback.textLanguage', "")
305
- ): void => {
306
- if (
307
- (typeof event === "string" ?
308
- event :
309
- get(event, 'payload.selectedTextTrack._language', null)
310
- ) === "off" && this._captions.length
311
- ) {
312
- // prevent loading of captions when user select "off" captions option
139
+ private _addPopoverIcon(): void {
140
+ const {downloadDisabled, printDisabled} = this.config;
141
+ if (this._removePopoverIcon) {
313
142
  return;
314
143
  }
315
- if (this._captionsList && this._captionsList.length > 0) {
316
- const captionAsset: KalturaCaptionAsset = this._findCaptionAsset(event);
317
- if (captionAsset) {
318
- this._transcriptLanguage = captionAsset.language;
319
- this._initLoading();
320
- this._initKitchensinkAndUpperBarItems();
321
- this._loadCaptionsAsset(captionAsset);
322
- } else {
323
- this._onError(
324
- undefined,
325
- "Current video doesn't have captions in selected language",
326
- "_getCaptionsByLang"
327
- );
328
- }
144
+ this._removePopoverIcon = this.player.ui.addComponent({
145
+ label: 'Download transcript',
146
+ area: ReservedPresetAreas.TopBarRightControls,
147
+ presets: [ReservedPresetNames.Playback, ReservedPresetNames.Live],
148
+ get: () => (
149
+ <DownloadPrintMenu
150
+ onDownload={this._handleDownload}
151
+ onPrint={this._handlePrint}
152
+ downloadDisabled={getConfigValue(downloadDisabled, isBoolean, false)}
153
+ printDisabled={getConfigValue(printDisabled, isBoolean, false)}
154
+ dropdownAriaLabel={`Download or print transcript`}
155
+ printButtonAriaLabel={`Print transcript`}
156
+ downloadButtonAriaLabel={`Download transcript`}
157
+ />
158
+ )
159
+ });
160
+ }
161
+
162
+ private _createOrUpdatePlugin = () => {
163
+ if (this._transcriptPanel) {
164
+ this._updateTranscriptPanel();
329
165
  } else {
330
- this._onError(undefined, "Current video doesn't have captions", "_getCaptionsByLang");
166
+ this._initLoading();
167
+ this._addPopoverIcon();
168
+ this._addTranscriptItem();
331
169
  }
332
170
  };
333
171
 
334
- private _loadCaptionsAsset = (captionAsset: KalturaCaptionAsset) => {
335
-
336
- // CaptionAssetServeAction should be imported from "kaltura-typescript-client/api/types/CaptionAssetServeAction"
337
- // replace CaptionAssetServeAction while client fixed (https://kaltura.atlassian.net/browse/FEV-470)
338
- const request = new CaptionAssetServeAction({ captionAssetId: captionAsset.id });
339
-
340
- this._kalturaClient.request(request).then(
341
- data => {
342
- this._getCaptionData(data, captionAsset);
172
+ private _addTranscriptItem(): void {
173
+ const buttonLabel = 'Transcript';
174
+ const {expandMode, position, expandOnFirstPlay} = this.config;
175
+ const pluginMode: PluginPositions = [SidePanelPositions.RIGHT, SidePanelPositions.LEFT].includes(position)
176
+ ? PluginPositions.VERTICAL
177
+ : PluginPositions.HORIZONTAL;
178
+ const {showTime, scrollOffset, searchDebounceTimeout, searchNextPrevDebounceTimeout} = this.config;
179
+ this._transcriptPanel = this.sidePanelsManager.addItem({
180
+ label: 'Transcript',
181
+ panelComponent: () => {
182
+ return (
183
+ <Transcript
184
+ showTime={getConfigValue(showTime, isBoolean, true)}
185
+ scrollOffset={getConfigValue(scrollOffset, Number.isInteger, 0)}
186
+ searchDebounceTimeout={getConfigValue(searchDebounceTimeout, Number.isInteger, 250)}
187
+ searchNextPrevDebounceTimeout={getConfigValue(searchNextPrevDebounceTimeout, Number.isInteger, 100)}
188
+ highlightedMap={this._activeCuePointsMap}
189
+ onSeek={this._seekTo}
190
+ pluginMode={pluginMode}
191
+ onItemClicked={this._seekTo}
192
+ captions={this._data}
193
+ isLoading={this._isLoading}
194
+ hasError={this._hasError}
195
+ onRetryLoad={this._createOrUpdatePlugin}
196
+ currentTime={this.player.currentTime}
197
+ videoDuration={this.player.duration}
198
+ kitchenSinkActive={!!this.sidePanelsManager.isItemActive(this._transcriptPanel)}
199
+ toggledWithEnter={this._triggeredByKeyboard}
200
+ onClose={this.onClose}
201
+ />
202
+ );
343
203
  },
344
- err => {
345
- // remove getting captions from error while client fixed (https://kaltura.atlassian.net/browse/FEV-470)
346
- this._getCaptionData(err, captionAsset);
347
- // this._onError(err, "Failed to fetch caption asset", "_loadCaptionsAsset");
204
+ iconComponent: ({isActive}: {isActive: boolean}) => {
205
+ return (
206
+ <Tooltip label={buttonLabel} type="bottom">
207
+ <PluginButton isActive={isActive} onClick={this.onClose} label={buttonLabel} />
208
+ </Tooltip>
209
+ );
210
+ },
211
+ presets: [ReservedPresetNames.Playback, ReservedPresetNames.Live, ReservedPresetNames.Ads],
212
+ position: position,
213
+ expandMode: expandMode,
214
+ onActivate: () => {
215
+ this._pluginState = PluginStates.OPENED;
348
216
  }
349
- )
350
- };
217
+ });
351
218
 
352
- private _getCaptionData = (data: any, captionAsset: KalturaCaptionAsset) => {
353
- const rawCaptions = get(data, 'error.message', data);
354
- if (rawCaptions) {
355
- this._captions = this._parseCaptions(rawCaptions, captionAsset);
356
- this._isLoading = false;
357
- this._updateKitchenSink();
358
- } else {
359
- this._onError(undefined, "Captions data is empty", "_loadCaptionsAsset");
219
+ if ((expandOnFirstPlay && !this._pluginState) || this._pluginState === PluginStates.OPENED) {
220
+ this.ready.then(() => {
221
+ this.sidePanelsManager.activateItem(this._transcriptPanel);
222
+ });
360
223
  }
361
224
  }
362
225
 
363
- private _parseCaptions = (data: string, captionAsset: KalturaCaptionAsset): CaptionItem[] => {
364
- try {
365
- const captionFormat = this._getCaptionFormat(captionAsset);
366
- if (data.toString().indexOf("Error: ") === 0) {
367
- // remove this condition once client fixed
368
- data = data.toString().replace("Error: ", "");
369
- }
370
- return getCaptionsByFormat(data, captionFormat);
371
- } catch(err) {
372
- this._onError(err, "Failed to parse the caption file", "_parseCaptions");
226
+ private _initLoading() {
227
+ if (!this._isLoading || this._hasError) {
228
+ this._isLoading = true;
229
+ this._hasError = false;
230
+ this._updateTranscriptPanel();
373
231
  }
374
- return [];
375
- };
376
-
377
- private _getCaptionFormat = (captionAsset: KalturaCaptionAsset): string => {
378
- const selectedLanguage: Record<string, any> =
379
- this._captionsList &&
380
- captionAsset &&
381
- (this._captionsList.find((item: KalturaCaptionAsset) => item.id === captionAsset.id) || {});
382
- return get(selectedLanguage, 'format', '');
383
- };
232
+ }
384
233
 
385
234
  private _seekTo = (time: number) => {
386
- this._player.currentTime = time;
235
+ this.player.currentTime = time;
387
236
  };
388
237
 
389
238
  private _handleDownload = () => {
390
- const { playerConfig } = this._configs;
391
- if (this._captions) {
392
- const entryMetadata = get(playerConfig, 'sources.metadata', {});
393
- downloadContent(
394
- makePlainText(this._captions),
395
- `${this._transcriptLanguage}${
396
- entryMetadata.name ? `-${entryMetadata.name}` : ""
397
- }.txt`
398
- );
239
+ const {config} = this.player;
240
+ const captions = this._captionMap.get(this._activeCaptionMapId) || [];
241
+
242
+ if (captions) {
243
+ const entryMetadata = get(config, 'sources.metadata', {});
244
+ const language = this._getCaptionMapId();
245
+
246
+ downloadContent(makePlainText(captions), `${language}${entryMetadata.name ? `-${entryMetadata.name}` : ''}.txt`);
399
247
  }
400
248
  };
401
249
 
402
250
  private _handlePrint = () => {
403
- if (this._captions) {
404
- printContent(makePlainText(this._captions));
251
+ const captions = this._captionMap.get(this._activeCaptionMapId) || [];
252
+
253
+ if (captions) {
254
+ printContent(makePlainText(captions));
405
255
  }
406
256
  };
407
257
 
408
- private _renderKitchenSinkContent = (props: KitchenSinkContentRendererProps) => {
409
- const {
410
- showTime,
411
- scrollOffset,
412
- searchDebounceTimeout,
413
- searchNextPrevDebounceTimeout
414
- } = this._configs.pluginConfig;
415
- return (
416
- <Transcript
417
- {...props}
418
- showTime={getConfigValue(showTime, isBoolean, true)}
419
- scrollOffset={getConfigValue(scrollOffset, Number.isInteger, 0)}
420
- searchDebounceTimeout={getConfigValue(searchDebounceTimeout, Number.isInteger, 250)}
421
- searchNextPrevDebounceTimeout={getConfigValue(
422
- searchNextPrevDebounceTimeout,
423
- Number.isInteger,
424
- 100
425
- )}
426
- onSeek={this._seekTo}
427
- captions={this._captions}
428
- isLoading={this._isLoading}
429
- hasError={this._hasError}
430
- onRetryLoad={this._loadCaptions}
431
- currentTime={this._player.currentTime}
432
- videoDuration={this._player.duration}
433
- kitchenSinkActive={!!this._kitchenSinkItem?.isActive()}
434
- toggledWithEnter={this._triggeredByKeyboard}
435
- />
436
- );
437
- };
438
- }
258
+ static isValid(): boolean {
259
+ return true;
260
+ }
439
261
 
440
- ContribPluginManager.registerPlugin(
441
- pluginName,
442
- (data: ContribPluginData<TranscriptPluginConfig>) => {
443
- return new TranscriptPlugin(data.contribServices, data.configs, data.player);
444
- },
445
- {
446
- defaultConfig: {
447
- expandMode: KitchenSinkExpandModes.AlongSideTheVideo,
448
- expandOnFirstPlay: true,
449
- showTime: true,
450
- position: KitchenSinkPositions.Bottom,
451
- scrollOffset: 0,
452
- searchDebounceTimeout: 250,
453
- searchNextPrevDebounceTimeout: 100,
454
- downloadDisabled: false,
455
- printDisabled: false
262
+ reset(): void {
263
+ if (this._removePopoverIcon) {
264
+ this._removePopoverIcon();
265
+ this._removePopoverIcon = null;
456
266
  }
267
+
268
+ this._captionMap = new Map();
269
+ this._activeCaptionMapId = '';
270
+ this._isLoading = false;
271
+ this._hasError = false;
457
272
  }
458
- );
273
+
274
+ destroy(): void {
275
+ this.reset();
276
+ }
277
+ }
@@ -0,0 +1,3 @@
1
+ export * from './types-ui';
2
+ export * from './transcript-config';
3
+ export * from './transcript-item-data';