@grfzhl/vue-hls-player 1.0.7 → 1.0.8
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,21 @@ 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.8
|
|
187
|
+
- Add slots to inject own elements nearby video element
|
|
188
|
+
- Add prop for autoplay video
|
|
189
|
+
|
|
176
190
|
v1.0.7
|
|
177
191
|
- Add function to handle own logic for fullscreen
|
|
178
192
|
|
|
@@ -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">
|
|
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,17 @@ 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
|
-
checkFullscreen();
|
|
163
194
|
emit('video-fullscreen-change', document.fullscreenElement)
|
|
164
195
|
};
|
|
165
196
|
|
|
@@ -194,25 +225,31 @@ function prepareVideoPlayer() {
|
|
|
194
225
|
track.addEventListener("cuechange", () => {
|
|
195
226
|
const activeCues = track.activeCues;
|
|
196
227
|
currentSubtitleLang.value = track.language
|
|
197
|
-
if
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
228
|
+
if(subtitlesContainer.value) {
|
|
229
|
+
if (activeCues && activeCues.length > 0) {
|
|
230
|
+
subtitlesContainer.value.textContent = activeCues[0].text
|
|
231
|
+
subtitlesContainer.value.style.display = "block";
|
|
232
|
+
} else {
|
|
233
|
+
subtitlesContainer.value.style.display = "none";
|
|
234
|
+
}
|
|
202
235
|
}
|
|
203
236
|
});
|
|
204
237
|
if (track.mode !== previousModes[index]) {
|
|
205
238
|
if (track.mode === "showing") {
|
|
206
239
|
const activeCues = track.activeCues;
|
|
207
240
|
currentSubtitleLang.value = track.language
|
|
208
|
-
if
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
241
|
+
if(subtitlesContainer.value) {
|
|
242
|
+
if (activeCues && activeCues.length > 0) {
|
|
243
|
+
subtitlesContainer.value.style.display = "block";
|
|
244
|
+
subtitlesContainer.value.textContent = activeCues[0].text
|
|
245
|
+
} else {
|
|
246
|
+
subtitlesContainer.value.style.display = "none";
|
|
247
|
+
}
|
|
213
248
|
}
|
|
214
249
|
} else {
|
|
215
|
-
subtitlesContainer.value
|
|
250
|
+
if(subtitlesContainer.value) {
|
|
251
|
+
subtitlesContainer.value.style.display = "none";
|
|
252
|
+
}
|
|
216
253
|
}
|
|
217
254
|
previousModes[index] = track.mode;
|
|
218
255
|
}
|
|
@@ -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
|