@playkit-js/transcript 3.0.1 → 3.1.0-canary.0-0df319e
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 +14 -0
- package/dist/playkit-transcript.js +1 -1
- package/dist/playkit-transcript.js.map +1 -1
- package/package.json +5 -4
- package/src/components/caption/caption.tsx +48 -27
- package/src/components/caption-list/captionList.tsx +5 -5
- package/src/components/close-button/index.tsx +9 -3
- package/src/components/icons/index.ts +4 -0
- package/src/components/plugin-button/plugin-button.scss +0 -19
- package/src/components/plugin-button/plugin-button.tsx +7 -9
- package/src/components/search/search.tsx +94 -58
- package/src/components/transcript/transcript.tsx +42 -26
- package/src/transcript-plugin.tsx +153 -97
- package/src/types/types-ui.ts +0 -7
- package/src/utils/index.ts +0 -1
- package/src/utils/utils.ts +34 -0
- package/src/components/download-print-menu/download-print-icons.tsx +0 -23
- package/src/components/download-print-menu/download-print-menu.scss +0 -44
- package/src/components/download-print-menu/download-print-menu.tsx +0 -164
- package/src/components/download-print-menu/index.ts +0 -1
- package/src/components/popover-menu/index.ts +0 -1
- package/src/components/popover-menu/popover-menu.scss +0 -6
- package/src/components/popover-menu/popover-menu.tsx +0 -52
- package/src/utils/popover/popover.scss +0 -30
- package/src/utils/popover/popover.tsx +0 -178
|
@@ -5,10 +5,19 @@ import * as styles from './transcript.scss';
|
|
|
5
5
|
import {Spinner} from '../spinner';
|
|
6
6
|
import {Search} from '../search';
|
|
7
7
|
import {CaptionList} from '../caption-list';
|
|
8
|
-
import {HighlightedMap, CuePointData
|
|
8
|
+
import {HighlightedMap, CuePointData} from '../../types';
|
|
9
9
|
import {CloseButton} from '../close-button';
|
|
10
10
|
import {ErrorIcon} from './error-icon';
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
const {ENTER, SPACE, TAB, ESC} = KalturaPlayer.ui.utils.KeyMap;
|
|
13
|
+
const {withText, Text} = KalturaPlayer.ui.preacti18n;
|
|
14
|
+
|
|
15
|
+
const translates = {
|
|
16
|
+
autoScrollLabel: <Text id="transcript.auto_scroll">Enable auto scroll</Text>,
|
|
17
|
+
errorTitle: <Text id="transcript.whoops">Whoops!</Text>,
|
|
18
|
+
errorDescripton: <Text id="transcript.load_failed">Failed to load transcript</Text>,
|
|
19
|
+
skipTranscript: <Text id="transcript.skip_transcript">Skip transcript</Text>
|
|
20
|
+
};
|
|
12
21
|
|
|
13
22
|
export interface TranscriptProps {
|
|
14
23
|
onSeek(time: number): void;
|
|
@@ -26,8 +35,11 @@ export interface TranscriptProps {
|
|
|
26
35
|
kitchenSinkActive: boolean;
|
|
27
36
|
toggledWithEnter: boolean;
|
|
28
37
|
highlightedMap: HighlightedMap;
|
|
29
|
-
pluginMode: PluginPositions;
|
|
30
38
|
onItemClicked: (n: number) => void;
|
|
39
|
+
autoScrollLabel?: string;
|
|
40
|
+
errorTitle?: string;
|
|
41
|
+
errorDescripton?: string;
|
|
42
|
+
skipTranscript?: string;
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
interface TranscriptState {
|
|
@@ -50,7 +62,7 @@ const initialSearch = {
|
|
|
50
62
|
|
|
51
63
|
const SEARCHBAR_HEIGHT = 38; // height of search bar with margins
|
|
52
64
|
|
|
53
|
-
export class
|
|
65
|
+
export class TranscriptComponent extends Component<TranscriptProps, TranscriptState> {
|
|
54
66
|
private _transcriptListRef: HTMLElement | null = null;
|
|
55
67
|
private _captionListRef: any = null;
|
|
56
68
|
private _skipTranscriptButtonRef: HTMLDivElement | null = null;
|
|
@@ -105,7 +117,8 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
105
117
|
<div
|
|
106
118
|
role="button"
|
|
107
119
|
className={`${styles.autoscrollButton} ${isAutoScrollEnabled ? '' : styles.autoscrollButtonVisible}`}
|
|
108
|
-
tabIndex={isAutoScrollEnabled ? -1 :
|
|
120
|
+
tabIndex={isAutoScrollEnabled ? -1 : 0}
|
|
121
|
+
aria-label={this.props.autoScrollLabel}
|
|
109
122
|
ref={node => {
|
|
110
123
|
this._autoscrollButtonRef = node;
|
|
111
124
|
}}>
|
|
@@ -201,8 +214,13 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
201
214
|
);
|
|
202
215
|
};
|
|
203
216
|
|
|
217
|
+
private _handleClick = (event: MouseEvent | KeyboardEvent) => {
|
|
218
|
+
event.preventDefault();
|
|
219
|
+
this._autoscrollButtonRef?.focus();
|
|
220
|
+
};
|
|
221
|
+
|
|
204
222
|
private _handleKeyDown = (event: KeyboardEvent) => {
|
|
205
|
-
if (event.keyCode ===
|
|
223
|
+
if (event.keyCode === TAB && !event.shiftKey) {
|
|
206
224
|
this.setState({
|
|
207
225
|
isAutoScrollEnabled: false
|
|
208
226
|
});
|
|
@@ -211,9 +229,8 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
211
229
|
event.preventDefault();
|
|
212
230
|
captionRef.focus();
|
|
213
231
|
}
|
|
214
|
-
} else if (event.keyCode === ENTER || event.keyCode ===
|
|
215
|
-
|
|
216
|
-
this._autoscrollButtonRef?.focus();
|
|
232
|
+
} else if (event.keyCode === ENTER || event.keyCode === SPACE) {
|
|
233
|
+
this._handleClick(event);
|
|
217
234
|
}
|
|
218
235
|
};
|
|
219
236
|
|
|
@@ -226,8 +243,9 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
226
243
|
}}
|
|
227
244
|
className={styles.skipTranscriptButton}
|
|
228
245
|
onKeyDown={this._handleKeyDown}
|
|
229
|
-
|
|
230
|
-
|
|
246
|
+
onClick={this._handleClick}
|
|
247
|
+
tabIndex={0}>
|
|
248
|
+
{this.props.skipTranscript}
|
|
231
249
|
</div>
|
|
232
250
|
);
|
|
233
251
|
};
|
|
@@ -235,9 +253,6 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
235
253
|
private _renderTranscript = () => {
|
|
236
254
|
const {captions, hasError, onRetryLoad, showTime, videoDuration, highlightedMap} = this.props;
|
|
237
255
|
const {isAutoScrollEnabled, searchMap, activeSearchIndex, searchLength} = this.state;
|
|
238
|
-
if (!captions || !captions.length) {
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
256
|
|
|
242
257
|
if (hasError) {
|
|
243
258
|
return (
|
|
@@ -245,17 +260,16 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
245
260
|
<div className={styles.errorIcon}>
|
|
246
261
|
<ErrorIcon />
|
|
247
262
|
</div>
|
|
248
|
-
<p className={styles.errorMainText}>
|
|
249
|
-
<p className={styles.errorDescriptionText}>
|
|
250
|
-
Failed to get transcript, please try again
|
|
251
|
-
<button className={styles.retryButton} onClick={onRetryLoad}>
|
|
252
|
-
Retry
|
|
253
|
-
</button>
|
|
254
|
-
</p>
|
|
263
|
+
<p className={styles.errorMainText}>{this.props.errorTitle}</p>
|
|
264
|
+
<p className={styles.errorDescriptionText}>{this.props.errorDescripton}</p>
|
|
255
265
|
</div>
|
|
256
266
|
);
|
|
257
267
|
}
|
|
258
268
|
|
|
269
|
+
if (!captions || !captions.length) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
|
|
259
273
|
const captionProps = {
|
|
260
274
|
showTime,
|
|
261
275
|
searchLength,
|
|
@@ -351,14 +365,14 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
351
365
|
};
|
|
352
366
|
|
|
353
367
|
private _handleEsc = (event: KeyboardEvent) => {
|
|
354
|
-
if (event.keyCode ===
|
|
368
|
+
if (event.keyCode === ESC) {
|
|
355
369
|
this.props.onClose();
|
|
356
370
|
}
|
|
357
371
|
};
|
|
358
372
|
|
|
359
373
|
render(props: TranscriptProps) {
|
|
360
|
-
const {isLoading, kitchenSinkActive} = props;
|
|
361
|
-
|
|
374
|
+
const {isLoading, kitchenSinkActive, hasError} = props;
|
|
375
|
+
const renderTranscriptButtons = !(isLoading || hasError);
|
|
362
376
|
return (
|
|
363
377
|
<div
|
|
364
378
|
className={`${styles.root} ${kitchenSinkActive ? '' : styles.hidden}`}
|
|
@@ -371,7 +385,7 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
371
385
|
|
|
372
386
|
<CloseButton onClick={this.props.onClose} />
|
|
373
387
|
|
|
374
|
-
{
|
|
388
|
+
{renderTranscriptButtons && this._renderSkipTranscriptButton()}
|
|
375
389
|
<div
|
|
376
390
|
className={styles.body}
|
|
377
391
|
onScroll={this._onScroll}
|
|
@@ -380,9 +394,11 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
380
394
|
}}>
|
|
381
395
|
{isLoading ? this._renderLoading() : this._renderTranscript()}
|
|
382
396
|
</div>
|
|
383
|
-
{this._renderScrollToButton()}
|
|
397
|
+
{renderTranscriptButtons && this._renderScrollToButton()}
|
|
384
398
|
</div>
|
|
385
399
|
</div>
|
|
386
400
|
);
|
|
387
401
|
}
|
|
388
402
|
}
|
|
403
|
+
|
|
404
|
+
export const Transcript = withText(translates)(TranscriptComponent);
|
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import {h} from 'preact';
|
|
2
2
|
import {OnClickEvent} from '@playkit-js/common';
|
|
3
3
|
import {ui} from 'kaltura-player-js';
|
|
4
|
-
import {
|
|
4
|
+
import {UpperBarManager, SidePanelsManager} from '@playkit-js/ui-managers';
|
|
5
|
+
import {ObjectUtils, downloadContent, printContent} from './utils';
|
|
6
|
+
import {icons} from './components/icons';
|
|
5
7
|
import {PluginButton} from './components/plugin-button/plugin-button';
|
|
6
8
|
import {Transcript} from './components/transcript';
|
|
7
9
|
import {getConfigValue, isBoolean, makePlainText, prepareCuePoint} from './utils';
|
|
8
|
-
import {TranscriptConfig,
|
|
9
|
-
import {DownloadPrintMenu, downloadContent, printContent} from './components/download-print-menu';
|
|
10
|
+
import {TranscriptConfig, PluginStates, HighlightedMap, CuePointData, ItemTypes, CuePoint} from './types';
|
|
10
11
|
|
|
11
12
|
const {SidePanelModes, SidePanelPositions, ReservedPresetNames, ReservedPresetAreas} = ui;
|
|
12
|
-
const {get} = ObjectUtils;
|
|
13
|
-
const {Tooltip} = KalturaPlayer.ui.components;
|
|
14
13
|
const {withText, Text} = KalturaPlayer.ui.preacti18n;
|
|
14
|
+
const {get} = ObjectUtils;
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
printDownloadAreaLabel: <Text id="transcript.print_download_area_label">Download or print current transcript</Text>,
|
|
18
|
-
printTranscript: <Text id="transcript.print_transcript">Print current transcript</Text>,
|
|
19
|
-
downloadTranscript: <Text id="transcript.download_transcript">Download current transcript</Text>
|
|
20
|
-
});
|
|
16
|
+
const LOADING_TIMEOUT = 10000;
|
|
21
17
|
|
|
22
18
|
interface TimedMetadataEvent {
|
|
23
19
|
payload: {
|
|
@@ -41,12 +37,14 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
41
37
|
private _activeCuePointsMap: HighlightedMap = {};
|
|
42
38
|
private _captionMap: Map<string, Array<CuePointData>> = new Map();
|
|
43
39
|
private _isLoading = false;
|
|
40
|
+
private _loadingTimeoutId?: ReturnType<typeof setTimeout>;
|
|
44
41
|
private _hasError = false;
|
|
45
42
|
private _triggeredByKeyboard = false;
|
|
46
43
|
|
|
47
|
-
private
|
|
48
|
-
|
|
49
|
-
private
|
|
44
|
+
private _transcriptPanel = -1;
|
|
45
|
+
private _transcriptIcon = -1;
|
|
46
|
+
private _printIcon = -1;
|
|
47
|
+
private _downloadIcon = -1;
|
|
50
48
|
private _pluginState: PluginStates | null = null;
|
|
51
49
|
|
|
52
50
|
constructor(name: string, player: KalturaPlayerTypes.Player, config: TranscriptConfig) {
|
|
@@ -54,7 +52,11 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
get sidePanelsManager() {
|
|
57
|
-
return this.player.getService('sidePanelsManager') as
|
|
55
|
+
return this.player.getService('sidePanelsManager') as SidePanelsManager | undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get upperBarManager() {
|
|
59
|
+
return this.player.getService('upperBarManager') as UpperBarManager | undefined;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
get cuePointManager() {
|
|
@@ -66,8 +68,8 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
loadMedia(): void {
|
|
69
|
-
if (!this.cuePointManager || !this.sidePanelsManager) {
|
|
70
|
-
this.logger.warn("kalturaCuepoints or
|
|
71
|
+
if (!this.cuePointManager || !this.sidePanelsManager || !this.upperBarManager) {
|
|
72
|
+
this.logger.warn("kalturaCuepoints, sidePanelsManager or upperBarManager haven't registered");
|
|
71
73
|
return;
|
|
72
74
|
}
|
|
73
75
|
if (this.player.isLive()) {
|
|
@@ -79,31 +81,44 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
private _initListeners(): void {
|
|
82
|
-
this.eventManager.
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
this._addPopoverIcon();
|
|
84
|
+
this.eventManager.listenOnce(this.player, this.player.Event.TRACKS_CHANGED, () => {
|
|
85
|
+
if (this._getTextTracks().length) {
|
|
86
|
+
this.eventManager.listen(this.player, this.player.Event.TIMED_METADATA_CHANGE, this._onTimedMetadataChange);
|
|
87
|
+
this.eventManager.listen(this.player, this.player.Event.TIMED_METADATA_ADDED, this._onTimedMetadataAdded);
|
|
88
|
+
this.eventManager.listen(this.player, this.player.Event.TEXT_TRACK_CHANGED, this._handleLanguageChange);
|
|
89
|
+
this._addDownloadIcon();
|
|
90
|
+
this._addPrintIcon();
|
|
90
91
|
this._addTranscriptItem();
|
|
92
|
+
this._initLoading();
|
|
91
93
|
}
|
|
92
94
|
});
|
|
93
|
-
this.eventManager.listen(this.player, this.player.Event.TIMED_METADATA_CHANGE, this._onTimedMetadataChange);
|
|
94
|
-
this.eventManager.listen(this.player, this.player.Event.TIMED_METADATA_ADDED, this._onTimedMetadataAdded);
|
|
95
|
-
this.eventManager.listen(this.player, this.player.Event.TEXT_TRACK_CHANGED, this._handleLanguageChange);
|
|
96
95
|
}
|
|
97
96
|
|
|
97
|
+
private _initLoading = () => {
|
|
98
|
+
clearTimeout(this._loadingTimeoutId);
|
|
99
|
+
this._isLoading = false;
|
|
100
|
+
this._hasError = false;
|
|
101
|
+
if (!this._captionMap.has(this._activeCaptionMapId)) {
|
|
102
|
+
// turn on loading animation till captions added to TextTrack
|
|
103
|
+
this._isLoading = true;
|
|
104
|
+
this._loadingTimeoutId = setTimeout(() => {
|
|
105
|
+
// display error slate
|
|
106
|
+
this._isLoading = false;
|
|
107
|
+
this._hasError = true;
|
|
108
|
+
this._updateTranscriptPanel();
|
|
109
|
+
}, LOADING_TIMEOUT);
|
|
110
|
+
}
|
|
111
|
+
this._updateTranscriptPanel();
|
|
112
|
+
};
|
|
113
|
+
|
|
98
114
|
private _handleLanguageChange = () => {
|
|
99
115
|
this._activeCaptionMapId = this._getCaptionMapId();
|
|
100
|
-
this.
|
|
101
|
-
this._updateTranscriptPanel();
|
|
116
|
+
this._initLoading();
|
|
102
117
|
};
|
|
103
118
|
|
|
104
119
|
private _updateTranscriptPanel() {
|
|
105
120
|
if (this._transcriptPanel) {
|
|
106
|
-
this.sidePanelsManager
|
|
121
|
+
this.sidePanelsManager?.update(this._transcriptPanel);
|
|
107
122
|
}
|
|
108
123
|
}
|
|
109
124
|
|
|
@@ -120,14 +135,15 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
120
135
|
};
|
|
121
136
|
|
|
122
137
|
private _onTimedMetadataChange = ({payload}: TimedMetadataEvent) => {
|
|
123
|
-
const transcriptCuePoints: Array<CuePoint> = payload.cues
|
|
124
|
-
|
|
138
|
+
const transcriptCuePoints: Array<CuePoint> = payload.cues
|
|
139
|
+
.filter((cue: CuePoint) => {
|
|
140
|
+
return cue.metadata.cuePointType === ItemTypes.Caption;
|
|
125
141
|
})
|
|
126
142
|
.filter((cue, index, array) => {
|
|
127
143
|
// filter out captions that has endTime eq to next caption startTime
|
|
128
144
|
const nextCue = array[index + 1];
|
|
129
145
|
return !nextCue || cue.endTime !== nextCue.startTime;
|
|
130
|
-
|
|
146
|
+
});
|
|
131
147
|
this._activeCuePointsMap = {};
|
|
132
148
|
transcriptCuePoints.forEach(cue => {
|
|
133
149
|
this._activeCuePointsMap[cue.id] = true;
|
|
@@ -139,11 +155,16 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
139
155
|
this._activeCaptionMapId = this._getCaptionMapId();
|
|
140
156
|
this._captionMap.set(this._activeCaptionMapId, newData);
|
|
141
157
|
this._isLoading = false;
|
|
158
|
+
clearTimeout(this._loadingTimeoutId);
|
|
142
159
|
this._updateTranscriptPanel();
|
|
143
160
|
};
|
|
144
161
|
|
|
162
|
+
private _getTextTracks = () => {
|
|
163
|
+
return this.player.getTracks(this.player.Track.TEXT) || [];
|
|
164
|
+
};
|
|
165
|
+
|
|
145
166
|
private _getCaptionMapId = (): string => {
|
|
146
|
-
const allTextTracks = this.
|
|
167
|
+
const allTextTracks = this._getTextTracks();
|
|
147
168
|
const activeTextTrack = allTextTracks.find(track => track.active);
|
|
148
169
|
if (activeTextTrack?.language === 'off') {
|
|
149
170
|
// use 1st captions from text-track list
|
|
@@ -152,42 +173,78 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
152
173
|
return `${activeTextTrack?.language}-${activeTextTrack?.label}`;
|
|
153
174
|
};
|
|
154
175
|
|
|
155
|
-
private
|
|
156
|
-
this.
|
|
157
|
-
|
|
176
|
+
private _activatePlugin = () => {
|
|
177
|
+
this.ready.then(() => {
|
|
178
|
+
this.sidePanelsManager?.activateItem(this._transcriptPanel);
|
|
179
|
+
this._pluginState === PluginStates.OPENED;
|
|
180
|
+
this.upperBarManager?.update(this._transcriptIcon);
|
|
181
|
+
});
|
|
158
182
|
};
|
|
159
183
|
|
|
160
|
-
private
|
|
161
|
-
|
|
162
|
-
|
|
184
|
+
private _deactivatePlugin = () => {
|
|
185
|
+
this.ready.then(() => {
|
|
186
|
+
this.sidePanelsManager?.deactivateItem(this._transcriptPanel);
|
|
187
|
+
this._pluginState = PluginStates.CLOSED;
|
|
188
|
+
this.upperBarManager?.update(this._transcriptIcon);
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
private _isPluginActive = () => {
|
|
193
|
+
return this.sidePanelsManager!.isItemActive(this._transcriptPanel);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
private _handleClickOnPluginIcon = (e: OnClickEvent, byKeyboard?: boolean) => {
|
|
197
|
+
if (this._isPluginActive()) {
|
|
198
|
+
this._triggeredByKeyboard = false;
|
|
199
|
+
this._deactivatePlugin();
|
|
200
|
+
} else {
|
|
201
|
+
this._triggeredByKeyboard = Boolean(byKeyboard);
|
|
202
|
+
this._activatePlugin();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
private _addDownloadIcon(): void {
|
|
207
|
+
const {downloadDisabled} = this.config;
|
|
208
|
+
if (this._downloadIcon > 0 || downloadDisabled) {
|
|
163
209
|
return;
|
|
164
210
|
}
|
|
165
|
-
|
|
166
|
-
label:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
printDisabled={getConfigValue(printDisabled, isBoolean, false)}
|
|
175
|
-
dropdownAriaLabel={printDownloadAreaLabel}
|
|
176
|
-
printButtonAriaLabel={printTranscript}
|
|
177
|
-
downloadButtonAriaLabel={downloadTranscript}
|
|
178
|
-
/>
|
|
211
|
+
const translate = {
|
|
212
|
+
label: <Text id="transcript.download_transcript">Download current transcript</Text>
|
|
213
|
+
};
|
|
214
|
+
this._downloadIcon = this.upperBarManager!.add({
|
|
215
|
+
label: translate.label as any,
|
|
216
|
+
svgIcon: {path: icons.DOWNLOAD_ICON, viewBox: `0 0 ${icons.BigSize} ${icons.BigSize}`},
|
|
217
|
+
onClick: this._handleDownload,
|
|
218
|
+
component: withText(translate)((props: {label: string}) => (
|
|
219
|
+
<PluginButton isActive={false} onClick={this._handleDownload} id={'download-transcript'} icon={icons.DOWNLOAD_ICON} label={props.label} />
|
|
179
220
|
))
|
|
180
|
-
});
|
|
221
|
+
}) as number;
|
|
222
|
+
}
|
|
223
|
+
private _addPrintIcon(): void {
|
|
224
|
+
const {printDisabled} = this.config;
|
|
225
|
+
if (this._printIcon > 0 || printDisabled) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const translate = {
|
|
229
|
+
label: <Text id="transcript.print_transcript">Print current transcript</Text>
|
|
230
|
+
};
|
|
231
|
+
this._printIcon = this.upperBarManager!.add({
|
|
232
|
+
label: translate.label as any,
|
|
233
|
+
svgIcon: {path: icons.PRINT_ICON, viewBox: `0 0 ${icons.BigSize} ${icons.BigSize}`},
|
|
234
|
+
onClick: this._handlePrint,
|
|
235
|
+
component: withText(translate)((props: {label: string}) => (
|
|
236
|
+
<PluginButton isActive={false} onClick={this._handlePrint} id={'print-transcript'} icon={icons.PRINT_ICON} label={props.label} />
|
|
237
|
+
))
|
|
238
|
+
}) as number;
|
|
181
239
|
}
|
|
182
240
|
|
|
183
241
|
private _addTranscriptItem(): void {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
this._transcriptPanel = this.sidePanelsManager.addItem({
|
|
242
|
+
if (this._transcriptPanel > 0) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const {expandMode, position, expandOnFirstPlay, showTime, scrollOffset, searchDebounceTimeout, searchNextPrevDebounceTimeout} = this.config;
|
|
247
|
+
this._transcriptPanel = this.sidePanelsManager!.add({
|
|
191
248
|
label: 'Transcript',
|
|
192
249
|
panelComponent: () => {
|
|
193
250
|
return (
|
|
@@ -198,7 +255,6 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
198
255
|
searchNextPrevDebounceTimeout={getConfigValue(searchNextPrevDebounceTimeout, Number.isInteger, 100)}
|
|
199
256
|
highlightedMap={this._activeCuePointsMap}
|
|
200
257
|
onSeek={this._seekTo}
|
|
201
|
-
pluginMode={pluginMode}
|
|
202
258
|
onItemClicked={this._seekTo}
|
|
203
259
|
captions={this._data}
|
|
204
260
|
isLoading={this._isLoading}
|
|
@@ -206,43 +262,36 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
206
262
|
onRetryLoad={this._updateTranscriptPanel}
|
|
207
263
|
currentTime={this.player.currentTime}
|
|
208
264
|
videoDuration={this.player.duration}
|
|
209
|
-
kitchenSinkActive={
|
|
265
|
+
kitchenSinkActive={this._isPluginActive()}
|
|
210
266
|
toggledWithEnter={this._triggeredByKeyboard}
|
|
211
|
-
onClose={this.
|
|
267
|
+
onClose={this._deactivatePlugin}
|
|
212
268
|
/>
|
|
213
269
|
);
|
|
214
270
|
},
|
|
215
|
-
iconComponent: ({isActive}: {isActive: boolean}) => {
|
|
216
|
-
return (
|
|
217
|
-
<Tooltip label={buttonLabel} type="bottom">
|
|
218
|
-
<PluginButton
|
|
219
|
-
isActive={isActive}
|
|
220
|
-
label={buttonLabel}
|
|
221
|
-
onClick={(e: OnClickEvent, byKeyboard?: boolean) => {
|
|
222
|
-
if (this.sidePanelsManager.isItemActive(this._transcriptPanel)) {
|
|
223
|
-
this._triggeredByKeyboard = false;
|
|
224
|
-
this._handleCloseClick();
|
|
225
|
-
} else {
|
|
226
|
-
this._triggeredByKeyboard = Boolean(byKeyboard);
|
|
227
|
-
this.sidePanelsManager.activateItem(this._transcriptPanel);
|
|
228
|
-
}
|
|
229
|
-
}}
|
|
230
|
-
/>
|
|
231
|
-
</Tooltip>
|
|
232
|
-
);
|
|
233
|
-
},
|
|
234
271
|
presets: [ReservedPresetNames.Playback, ReservedPresetNames.Live, ReservedPresetNames.Ads],
|
|
235
272
|
position: position,
|
|
236
273
|
expandMode: expandMode === SidePanelModes.ALONGSIDE ? SidePanelModes.ALONGSIDE : SidePanelModes.OVER,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
274
|
+
onDeactivate: this._deactivatePlugin
|
|
275
|
+
}) as number;
|
|
276
|
+
const translates = {
|
|
277
|
+
showTranscript: <Text id="transcript.show_plugin">Show Transcript</Text>,
|
|
278
|
+
hideTranscript: <Text id="transcript.hide_plugin">Hide Transcript</Text>
|
|
279
|
+
};
|
|
280
|
+
this._transcriptIcon = this.upperBarManager!.add({
|
|
281
|
+
label: 'Transcript',
|
|
282
|
+
svgIcon: {path: icons.PLUGIN_ICON, viewBox: `0 0 ${icons.BigSize} ${icons.BigSize}`},
|
|
283
|
+
onClick: this._handleClickOnPluginIcon as () => void,
|
|
284
|
+
component: withText(translates)((props: {showTranscript: string; hideTranscript: string}) => {
|
|
285
|
+
const isActive = this._isPluginActive();
|
|
286
|
+
const label = isActive ? props.hideTranscript : props.showTranscript;
|
|
287
|
+
return (
|
|
288
|
+
<PluginButton isActive={isActive} onClick={this._handleClickOnPluginIcon} id="transcript-icon" label={label} icon={icons.PLUGIN_ICON} />
|
|
289
|
+
);
|
|
290
|
+
})
|
|
291
|
+
}) as number;
|
|
241
292
|
|
|
242
293
|
if ((expandOnFirstPlay && !this._pluginState) || this._pluginState === PluginStates.OPENED) {
|
|
243
|
-
this.
|
|
244
|
-
this.sidePanelsManager.activateItem(this._transcriptPanel);
|
|
245
|
-
});
|
|
294
|
+
this._activatePlugin();
|
|
246
295
|
}
|
|
247
296
|
}
|
|
248
297
|
|
|
@@ -276,17 +325,24 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
276
325
|
|
|
277
326
|
reset(): void {
|
|
278
327
|
this.eventManager.removeAll();
|
|
279
|
-
if (this.
|
|
280
|
-
this.
|
|
281
|
-
this.
|
|
328
|
+
if (Math.max(this._transcriptPanel, this._transcriptIcon) > 0) {
|
|
329
|
+
this.sidePanelsManager?.remove(this._transcriptPanel);
|
|
330
|
+
this.upperBarManager!.remove(this._transcriptIcon);
|
|
331
|
+
this._transcriptPanel = -1;
|
|
332
|
+
this._transcriptIcon = -1;
|
|
282
333
|
}
|
|
283
|
-
if (this.
|
|
284
|
-
this.
|
|
285
|
-
this.
|
|
334
|
+
if (this._printIcon > 0) {
|
|
335
|
+
this.upperBarManager!.remove(this._printIcon);
|
|
336
|
+
this._printIcon = -1;
|
|
337
|
+
}
|
|
338
|
+
if (this._downloadIcon > 0) {
|
|
339
|
+
this.upperBarManager!.remove(this._downloadIcon);
|
|
340
|
+
this._downloadIcon = -1;
|
|
286
341
|
}
|
|
287
342
|
this._captionMap = new Map();
|
|
288
343
|
this._activeCaptionMapId = '';
|
|
289
344
|
this._isLoading = false;
|
|
345
|
+
clearTimeout(this._loadingTimeoutId);
|
|
290
346
|
this._hasError = false;
|
|
291
347
|
this._triggeredByKeyboard = false;
|
|
292
348
|
}
|
package/src/types/types-ui.ts
CHANGED
package/src/utils/index.ts
CHANGED
package/src/utils/utils.ts
CHANGED
|
@@ -84,3 +84,37 @@ export const prepareCuePoint = (cuePoint: CuePoint): CuePointData => {
|
|
|
84
84
|
|
|
85
85
|
return itemData;
|
|
86
86
|
};
|
|
87
|
+
|
|
88
|
+
export function downloadContent(content: string, name: string): void {
|
|
89
|
+
const blob = new Blob([content], {type: 'text/plain;charset=utf-8;'});
|
|
90
|
+
const anchor = document.createElement('a');
|
|
91
|
+
const {navigator} = window as any;
|
|
92
|
+
|
|
93
|
+
if (navigator.msSaveBlob) {
|
|
94
|
+
// IE
|
|
95
|
+
navigator.msSaveOrOpenBlob(blob, name);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (navigator.userAgent.search('Firefox') !== -1) {
|
|
99
|
+
// Firefox
|
|
100
|
+
anchor.style.display = 'none';
|
|
101
|
+
anchor.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content));
|
|
102
|
+
} else {
|
|
103
|
+
// Chrome
|
|
104
|
+
anchor.setAttribute('href', URL.createObjectURL(blob));
|
|
105
|
+
}
|
|
106
|
+
anchor.setAttribute('target', '_blank');
|
|
107
|
+
anchor.setAttribute('download', name);
|
|
108
|
+
anchor.click();
|
|
109
|
+
anchor.remove();
|
|
110
|
+
}
|
|
111
|
+
export function printContent(content: string): void {
|
|
112
|
+
const printWindow = window.open('', '', 'width=400,height=600');
|
|
113
|
+
if (printWindow) {
|
|
114
|
+
printWindow.document.write(content);
|
|
115
|
+
printWindow.document.close();
|
|
116
|
+
printWindow.focus();
|
|
117
|
+
printWindow.print();
|
|
118
|
+
printWindow.close();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import {h} from 'preact';
|
|
2
|
-
|
|
3
|
-
export const PrintIcon = () => (
|
|
4
|
-
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
|
|
5
|
-
<g id="Icons/32/print" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
6
|
-
<path
|
|
7
|
-
d="M21,3 C22.0543618,3 22.9181651,3.81587779 22.9945143,4.85073766 L23,5 L23,9 L26,9 C27.1045695,9 28,9.8954305 28,11 L28,21 C28,22.1045695 27.1045695,23 26,23 L23,23 L23,28 C23,29.1045695 22.1045695,30 21,30 L11,30 C9.8954305,30 9,29.1045695 9,28 L9,23 L6,23 C4.8954305,23 4,22.1045695 4,21 L4,11 C4,9.8954305 4.8954305,9 6,9 L9,9 L9,5 C9,3.9456382 9.81587779,3.08183488 10.8507377,3.00548574 L11,3 L21,3 Z M21,19 L11,19 L11,28 L21,28 L21,19 Z M26,11 L6,11 L6,21 L9,21 L9,19 C8.44771525,19 8,18.5522847 8,18 C8,17.4477153 8.44771525,17 9,17 L23,17 C23.5522847,17 24,17.4477153 24,18 C24,18.5522847 23.5522847,19 23,19 L23,21 L26,21 L26,11 Z M21,5 L11,5 L11,9 L21,9 L21,5 Z"
|
|
8
|
-
id="Combined-Shape"
|
|
9
|
-
fill="#fff"></path>
|
|
10
|
-
</g>
|
|
11
|
-
</svg>
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
export const DownloadIcon = () => (
|
|
15
|
-
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
|
|
16
|
-
<g id="Icons/32/Download" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
17
|
-
<path
|
|
18
|
-
d="M26,25 C26.5522847,25 27,25.4477153 27,26 C27,26.5522847 26.5522847,27 26,27 L26,27 L7,27 C6.44771525,27 6,26.5522847 6,26 C6,25.4477153 6.44771525,25 7,25 L7,25 Z M15.897,20.797 L15.817,20.73 L15.8163834,20.7298413 L8.34305882,13.7298413 C7.93997861,13.3522902 7.91928313,12.7194636 8.29683417,12.3163834 C8.67438521,11.9133032 9.30721188,11.8926077 9.71029209,12.2701587 L15.4996721,17.693 L15.5,6 C15.5,5.48716416 15.8860402,5.06449284 16.3833789,5.00672773 L16.5,5 C17.0522847,5 17.5,5.44771525 17.5,6 L17.4996721,17.694 L23.2951711,12.2699211 C23.6673663,11.9215418 24.2352038,11.9125649 24.6172049,12.230382 L24.7086128,12.3166371 C25.0860237,12.7198486 25.0651082,13.352668 24.6618968,13.7300789 L17.1833629,20.7300789 L17.1610165,20.7503813 L17.1610165,20.7503813 C17.1421868,20.7669999 17.1224361,20.7831339 17.102079,20.7985075 C17.0891381,20.8082894 17.0764369,20.8174134 17.0635772,20.826204 C17.0434306,20.8399634 17.0223437,20.8532674 17.0007451,20.8657864 C16.9872099,20.8736423 16.9734873,20.8811624 16.959633,20.8883367 L16.8877511,20.9220455 L16.8877511,20.9220455 C16.8756318,20.927087 16.8632234,20.9320132 16.8507409,20.9366814 C16.83028,20.9444208 16.8097352,20.9513578 16.7889039,20.9576336 C16.7705976,20.9630349 16.752126,20.968019 16.7335525,20.9724647 C16.6585039,20.9905214 16.5803589,21 16.5,21 C16.4170842,21 16.3365254,20.9899086 16.2594848,20.9708871 C16.2500284,20.9684434 16.2399293,20.9657886 16.2298654,20.9629733 C16.2028024,20.9554899 16.1769173,20.947049 16.1515197,20.9376057 C16.1370523,20.9321598 16.1223107,20.9262914 16.1076867,20.9200585 C16.0832011,20.9096448 16.0596143,20.8984375 16.036557,20.886357 C16.025923,20.8807972 16.0148138,20.8747205 16.0037984,20.8684173 C15.9792921,20.8543502 15.955966,20.8396537 15.9333153,20.8240474 L15.898,20.798 L15.897,20.797 Z M15.867,20.774 L15.888,20.79 L15.8735171,20.7794831 L15.8735171,20.7794831 L15.867,20.774 Z M15.817,20.73 L15.9035191,20.8027045 C15.8784859,20.7840722 15.8543541,20.7642966 15.831201,20.7434548 L15.817,20.73 Z"
|
|
19
|
-
id="Combined-Shape"
|
|
20
|
-
fill="#ffffff"></path>
|
|
21
|
-
</g>
|
|
22
|
-
</svg>
|
|
23
|
-
);
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
@import '../../variables.scss';
|
|
2
|
-
|
|
3
|
-
.download-print-button {
|
|
4
|
-
display: inline-block;
|
|
5
|
-
width: 36px;
|
|
6
|
-
height: 36px;
|
|
7
|
-
color: $white-color;
|
|
8
|
-
border: none;
|
|
9
|
-
cursor: pointer;
|
|
10
|
-
padding: 0;
|
|
11
|
-
background: none;
|
|
12
|
-
border-radius: 4px;
|
|
13
|
-
outline-offset: 0 -1px;
|
|
14
|
-
&:hover,
|
|
15
|
-
&:active {
|
|
16
|
-
border-radius: 4px;
|
|
17
|
-
background-color: rgba(0, 0, 0, 0.8);
|
|
18
|
-
}
|
|
19
|
-
&:focus {
|
|
20
|
-
outline: 1px solid $focus-color;
|
|
21
|
-
}
|
|
22
|
-
.icon {
|
|
23
|
-
width: 100%;
|
|
24
|
-
height: 100%;
|
|
25
|
-
background-position: center;
|
|
26
|
-
background-size: cover;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
.popover-menu-item {
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: center;
|
|
32
|
-
padding: 9px 24px 9px 16px;
|
|
33
|
-
white-space: nowrap;
|
|
34
|
-
min-height: 30px;
|
|
35
|
-
line-height: 18px;
|
|
36
|
-
font-size: 15px;
|
|
37
|
-
&:hover {
|
|
38
|
-
cursor: pointer;
|
|
39
|
-
background-color: $semigray-color;
|
|
40
|
-
}
|
|
41
|
-
&:focus {
|
|
42
|
-
outline: 1px solid $focus-color;
|
|
43
|
-
}
|
|
44
|
-
}
|