@mindedge/vuetify-player 0.2.0 → 0.3.0

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.
@@ -1,470 +1,549 @@
1
1
  <template>
2
2
  <v-container>
3
- <div v-if="buffering" class="player-overlay">
4
- <v-progress-circular :size="50" indeterminate></v-progress-circular>
5
- </div>
6
- <video
7
- ref="player"
8
- tabindex="0"
9
- :class="'player-' + type"
10
- :height="attributes.height"
11
- :width="attributes.width"
12
- :autoplay="attributes.autoplay"
13
- :autopictureinpicture="attributes.autopictureinpicture"
14
- :controlslist="attributes.controlslist"
15
- :crossorigin="attributes.crossorigin"
16
- :disablepictureinpicture="attributes.disablepictureinpicture"
17
- :disableremoteplayback="attributes.disableremoteplayback"
18
- :loop="attributes.loop"
19
- :muted="attributes.muted"
20
- :playsinline="attributes.playsinline"
21
- :poster="src.poster || attributes.poster"
22
- :preload="attributes.preload"
23
- @click="onPlayToggle"
24
- @seeking="onSeeking"
25
- @timeupdate="onTimeupdate"
26
- @progress="onMediaProgress"
27
- @loadedmetadata="onLoadedmetadata"
28
- @loadeddata="onLoadeddata"
29
- @focus="onVideoHover"
30
- @mouseover="onVideoHover"
31
- @mouseout="onVideoLeave"
32
- @ended="onEnded"
33
- @error="$emit('error', $event)"
34
- @canplay="onCanplay"
35
- @waiting="onWaiting"
36
- @canplaythrough="$emit('canplaythrough', $event)"
37
- @emptied="$emit('emptied', $event)"
38
- @stalled="$emit('stalled', $event)"
39
- @abort="$emit('abort', $event)"
40
- >
41
- <source
42
- v-for="(source, index) of current.sources"
43
- :key="index + '_mediasources'"
44
- :src="source.src"
45
- :type="source.type"
46
- :label="source.label"
47
- />
48
- <track
49
- v-for="(track, index) of current.tracks"
50
- :key="index + '_mediatracks'"
51
- :default="track.default"
52
- :src="track.src"
53
- :kind="track.kind"
54
- :srclang="track.srclang"
55
- @cuechange="onCuechange"
56
- />
57
- {{ t(language, 'player.no_support') }}
58
- </video>
59
-
60
- <div
61
- class="controls-container"
62
- v-if="attributes.controls"
63
- @mouseover="onControlsHover"
64
- >
65
- <v-slide-y-reverse-transition>
66
- <div v-if="player && options.controls" class="controls">
67
- <v-slider
68
- dark
69
- v-model="currentPercent"
70
- :min="0"
71
- :max="scrub.max"
72
- :label="
73
- filters.playerShortDuration(
74
- percentToTimeSeconds(currentPercent)
75
- ) +
76
- ' / ' +
77
- filters.playerShortDuration(player.duration)
78
- "
79
- inverse-label
80
- @mousedown="onPause"
81
- @change="onScrubTime"
82
- >
83
- <template #prepend>
84
- <!-- Play button -->
85
- <v-tooltip top>
86
- <template v-slot:activator="{ on, attrs }">
87
- <v-btn
88
- small
89
- text
90
- v-bind="attrs"
91
- v-on="on"
92
- @click="onPlayToggle"
93
- >
94
- <v-icon>{{
95
- options.paused
96
- ? 'mdi-play'
97
- : 'mdi-pause'
98
- }}</v-icon>
99
- <span class="d-sr-only">
100
- {{
101
- options.paused
102
- ? t(language, 'player.play')
103
- : t(
104
- language,
105
- 'player.pause'
106
- )
107
- }}
108
- </span>
109
- </v-btn>
110
- </template>
111
- <span>{{
112
- options.paused
113
- ? t(language, 'player.play')
114
- : t(language, 'player.pause')
115
- }}</span>
116
- </v-tooltip>
117
-
118
- <!-- Rewind Button-->
119
- <v-tooltip v-if="attributes.rewind" top>
120
- <template v-slot:activator="{ on, attrs }">
121
- <v-btn
122
- small
123
- text
124
- v-bind="attrs"
125
- v-on="on"
126
- @click="onRewind"
127
- >
128
- <v-icon>mdi-rewind-10</v-icon>
129
- <span class="sr-only">{{
130
- t(language, 'player.rewind_10')
131
- }}</span>
132
- </v-btn>
133
- </template>
134
- <span>{{
135
- t(language, 'player.rewind_10')
136
- }}</span>
137
- </v-tooltip>
138
- </template>
139
-
140
- <template #append>
141
- <!-- Closed Captions -->
142
- <v-menu
143
- v-if="
144
- current.tracks && current.tracks.length > 0
3
+ <v-row>
4
+ <v-col :cols="!options.expandedCaptions ? 12 : 6">
5
+ <div v-if="buffering" class="player-overlay">
6
+ <v-progress-circular
7
+ :size="50"
8
+ indeterminate
9
+ ></v-progress-circular>
10
+ </div>
11
+ <video
12
+ ref="player"
13
+ tabindex="0"
14
+ :class="'player-' + type"
15
+ :height="attributes.height"
16
+ :width="attributes.width"
17
+ :autoplay="attributes.autoplay"
18
+ :autopictureinpicture="attributes.autopictureinpicture"
19
+ :controlslist="attributes.controlslist"
20
+ :crossorigin="attributes.crossorigin"
21
+ :disablepictureinpicture="
22
+ attributes.disablepictureinpicture
23
+ "
24
+ :disableremoteplayback="attributes.disableremoteplayback"
25
+ :loop="attributes.loop"
26
+ :muted="attributes.muted"
27
+ :playsinline="attributes.playsinline"
28
+ :poster="src.poster || attributes.poster"
29
+ :preload="attributes.preload"
30
+ @click="onPlayToggle"
31
+ @seeking="onSeeking"
32
+ @timeupdate="onTimeupdate"
33
+ @progress="onMediaProgress"
34
+ @loadedmetadata="onLoadedmetadata"
35
+ @loadeddata="onLoadeddata"
36
+ @focus="onVideoHover"
37
+ @mouseover="onVideoHover"
38
+ @mouseout="onVideoLeave"
39
+ @ended="onEnded"
40
+ @error="$emit('error', $event)"
41
+ @canplay="onCanplay"
42
+ @waiting="onWaiting"
43
+ @canplaythrough="$emit('canplaythrough', $event)"
44
+ @emptied="$emit('emptied', $event)"
45
+ @stalled="$emit('stalled', $event)"
46
+ @abort="$emit('abort', $event)"
47
+ >
48
+ <source
49
+ v-for="(source, index) of current.sources"
50
+ :key="index + '_mediasources'"
51
+ :src="source.src"
52
+ :type="source.type"
53
+ :label="source.label"
54
+ />
55
+ <track
56
+ v-for="(track, index) of current.tracks"
57
+ :key="index + '_mediatracks'"
58
+ :default="track.default"
59
+ :src="track.src"
60
+ :kind="track.kind"
61
+ :srclang="track.srclang"
62
+ @cuechange="onCuechange"
63
+ />
64
+ {{ t(language, 'player.no_support') }}
65
+ </video>
66
+
67
+ <div
68
+ class="controls-container"
69
+ v-if="attributes.controls"
70
+ @mouseover="onControlsHover"
71
+ >
72
+ <v-slide-y-reverse-transition>
73
+ <div v-if="player && options.controls" class="controls">
74
+ <v-slider
75
+ dark
76
+ v-model="currentPercent"
77
+ :min="0"
78
+ :max="scrub.max"
79
+ :label="
80
+ filters.playerShortDuration(
81
+ percentToTimeSeconds(currentPercent)
82
+ ) +
83
+ ' / ' +
84
+ filters.playerShortDuration(player.duration)
145
85
  "
146
- open-on-hover
147
- top
148
- offset-y
86
+ inverse-label
87
+ @mousedown="onPause"
88
+ @change="onScrubTime"
149
89
  >
150
- <template v-slot:activator="{ on, attrs }">
151
- <v-btn
152
- small
153
- text
154
- v-bind="attrs"
155
- v-on="on"
156
- @click="onCCToggle"
157
- >
158
- <v-icon>{{
159
- options.cc
160
- ? 'mdi-closed-caption'
161
- : 'mdi-closed-caption-outline'
162
- }}</v-icon>
163
- <span class="d-sr-only">{{
164
- t(language, 'player.toggle_cc')
90
+ <template #prepend>
91
+ <!-- Play button -->
92
+ <v-tooltip top>
93
+ <template
94
+ v-slot:activator="{ on, attrs }"
95
+ >
96
+ <v-btn
97
+ small
98
+ text
99
+ v-bind="attrs"
100
+ v-on="on"
101
+ @click="onPlayToggle"
102
+ >
103
+ <v-icon>{{
104
+ options.paused
105
+ ? 'mdi-play'
106
+ : 'mdi-pause'
107
+ }}</v-icon>
108
+ <span class="d-sr-only">
109
+ {{
110
+ options.paused
111
+ ? t(
112
+ language,
113
+ 'player.play'
114
+ )
115
+ : t(
116
+ language,
117
+ 'player.pause'
118
+ )
119
+ }}
120
+ </span>
121
+ </v-btn>
122
+ </template>
123
+ <span>{{
124
+ options.paused
125
+ ? t(language, 'player.play')
126
+ : t(language, 'player.pause')
165
127
  }}</span>
166
- </v-btn>
167
- </template>
128
+ </v-tooltip>
168
129
 
169
- <v-list>
170
- <v-list-item-group>
171
- <v-list-item
172
- v-for="track in current.tracks"
173
- :key="track.srclang"
174
- @click="
175
- onSelectTrack(track.srclang)
176
- "
130
+ <!-- Rewind Button-->
131
+ <v-tooltip v-if="attributes.rewind" top>
132
+ <template
133
+ v-slot:activator="{ on, attrs }"
177
134
  >
178
- <v-list-item-title>{{
179
- track.srclang
180
- }}</v-list-item-title>
181
- </v-list-item>
182
- </v-list-item-group>
183
- </v-list>
184
- </v-menu>
185
-
186
- <!-- Volume -->
187
- <v-menu open-on-hover top offset-y>
188
- <template v-slot:activator="{ on, attrs }">
189
- <v-btn
190
- small
191
- text
192
- v-bind="attrs"
193
- v-on="on"
194
- @click="onMuteToggle"
135
+ <v-btn
136
+ small
137
+ text
138
+ v-bind="attrs"
139
+ v-on="on"
140
+ @click="onRewind"
141
+ >
142
+ <v-icon>mdi-rewind-10</v-icon>
143
+ <span class="sr-only">{{
144
+ t(
145
+ language,
146
+ 'player.rewind_10'
147
+ )
148
+ }}</span>
149
+ </v-btn>
150
+ </template>
151
+ <span>{{
152
+ t(language, 'player.rewind_10')
153
+ }}</span>
154
+ </v-tooltip>
155
+ </template>
156
+
157
+ <template #append>
158
+ <!-- Closed Captions -->
159
+ <v-menu
160
+ v-if="
161
+ current.tracks &&
162
+ current.tracks.length > 0
163
+ "
164
+ open-on-hover
165
+ top
166
+ offset-y
195
167
  >
196
- <v-icon
197
- v-if="
198
- !options.muted &&
199
- options.volume > 0.75
200
- "
201
- >mdi-volume-high</v-icon
168
+ <template
169
+ v-slot:activator="{ on, attrs }"
202
170
  >
203
- <v-icon
204
- v-if="
205
- !options.muted &&
206
- options.volume >= 0.25 &&
207
- options.volume <= 0.75
208
- "
209
- >mdi-volume-medium</v-icon
171
+ <v-btn
172
+ small
173
+ text
174
+ v-bind="attrs"
175
+ v-on="on"
176
+ @click="onCCToggle"
177
+ >
178
+ <v-icon>{{
179
+ options.cc
180
+ ? 'mdi-closed-caption'
181
+ : 'mdi-closed-caption-outline'
182
+ }}</v-icon>
183
+ <span class="d-sr-only">{{
184
+ t(
185
+ language,
186
+ 'player.toggle_cc'
187
+ )
188
+ }}</span>
189
+ </v-btn>
190
+ </template>
191
+
192
+ <v-list>
193
+ <v-list-item-group>
194
+ <v-list-item
195
+ v-for="track in current.tracks"
196
+ :key="track.srclang"
197
+ @click="
198
+ onSelectTrack(
199
+ track.srclang
200
+ )
201
+ "
202
+ >
203
+ <v-list-item-title>{{
204
+ track.srclang
205
+ }}</v-list-item-title>
206
+ </v-list-item>
207
+ </v-list-item-group>
208
+ </v-list>
209
+ </v-menu>
210
+
211
+ <!-- Volume -->
212
+ <v-menu open-on-hover top offset-y>
213
+ <template
214
+ v-slot:activator="{ on, attrs }"
210
215
  >
211
- <v-icon
212
- v-if="
213
- !options.muted &&
214
- options.volume > 0 &&
215
- options.volume < 0.25
216
- "
217
- >mdi-volume-low</v-icon
216
+ <v-btn
217
+ small
218
+ text
219
+ v-bind="attrs"
220
+ v-on="on"
221
+ @click="onMuteToggle"
222
+ >
223
+ <v-icon
224
+ v-if="
225
+ !options.muted &&
226
+ options.volume > 0.75
227
+ "
228
+ >mdi-volume-high</v-icon
229
+ >
230
+ <v-icon
231
+ v-if="
232
+ !options.muted &&
233
+ options.volume >=
234
+ 0.25 &&
235
+ options.volume <= 0.75
236
+ "
237
+ >mdi-volume-medium</v-icon
238
+ >
239
+ <v-icon
240
+ v-if="
241
+ !options.muted &&
242
+ options.volume > 0 &&
243
+ options.volume < 0.25
244
+ "
245
+ >mdi-volume-low</v-icon
246
+ >
247
+ <v-icon
248
+ v-if="
249
+ options.muted ||
250
+ options.volume === 0
251
+ "
252
+ >mdi-volume-off</v-icon
253
+ >
254
+ <span class="d-sr-only">{{
255
+ t(
256
+ language,
257
+ 'player.volume_slider'
258
+ )
259
+ }}</span>
260
+ </v-btn>
261
+ </template>
262
+
263
+ <v-sheet class="pa-5">
264
+ <span class="d-sr-only">{{
265
+ t(
266
+ language,
267
+ 'player.volume_slider'
268
+ )
269
+ }}</span>
270
+ <v-slider
271
+ v-model="options.volume"
272
+ inverse-label
273
+ :min="0"
274
+ :max="1"
275
+ :step="0.1"
276
+ vertical
277
+ @change="onVolumeChange"
278
+ ></v-slider>
279
+ </v-sheet>
280
+ </v-menu>
281
+
282
+ <!-- Fullscreen -->
283
+ <v-tooltip v-if="fullscreenEnabled" top>
284
+ <template
285
+ v-slot:activator="{ on, attrs }"
218
286
  >
219
- <v-icon
220
- v-if="
221
- options.muted ||
222
- options.volume === 0
223
- "
224
- >mdi-volume-off</v-icon
287
+ <v-btn
288
+ small
289
+ text
290
+ v-bind="attrs"
291
+ v-on="on"
292
+ @click="onFullscreen"
293
+ >
294
+ <v-icon>{{
295
+ !options.fullscreen
296
+ ? 'mdi-fullscreen'
297
+ : 'mdi-fullscreen-exit'
298
+ }}</v-icon>
299
+ <span class="d-sr-only">{{
300
+ t(
301
+ language,
302
+ 'player.toggle_fullscreen'
303
+ )
304
+ }}</span>
305
+ </v-btn></template
225
306
  >
226
- <span class="d-sr-only">{{
227
- t(language, 'player.volume_slider')
228
- }}</span>
229
- </v-btn>
230
- </template>
231
-
232
- <v-sheet class="pa-5">
233
- <span class="d-sr-only">{{
234
- t(language, 'player.volume_slider')
235
- }}</span>
236
- <v-slider
237
- v-model="options.volume"
238
- inverse-label
239
- :min="0"
240
- :max="1"
241
- :step="0.1"
242
- vertical
243
- @change="onVolumeChange"
244
- ></v-slider>
245
- </v-sheet>
246
- </v-menu>
247
-
248
- <!-- Fullscreen -->
249
- <v-tooltip v-if="fullscreenEnabled" top>
250
- <template v-slot:activator="{ on, attrs }">
251
- <v-btn
252
- small
253
- text
254
- v-bind="attrs"
255
- v-on="on"
256
- @click="onFullscreen"
257
- >
258
- <v-icon>{{
259
- !options.fullscreen
260
- ? 'mdi-fullscreen'
261
- : 'mdi-fullscreen-exit'
262
- }}</v-icon>
263
- <span class="d-sr-only">{{
307
+ <span>{{
264
308
  t(
265
309
  language,
266
310
  'player.toggle_fullscreen'
267
311
  )
268
312
  }}</span>
269
- </v-btn></template
270
- >
271
- <span>{{
272
- t(language, 'player.toggle_fullscreen')
273
- }}</span>
274
- </v-tooltip>
275
-
276
- <!-- Picture in picture -->
277
- <v-tooltip
278
- v-if="!attributes.disablepictureinpicture"
279
- top
280
- >
281
- <template v-slot:activator="{ on, attrs }">
282
- <v-btn
283
- small
284
- text
285
- v-bind="attrs"
286
- v-on="on"
287
- @click="onPictureInPicture"
313
+ </v-tooltip>
314
+
315
+ <!-- Picture in picture -->
316
+ <v-tooltip
317
+ v-if="
318
+ !attributes.disablepictureinpicture
319
+ "
320
+ top
288
321
  >
289
- <v-icon
290
- >mdi-picture-in-picture-bottom-right</v-icon
322
+ <template
323
+ v-slot:activator="{ on, attrs }"
324
+ >
325
+ <v-btn
326
+ small
327
+ text
328
+ v-bind="attrs"
329
+ v-on="on"
330
+ @click="onPictureInPicture"
331
+ >
332
+ <v-icon
333
+ >mdi-picture-in-picture-bottom-right</v-icon
334
+ >
335
+ <span class="d-sr-only">{{
336
+ t(
337
+ language,
338
+ 'player.toggle_picture_in_picture'
339
+ )
340
+ }}</span>
341
+ </v-btn></template
291
342
  >
292
- <span class="d-sr-only">{{
343
+ <span>{{
293
344
  t(
294
345
  language,
295
346
  'player.toggle_picture_in_picture'
296
347
  )
297
348
  }}</span>
298
- </v-btn></template
299
- >
300
- <span>{{
301
- t(
302
- language,
303
- 'player.toggle_picture_in_picture'
304
- )
305
- }}</span>
306
- </v-tooltip>
307
-
308
- <!-- Remote playback -->
309
- <v-tooltip v-if="options.remoteplayback" top>
310
- <template v-slot:activator="{ on, attrs }">
311
- <v-btn
312
- small
313
- text
314
- v-bind="attrs"
315
- v-on="on"
316
- @click="onRemoteplayback"
349
+ </v-tooltip>
350
+
351
+ <!-- Remote playback -->
352
+ <v-tooltip
353
+ v-if="options.remoteplayback"
354
+ top
317
355
  >
318
- <v-icon>mdi-cast</v-icon>
319
- <span class="d-sr-only">{{
356
+ <template
357
+ v-slot:activator="{ on, attrs }"
358
+ >
359
+ <v-btn
360
+ small
361
+ text
362
+ v-bind="attrs"
363
+ v-on="on"
364
+ @click="onRemoteplayback"
365
+ >
366
+ <v-icon>mdi-cast</v-icon>
367
+ <span class="d-sr-only">{{
368
+ t(
369
+ language,
370
+ 'player.toggle_remote_playback'
371
+ )
372
+ }}</span>
373
+ </v-btn></template
374
+ >
375
+ <span>{{
320
376
  t(
321
377
  language,
322
378
  'player.toggle_remote_playback'
323
379
  )
324
380
  }}</span>
325
- </v-btn></template
326
- >
327
- <span>{{
328
- t(language, 'player.toggle_remote_playback')
329
- }}</span>
330
- </v-tooltip>
331
-
332
- <!-- Download -->
333
- <v-tooltip v-if="options.download" top>
334
- <template v-slot:activator="{ on, attrs }">
335
- <v-btn
336
- small
337
- text
338
- v-bind="attrs"
339
- v-on="on"
340
- @click="onDownload"
341
- >
342
- <v-icon>mdi-download</v-icon>
343
- <span class="d-sr-only">{{
344
- t(language, 'player.download')
345
- }}</span>
346
- </v-btn></template
347
- >
348
- <span>{{
349
- t(language, 'player.download')
350
- }}</span>
351
- </v-tooltip>
352
-
353
- <!-- Settings -->
354
- <v-menu
355
- top
356
- offset-y
357
- :close-on-content-click="false"
358
- nudge-left="100"
359
- >
360
- <template v-slot:activator="{ on, attrs }">
361
- <v-btn small text v-bind="attrs" v-on="on">
362
- <v-icon>mdi-cog</v-icon>
363
- <span class="d-sr-only">{{
364
- t(
365
- language,
366
- 'player.toggle_settings'
367
- )
368
- }}</span>
369
- </v-btn>
370
- </template>
381
+ </v-tooltip>
371
382
 
372
- <v-list>
373
- <v-list-item>
374
- <v-list-item-title>
375
- <v-icon>mdi-play-speed</v-icon>
376
- {{
377
- t(
378
- language,
379
- 'player.playback_speed'
380
- )
381
- }}
382
- </v-list-item-title>
383
- </v-list-item>
384
- <v-list-item>
385
- <v-list-item-title class="text-center">
383
+ <!-- Download -->
384
+ <v-tooltip v-if="options.download" top>
385
+ <template
386
+ v-slot:activator="{ on, attrs }"
387
+ >
386
388
  <v-btn
387
389
  small
388
- :disabled="
389
- options.playbackRateIndex ===
390
- 0
391
- "
392
- @click="
393
- onPlaybackSpeed(
394
- options.playbackRateIndex -
395
- 1
396
- )
397
- "
390
+ text
391
+ v-bind="attrs"
392
+ v-on="on"
393
+ @click="onDownload"
398
394
  >
399
- <v-icon>
400
- mdi-clock-minus-outline
401
- </v-icon>
395
+ <v-icon>mdi-download</v-icon>
402
396
  <span class="d-sr-only">{{
403
397
  t(
404
398
  language,
405
- 'player.playback_decrease'
399
+ 'player.download'
406
400
  )
407
401
  }}</span>
408
- </v-btn>
409
- <span class="pl-2 pr-2"
410
- >{{
411
- attributes.playbackrates[
412
- options
413
- .playbackRateIndex
414
- ]
415
- }}x</span
416
- >
402
+ </v-btn></template
403
+ >
404
+ <span>{{
405
+ t(language, 'player.download')
406
+ }}</span>
407
+ </v-tooltip>
408
+
409
+ <!-- Settings -->
410
+ <v-menu
411
+ top
412
+ offset-y
413
+ :close-on-content-click="false"
414
+ nudge-left="100"
415
+ >
416
+ <template
417
+ v-slot:activator="{ on, attrs }"
418
+ >
417
419
  <v-btn
418
420
  small
419
- :disabled="
420
- options.playbackRateIndex >=
421
- attributes.playbackrates
422
- .length -
423
- 1
424
- "
425
- @click="
426
- onPlaybackSpeed(
427
- options.playbackRateIndex +
428
- 1
429
- )
430
- "
421
+ text
422
+ v-bind="attrs"
423
+ v-on="on"
431
424
  >
432
- <v-icon>
433
- mdi-clock-plus-outline
434
- </v-icon>
425
+ <v-icon>mdi-cog</v-icon>
435
426
  <span class="d-sr-only">{{
436
427
  t(
437
428
  language,
438
- 'player.playback_increase'
429
+ 'player.toggle_settings'
439
430
  )
440
431
  }}</span>
441
432
  </v-btn>
442
- </v-list-item-title>
443
- </v-list-item>
444
- </v-list>
445
- </v-menu>
446
- </template>
447
- </v-slider>
433
+ </template>
434
+
435
+ <v-list>
436
+ <v-list-item>
437
+ <v-list-item-title>
438
+ <v-icon
439
+ >mdi-play-speed</v-icon
440
+ >
441
+ {{
442
+ t(
443
+ language,
444
+ 'player.playback_speed'
445
+ )
446
+ }}
447
+ </v-list-item-title>
448
+ </v-list-item>
449
+ <v-list-item>
450
+ <v-list-item-title
451
+ class="text-center"
452
+ >
453
+ <v-btn
454
+ small
455
+ :disabled="
456
+ options.playbackRateIndex ===
457
+ 0
458
+ "
459
+ @click="
460
+ onPlaybackSpeed(
461
+ options.playbackRateIndex -
462
+ 1
463
+ )
464
+ "
465
+ >
466
+ <v-icon>
467
+ mdi-clock-minus-outline
468
+ </v-icon>
469
+ <span
470
+ class="d-sr-only"
471
+ >{{
472
+ t(
473
+ language,
474
+ 'player.playback_decrease'
475
+ )
476
+ }}</span
477
+ >
478
+ </v-btn>
479
+ <span class="pl-2 pr-2"
480
+ >{{
481
+ attributes
482
+ .playbackrates[
483
+ options
484
+ .playbackRateIndex
485
+ ]
486
+ }}x</span
487
+ >
488
+ <v-btn
489
+ small
490
+ :disabled="
491
+ options.playbackRateIndex >=
492
+ attributes
493
+ .playbackrates
494
+ .length -
495
+ 1
496
+ "
497
+ @click="
498
+ onPlaybackSpeed(
499
+ options.playbackRateIndex +
500
+ 1
501
+ )
502
+ "
503
+ >
504
+ <v-icon>
505
+ mdi-clock-plus-outline
506
+ </v-icon>
507
+ <span
508
+ class="d-sr-only"
509
+ >{{
510
+ t(
511
+ language,
512
+ 'player.playback_increase'
513
+ )
514
+ }}</span
515
+ >
516
+ </v-btn>
517
+ </v-list-item-title>
518
+ </v-list-item>
519
+ </v-list>
520
+ </v-menu>
521
+ </template>
522
+ </v-slider>
523
+ </div>
524
+ </v-slide-y-reverse-transition>
448
525
  </div>
449
- </v-slide-y-reverse-transition>
450
- </div>
451
-
452
- <v-col
453
- v-if="
454
- attributes.captionsmenu &&
455
- current.tracks &&
456
- captions &&
457
- captions.cues &&
458
- Object.keys(captions.cues).length
459
- "
460
- cols="12"
461
- >
462
- <CaptionsMenu
463
- v-model="captions"
464
- :language="language"
465
- @click:cue="onCueClick"
466
- ></CaptionsMenu>
467
- </v-col>
526
+ </v-col>
527
+
528
+ <v-col
529
+ v-if="
530
+ attributes.captionsmenu &&
531
+ current.tracks &&
532
+ captions &&
533
+ captions.cues &&
534
+ Object.keys(captions.cues).length
535
+ "
536
+ :cols="!options.expandedCaptions ? 12 : 6"
537
+ >
538
+ <CaptionsMenu
539
+ v-model="captions"
540
+ :language="language"
541
+ @click:cue="onCueClick"
542
+ @click:expand="onClickExpandCaptions"
543
+ @click:paragraph="onClickParagraph"
544
+ ></CaptionsMenu>
545
+ </v-col>
546
+ </v-row>
468
547
  </v-container>
469
548
  </template>
470
549
 
@@ -541,6 +620,7 @@ export default {
541
620
  paused: true,
542
621
  playbackRateIndex: 0,
543
622
  fullscreen: false,
623
+ expandedCaptions: false,
544
624
  download: false,
545
625
  remoteplayback: false,
546
626
  controlslist: [],
@@ -569,6 +649,13 @@ export default {
569
649
  onCueClick(time) {
570
650
  this.setTime(time)
571
651
  },
652
+ onClickExpandCaptions(expanded) {
653
+ this.options.expandedCaptions = expanded
654
+ this.$emit('click:captions-expand', expanded)
655
+ },
656
+ onClickParagraph(isParagraph) {
657
+ this.$emit('click:captions-paragraph', isParagraph)
658
+ },
572
659
  onDownload() {
573
660
  window.open(this.src.sources[0].src, '_blank')
574
661
  },
@@ -684,11 +771,9 @@ export default {
684
771
  },
685
772
  onSeeking(e) {
686
773
  this.$emit('seeking', e)
687
- // console.log('onSeeking', e)
688
774
  },
689
775
  onMediaProgress(e) {
690
776
  this.$emit('progress', e)
691
- //console.log('onMediaProgress', e)
692
777
  },
693
778
  onCCToggle() {
694
779
  this.options.cc = !this.options.cc
@@ -743,6 +828,28 @@ export default {
743
828
  onCuechange(e) {
744
829
  if (e && e.srcElement && e.srcElement.track) {
745
830
  const track = e.srcElement.track
831
+
832
+ // Remove transcript classes from cues manually
833
+ // This is because Firefox doesn't support ::cue(<selector>) so we can't remove them via css
834
+ if (
835
+ typeof track.activeCues !== 'undefined' &&
836
+ track.activeCues.length > 0
837
+ ) {
838
+ // Store the raw version so we can retain it for the CaptionsMenu
839
+ if (typeof track.activeCues[0].rawText === 'undefined') {
840
+ track.activeCues[0].rawText = track.activeCues[0].text
841
+ }
842
+
843
+ // Now remove `<c.transcript>` tags
844
+ const transcriptTagRegex = /<c.transcript>.*?<\/c>/gi
845
+
846
+ track.activeCues[0].text =
847
+ track.activeCues[0].text.replaceAll(
848
+ transcriptTagRegex,
849
+ ''
850
+ )
851
+ }
852
+
746
853
  this.setCues(track)
747
854
  }
748
855
 
@@ -821,6 +928,15 @@ export default {
821
928
  this.options.controlslist = this.attributes.controlslist.split(' ')
822
929
  }
823
930
 
931
+ if (
932
+ typeof this.attributes.playbackrates === 'undefined' ||
933
+ this.attributes.playbackrates.length === 0
934
+ ) {
935
+ throw new Error(
936
+ 'attributes.playbackrates must be defined and an array of numbers!'
937
+ )
938
+ }
939
+
824
940
  // Adjust the playback speed to 1 by default
825
941
  if (this.attributes.playbackrates.indexOf(1) !== -1) {
826
942
  this.options.playbackRateIndex =
@@ -910,6 +1026,11 @@ export default {
910
1026
  max-height: 100%;
911
1027
  background: #000;
912
1028
  }
1029
+ /* Hide transcript classes from player */
1030
+ .player-video::cue(c.transcript) {
1031
+ visibility: hidden;
1032
+ font-size: 0;
1033
+ }
913
1034
  .player-overlay {
914
1035
  position: relative;
915
1036
  color: #fff;