Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[Video] throw HLS errors to be caught by error boundary (#5166)

* throw HLS errors to be caught by error boundary

* wording tweak

* do the same on native

* fix type error

authored by

Samuel Newman and committed by
GitHub
428607d9 60b74f7a

+55 -19
+9 -3
src/view/com/util/post-embeds/VideoEmbed.tsx
··· 1 1 import React, {useCallback, useEffect, useId, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {Image} from 'expo-image' 4 - import {VideoPlayerStatus} from 'expo-video' 4 + import {PlayerError, VideoPlayerStatus} from 'expo-video' 5 5 import {AppBskyEmbedVideo} from '@atproto/api' 6 6 import {msg, Trans} from '@lingui/macro' 7 7 import {useLingui} from '@lingui/react' ··· 78 78 (playerStatus === 'waitingToPlayAtSpecifiedRate' || 79 79 playerStatus === 'loading') 80 80 81 + // send error up to error boundary 82 + const [error, setError] = useState<Error | PlayerError | null>(null) 83 + if (error) { 84 + throw error 85 + } 86 + 81 87 useEffect(() => { 82 88 if (isActive) { 83 89 // eslint-disable-next-line @typescript-eslint/no-shadow ··· 92 98 ) 93 99 const statusSub = player.addListener( 94 100 'statusChange', 95 - (status, _oldStatus, error) => { 101 + (status, _oldStatus, playerError) => { 96 102 setPlayerStatus(status) 97 103 if (status === 'error') { 98 - throw error 104 + setError(playerError ?? new Error('Unknown player error')) 99 105 } 100 106 }, 101 107 )
+20 -15
src/view/com/util/post-embeds/VideoEmbed.web.tsx
··· 1 1 import React, {useCallback, useEffect, useRef, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {AppBskyEmbedVideo} from '@atproto/api' 4 - import {Trans} from '@lingui/macro' 4 + import {msg} from '@lingui/macro' 5 + import {useLingui} from '@lingui/react' 5 6 6 7 import {clamp} from '#/lib/numbers' 7 8 import {useGate} from '#/lib/statsig/statsig' 8 9 import { 9 10 HLSUnsupportedError, 10 11 VideoEmbedInnerWeb, 12 + VideoNotFoundError, 11 13 } from '#/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb' 12 14 import {atoms as a} from '#/alf' 13 15 import {ErrorBoundary} from '../ErrorBoundary' ··· 152 154 } 153 155 154 156 function VideoError({error, retry}: {error: unknown; retry: () => void}) { 155 - const isHLS = error instanceof HLSUnsupportedError 157 + const {_} = useLingui() 158 + 159 + let showRetryButton = true 160 + let text = null 161 + 162 + if (error instanceof VideoNotFoundError) { 163 + text = _(msg`Video not found.`) 164 + } else if (error instanceof HLSUnsupportedError) { 165 + showRetryButton = false 166 + text = _( 167 + msg`Your browser does not support the video format. Please try a different browser.`, 168 + ) 169 + } else { 170 + text = _(msg`An error occurred while loading the video. Please try again.`) 171 + } 156 172 157 173 return ( 158 174 <VideoFallback.Container> 159 - <VideoFallback.Text> 160 - {isHLS ? ( 161 - <Trans> 162 - Your browser does not support the video format. Please try a 163 - different browser. 164 - </Trans> 165 - ) : ( 166 - <Trans> 167 - An error occurred while loading the video. Please try again later. 168 - </Trans> 169 - )} 170 - </VideoFallback.Text> 171 - {!isHLS && <VideoFallback.RetryButton onPress={retry} />} 175 + <VideoFallback.Text>{text}</VideoFallback.Text> 176 + {showRetryButton && <VideoFallback.RetryButton onPress={retry} />} 172 177 </VideoFallback.Container> 173 178 ) 174 179 }
+26 -1
src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx
··· 23 23 const [hasSubtitleTrack, setHasSubtitleTrack] = useState(false) 24 24 const figId = useId() 25 25 26 + // send error up to error boundary 27 + const [error, setError] = useState<Error | null>(null) 28 + if (error) { 29 + throw error 30 + } 31 + 26 32 const hlsRef = useRef<Hls | undefined>(undefined) 27 33 28 34 useEffect(() => { ··· 38 44 // initial value, later on it's managed by Controls 39 45 hls.autoLevelCapping = 0 40 46 41 - hls.on(Hls.Events.SUBTITLE_TRACKS_UPDATED, (event, data) => { 47 + hls.on(Hls.Events.SUBTITLE_TRACKS_UPDATED, (_event, data) => { 42 48 if (data.subtitleTracks.length > 0) { 43 49 setHasSubtitleTrack(true) 50 + } 51 + }) 52 + 53 + hls.on(Hls.Events.ERROR, (_event, data) => { 54 + if (data.fatal) { 55 + if ( 56 + data.details === 'manifestLoadError' && 57 + data.response?.code === 404 58 + ) { 59 + setError(new VideoNotFoundError()) 60 + } else { 61 + setError(data.error) 62 + } 44 63 } 45 64 }) 46 65 ··· 104 123 super('HLS is not supported') 105 124 } 106 125 } 126 + 127 + export class VideoNotFoundError extends Error { 128 + constructor() { 129 + super('Video not found') 130 + } 131 + }