8484<template >
8585 <div v-if =" infoLoaded" >
8686 <div style =" position : relative ;" >
87- <video class =" player" ref =" video" :poster =" uri(thumbnail)" playsinline controls
88- crossorigin = " anonymous " preload =" auto" autoplay >
87+ <video class =" player" ref =" video" :poster =" uri(thumbnail)" playsinline controls crossorigin = " anonymous "
88+ preload =" auto" autoplay >
8989 <source v-for =" source in sources" :key =" source.src" :src =" source.src" @error =" source.onerror"
9090 :type =" source.type" />
9191 <track v-for =" (track, i) in tracks" :key =" track.file" :kind =" track.kind" :label =" track.label"
@@ -416,6 +416,68 @@ const captureFirstFramePoster = async (el: HTMLVideoElement): Promise<void> => {
416416 applyMediaSessionMetadata ()
417417}
418418
419+ const restoreDefaultTextTrack = async () => {
420+ const el = video .value
421+ if (! el ) {
422+ return
423+ }
424+
425+ try {
426+ const tracksList = el .textTracks
427+ if (! tracksList || 0 === tracksList .length ) {
428+ return
429+ }
430+
431+ // Check if first track has cues - if not, we need to reload tracks
432+ const firstTrack = tracksList [0 ] as TextTrack | undefined
433+ const needsReload = firstTrack && (! firstTrack .cues || firstTrack .cues .length === 0 )
434+
435+ if (needsReload ) {
436+ const trackElements = el .querySelectorAll (' track' )
437+
438+ trackElements .forEach ((trackEl , idx ) => {
439+ const parent = trackEl .parentNode
440+ const clone = trackEl .cloneNode (true ) as HTMLTrackElement
441+ trackEl .remove ()
442+ setTimeout (() => {
443+ if (parent ) {
444+ parent .appendChild (clone )
445+ // Set mode after cues load
446+ clone .addEventListener (' load' , () => {
447+ const trackObj = clone .track
448+ if (trackObj ) {
449+ trackObj .mode = idx === 0 ? ' showing' : ' disabled'
450+ }
451+ }, { once: true })
452+ }
453+ }, idx * 10 )
454+ })
455+
456+ return
457+ }
458+
459+ for (let i = 0 ; i < tracksList .length ; i += 1 ) {
460+ const track = tracksList [i ] as TextTrack | undefined
461+ if (track ) {
462+ track .mode = ' disabled'
463+ }
464+ }
465+
466+ await new Promise (resolve => setTimeout (resolve , 50 ))
467+
468+ for (let i = 0 ; i < tracksList .length ; i += 1 ) {
469+ const track = tracksList [i ] as TextTrack | undefined
470+ if (! track ) {
471+ continue
472+ }
473+ const newMode = 0 === i ? ' showing' : ' disabled'
474+ track .mode = newMode
475+ }
476+ } catch (error ) {
477+ console .warn (' Failed to restore subtitle track state' , error )
478+ }
479+ }
480+
419481onMounted (async () => {
420482 disableOpacity ()
421483 const req = await request (makeDownload (config , props .item , ' api/file/info' ))
@@ -452,15 +514,15 @@ onMounted(async () => {
452514
453515 if (isApple ) {
454516 const allowedCodec = response .mimetype && response .mimetype .includes (' video/mp4' )
455- const src = makeDownload (config , props .item , allowedCodec ? ' api/download' : ' m3u8' )
517+ const src = makeDownload (config , props .item , allowedCodec ? ' api/download' : ' m3u8' , allowedCodec ? false : true )
456518 sources .value .push ({
457519 src ,
458520 type: allowedCodec ? response .mimetype : ' application/x-mpegURL' ,
459521 onerror : (err : Event ) => src_error (err ),
460522 })
461523 usingHls .value = ! allowedCodec
462524 } else {
463- const src = makeDownload (config , props .item , ' api/download' )
525+ const src = makeDownload (config , props .item , ' api/download' , false )
464526 sources .value .push ({ src , type: response .mimetype , onerror : (err : Event ) => src_error (err ), })
465527 usingHls .value = false
466528 }
@@ -597,6 +659,7 @@ const prepareVideoPlayer = () => {
597659
598660 video .value .volume = volume .value
599661 video .value .addEventListener (' volumechange' , volume_change_handler )
662+ restoreDefaultTextTrack ()
600663
601664 if (hasVideoStream .value ) {
602665 if (' requestVideoFrameCallback' in video .value ) {
@@ -623,7 +686,7 @@ const src_error = async (e: any) => {
623686 }
624687
625688 console .warn (' Source failed to load, attempting HLS fallback via hls.js...' , e )
626- attach_hls (makeDownload (config , props .item , ' m3u8' ))
689+ attach_hls (makeDownload (config , props .item , ' m3u8' , true ))
627690}
628691
629692const attach_hls = (link : string ) => {
@@ -640,6 +703,15 @@ const attach_hls = (link: string) => {
640703 })
641704
642705 hls .on (Hls .Events .MANIFEST_PARSED , () => applyMediaSessionMetadata ())
706+ hls .on (Hls .Events .MANIFEST_PARSED , async () => {
707+ await new Promise (resolve => setTimeout (resolve , 100 ))
708+ await restoreDefaultTextTrack ()
709+ })
710+
711+ hls .on (Hls .Events .MEDIA_ATTACHED , async () => {
712+ await new Promise (resolve => setTimeout (resolve , 200 ))
713+ await restoreDefaultTextTrack ()
714+ })
643715
644716 hls .on (Hls .Events .LEVEL_LOADED , () => {
645717 if (video .value ) {
@@ -667,6 +739,6 @@ const forceSwitchToHls = () => {
667739 return
668740 }
669741
670- src_error ( new Event ( ' forceSwitch ' ))
742+ attach_hls ( makeDownload ( config , props . item , ' m3u8 ' , true ))
671743}
672744 </script >
0 commit comments