@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
- console.log('[LangSwitch] currentSubtitleLang set to', currentSubtitleLang.value);
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
- const current = props.subtitles.filter((subt) => {
331
- if(currentSubtitleLang.value) {
332
- return subt.lang === currentSubtitleLang.value
333
- } else {
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
- return null
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 ? defaultSub.lang : props.subtitles[0].lang;
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
- console.log('[SubtitleInit] Track found:', tLang, '->', tLang === currentSubtitleLang.value.toLowerCase() ? 'showing' : 'disabled');
567
- track.mode = tLang === currentSubtitleLang.value.toLowerCase() ? 'showing' : 'disabled';
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
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@grfzhl/vue-hls-player",
3
3
  "private": false,
4
- "version": "1.1.23",
4
+ "version": "1.1.24",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist"