@grfzhl/vue-hls-player 1.1.23 → 1.1.24
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
|
@@ -207,6 +207,10 @@ At the moment the following attribute are supported:
|
|
|
207
207
|
```
|
|
208
208
|
|
|
209
209
|
### Last release:
|
|
210
|
+
v1.1.24
|
|
211
|
+
- Add user-initiated language-changed emit.
|
|
212
|
+
- Fix unwanted language re-sync on player init.
|
|
213
|
+
- Improve audio/subtitle switch stability.
|
|
210
214
|
v1.1.23
|
|
211
215
|
- Fix missing property for subtitles in vue definition
|
|
212
216
|
- Clean up code
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
<slot name="between-video-and-transcript"></slot>
|
|
72
72
|
<slot name="before-transcripts"></slot>
|
|
73
73
|
<SubtitleBlock
|
|
74
|
-
:key="currentLang"
|
|
74
|
+
:key="`${currentLang}-${currentSubtitleLang}`"
|
|
75
75
|
ref="transcriptRef"
|
|
76
76
|
:subtitle="currentSubtitle"
|
|
77
77
|
:cursor="videoCursor"
|
|
@@ -167,7 +167,7 @@ const props = defineProps({
|
|
|
167
167
|
}
|
|
168
168
|
})
|
|
169
169
|
|
|
170
|
-
const emit = defineEmits(['pause', 'video-ended', 'video-fullscreen-change', 'pointer-update'])
|
|
170
|
+
const emit = defineEmits(['pause', 'video-ended', 'video-fullscreen-change', 'pointer-update', 'language-changed'])
|
|
171
171
|
const video = ref(null)
|
|
172
172
|
const subtitlesContainer = ref(null)
|
|
173
173
|
const currentSubtitleLang = ref(null)
|
|
@@ -186,6 +186,30 @@ let hls = null
|
|
|
186
186
|
let buttonElement = null
|
|
187
187
|
// --- lang switcher ---
|
|
188
188
|
const currentLang = ref(props.defaultLang || 'en')
|
|
189
|
+
const isUserInitiatedLangChange = ref(false)
|
|
190
|
+
|
|
191
|
+
let initialLoad = true;
|
|
192
|
+
|
|
193
|
+
watch(
|
|
194
|
+
() => props.defaultLang,
|
|
195
|
+
(newLang, oldLang) => {
|
|
196
|
+
if (initialLoad) {
|
|
197
|
+
initialLoad = false;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (newLang && newLang !== oldLang && newLang !== currentLang.value) {
|
|
202
|
+
const hasSub = props.subtitles?.find(s => s.lang === newLang);
|
|
203
|
+
if (hasSub) {
|
|
204
|
+
currentSubtitleLang.value = newLang;
|
|
205
|
+
Array.from(video.value?.textTracks || []).forEach(track => {
|
|
206
|
+
const tLang = (track.language || track.srclang || '').toLowerCase();
|
|
207
|
+
track.mode = tLang === newLang.toLowerCase() ? 'showing' : 'disabled';
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
)
|
|
189
213
|
// --- Remember and restore last subtitle language ---
|
|
190
214
|
async function selectLang(lang) {
|
|
191
215
|
if (!video.value || !hls) return;
|
|
@@ -225,13 +249,17 @@ async function selectLang(lang) {
|
|
|
225
249
|
// Matching subtitles for matchLang
|
|
226
250
|
Array.from(video.value?.textTracks || []).forEach(track => {
|
|
227
251
|
const tLang = (track.language || track.srclang || '').toLowerCase();
|
|
228
|
-
console.log('[LangSwitch] Track', tLang, 'current mode:', track.mode);
|
|
229
252
|
track.mode = tLang === matchLang ? 'showing' : 'disabled';
|
|
230
|
-
console.log('[LangSwitch] Track', tLang, '→ new mode:', track.mode);
|
|
231
253
|
});
|
|
232
254
|
|
|
233
255
|
currentSubtitleLang.value = lang;
|
|
234
|
-
|
|
256
|
+
// Emit ONLY if user initiated the change
|
|
257
|
+
setTimeout(() => {
|
|
258
|
+
if (isUserInitiatedLangChange.value) {
|
|
259
|
+
emit('language-changed', lang);
|
|
260
|
+
isUserInitiatedLangChange.value = false; // Reset flag
|
|
261
|
+
}
|
|
262
|
+
}, 100);
|
|
235
263
|
});
|
|
236
264
|
// Attach HLS
|
|
237
265
|
await hls.loadSource(newSource.file_url);
|
|
@@ -325,18 +353,20 @@ const mutedAttr = computed(() => {
|
|
|
325
353
|
return (props.autoplay || props.isMuted);
|
|
326
354
|
})
|
|
327
355
|
|
|
356
|
+
|
|
328
357
|
const currentSubtitle = computed(() => {
|
|
329
|
-
if(props.subtitles)
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
return subt.lang === "en"
|
|
335
|
-
}
|
|
336
|
-
})
|
|
337
|
-
return current.length ? current[0] : null
|
|
358
|
+
if (!props.subtitles?.length) return null;
|
|
359
|
+
|
|
360
|
+
if (currentSubtitleLang.value) {
|
|
361
|
+
const match = props.subtitles.find(s => s.lang === currentSubtitleLang.value);
|
|
362
|
+
if (match) return match;
|
|
338
363
|
}
|
|
339
|
-
|
|
364
|
+
|
|
365
|
+
// Fallback
|
|
366
|
+
const defaultMatch = props.subtitles.find(s => s.lang === props.defaultLang);
|
|
367
|
+
if (defaultMatch) return defaultMatch;
|
|
368
|
+
|
|
369
|
+
return props.subtitles[0];
|
|
340
370
|
})
|
|
341
371
|
|
|
342
372
|
watch(() => props.autoplay, (a) => {
|
|
@@ -560,15 +590,16 @@ function prepareVideoPlayer(link) {
|
|
|
560
590
|
// Initialize subtitles
|
|
561
591
|
if (props.subtitles?.length > 0) {
|
|
562
592
|
const defaultSub = props.subtitles.find(s => s.lang === props.defaultLang);
|
|
563
|
-
currentSubtitleLang.value = defaultSub ?
|
|
593
|
+
currentSubtitleLang.value = defaultSub ? props.defaultLang : props.subtitles[0].lang;
|
|
564
594
|
Array.from(video.value?.textTracks || []).forEach(track => {
|
|
565
595
|
const tLang = (track.language || track.srclang || '').toLowerCase();
|
|
566
|
-
|
|
567
|
-
track.mode =
|
|
596
|
+
const shouldShow = tLang === currentSubtitleLang.value.toLowerCase();
|
|
597
|
+
track.mode = shouldShow ? 'showing' : 'disabled';
|
|
598
|
+
// console.log('[SubtitleInit] Track found:', tLang, '->', shouldShow ? 'showing' : 'disabled');
|
|
568
599
|
});
|
|
569
600
|
}
|
|
570
601
|
|
|
571
|
-
selectLang(props.defaultLang);
|
|
602
|
+
selectLang(props.defaultLang);
|
|
572
603
|
// HLS attached to <video>
|
|
573
604
|
hls.recoverMediaError();
|
|
574
605
|
|
|
@@ -848,6 +879,7 @@ const mutationObserver = (mutationsList, observer) => {
|
|
|
848
879
|
li.addEventListener('click', () => {
|
|
849
880
|
audioCol.querySelectorAll('li').forEach(el => el.classList.remove('active'));
|
|
850
881
|
li.classList.add('active');
|
|
882
|
+
isUserInitiatedLangChange.value = true;
|
|
851
883
|
selectLang(src.lang);
|
|
852
884
|
menu.style.display = 'none';
|
|
853
885
|
});
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
@pause="pause"
|
|
18
18
|
@video-ended="onVideoEnd"
|
|
19
19
|
@video-fullscreen-change="onFullscreenChange"
|
|
20
|
+
@language-changed="onLanguageChanged"
|
|
20
21
|
v-model="videoElement"
|
|
21
22
|
ref="childRef"
|
|
22
23
|
>
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
import BasePlayer from './BasePlayer.vue'
|
|
33
34
|
import { ref, toRef } from 'vue'
|
|
34
35
|
|
|
35
|
-
const emit = defineEmits(['pause', 'video-ended', 'video-fullscreen-change'])
|
|
36
|
+
const emit = defineEmits(['pause', 'video-ended', 'video-fullscreen-change', 'language-changed'])
|
|
36
37
|
|
|
37
38
|
const videoElement = ref(null);
|
|
38
39
|
const childRef = ref(null)
|
|
@@ -114,6 +115,9 @@ function onFullscreenChange(data) {
|
|
|
114
115
|
emit('video-fullscreen-change', data);
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
function onLanguageChanged(data) {
|
|
119
|
+
emit('language-changed', data);
|
|
120
|
+
}
|
|
117
121
|
function startFullscreen() {
|
|
118
122
|
childRef.value.startFullscreen();
|
|
119
123
|
}
|