Add video playback observability to Sentry (#11111)

This commit is contained in:
Spence Pope
2026-07-14 10:38:39 -04:00
committed by GitHub
parent 30e862bc26
commit a5adb0e97a
6 changed files with 324 additions and 6 deletions
+55 -1
View File
@@ -47,6 +47,10 @@ import {HITSLOP_20} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {
createPlaybackTelemetry,
type PlaybackTelemetry,
} from '#/lib/media/video/playbackTelemetry'
import {
type CommonNavigatorParams,
type NavigationProp,
@@ -551,7 +555,7 @@ let VideoItem = ({
<>
<VideoItemPlaceholder embed={embed} />
{shouldRenderVideo && player && (
<VideoItemInner player={player} embed={embed} />
<VideoItemInner player={player} embed={embed} active={active} />
)}
{moderation && (
<Overlay
@@ -575,13 +579,17 @@ VideoItem = memo(VideoItem)
function VideoItemInner({
player,
embed,
active,
}: {
player: VideoPlayer
embed: AppBskyEmbedVideo.View
active: boolean
}) {
const {bottom} = useSafeAreaInsets()
const [isReady, setIsReady] = useState(!IS_ANDROID)
usePlaybackTelemetry({player, active})
useEventListener(player, 'timeUpdate', evt => {
if (IS_ANDROID && !isReady && evt.currentTime >= 0.05) {
setIsReady(true)
@@ -609,6 +617,52 @@ function VideoItemInner({
)
}
/**
* Opens a Sentry playback span while this item is the active video. Adjacent
* players are preloaded by updateVideoState, so record whether the player was
* already ready at activation to separate load time from swipe latency.
*/
function usePlaybackTelemetry({
player,
active,
}: {
player: VideoPlayer
active: boolean
}) {
const telemetryRef = useRef<PlaybackTelemetry | null>(null)
useEffect(() => {
if (!active) return
telemetryRef.current ??= createPlaybackTelemetry({
surface: 'immersiveFeed',
presentation: 'video',
})
const telemetry = telemetryRef.current
const preloaded = player.status === 'readyToPlay'
telemetry.activated({preloaded})
if (preloaded) {
telemetry.ready()
}
return () => {
telemetry.deactivated()
}
}, [active, player])
useEventListener(player, 'statusChange', evt => {
if (evt.status === 'readyToPlay') {
telemetryRef.current?.ready()
} else if (evt.status === 'error') {
telemetryRef.current?.error(evt.error?.message ?? 'unknown')
}
})
useEventListener(player, 'playingChange', evt => {
if (evt.isPlaying) {
telemetryRef.current?.playing()
}
})
}
function ModerationOverlay({
embed,
onPressShow,