@grfzhl/vue-hls-player 1.0.7 → 1.0.9
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
|
@@ -172,7 +172,25 @@ example:
|
|
|
172
172
|
}]
|
|
173
173
|
```
|
|
174
174
|
|
|
175
|
+
**isMuted**:
|
|
176
|
+
1 value: true or false, type: Boolean
|
|
177
|
+
|
|
178
|
+
it makes the video muted
|
|
179
|
+
|
|
180
|
+
**autoplay**:
|
|
181
|
+
1. value: true or false, type Boolean
|
|
182
|
+
|
|
183
|
+
it will set the native <video> autoplay property
|
|
184
|
+
|
|
175
185
|
### Last release:
|
|
186
|
+
v1.0.9
|
|
187
|
+
- Fix sizes in fullscreen mode for video
|
|
188
|
+
- Hide transcript block completely when hidden
|
|
189
|
+
|
|
190
|
+
v1.0.8
|
|
191
|
+
- Add slots to inject own elements nearby video element
|
|
192
|
+
- Add prop for autoplay video
|
|
193
|
+
|
|
176
194
|
v1.0.7
|
|
177
195
|
- Add function to handle own logic for fullscreen
|
|
178
196
|
|
|
@@ -1,38 +1,45 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="video-container">
|
|
3
|
-
<media-
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
3
|
+
<div class="media-container" id="hls-player-media-container">
|
|
4
|
+
<slot name="before-media"></slot>
|
|
5
|
+
<media-theme-sutro class="video-player-theme-container" :class="{'is-fullscreen': isFullscreen}">
|
|
6
|
+
<video
|
|
7
|
+
class="hls-player"
|
|
8
|
+
slot="media"
|
|
9
|
+
@pause="pause"
|
|
10
|
+
@keyup="changeSpeed"
|
|
11
|
+
@ended="onVideoEnd"
|
|
12
|
+
@seek="seekVideo"
|
|
13
|
+
ref="video"
|
|
14
|
+
:poster="previewImageLink"
|
|
15
|
+
:controls="false"
|
|
16
|
+
:title="title"
|
|
17
|
+
controlslist="nodownload"
|
|
18
|
+
playsinline
|
|
19
|
+
crossorigin
|
|
20
|
+
:muted="mutedAttr"
|
|
21
|
+
:autoplay="autoplay && isMuted"
|
|
22
|
+
>
|
|
23
|
+
<source
|
|
24
|
+
:src="link"
|
|
25
|
+
type="application/x-mpegURL"
|
|
26
|
+
/>
|
|
27
|
+
<track
|
|
28
|
+
v-if="subtitles.length"
|
|
29
|
+
v-for="(subtitle, i) in subtitles"
|
|
30
|
+
:src="subtitle.link"
|
|
31
|
+
kind="subtitles"
|
|
32
|
+
:srclang="subtitle.lang"
|
|
33
|
+
:label="subtitle.label" :default="i === 0" />
|
|
34
|
+
</video>
|
|
35
|
+
</media-theme-sutro>
|
|
36
|
+
<div class="custom-subtitles" v-show="(isFullscreen) || (!showTranscriptBlock)">
|
|
37
|
+
<div class="subtitle-text" ref="subtitlesContainer" style="display: none;"></div>
|
|
38
|
+
</div>
|
|
39
|
+
<slot name="after-media"></slot>
|
|
34
40
|
</div>
|
|
35
41
|
</div>
|
|
42
|
+
<slot name="before-transcripts"></slot>
|
|
36
43
|
<SubtitleBlock
|
|
37
44
|
:subtitle="currentSubtitle"
|
|
38
45
|
:cursor="videoCursor"
|
|
@@ -40,10 +47,12 @@
|
|
|
40
47
|
@seek="seekVideo"
|
|
41
48
|
@toggleTranscript="toggleTranscript">
|
|
42
49
|
</SubtitleBlock>
|
|
50
|
+
<slot name="after-transcripts"></slot>
|
|
51
|
+
<slot name="default"></slot>
|
|
43
52
|
</template>
|
|
44
53
|
|
|
45
54
|
<script setup>
|
|
46
|
-
import { onMounted, onUpdated, ref, onUnmounted, computed } from 'vue'
|
|
55
|
+
import { onMounted, onUpdated, ref, onUnmounted, computed, watch } from 'vue'
|
|
47
56
|
import Hls from 'hls.js'
|
|
48
57
|
import 'player.style/sutro';
|
|
49
58
|
import SubtitleBlock from './SubtitleBlock.vue';
|
|
@@ -69,6 +78,10 @@ const props = defineProps({
|
|
|
69
78
|
type: Boolean,
|
|
70
79
|
default: false
|
|
71
80
|
},
|
|
81
|
+
autoplay: {
|
|
82
|
+
type: Boolean,
|
|
83
|
+
default: false
|
|
84
|
+
},
|
|
72
85
|
/**
|
|
73
86
|
* array of object, for
|
|
74
87
|
* subtitles to append:
|
|
@@ -99,10 +112,17 @@ const currentSubtitleLang = ref(null)
|
|
|
99
112
|
const videoCursor = ref(0)
|
|
100
113
|
const isFullscreen = ref(false);
|
|
101
114
|
|
|
115
|
+
const videoElement = defineModel()
|
|
116
|
+
|
|
102
117
|
onMounted(() => {
|
|
103
118
|
prepareVideoPlayer()
|
|
104
119
|
if (video.value) {
|
|
105
|
-
|
|
120
|
+
|
|
121
|
+
// pass video element as reference to model
|
|
122
|
+
if (!videoElement.value) {
|
|
123
|
+
videoElement.value = video.value;
|
|
124
|
+
}
|
|
125
|
+
|
|
106
126
|
video.value.addEventListener('timeupdate', updateCurrentTime);
|
|
107
127
|
document.addEventListener('fullscreenchange', onFullscreenChange);
|
|
108
128
|
|
|
@@ -135,6 +155,7 @@ onMounted(() => {
|
|
|
135
155
|
})
|
|
136
156
|
|
|
137
157
|
onUpdated(() => {
|
|
158
|
+
|
|
138
159
|
})
|
|
139
160
|
|
|
140
161
|
onUnmounted(() => {
|
|
@@ -144,6 +165,11 @@ onUnmounted(() => {
|
|
|
144
165
|
}
|
|
145
166
|
});
|
|
146
167
|
|
|
168
|
+
const mutedAttr = computed(() => {
|
|
169
|
+
// autoplay is only possible when muted
|
|
170
|
+
return (props.autoplay || props.isMuted);
|
|
171
|
+
})
|
|
172
|
+
|
|
147
173
|
const currentSubtitle = computed(() => {
|
|
148
174
|
if(props.subtitles) {
|
|
149
175
|
const current = props.subtitles.filter((subt) => {
|
|
@@ -154,12 +180,18 @@ const currentSubtitle = computed(() => {
|
|
|
154
180
|
return null
|
|
155
181
|
})
|
|
156
182
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
183
|
+
watch([props, videoElement], (a) => {
|
|
184
|
+
if(a[0].autoplay && a[1]) {
|
|
185
|
+
// autoplay is only possible when muted
|
|
186
|
+
a[1].muted = true
|
|
187
|
+
setTimeout(() => {
|
|
188
|
+
a[1].play();
|
|
189
|
+
}, 200)
|
|
190
|
+
}
|
|
191
|
+
})
|
|
160
192
|
|
|
161
193
|
function onFullscreenChange() {
|
|
162
|
-
|
|
194
|
+
isFullscreen.value = !!document.fullscreenElement
|
|
163
195
|
emit('video-fullscreen-change', document.fullscreenElement)
|
|
164
196
|
};
|
|
165
197
|
|
|
@@ -194,25 +226,31 @@ function prepareVideoPlayer() {
|
|
|
194
226
|
track.addEventListener("cuechange", () => {
|
|
195
227
|
const activeCues = track.activeCues;
|
|
196
228
|
currentSubtitleLang.value = track.language
|
|
197
|
-
if
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
229
|
+
if(subtitlesContainer.value) {
|
|
230
|
+
if (activeCues && activeCues.length > 0) {
|
|
231
|
+
subtitlesContainer.value.textContent = activeCues[0].text
|
|
232
|
+
subtitlesContainer.value.style.display = "block";
|
|
233
|
+
} else {
|
|
234
|
+
subtitlesContainer.value.style.display = "none";
|
|
235
|
+
}
|
|
202
236
|
}
|
|
203
237
|
});
|
|
204
238
|
if (track.mode !== previousModes[index]) {
|
|
205
239
|
if (track.mode === "showing") {
|
|
206
240
|
const activeCues = track.activeCues;
|
|
207
241
|
currentSubtitleLang.value = track.language
|
|
208
|
-
if
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
242
|
+
if(subtitlesContainer.value) {
|
|
243
|
+
if (activeCues && activeCues.length > 0) {
|
|
244
|
+
subtitlesContainer.value.style.display = "block";
|
|
245
|
+
subtitlesContainer.value.textContent = activeCues[0].text
|
|
246
|
+
} else {
|
|
247
|
+
subtitlesContainer.value.style.display = "none";
|
|
248
|
+
}
|
|
213
249
|
}
|
|
214
250
|
} else {
|
|
215
|
-
subtitlesContainer.value
|
|
251
|
+
if(subtitlesContainer.value) {
|
|
252
|
+
subtitlesContainer.value.style.display = "none";
|
|
253
|
+
}
|
|
216
254
|
}
|
|
217
255
|
previousModes[index] = track.mode;
|
|
218
256
|
}
|
|
@@ -270,9 +308,22 @@ function changeSpeed(e) {
|
|
|
270
308
|
|
|
271
309
|
.video-player-theme-container, .hls-player {
|
|
272
310
|
width: 100%;
|
|
311
|
+
height: 100%;
|
|
273
312
|
}
|
|
274
313
|
|
|
275
314
|
.video-container {
|
|
276
315
|
position: relative;
|
|
316
|
+
line-height: 0;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.video-player-theme-container.is-fullscreen {
|
|
320
|
+
width: 100vw;
|
|
321
|
+
height: 100vh;
|
|
322
|
+
object-fit: cover;
|
|
277
323
|
}
|
|
324
|
+
.video-player-theme-container.is-fullscreen .hls-player {
|
|
325
|
+
width: 100vw;
|
|
326
|
+
height: 100vh;
|
|
327
|
+
object-fit: cover;
|
|
328
|
+
}
|
|
278
329
|
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="transcript-container" ref="subtitlesContainer">
|
|
2
|
+
<div class="transcript-container" ref="subtitlesContainer" v-if="showTranscriptBlock">
|
|
3
3
|
<div class="transcript-toggle">
|
|
4
4
|
<button data-headlessui-state="open" @click="toggleTranscript()">
|
|
5
5
|
<div class="icon">
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
:link="link"
|
|
5
5
|
:progress="progress"
|
|
6
6
|
:isMuted="isMuted"
|
|
7
|
+
:autoplay="autoplay"
|
|
7
8
|
:isControls="isControls"
|
|
8
9
|
:onVideoEnd="onVideoEnd"
|
|
9
10
|
:isFullscreen="isFullscreen"
|
|
@@ -12,14 +13,23 @@
|
|
|
12
13
|
@video-ended="onVideoEnd"
|
|
13
14
|
@video-fullscreen-change="onFullscreenChange"
|
|
14
15
|
@video-fullscreen-action="oVideoFullscreenAction"
|
|
15
|
-
|
|
16
|
+
v-model="videoElement"
|
|
17
|
+
>
|
|
18
|
+
<template v-slot:before-media><slot name="before-media"></slot></template>
|
|
19
|
+
<template v-slot:after-media><slot name="after-media"></slot></template>
|
|
20
|
+
<template v-slot:before-transcripts><slot name="before-transcripts"></slot></template>
|
|
21
|
+
<template v-slot:after-transcripts><slot name="after-transcripts"></slot></template>
|
|
22
|
+
</BasePlayer>
|
|
16
23
|
</template>
|
|
17
24
|
|
|
18
25
|
<script setup>
|
|
19
26
|
import BasePlayer from './BasePlayer.vue'
|
|
27
|
+
import { ref } from 'vue'
|
|
20
28
|
|
|
21
29
|
const emit = defineEmits(['pause', 'video-ended', 'video-fullscreen-change'])
|
|
22
30
|
|
|
31
|
+
const videoElement = ref(null);
|
|
32
|
+
|
|
23
33
|
defineProps({
|
|
24
34
|
previewImageLink: {
|
|
25
35
|
type: String,
|
|
@@ -37,6 +47,10 @@ defineProps({
|
|
|
37
47
|
type: Boolean,
|
|
38
48
|
default: false
|
|
39
49
|
},
|
|
50
|
+
autoplay: {
|
|
51
|
+
type: Boolean,
|
|
52
|
+
default: false
|
|
53
|
+
},
|
|
40
54
|
isControls: {
|
|
41
55
|
type: Boolean,
|
|
42
56
|
default: true
|
|
@@ -11,8 +11,15 @@
|
|
|
11
11
|
:link="link"
|
|
12
12
|
:progress="progress"
|
|
13
13
|
:isMuted="isMuted"
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
:autoplay="autoplay"
|
|
15
|
+
v-model="videoElement"
|
|
16
|
+
>
|
|
17
|
+
<template v-slot:before-media><slot name="before-media"></slot></template>
|
|
18
|
+
<template v-slot:after-media><slot name="after-media"></slot></template>
|
|
19
|
+
<template v-slot:before-transcripts><slot name="before-transcripts"></slot></template>
|
|
20
|
+
<template v-slot:after-transcripts><slot name="after-transcripts"></slot></template>
|
|
21
|
+
</VDefaultVideoPlayer>
|
|
22
|
+
|
|
16
23
|
<VPreviewVideoPlayer
|
|
17
24
|
v-else-if="type === 'preview'"
|
|
18
25
|
:previewImageLink="previewImageLink"
|
|
@@ -23,9 +30,12 @@
|
|
|
23
30
|
<script setup>
|
|
24
31
|
import VDefaultVideoPlayer from './VDefaultVideoPlayer.vue'
|
|
25
32
|
import VPreviewVideoPlayer from './VPreviewVideoPlayer.vue'
|
|
33
|
+
import { ref } from 'vue'
|
|
26
34
|
|
|
27
35
|
const emit = defineEmits(['pause', 'video-ended', 'video-fullscreen-change', 'video-fullscreen-action'])
|
|
28
36
|
|
|
37
|
+
const videoElement = ref(null);
|
|
38
|
+
|
|
29
39
|
defineProps({
|
|
30
40
|
previewImageLink: {
|
|
31
41
|
type: String,
|
|
@@ -47,6 +57,10 @@ defineProps({
|
|
|
47
57
|
type: Boolean,
|
|
48
58
|
default: false
|
|
49
59
|
},
|
|
60
|
+
autoplay: {
|
|
61
|
+
type: Boolean,
|
|
62
|
+
default: false
|
|
63
|
+
},
|
|
50
64
|
isControls: {
|
|
51
65
|
type: Boolean,
|
|
52
66
|
default: true
|