@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.
- package/CHANGELOG.md +9 -0
- package/LICENSE +5 -5
- package/README.md +122 -24
- package/dist/1501adfdf5c835667ce7.svg +9 -0
- package/dist/301e7a199b2cd06c2edf.svg +9 -0
- package/dist/33bce27c0f546e80478c.svg +36 -0
- package/dist/6a4867d3d9170cc2a24d.svg +9 -0
- package/dist/73bab0af28a1c7aed29f.svg +9 -0
- package/dist/84087eb1cff72e5e6bd3.svg +9 -0
- package/dist/95d7192dc427afb678d0.svg +9 -0
- package/dist/cea5d6a7f050cbd199a1.svg +9 -0
- package/dist/d93f06ff32cdfcd016df.svg +9 -0
- package/dist/e5496f4c01207db44ffc.svg +9 -0
- package/dist/playkit-transcript.js +1 -31
- package/dist/playkit-transcript.js.map +1 -1
- package/package.json +53 -50
- package/src/components/a11y-wrapper/a11y-wrapper.ts +26 -0
- package/src/components/a11y-wrapper/index.ts +1 -0
- package/src/components/caption/caption.scss +1 -1
- package/src/components/caption/caption.tsx +118 -140
- package/src/components/caption/index.ts +1 -1
- package/src/components/caption-list/captionList.scss +8 -8
- package/src/components/caption-list/captionList.tsx +115 -117
- package/src/components/caption-list/index.ts +1 -1
- package/src/components/close-button/close-button.scss +11 -0
- package/src/components/close-button/index.tsx +23 -0
- package/src/components/download-print-menu/download-print-menu.scss +49 -48
- package/src/components/download-print-menu/download-print-menu.tsx +147 -125
- package/src/components/download-print-menu/index.ts +1 -1
- package/src/components/icons/index.ts +11 -0
- package/src/components/plugin-button/plugin-button.scss +26 -0
- package/src/components/plugin-button/plugin-button.tsx +29 -0
- package/src/components/popover-menu/index.ts +1 -1
- package/src/components/popover-menu/popover-menu.scss +3 -3
- package/src/components/popover-menu/popover-menu.tsx +25 -25
- package/src/components/search/index.ts +1 -1
- package/src/components/search/search.scss +1 -1
- package/src/components/search/search.tsx +137 -144
- package/src/components/spinner/index.ts +1 -1
- package/src/components/spinner/spinner.scss +58 -50
- package/src/components/spinner/spinner.tsx +9 -9
- package/src/components/transcript/index.ts +1 -1
- package/src/components/transcript/transcript.scss +9 -33
- package/src/components/transcript/transcript.tsx +333 -454
- package/src/global.d.ts +6 -6
- package/src/index.ts +13 -1
- package/src/transcript-plugin.scss +3 -3
- package/src/transcript-plugin.tsx +210 -391
- package/src/types/index.ts +3 -0
- package/src/types/transcript-config.ts +11 -0
- package/src/types/transcript-item-data.ts +29 -0
- package/src/types/types-ui.ts +11 -0
- package/src/utils/debounce.ts +36 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/object-utils.ts +34 -0
- package/src/utils/popover/popover.scss +30 -0
- package/src/utils/popover/popover.tsx +178 -0
- package/src/utils/utils.ts +86 -0
- package/src/variables.scss +14 -0
- package/src/assets/close.svg +0 -10
- package/src/utils.ts +0 -192
|
@@ -1,458 +1,277 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
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
|
-
|
|
86
|
-
const { playerConfig } = this._configs;
|
|
39
|
+
private _removePopoverIcon: null | Function = null;
|
|
87
40
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
endpointUrl: playerConfig.provider.env.serviceUrl
|
|
91
|
-
});
|
|
41
|
+
private _transcriptPanel = null;
|
|
42
|
+
private _pluginState: PluginStates | null = null;
|
|
92
43
|
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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
|
-
|
|
111
|
-
this.
|
|
52
|
+
get cuePointManager() {
|
|
53
|
+
return this.player.getService('kalturaCuepoints') as any;
|
|
112
54
|
}
|
|
113
55
|
|
|
114
|
-
private
|
|
115
|
-
|
|
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
|
-
|
|
132
|
-
if (!this.
|
|
133
|
-
this.
|
|
134
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
this.
|
|
141
|
-
|
|
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
|
-
|
|
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
|
-
|
|
195
|
-
|
|
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
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
231
|
-
|
|
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
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
this.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if (
|
|
276
|
-
const
|
|
277
|
-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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.
|
|
166
|
+
this._initLoading();
|
|
167
|
+
this._addPopoverIcon();
|
|
168
|
+
this._addTranscriptItem();
|
|
331
169
|
}
|
|
332
170
|
};
|
|
333
171
|
|
|
334
|
-
private
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
this.
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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
|
-
|
|
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.
|
|
235
|
+
this.player.currentTime = time;
|
|
387
236
|
};
|
|
388
237
|
|
|
389
238
|
private _handleDownload = () => {
|
|
390
|
-
const {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
404
|
-
|
|
251
|
+
const captions = this._captionMap.get(this._activeCaptionMapId) || [];
|
|
252
|
+
|
|
253
|
+
if (captions) {
|
|
254
|
+
printContent(makePlainText(captions));
|
|
405
255
|
}
|
|
406
256
|
};
|
|
407
257
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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
|
+
}
|