adjust vtt cue line to avoid occlusion by video controls
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@ export function VideoEmbedInnerWeb({
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
const {hlsRef, loop} = useHLS({
|
const {hlsRef, loop, cueLineRef} = useHLS({
|
||||||
playlist: embed.playlist,
|
playlist: embed.playlist,
|
||||||
setHasSubtitleTrack,
|
setHasSubtitleTrack,
|
||||||
setError,
|
setError,
|
||||||
@@ -90,6 +90,7 @@ export function VideoEmbedInnerWeb({
|
|||||||
hasSubtitleTrack={hasSubtitleTrack}
|
hasSubtitleTrack={hasSubtitleTrack}
|
||||||
isGif={embed.presentation === 'gif'}
|
isGif={embed.presentation === 'gif'}
|
||||||
altText={embed.alt}
|
altText={embed.alt}
|
||||||
|
cueLineRef={cueLineRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</View>
|
</View>
|
||||||
@@ -145,6 +146,7 @@ function useHLS({
|
|||||||
}, [Hls, setHlsLoading])
|
}, [Hls, setHlsLoading])
|
||||||
|
|
||||||
const hlsRef = useRef<HlsTypes.default | undefined>(undefined)
|
const hlsRef = useRef<HlsTypes.default | undefined>(undefined)
|
||||||
|
const cueLineRef = useRef<number>(-2)
|
||||||
const [lowQualityFragments, setLowQualityFragments] = useState<
|
const [lowQualityFragments, setLowQualityFragments] = useState<
|
||||||
HlsTypes.Fragment[]
|
HlsTypes.Fragment[]
|
||||||
>([])
|
>([])
|
||||||
@@ -220,6 +222,14 @@ function useHLS({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
hls.on(Hls.Events.CUES_PARSED, (_event, data) => {
|
||||||
|
if (Array.isArray(data.cues)) {
|
||||||
|
for (const cue of data.cues) {
|
||||||
|
cue.line = cueLineRef.current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
hls.on(Hls.Events.FRAG_BUFFERED, (_event, {frag}) => {
|
hls.on(Hls.Events.FRAG_BUFFERED, (_event, {frag}) => {
|
||||||
if (frag.level === 0) {
|
if (frag.level === 0) {
|
||||||
setLowQualityFragments(prev => [...prev, frag])
|
setLowQualityFragments(prev => [...prev, frag])
|
||||||
@@ -307,5 +317,6 @@ function useHLS({
|
|||||||
return {
|
return {
|
||||||
hlsRef,
|
hlsRef,
|
||||||
loop: !hasLowQualityFragmentAtStart,
|
loop: !hasLowQualityFragmentAtStart,
|
||||||
|
cueLineRef,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export function Controls({
|
|||||||
hasSubtitleTrack,
|
hasSubtitleTrack,
|
||||||
isGif,
|
isGif,
|
||||||
altText,
|
altText,
|
||||||
|
cueLineRef,
|
||||||
}: {
|
}: {
|
||||||
videoRef: React.RefObject<HTMLVideoElement | null>
|
videoRef: React.RefObject<HTMLVideoElement | null>
|
||||||
hlsRef: React.RefObject<Hls | undefined | null>
|
hlsRef: React.RefObject<Hls | undefined | null>
|
||||||
@@ -61,6 +62,7 @@ export function Controls({
|
|||||||
hasSubtitleTrack: boolean
|
hasSubtitleTrack: boolean
|
||||||
isGif: boolean
|
isGif: boolean
|
||||||
altText?: string
|
altText?: string
|
||||||
|
cueLineRef: React.RefObject<number>
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
play,
|
play,
|
||||||
@@ -294,6 +296,27 @@ export function Controls({
|
|||||||
((focused || autoplayDisabled) && !playing) ||
|
((focused || autoplayDisabled) && !playing) ||
|
||||||
(interactingViaKeypress ? hasFocus : hovered)
|
(interactingViaKeypress ? hasFocus : hovered)
|
||||||
|
|
||||||
|
// adjust subtitle cue positioning to avoid occlusion by controls
|
||||||
|
const cueLine = showControls ? -4 : -2
|
||||||
|
useEffect(() => {
|
||||||
|
cueLineRef.current = cueLine
|
||||||
|
const video = videoRef.current
|
||||||
|
if (!video) return
|
||||||
|
for (let i = 0; i < video.textTracks.length; i++) {
|
||||||
|
const track = video.textTracks[i]
|
||||||
|
if (track.cues) {
|
||||||
|
for (let j = 0; j < track.cues.length; j++) {
|
||||||
|
;(track.cues[j] as VTTCue).line = cueLine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// toggle track mode to force the browser to re-render active cues
|
||||||
|
if (track.mode === 'showing') {
|
||||||
|
track.mode = 'hidden'
|
||||||
|
track.mode = 'showing'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [cueLine, videoRef, cueLineRef])
|
||||||
|
|
||||||
if (isGif) {
|
if (isGif) {
|
||||||
return (
|
return (
|
||||||
<GifPresentationControls
|
<GifPresentationControls
|
||||||
|
|||||||
Reference in New Issue
Block a user