@mindedge/vuetify-player 0.1.2 → 0.2.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.
@@ -0,0 +1,926 @@
1
+ <template>
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
145
+ "
146
+ open-on-hover
147
+ top
148
+ offset-y
149
+ >
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')
165
+ }}</span>
166
+ </v-btn>
167
+ </template>
168
+
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
+ "
177
+ >
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"
195
+ >
196
+ <v-icon
197
+ v-if="
198
+ !options.muted &&
199
+ options.volume > 0.75
200
+ "
201
+ >mdi-volume-high</v-icon
202
+ >
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
210
+ >
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
218
+ >
219
+ <v-icon
220
+ v-if="
221
+ options.muted ||
222
+ options.volume === 0
223
+ "
224
+ >mdi-volume-off</v-icon
225
+ >
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">{{
264
+ t(
265
+ language,
266
+ 'player.toggle_fullscreen'
267
+ )
268
+ }}</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"
288
+ >
289
+ <v-icon
290
+ >mdi-picture-in-picture-bottom-right</v-icon
291
+ >
292
+ <span class="d-sr-only">{{
293
+ t(
294
+ language,
295
+ 'player.toggle_picture_in_picture'
296
+ )
297
+ }}</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"
317
+ >
318
+ <v-icon>mdi-cast</v-icon>
319
+ <span class="d-sr-only">{{
320
+ t(
321
+ language,
322
+ 'player.toggle_remote_playback'
323
+ )
324
+ }}</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>
371
+
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">
386
+ <v-btn
387
+ small
388
+ :disabled="
389
+ options.playbackRateIndex ===
390
+ 0
391
+ "
392
+ @click="
393
+ onPlaybackSpeed(
394
+ options.playbackRateIndex -
395
+ 1
396
+ )
397
+ "
398
+ >
399
+ <v-icon>
400
+ mdi-clock-minus-outline
401
+ </v-icon>
402
+ <span class="d-sr-only">{{
403
+ t(
404
+ language,
405
+ 'player.playback_decrease'
406
+ )
407
+ }}</span>
408
+ </v-btn>
409
+ <span class="pl-2 pr-2"
410
+ >{{
411
+ attributes.playbackrates[
412
+ options
413
+ .playbackRateIndex
414
+ ]
415
+ }}x</span
416
+ >
417
+ <v-btn
418
+ 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
+ "
431
+ >
432
+ <v-icon>
433
+ mdi-clock-plus-outline
434
+ </v-icon>
435
+ <span class="d-sr-only">{{
436
+ t(
437
+ language,
438
+ 'player.playback_increase'
439
+ )
440
+ }}</span>
441
+ </v-btn>
442
+ </v-list-item-title>
443
+ </v-list-item>
444
+ </v-list>
445
+ </v-menu>
446
+ </template>
447
+ </v-slider>
448
+ </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>
468
+ </v-container>
469
+ </template>
470
+
471
+ <script>
472
+ import filters from '../filters'
473
+ import CaptionsMenu from './CaptionsMenu.vue'
474
+ import { t } from '../../i18n/i18n'
475
+
476
+ export default {
477
+ name: 'Html5Player',
478
+ components: {
479
+ CaptionsMenu,
480
+ },
481
+ props: {
482
+ language: { type: String, required: false, default: 'en-US' },
483
+ type: {
484
+ type: String,
485
+ required: false,
486
+ default: 'video',
487
+ },
488
+ attributes: {
489
+ type: Object,
490
+ required: true,
491
+ },
492
+ src: {
493
+ type: Object,
494
+ required: true,
495
+ },
496
+ },
497
+ watch: {},
498
+ computed: {
499
+ current() {
500
+ // We're playing an ad currently
501
+ if (this.activeAd) {
502
+ return this.activeAd
503
+
504
+ // We hit an ad spot~ play_at_percent
505
+ } else if (
506
+ !this.activeAd &&
507
+ typeof this.ads[this.currentPercent] !== 'undefined' &&
508
+ this.ads[this.currentPercent].sources &&
509
+ this.ads[this.currentPercent].sources.length &&
510
+ !this.ads[this.currentPercent].complete
511
+ ) {
512
+ this.setActiveAd(this.currentPercent)
513
+ return this.ads[this.currentPercent]
514
+ } else {
515
+ // Only change sources if we're not watching an ad or pre/postroll
516
+ return this.src
517
+ }
518
+ },
519
+ playerClass() {
520
+ let classList = 'player-' + this.type
521
+ return classList
522
+ },
523
+ },
524
+ data() {
525
+ return {
526
+ t,
527
+ filters,
528
+ ads: {},
529
+ activeAd: null,
530
+ currentPercent: 0,
531
+ player: {},
532
+ captions: { nonce: 0 },
533
+ fullscreenEnabled: false,
534
+ options: {
535
+ cc: true,
536
+ ccLang: this.language,
537
+ controls: true,
538
+ controlsDebounce: null,
539
+ volume: 0.5, // default 50%
540
+ muted: false,
541
+ paused: true,
542
+ playbackRateIndex: 0,
543
+ fullscreen: false,
544
+ download: false,
545
+ remoteplayback: false,
546
+ controlslist: [],
547
+ },
548
+ watchPlayer: 0,
549
+ scrub: { max: 100 },
550
+ buffering: false,
551
+ }
552
+ },
553
+ methods: {
554
+ setActiveAd(currentPercent) {
555
+ this.activeAd = this.ads[currentPercent]
556
+ },
557
+ percentToTimeSeconds(percent) {
558
+ const scaleFactor = this.player.duration / this.scrub.max
559
+ return Math.floor(percent * scaleFactor)
560
+ },
561
+ onCanplay(e) {
562
+ this.buffering = false
563
+ this.$emit('canplay', e)
564
+ },
565
+ onWaiting(e) {
566
+ this.buffering = true
567
+ this.$emit('waiting', e)
568
+ },
569
+ onCueClick(time) {
570
+ this.setTime(time)
571
+ },
572
+ onDownload() {
573
+ window.open(this.src.sources[0].src, '_blank')
574
+ },
575
+ onRewind() {
576
+ // Rewind in seconds
577
+ const seconds = 10
578
+
579
+ if (this.player.currentTime <= seconds) {
580
+ this.setTime(0)
581
+ } else {
582
+ this.setTime(this.player.currentTime - seconds)
583
+ }
584
+ },
585
+ onFullscreen() {
586
+ this.options.fullscreen = !document.fullscreenElement
587
+ // Return the whole element to be fullscreened so the controls come with it
588
+ this.$emit('click:fullscreen', this.$el)
589
+ },
590
+ onPictureInPicture() {
591
+ //this.options.pip = !document.fullscreenElement;
592
+ // Return the player aka HTMLVideoElement
593
+ this.$emit('click:pictureinpicture', this.$refs.player)
594
+ },
595
+ onRemoteplayback() {
596
+ this.$emit('click:remoteplayback', this.$refs.player)
597
+ },
598
+ onVideoHover(e) {
599
+ this.options.controls = true
600
+ clearTimeout(this.options.controlsDebounce)
601
+ this.$emit('mouseover', e)
602
+ },
603
+ onVideoLeave(e) {
604
+ const self = this
605
+ // Clear any existing timeouts before we create one
606
+ clearTimeout(this.options.controlsDebounce)
607
+ this.options.controlsDebounce = setTimeout(() => {
608
+ self.options.controls = false
609
+ }, 50)
610
+ this.$emit('mouseout', e)
611
+ },
612
+ onEnded(e) {
613
+ if (this.activeAd) {
614
+ this.ads[this.activeAd.play_at_percent].complete = true
615
+ // Go back to the play_at_percent for the main video
616
+ this.currentPercent = this.activeAd.play_at_percent
617
+ this.activeAd = null
618
+
619
+ // Reload the player to refresh all the sources / tracks
620
+ this.load(e)
621
+ // Start playing the main video
622
+ this.play(e)
623
+ } else if (
624
+ this.activeAd !== null &&
625
+ this.activeAd.play_at_percent === 100
626
+ ) {
627
+ // Ended but this ad was a postroll
628
+ this.$emit('ended', e)
629
+ } else {
630
+ // Ended without an ad
631
+ this.$emit('ended', e)
632
+ }
633
+ },
634
+ onControlsHover() {
635
+ clearTimeout(this.options.controlsDebounce)
636
+ this.options.controls = true
637
+ },
638
+ onControlsLeave() {
639
+ const self = this
640
+ // Clear any existing timeouts before we create one
641
+ clearTimeout(this.options.controlsDebounce)
642
+ this.options.controlsDebounce = setTimeout(() => {
643
+ self.options.controls = false
644
+ }, 50)
645
+ },
646
+ /**
647
+ * Select a specific track by lang
648
+ *
649
+ * @param String|null lang The lang to load. Eg en-US, sv-SE, etc. Pass nothing / null to turn off captions
650
+ */
651
+ onSelectTrack(lang = null) {
652
+ if (this.player.textTracks && this.player.textTracks.length > 0) {
653
+ for (let i = 0; i < this.player.textTracks.length; i++) {
654
+ const tt = this.player.textTracks[i]
655
+
656
+ if (tt.language === lang) {
657
+ this.options.ccLang = lang
658
+ this.player.textTracks[i].mode = 'showing'
659
+
660
+ this.setCues(tt)
661
+
662
+ // Emit the current track
663
+ this.$emit('trackchange', tt)
664
+ } else {
665
+ this.player.textTracks[i].mode = 'disabled'
666
+ }
667
+ }
668
+ }
669
+ },
670
+ onPlaybackSpeed(index) {
671
+ this.player.playbackRate = this.attributes.playbackrates[index]
672
+ this.options.playbackRateIndex = index
673
+ this.$emit('ratechange', this.player.playbackRate)
674
+ },
675
+ onTimeupdate(e) {
676
+ this.currentPercent = Math.floor(
677
+ (this.player.currentTime / this.player.duration) * 100
678
+ )
679
+
680
+ this.$emit('timeupdate', {
681
+ event: e,
682
+ current_percent: this.currentPercent,
683
+ })
684
+ },
685
+ onSeeking(e) {
686
+ this.$emit('seeking', e)
687
+ // console.log('onSeeking', e)
688
+ },
689
+ onMediaProgress(e) {
690
+ this.$emit('progress', e)
691
+ //console.log('onMediaProgress', e)
692
+ },
693
+ onCCToggle() {
694
+ this.options.cc = !this.options.cc
695
+
696
+ if (this.options.cc) {
697
+ this.onSelectTrack(this.options.ccLang)
698
+ } else {
699
+ this.onSelectTrack()
700
+ }
701
+ },
702
+ onPlayToggle(e) {
703
+ const self = this
704
+ this.options.controls = true
705
+
706
+ // Clear any existing timeouts and close the controls in 5 seconds
707
+ clearTimeout(this.options.controlsDebounce)
708
+ this.options.controlsDebounce = setTimeout(() => {
709
+ self.options.controls = false
710
+ }, 5000)
711
+
712
+ if (this.player.paused) {
713
+ this.play(e)
714
+ } else {
715
+ this.pause(e)
716
+ }
717
+ },
718
+ onMuteToggle() {
719
+ if (this.player.muted) {
720
+ this.options.muted = false
721
+ this.player.muted = false
722
+ this.$emit('volumechange', this.options.volume)
723
+ } else {
724
+ this.options.muted = true
725
+ this.player.muted = true
726
+ this.$emit('volumechange', 0)
727
+ }
728
+ },
729
+ onPlay(e) {
730
+ this.play(e)
731
+ },
732
+ onPause(e) {
733
+ this.pause(e)
734
+ },
735
+ onScrubTime(value) {
736
+ // Value is a number from 0 to scrub max (eg 100). Translate that into a time
737
+ // We don't want the scrub.max to equal time.duration since we don't want
738
+ // thousands of "targets" for long videos
739
+ const scaleFactor = this.player.duration / this.scrub.max
740
+ this.setTime(value * scaleFactor)
741
+ this.player.pause()
742
+ },
743
+ onCuechange(e) {
744
+ if (e && e.srcElement && e.srcElement.track) {
745
+ const track = e.srcElement.track
746
+ this.setCues(track)
747
+ }
748
+
749
+ this.$emit('cuechange', e)
750
+ },
751
+ /**
752
+ * The this.player.textTracks are now loaded
753
+ */
754
+ onLoadeddata(e) {
755
+ // Set the default captions since apparently the default attribute means nothing
756
+ if (this.current.tracks && this.current.tracks.length > 0) {
757
+ for (const track of this.current.tracks) {
758
+ if (track.default) {
759
+ this.onSelectTrack(track.srclang)
760
+ }
761
+ }
762
+ }
763
+
764
+ this.$emit('loadeddata', e)
765
+ },
766
+ onLoadedmetadata(e) {
767
+ // Set the player object since metadata (and therefore duration) is now loaded
768
+ // this.$refs.player is a type of HTMLMediaElement
769
+ // See https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
770
+ //this.player.media = this.$refs.player;
771
+ this.$emit('loadedmetadata', e)
772
+ this.player = this.$refs.player
773
+ this.player.volume = this.options.volume
774
+ this.$emit('volumechange', this.options.volume)
775
+ },
776
+ onVolumeChange(value) {
777
+ this.options.volume = value
778
+ this.player.volume = value
779
+ this.$emit('volumechange', value)
780
+ },
781
+ onDurationChange() {
782
+ // console.log('onDurationChange');
783
+ // console.log(e);
784
+ },
785
+ setTime(time) {
786
+ this.player.currentTime = time
787
+ },
788
+ setCues(track) {
789
+ // Create reactive fields
790
+ this.$set(this.captions, 'language', track.language)
791
+ this.$set(this.captions, 'cues', track.cues)
792
+ this.$set(this.captions, 'activeCues', track.activeCues)
793
+
794
+ // Required so the v-model will actually update.
795
+ this.captions.nonce = Math.random()
796
+ },
797
+ load(e = null) {
798
+ // Reload the player to refresh all the sources / tracks
799
+ this.player.load()
800
+ this.$emit('load', e)
801
+ },
802
+ pause(e = null) {
803
+ this.player.pause()
804
+ this.options.paused = true
805
+ this.$emit('pause', e)
806
+ },
807
+ play(e = null) {
808
+ // Start playing the main video
809
+ this.player.play()
810
+ this.options.paused = false
811
+ this.$emit('play', e)
812
+ },
813
+ },
814
+ beforeMount() {
815
+ // Parse the controlslist string
816
+ if (
817
+ this.attributes.controlslist &&
818
+ typeof this.attributes.controlslist === 'string' &&
819
+ this.attributes.controlslist !== ''
820
+ ) {
821
+ this.options.controlslist = this.attributes.controlslist.split(' ')
822
+ }
823
+
824
+ // Adjust the playback speed to 1 by default
825
+ if (this.attributes.playbackrates.indexOf(1) !== -1) {
826
+ this.options.playbackRateIndex =
827
+ this.attributes.playbackrates.indexOf(1)
828
+ } else {
829
+ // 1 aka normal playback not enabled (What monster would do this?!)
830
+ // Set the playback rate to "middle of the road" for whatever is available
831
+ this.options.playbackRateIndex = Math.floor(
832
+ this.attributes.playbackrates.length / 2
833
+ )
834
+ }
835
+
836
+ // Initialize the ads aka pre/post/midroll
837
+ if (this.src.ads && this.src.ads.length) {
838
+ for (const ad of this.src.ads) {
839
+ // Map to a percent so we can avoid dupe timings and have easier lookups
840
+ this.ads[ad.play_at_percent] = ad
841
+ this.ads[ad.play_at_percent].complete = false
842
+ }
843
+ }
844
+
845
+ // Determine fullscreen settings
846
+ if (
847
+ this.attributes.playsinline ||
848
+ !document.fullscreenEnabled ||
849
+ this.options.controlslist.indexOf('nofullscreen') !== -1
850
+ ) {
851
+ this.fullscreenEnabled = false
852
+ } else {
853
+ this.fullscreenEnabled = true
854
+ }
855
+
856
+ // Determine remote playback settings
857
+ if (
858
+ this.attributes.disableremoteplayback ||
859
+ this.options.controlslist.indexOf('noremoteplayback') !== -1
860
+ ) {
861
+ this.options.remoteplayback = false
862
+ } else {
863
+ this.options.remoteplayback = true
864
+ }
865
+
866
+ // Determine download settings
867
+ if (this.options.controlslist.indexOf('nodownload') !== -1) {
868
+ this.options.download = false
869
+ } else {
870
+ this.options.download = true
871
+ }
872
+ },
873
+ mounted() {},
874
+ }
875
+ </script>
876
+
877
+ <style scoped>
878
+ .controls-container {
879
+ height: 40px;
880
+ position: relative;
881
+ top: -50px;
882
+ margin-bottom: -40px;
883
+ overflow: hidden;
884
+ }
885
+ .controls {
886
+ height: 40px;
887
+ background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.7));
888
+ }
889
+ .volume-slider {
890
+ position: relative;
891
+ right: -50px;
892
+ top: -180px;
893
+ height: 180px;
894
+ width: 50px;
895
+ margin-left: -50px;
896
+ padding-bottom: 10px;
897
+ }
898
+ .slider-active-area {
899
+ width: 50px;
900
+ height: 200px;
901
+ margin-right: -50px;
902
+ margin-bottom: -200px;
903
+ position: relative;
904
+ top: -160px; /* height of this - controls height */
905
+ }
906
+ .player-audio {
907
+ height: 40px;
908
+ }
909
+ .player-video {
910
+ max-height: 100%;
911
+ background: #000;
912
+ }
913
+ .player-overlay {
914
+ position: relative;
915
+ color: #fff;
916
+ left: 25%;
917
+ width: 50%;
918
+ top: 100px;
919
+ height: 0;
920
+ text-align: center;
921
+ }
922
+ .player-overlay > div {
923
+ background: rgba(0, 0, 0, 0.25);
924
+ border-radius: 100%;
925
+ }
926
+ </style>