@mindedge/vuetify-player 0.4.1 → 0.4.3
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/README.md +8 -3
- package/package.json +1 -1
- package/src/components/Media/CaptionsMenu.vue +0 -9
- package/src/components/Media/Html5Player.vue +176 -44
- package/src/components/Media/SettingsMenu.vue +1 -1
- package/src/components/VuetifyPlayer.vue +91 -4
- package/src/i18n/en-US.js +1 -0
- package/src/i18n/es-ES.js +1 -0
- package/src/i18n/sv-SE.js +1 -0
package/README.md
CHANGED
|
@@ -217,6 +217,8 @@ See [Full media `src` structure for where the ads array is placed](#full-media-s
|
|
|
217
217
|
| `playlistmenu` | `Boolean` | true \| false | true | Show the playlist menu if there's multiple videos |
|
|
218
218
|
| `playlistautoadvance` | `Boolean` | true \| false | true | Play the next source group |
|
|
219
219
|
| `playbackrates` | `Array` | [`array of numbers`] | [0.5, 1, 1.5, 2] | Default playback speeds. Anything below 0.25 and above 4 will make cause audio distortion |
|
|
220
|
+
| `volume` | `Number` | 0-1 | 0.5 | Supports `.sync`. The initial volume of the player. |
|
|
221
|
+
| `cc` | `Boolean` | true \| false | false | Supports `.sync`. The initial state of the player embedded closed captions. |
|
|
220
222
|
| `captions-expanded` | `Boolean` | true \| false | undefined | Supports `.sync`. The initial state of the captions transcript being expanded. |
|
|
221
223
|
| `captions-hide-expand` | `Boolean` | true \| false | true | Show / allow the captions transcript expand button for users |
|
|
222
224
|
| `captions-paragraph-view` | `Boolean` | true \| false | undefined | Supports `.sync`. The initial state of the captions transcript paragraph view |
|
|
@@ -258,6 +260,8 @@ See [Full media `src` structure for where the ads array is placed](#full-media-s
|
|
|
258
260
|
| `click:captions-paragraph-view` | `true` \| `false` | When the view as paragraph button is clicked. true when viewing as a paragraph, false when viewing as timed captions |
|
|
259
261
|
| `click:captions-autoscroll` | `true` \| `false` | When the autoscroll captions button is clicked. true on autoscrolling otherwise false |
|
|
260
262
|
| `click:captions-close` | `true` \| `false` | When the c;pse captions button is clicked. true on closed, false on visible |
|
|
263
|
+
| `update:volume` | `Number` | When the volume is updated. This is the same as `volumechange` |
|
|
264
|
+
| `update:cc` | `true` \| `false` | When the players closed captions state is updated |
|
|
261
265
|
| `update:captions-expanded` | `true` \| `false` | When the captions expand state is updated |
|
|
262
266
|
| `update:captions-paragraph-view` | `true` \| `false` | When the captions paragraph-view state is updated |
|
|
263
267
|
| `update:captions-autoscroll` | `true` \| `false` | When the captions autoscroll state is updated |
|
|
@@ -265,9 +269,10 @@ See [Full media `src` structure for where the ads array is placed](#full-media-s
|
|
|
265
269
|
|
|
266
270
|
## Supported `<VuetifyPlayer>` Slots
|
|
267
271
|
|
|
268
|
-
| Slot name | Attributes | Description
|
|
269
|
-
| ----------- | ---------- |
|
|
270
|
-
| `no-source` | `none` | Displayed over the media skeleton loader when no media source is configured
|
|
272
|
+
| Slot name | Attributes | Description |
|
|
273
|
+
| ----------- | ---------- | ------------------------------------------------------------------------------------ |
|
|
274
|
+
| `no-source` | `none` | Displayed over the media skeleton loader when no media source is configured |
|
|
275
|
+
| `loading` | `none` | Displayed over the media skeleton loader when the playlist has changed via its props |
|
|
271
276
|
|
|
272
277
|
## Captions
|
|
273
278
|
|
package/package.json
CHANGED
|
@@ -147,15 +147,6 @@
|
|
|
147
147
|
@click="onCueClick(cue.startTime)"
|
|
148
148
|
>
|
|
149
149
|
<template v-if="!expandedState">
|
|
150
|
-
<v-list-item-icon v-if="!paragraphViewState">
|
|
151
|
-
<v-icon
|
|
152
|
-
>{{
|
|
153
|
-
index === captionIndex
|
|
154
|
-
? 'mdi-arrow-right-drop-circle-outline'
|
|
155
|
-
: 'mdi-checkbox-blank-circle-outline'
|
|
156
|
-
}}
|
|
157
|
-
</v-icon>
|
|
158
|
-
</v-list-item-icon>
|
|
159
150
|
<v-list-item-content>
|
|
160
151
|
<v-list-item-title
|
|
161
152
|
v-html="cue.rawText || cue.text"
|
|
@@ -94,27 +94,42 @@
|
|
|
94
94
|
<v-slide-y-reverse-transition>
|
|
95
95
|
<div v-if="player && state.controls" class="controls">
|
|
96
96
|
<v-slider
|
|
97
|
-
dark
|
|
98
97
|
v-model="currentPercent"
|
|
98
|
+
class="controls--slider"
|
|
99
|
+
hide-details
|
|
100
|
+
dark
|
|
99
101
|
:min="0"
|
|
100
102
|
:max="scrub.max"
|
|
101
|
-
:label="
|
|
102
|
-
filters.playerShortDuration(
|
|
103
|
-
percentToTimeSeconds(currentPercent)
|
|
104
|
-
) +
|
|
105
|
-
' / ' +
|
|
106
|
-
filters.playerShortDuration(player.duration)
|
|
107
|
-
"
|
|
108
103
|
inverse-label
|
|
109
|
-
@mousedown="onPause"
|
|
110
104
|
@change="onScrubTime"
|
|
111
105
|
>
|
|
112
|
-
<template #
|
|
106
|
+
<template #label>
|
|
107
|
+
<div class="controls-timestamp--container">
|
|
108
|
+
<div class="controls-timestamp">
|
|
109
|
+
{{
|
|
110
|
+
filters.playerShortDuration(
|
|
111
|
+
percentToTimeSeconds(
|
|
112
|
+
currentPercent
|
|
113
|
+
)
|
|
114
|
+
) +
|
|
115
|
+
' / ' +
|
|
116
|
+
filters.playerShortDuration(
|
|
117
|
+
player.duration
|
|
118
|
+
)
|
|
119
|
+
}}
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</template>
|
|
123
|
+
</v-slider>
|
|
124
|
+
|
|
125
|
+
<div class="controls-buttons">
|
|
126
|
+
<div class="controls-buttons--prepend">
|
|
113
127
|
<!-- Play button -->
|
|
114
128
|
<v-tooltip v-if="!state.replay" top>
|
|
115
129
|
<template #activator="{ on, attrs }">
|
|
116
130
|
<v-btn
|
|
117
|
-
|
|
131
|
+
class="controls--button"
|
|
132
|
+
x-small
|
|
118
133
|
text
|
|
119
134
|
v-bind="attrs"
|
|
120
135
|
v-on="on"
|
|
@@ -151,7 +166,8 @@
|
|
|
151
166
|
<v-tooltip v-if="state.replay" top>
|
|
152
167
|
<template #activator="{ on, attrs }">
|
|
153
168
|
<v-btn
|
|
154
|
-
|
|
169
|
+
class="controls--button"
|
|
170
|
+
x-small
|
|
155
171
|
text
|
|
156
172
|
v-bind="attrs"
|
|
157
173
|
v-on="on"
|
|
@@ -180,11 +196,12 @@
|
|
|
180
196
|
>
|
|
181
197
|
<template #activator="{ on, attrs }">
|
|
182
198
|
<v-btn
|
|
183
|
-
|
|
199
|
+
class="controls--button hide-mobile"
|
|
200
|
+
x-small
|
|
184
201
|
text
|
|
185
202
|
v-bind="attrs"
|
|
186
203
|
v-on="on"
|
|
187
|
-
@click="rewind"
|
|
204
|
+
@click="rewind(10)"
|
|
188
205
|
>
|
|
189
206
|
<v-icon>mdi-rewind-10</v-icon>
|
|
190
207
|
<span class="sr-only">{{
|
|
@@ -199,9 +216,9 @@
|
|
|
199
216
|
t(language, 'player.rewind_10')
|
|
200
217
|
}}</span>
|
|
201
218
|
</v-tooltip>
|
|
202
|
-
</
|
|
219
|
+
</div>
|
|
203
220
|
|
|
204
|
-
<
|
|
221
|
+
<div class="controls-buttons--append">
|
|
205
222
|
<!-- Closed Captions -->
|
|
206
223
|
<v-menu
|
|
207
224
|
v-if="
|
|
@@ -215,14 +232,15 @@
|
|
|
215
232
|
>
|
|
216
233
|
<template #activator="{ on, attrs }">
|
|
217
234
|
<v-btn
|
|
218
|
-
|
|
235
|
+
class="controls--button"
|
|
236
|
+
x-small
|
|
219
237
|
text
|
|
220
238
|
v-bind="attrs"
|
|
221
239
|
v-on="on"
|
|
222
|
-
@click="
|
|
240
|
+
@click="onClickCCToggle"
|
|
223
241
|
>
|
|
224
242
|
<v-icon>{{
|
|
225
|
-
|
|
243
|
+
ccState
|
|
226
244
|
? 'mdi-closed-caption'
|
|
227
245
|
: 'mdi-closed-caption-outline'
|
|
228
246
|
}}</v-icon>
|
|
@@ -263,7 +281,8 @@
|
|
|
263
281
|
>
|
|
264
282
|
<template #activator="{ on, attrs }">
|
|
265
283
|
<v-btn
|
|
266
|
-
|
|
284
|
+
class="controls--button"
|
|
285
|
+
x-small
|
|
267
286
|
text
|
|
268
287
|
v-bind="attrs"
|
|
269
288
|
v-on="on"
|
|
@@ -331,7 +350,8 @@
|
|
|
331
350
|
<v-tooltip v-if="allowFullscreen" top>
|
|
332
351
|
<template #activator="{ on, attrs }">
|
|
333
352
|
<v-btn
|
|
334
|
-
|
|
353
|
+
class="controls--button"
|
|
354
|
+
x-small
|
|
335
355
|
text
|
|
336
356
|
v-bind="attrs"
|
|
337
357
|
v-on="on"
|
|
@@ -367,7 +387,8 @@
|
|
|
367
387
|
>
|
|
368
388
|
<template #activator="{ on, attrs }">
|
|
369
389
|
<v-btn
|
|
370
|
-
|
|
390
|
+
class="controls--button hide-mobile"
|
|
391
|
+
x-small
|
|
371
392
|
text
|
|
372
393
|
v-bind="attrs"
|
|
373
394
|
v-on="on"
|
|
@@ -396,7 +417,8 @@
|
|
|
396
417
|
<v-tooltip v-if="allowRemotePlayback" top>
|
|
397
418
|
<template #activator="{ on, attrs }">
|
|
398
419
|
<v-btn
|
|
399
|
-
|
|
420
|
+
class="controls--button hide-mobile"
|
|
421
|
+
x-small
|
|
400
422
|
text
|
|
401
423
|
v-bind="attrs"
|
|
402
424
|
v-on="on"
|
|
@@ -423,7 +445,8 @@
|
|
|
423
445
|
<v-tooltip v-if="allowDownload" top>
|
|
424
446
|
<template #activator="{ on, attrs }">
|
|
425
447
|
<v-btn
|
|
426
|
-
|
|
448
|
+
class="controls--button hide-mobile"
|
|
449
|
+
x-small
|
|
427
450
|
text
|
|
428
451
|
v-bind="attrs"
|
|
429
452
|
v-on="on"
|
|
@@ -456,8 +479,8 @@
|
|
|
456
479
|
onPlaybackSpeedChange
|
|
457
480
|
"
|
|
458
481
|
></SettingsMenu>
|
|
459
|
-
</
|
|
460
|
-
</
|
|
482
|
+
</div>
|
|
483
|
+
</div>
|
|
461
484
|
</div>
|
|
462
485
|
</v-slide-y-reverse-transition>
|
|
463
486
|
</div>
|
|
@@ -531,6 +554,16 @@ export default {
|
|
|
531
554
|
type: Object,
|
|
532
555
|
required: true,
|
|
533
556
|
},
|
|
557
|
+
volume: {
|
|
558
|
+
type: Number,
|
|
559
|
+
required: false,
|
|
560
|
+
default: undefined,
|
|
561
|
+
},
|
|
562
|
+
cc: {
|
|
563
|
+
type: Boolean,
|
|
564
|
+
required: false,
|
|
565
|
+
default: undefined,
|
|
566
|
+
},
|
|
534
567
|
captionsExpanded: {
|
|
535
568
|
type: Boolean,
|
|
536
569
|
required: false,
|
|
@@ -600,6 +633,7 @@ export default {
|
|
|
600
633
|
'click:captions-autoscroll',
|
|
601
634
|
'click:captions-close',
|
|
602
635
|
'click:captions-cue',
|
|
636
|
+
'update:cc',
|
|
603
637
|
'update:captions-expanded',
|
|
604
638
|
'update:captions-paragraph-view',
|
|
605
639
|
'update:captions-autoscroll',
|
|
@@ -653,6 +687,19 @@ export default {
|
|
|
653
687
|
|
|
654
688
|
return type
|
|
655
689
|
},
|
|
690
|
+
ccState: {
|
|
691
|
+
get() {
|
|
692
|
+
if (typeof this.cc !== 'undefined') {
|
|
693
|
+
return this.cc
|
|
694
|
+
} else {
|
|
695
|
+
return this.state.cc
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
set(v) {
|
|
699
|
+
this.$emit('update:cc', v)
|
|
700
|
+
this.state.cc = v
|
|
701
|
+
},
|
|
702
|
+
},
|
|
656
703
|
captionsVisibleState: {
|
|
657
704
|
get() {
|
|
658
705
|
if (typeof this.captionsVisible !== 'undefined') {
|
|
@@ -731,6 +778,7 @@ export default {
|
|
|
731
778
|
controlsDebounce: null,
|
|
732
779
|
volume: 0.5, // default 50%
|
|
733
780
|
muted: false,
|
|
781
|
+
unmuteVolume: 0, // The stored volume to return to the initial volume when unmuting
|
|
734
782
|
paused: true,
|
|
735
783
|
playbackRateIndex: 0,
|
|
736
784
|
fullscreen: false,
|
|
@@ -782,6 +830,11 @@ export default {
|
|
|
782
830
|
this.ads[ad.play_at_percent].complete = false
|
|
783
831
|
}
|
|
784
832
|
}
|
|
833
|
+
|
|
834
|
+
// Set the initial volume
|
|
835
|
+
if (typeof this.volume !== 'undefined') {
|
|
836
|
+
this.state.volume = this.volume
|
|
837
|
+
}
|
|
785
838
|
},
|
|
786
839
|
mounted() {
|
|
787
840
|
if (
|
|
@@ -929,7 +982,7 @@ export default {
|
|
|
929
982
|
*
|
|
930
983
|
* @param String|null lang The lang to load. Eg en-US, sv-SE, etc. Pass nothing / null to turn off captions
|
|
931
984
|
*/
|
|
932
|
-
onSelectTrack(lang = null) {
|
|
985
|
+
onSelectTrack(lang = null, mode = 'showing') {
|
|
933
986
|
if (this.player.textTracks && this.player.textTracks.length > 0) {
|
|
934
987
|
for (let i = 0; i < this.player.textTracks.length; i++) {
|
|
935
988
|
// Disable all tracks by default
|
|
@@ -938,7 +991,7 @@ export default {
|
|
|
938
991
|
|
|
939
992
|
if (this.player.textTracks[i].language === lang) {
|
|
940
993
|
this.state.ccLang = lang
|
|
941
|
-
this.player.textTracks[i].mode =
|
|
994
|
+
this.player.textTracks[i].mode = mode
|
|
942
995
|
|
|
943
996
|
this.setCues(this.player.textTracks[i])
|
|
944
997
|
|
|
@@ -980,13 +1033,16 @@ export default {
|
|
|
980
1033
|
onMediaProgress(e) {
|
|
981
1034
|
this.$emit('progress', e)
|
|
982
1035
|
},
|
|
983
|
-
|
|
984
|
-
|
|
1036
|
+
onClickCCToggle() {
|
|
1037
|
+
// We can't read the opposite of !this.ccState because it's a computed off of props
|
|
1038
|
+
// So this.ccState = !this.ccState takes 1 tick to reflect the actual changes between the getter and setter
|
|
1039
|
+
const state = !this.ccState
|
|
1040
|
+
this.ccState = state
|
|
985
1041
|
|
|
986
|
-
if (
|
|
1042
|
+
if (state) {
|
|
987
1043
|
this.onSelectTrack(this.state.ccLang)
|
|
988
1044
|
} else {
|
|
989
|
-
this.onSelectTrack()
|
|
1045
|
+
this.onSelectTrack(this.state.ccLang, 'hidden')
|
|
990
1046
|
}
|
|
991
1047
|
},
|
|
992
1048
|
onClickReplay(e) {
|
|
@@ -1016,10 +1072,19 @@ export default {
|
|
|
1016
1072
|
},
|
|
1017
1073
|
muteToggle() {
|
|
1018
1074
|
if (this.player.muted) {
|
|
1075
|
+
// Restore the inital volume
|
|
1076
|
+
this.state.volume = this.state.unmuteVolume
|
|
1077
|
+
this.player.volume = this.state.unmuteVolume
|
|
1078
|
+
this.state.unmuteVolume = 0
|
|
1019
1079
|
this.state.muted = false
|
|
1020
1080
|
this.player.muted = false
|
|
1021
1081
|
this.$emit('volumechange', this.state.volume)
|
|
1022
1082
|
} else {
|
|
1083
|
+
// Store the initial volume
|
|
1084
|
+
this.state.unmuteVolume = this.state.volume
|
|
1085
|
+
this.state.volume = 0
|
|
1086
|
+
this.player.volume = 0
|
|
1087
|
+
|
|
1023
1088
|
this.state.muted = true
|
|
1024
1089
|
this.player.muted = true
|
|
1025
1090
|
this.$emit('volumechange', 0)
|
|
@@ -1037,7 +1102,6 @@ export default {
|
|
|
1037
1102
|
// thousands of "targets" for long videos
|
|
1038
1103
|
const scaleFactor = this.player.duration / this.scrub.max
|
|
1039
1104
|
this.setTime(value * scaleFactor)
|
|
1040
|
-
this.player.pause()
|
|
1041
1105
|
},
|
|
1042
1106
|
onCuechange(e) {
|
|
1043
1107
|
if (e && e.srcElement && e.srcElement.track) {
|
|
@@ -1084,15 +1148,29 @@ export default {
|
|
|
1084
1148
|
* The this.player.textTracks are now loaded
|
|
1085
1149
|
*/
|
|
1086
1150
|
onLoadeddata(e) {
|
|
1151
|
+
let defaultTrackLang = null
|
|
1087
1152
|
// Set the default captions since apparently the default attribute means nothing
|
|
1088
1153
|
if (this.current.tracks && this.current.tracks.length > 0) {
|
|
1089
1154
|
for (const track of this.current.tracks) {
|
|
1090
1155
|
if (track.default) {
|
|
1156
|
+
defaultTrackLang = track.srclang
|
|
1091
1157
|
this.onSelectTrack(track.srclang)
|
|
1092
1158
|
}
|
|
1093
1159
|
}
|
|
1094
1160
|
}
|
|
1095
1161
|
|
|
1162
|
+
// Toggle the closed captions if the state is disabled
|
|
1163
|
+
if (!this.ccState) {
|
|
1164
|
+
this.onSelectTrack(defaultTrackLang, 'hidden')
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// We're starting muted so set the appropriate return volume / muted values
|
|
1168
|
+
if (this.state.volume === 0) {
|
|
1169
|
+
this.state.unmuteVolume = 0.5
|
|
1170
|
+
this.state.muted = true
|
|
1171
|
+
this.player.muted = true
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1096
1174
|
this.$emit('loadeddata', e)
|
|
1097
1175
|
},
|
|
1098
1176
|
onLoadedmetadata(e) {
|
|
@@ -1100,10 +1178,16 @@ export default {
|
|
|
1100
1178
|
// this.$refs.player is a type of HTMLMediaElement
|
|
1101
1179
|
// See https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
|
|
1102
1180
|
//this.player.media = this.$refs.player;
|
|
1103
|
-
this.$
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1181
|
+
if (this.$refs.player) {
|
|
1182
|
+
this.player = this.$refs.player
|
|
1183
|
+
this.player.volume = this.state.volume
|
|
1184
|
+
this.$emit('volumechange', this.state.volume)
|
|
1185
|
+
this.$emit('loadedmetadata', e)
|
|
1186
|
+
} else {
|
|
1187
|
+
console.error(
|
|
1188
|
+
'Html5Player->onLoadedmetadata() but player not ready'
|
|
1189
|
+
)
|
|
1190
|
+
}
|
|
1107
1191
|
},
|
|
1108
1192
|
volumeChange(value) {
|
|
1109
1193
|
// Value needs to be a decimal value between 0 and 1
|
|
@@ -1112,6 +1196,13 @@ export default {
|
|
|
1112
1196
|
} else if (value < 0) {
|
|
1113
1197
|
value = 0
|
|
1114
1198
|
}
|
|
1199
|
+
|
|
1200
|
+
// Unmuted if we're adjusting the volume up
|
|
1201
|
+
if (value > 0 && (this.player.muted || this.state.muted)) {
|
|
1202
|
+
this.state.muted = false
|
|
1203
|
+
this.player.muted = false
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1115
1206
|
this.state.volume = value
|
|
1116
1207
|
this.player.volume = value
|
|
1117
1208
|
this.$emit('volumechange', value)
|
|
@@ -1130,10 +1221,23 @@ export default {
|
|
|
1130
1221
|
this.player.currentTime = time
|
|
1131
1222
|
},
|
|
1132
1223
|
setCues(track) {
|
|
1224
|
+
// Filter out any cues / active cues that start after the video has already ended
|
|
1225
|
+
// This way we don't show captions that we can't skip to
|
|
1226
|
+
const cues = Object.keys(track.cues || {})
|
|
1227
|
+
.map((key) => track.cues[key])
|
|
1228
|
+
.filter((c) => {
|
|
1229
|
+
return c.startTime < this.player.duration
|
|
1230
|
+
})
|
|
1231
|
+
const activeCues = Object.keys(track.activeCues || {})
|
|
1232
|
+
.map((key) => track.activeCues[key])
|
|
1233
|
+
.filter((c) => {
|
|
1234
|
+
return c.startTime < this.player.duration
|
|
1235
|
+
})
|
|
1236
|
+
|
|
1133
1237
|
// Create reactive fields
|
|
1134
1238
|
this.$set(this.captions, 'language', track.language)
|
|
1135
|
-
this.$set(this.captions, 'cues',
|
|
1136
|
-
this.$set(this.captions, 'activeCues',
|
|
1239
|
+
this.$set(this.captions, 'cues', cues)
|
|
1240
|
+
this.$set(this.captions, 'activeCues', activeCues)
|
|
1137
1241
|
|
|
1138
1242
|
// Required so the v-model will actually update.
|
|
1139
1243
|
this.captions.nonce = Math.random()
|
|
@@ -1251,18 +1355,46 @@ export default {
|
|
|
1251
1355
|
</script>
|
|
1252
1356
|
|
|
1253
1357
|
<style scoped>
|
|
1358
|
+
@media (max-width: 600px) {
|
|
1359
|
+
.hide-mobile {
|
|
1360
|
+
display: none;
|
|
1361
|
+
}
|
|
1362
|
+
.controls-timestamp--container {
|
|
1363
|
+
width: 0px;
|
|
1364
|
+
}
|
|
1365
|
+
.controls-timestamp {
|
|
1366
|
+
width: 150px;
|
|
1367
|
+
text-align: right;
|
|
1368
|
+
margin-left: -150px;
|
|
1369
|
+
margin-top: -25px;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1254
1372
|
.controls-container {
|
|
1255
|
-
height:
|
|
1373
|
+
height: 80px;
|
|
1256
1374
|
position: relative;
|
|
1257
|
-
top: -
|
|
1258
|
-
margin-bottom: -
|
|
1375
|
+
top: -90px;
|
|
1376
|
+
margin-bottom: -80px;
|
|
1259
1377
|
}
|
|
1260
1378
|
.controls {
|
|
1261
|
-
height:
|
|
1379
|
+
height: 80px;
|
|
1262
1380
|
background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.7));
|
|
1263
1381
|
}
|
|
1382
|
+
.controls--slider {
|
|
1383
|
+
width: 100%;
|
|
1384
|
+
padding-left: 0.5rem;
|
|
1385
|
+
padding-right: 0.5rem;
|
|
1386
|
+
}
|
|
1387
|
+
.controls-buttons {
|
|
1388
|
+
display: flex;
|
|
1389
|
+
}
|
|
1390
|
+
.controls-buttons--prepend {
|
|
1391
|
+
margin-right: auto;
|
|
1392
|
+
}
|
|
1393
|
+
.controls-buttons--append {
|
|
1394
|
+
margin-left: auto;
|
|
1395
|
+
}
|
|
1264
1396
|
.player-audio {
|
|
1265
|
-
height:
|
|
1397
|
+
height: 80px;
|
|
1266
1398
|
}
|
|
1267
1399
|
.player-video {
|
|
1268
1400
|
max-height: 100%;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<!-- Settings -->
|
|
3
3
|
<v-menu :attach="attach" top left offset-y :close-on-content-click="false">
|
|
4
4
|
<template #activator="{ on, attrs }">
|
|
5
|
-
<v-btn small text v-bind="attrs" v-on="on">
|
|
5
|
+
<v-btn x-small text v-bind="attrs" v-on="on">
|
|
6
6
|
<v-icon>mdi-cog</v-icon>
|
|
7
7
|
<span class="d-sr-only">{{
|
|
8
8
|
t(language, 'player.toggle_settings')
|
|
@@ -2,7 +2,29 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<v-row tabindex="0">
|
|
4
4
|
<v-col cols="12">
|
|
5
|
-
<div v-if="
|
|
5
|
+
<div v-if="loading">
|
|
6
|
+
<div class="player-skeleton--no-source">
|
|
7
|
+
<slot name="loading">
|
|
8
|
+
<div>
|
|
9
|
+
<v-progress-circular indeterminate />
|
|
10
|
+
<p>
|
|
11
|
+
{{ t(language, 'player.loading') }}
|
|
12
|
+
</p>
|
|
13
|
+
</div>
|
|
14
|
+
</slot>
|
|
15
|
+
</div>
|
|
16
|
+
<v-skeleton-loader
|
|
17
|
+
type="image, image, list-item-avatar"
|
|
18
|
+
class="player-skeleton"
|
|
19
|
+
></v-skeleton-loader>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div
|
|
23
|
+
v-if="
|
|
24
|
+
!loading &&
|
|
25
|
+
parseSourceType(current.src.sources) === null
|
|
26
|
+
"
|
|
27
|
+
>
|
|
6
28
|
<div class="player-skeleton--no-source">
|
|
7
29
|
<slot name="no-source">
|
|
8
30
|
<div>
|
|
@@ -18,9 +40,13 @@
|
|
|
18
40
|
class="player-skeleton"
|
|
19
41
|
></v-skeleton-loader>
|
|
20
42
|
</div>
|
|
43
|
+
|
|
21
44
|
<YoutubePlayer
|
|
22
45
|
ref="youtubePlayer"
|
|
23
|
-
v-if="
|
|
46
|
+
v-if="
|
|
47
|
+
!loading &&
|
|
48
|
+
parseSourceType(current.src.sources) === 'youtube'
|
|
49
|
+
"
|
|
24
50
|
:language="language"
|
|
25
51
|
:type="current.type"
|
|
26
52
|
:attributes="current.attributes"
|
|
@@ -30,13 +56,19 @@
|
|
|
30
56
|
@focusout="onFocusout"
|
|
31
57
|
@click:fullscreen="onFullscreen"
|
|
32
58
|
></YoutubePlayer>
|
|
59
|
+
|
|
33
60
|
<Html5Player
|
|
34
61
|
ref="html5Player"
|
|
35
|
-
v-if="
|
|
62
|
+
v-if="
|
|
63
|
+
!loading &&
|
|
64
|
+
parseSourceType(current.src.sources) === 'html5'
|
|
65
|
+
"
|
|
36
66
|
:language="language"
|
|
37
67
|
:type="current.type"
|
|
38
68
|
:attributes="current.attributes"
|
|
39
69
|
:src="current.src"
|
|
70
|
+
:volume.sync="volumeState"
|
|
71
|
+
:cc.sync="ccState"
|
|
40
72
|
:captions-expanded.sync="captionsExpandedState"
|
|
41
73
|
:captions-hide-expand="captionsHideExpand"
|
|
42
74
|
:captions-paragraph-view="captionsParagraphView"
|
|
@@ -54,6 +86,7 @@
|
|
|
54
86
|
@seeking="$emit('seeking', $event)"
|
|
55
87
|
@timeupdate="$emit('timeupdate', $event)"
|
|
56
88
|
@progress="$emit('progress', $event)"
|
|
89
|
+
@volumechange="onVolumeChange"
|
|
57
90
|
@canplay="$emit('canplay', $event)"
|
|
58
91
|
@waiting="$emit('waiting', $event)"
|
|
59
92
|
@canplaythrough="$emit('canplaythrough', $event)"
|
|
@@ -157,9 +190,11 @@ export default {
|
|
|
157
190
|
rewind: { type: Boolean, required: false, default: false }, // Enabled the rewind 10s button
|
|
158
191
|
loop: { type: Boolean, required: false, default: false }, // Loop the video on completion
|
|
159
192
|
muted: { type: Boolean, required: false, default: false }, // Start the video muted
|
|
193
|
+
volume: { type: Number, required: false, default: undefined }, // The initial volume level. Undefined will use the local value of 0.5
|
|
160
194
|
playsinline: { Boolean: String, required: false, default: false }, // Force inline & disable fullscreen
|
|
161
195
|
poster: { type: String, required: false, default: '' }, // Overridden with the playlist.poster if one is set there
|
|
162
196
|
preload: { type: String, required: false, default: '' },
|
|
197
|
+
cc: { type: Boolean, required: false, default: undefined }, // The initial state of the closed captions (if available). Undefined will use the local value of false
|
|
163
198
|
captionsmenu: { type: Boolean, required: false, default: true }, // Show the captions below the video
|
|
164
199
|
captionsExpanded: {
|
|
165
200
|
type: Boolean,
|
|
@@ -218,6 +253,7 @@ export default {
|
|
|
218
253
|
'seeking',
|
|
219
254
|
'timeupdate',
|
|
220
255
|
'progress',
|
|
256
|
+
'volumechange',
|
|
221
257
|
'canplay',
|
|
222
258
|
'waiting',
|
|
223
259
|
'canplaythrough',
|
|
@@ -235,12 +271,24 @@ export default {
|
|
|
235
271
|
'click:captions-paragraph-view',
|
|
236
272
|
'click:captions-autoscroll',
|
|
237
273
|
'click:captions-close',
|
|
274
|
+
'update:volume',
|
|
275
|
+
'update:cc',
|
|
238
276
|
'update:captions-expanded',
|
|
239
277
|
'update:captions-paragraph-view',
|
|
240
278
|
'update:captions-autoscroll',
|
|
241
279
|
'update:captions-visible',
|
|
242
280
|
],
|
|
243
|
-
watch: {
|
|
281
|
+
watch: {
|
|
282
|
+
playlist: {
|
|
283
|
+
handler(newValue, oldValue) {
|
|
284
|
+
// Make sure there was actual changes to prevent unnecessary reloads
|
|
285
|
+
if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
|
|
286
|
+
// Force-reload the media on playlist changes since we changed the source / tracks
|
|
287
|
+
this.reloadMedia()
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
244
292
|
computed: {
|
|
245
293
|
player() {
|
|
246
294
|
if (this.parseSourceType(this.current.src.sources) === 'youtube') {
|
|
@@ -303,6 +351,32 @@ export default {
|
|
|
303
351
|
return 8
|
|
304
352
|
}
|
|
305
353
|
},
|
|
354
|
+
ccState: {
|
|
355
|
+
get() {
|
|
356
|
+
if (typeof this.cc !== 'undefined') {
|
|
357
|
+
return this.cc
|
|
358
|
+
} else {
|
|
359
|
+
return this.captions.cc
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
set(v) {
|
|
363
|
+
this.$emit('update:cc', v)
|
|
364
|
+
this.captions.cc = v
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
volumeState: {
|
|
368
|
+
get() {
|
|
369
|
+
if (typeof this.volume !== 'undefined') {
|
|
370
|
+
return this.volume
|
|
371
|
+
} else {
|
|
372
|
+
return this.localVolume
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
set(v) {
|
|
376
|
+
this.player.volumeChange(v)
|
|
377
|
+
this.localVolume = v
|
|
378
|
+
},
|
|
379
|
+
},
|
|
306
380
|
captionsVisibleState: {
|
|
307
381
|
get() {
|
|
308
382
|
if (typeof this.captionsVisible !== 'undefined') {
|
|
@@ -336,16 +410,25 @@ export default {
|
|
|
336
410
|
data() {
|
|
337
411
|
return {
|
|
338
412
|
t,
|
|
413
|
+
loading: false,
|
|
339
414
|
sourceIndex: 0,
|
|
340
415
|
captions: {
|
|
416
|
+
cc: false,
|
|
341
417
|
visible: true,
|
|
342
418
|
expanded: false,
|
|
343
419
|
},
|
|
420
|
+
localVolume: 0.5, // The initial volume level
|
|
344
421
|
mediaFocus: false,
|
|
345
422
|
keyListener: null,
|
|
346
423
|
}
|
|
347
424
|
},
|
|
348
425
|
methods: {
|
|
426
|
+
reloadMedia() {
|
|
427
|
+
this.loading = true
|
|
428
|
+
setTimeout(() => {
|
|
429
|
+
this.loading = false
|
|
430
|
+
}, 500)
|
|
431
|
+
},
|
|
349
432
|
onEnded(e) {
|
|
350
433
|
if (
|
|
351
434
|
this.playlistautoadvance &&
|
|
@@ -359,6 +442,10 @@ export default {
|
|
|
359
442
|
// Loaded a new video
|
|
360
443
|
this.$emit('loadeddata', e)
|
|
361
444
|
},
|
|
445
|
+
onVolumeChange(e) {
|
|
446
|
+
this.$emit('update:volume', e)
|
|
447
|
+
this.$emit('volumechange', e)
|
|
448
|
+
},
|
|
362
449
|
onRemoteplayback(el) {
|
|
363
450
|
// Make sure the browser supports remote playback
|
|
364
451
|
if (!el.remote || !el.remote.watchAvailability) {
|
package/src/i18n/en-US.js
CHANGED
package/src/i18n/es-ES.js
CHANGED
|
@@ -10,6 +10,7 @@ export default {
|
|
|
10
10
|
next: 'Reproducir el siguiente elemento en la lista de reproducción',
|
|
11
11
|
},
|
|
12
12
|
player: {
|
|
13
|
+
loading: 'Cargando...',
|
|
13
14
|
not_configured: 'Medios no configurados',
|
|
14
15
|
playback_speed: 'Velocidad de reproducción',
|
|
15
16
|
playback_decrease: 'Disminuir la velocidad de reproducción',
|
package/src/i18n/sv-SE.js
CHANGED