@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindedge/vuetify-player",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "private": false,
5
5
  "description": "Accessible, localized, full featured media player with Vuetifyjs",
6
6
  "author": "Jacob Rogaishio",
@@ -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 #prepend>
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
- small
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
- small
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
- small
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
- </template>
219
+ </div>
203
220
 
204
- <template #append>
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
- small
235
+ class="controls--button"
236
+ x-small
219
237
  text
220
238
  v-bind="attrs"
221
239
  v-on="on"
222
- @click="CCToggle"
240
+ @click="onClickCCToggle"
223
241
  >
224
242
  <v-icon>{{
225
- state.cc
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
- small
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
- small
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
- small
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
- small
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
- small
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
- </template>
460
- </v-slider>
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 = 'showing'
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
- CCToggle() {
984
- this.state.cc = !this.state.cc
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 (this.state.cc) {
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.$emit('loadedmetadata', e)
1104
- this.player = this.$refs.player
1105
- this.player.volume = this.state.volume
1106
- this.$emit('volumechange', this.state.volume)
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', track.cues)
1136
- this.$set(this.captions, 'activeCues', track.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: 40px;
1373
+ height: 80px;
1256
1374
  position: relative;
1257
- top: -50px;
1258
- margin-bottom: -40px;
1375
+ top: -90px;
1376
+ margin-bottom: -80px;
1259
1377
  }
1260
1378
  .controls {
1261
- height: 40px;
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: 40px;
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="parseSourceType(current.src.sources) === null">
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="parseSourceType(current.src.sources) === 'youtube'"
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="parseSourceType(current.src.sources) === 'html5'"
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
@@ -10,6 +10,7 @@ export default {
10
10
  next: 'Play next item in playlist',
11
11
  },
12
12
  player: {
13
+ loading: 'Loading...',
13
14
  not_configured: 'Media not configured',
14
15
  playback_speed: 'Playback Speed',
15
16
  playback_decrease: 'Decrease playback speed',
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
@@ -10,6 +10,7 @@ export default {
10
10
  next: 'Spela nästa',
11
11
  },
12
12
  player: {
13
+ loading: 'Laddar...',
13
14
  not_configured: 'Media inte konfigurerad',
14
15
  playback_speed: 'Uppspelningshastighet',
15
16
  playback_decrease: 'Minska uppspelningshastigheten',