diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts index 5607d4865a443..09432f65621b9 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts @@ -21,7 +21,7 @@ import { SessionRecordingUsageType, } from '~/types' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { eventWithTime } from '@rrweb/types' +import { EventType, eventWithTime } from '@rrweb/types' import { Dayjs, dayjs } from 'lib/dayjs' import type { sessionRecordingDataLogicType } from './sessionRecordingDataLogicType' import { chainToElements } from 'lib/utils/elements-chain' @@ -600,6 +600,42 @@ export const sessionRecordingDataLogic = kea([ }, ], + snapshotsInvalid: [ + (s, p) => [s.snapshotsByWindowId, s.fullyLoaded, p.sessionRecordingId], + (snapshotsByWindowId, fullyLoaded, sessionRecordingId): boolean => { + if (!fullyLoaded) { + return false + } + + const windowsHaveFullSnapshot = Object.entries(snapshotsByWindowId).reduce( + (acc, [windowId, events]) => { + acc[`window-id-${windowId}-has-full-snapshot`] = events.some( + (event) => event.type === EventType.FullSnapshot + ) + return acc + }, + {} + ) + const anyWindowMissingFullSnapshot = !Object.values(windowsHaveFullSnapshot).some((x) => x) + const everyWindowMissingFullSnapshot = !Object.values(windowsHaveFullSnapshot).every((x) => x) + + if (everyWindowMissingFullSnapshot) { + // video is definitely unplayable + posthog.capture('recording_has_no_full_snapshot', { + ...windowsHaveFullSnapshot, + sessionId: sessionRecordingId, + }) + } else if (anyWindowMissingFullSnapshot) { + posthog.capture('recording_window_missing_full_snapshot', { + ...windowsHaveFullSnapshot, + sessionId: sessionRecordingId, + }) + } + + return everyWindowMissingFullSnapshot || anyWindowMissingFullSnapshot + }, + ], + bufferedToTime: [ (s) => [s.segments], (segments): number | null => { diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts index 301f1d48e8dfe..c9f2d39d0af6d 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts @@ -104,6 +104,7 @@ export const sessionRecordingPlayerLogic = kea( 'sessionPlayerData', 'sessionPlayerSnapshotDataLoading', 'sessionPlayerMetaDataLoading', + 'snapshotsInvalid', ], playerSettingsLogic, ['speed', 'skipInactivitySetting'], @@ -345,6 +346,7 @@ export const sessionRecordingPlayerLogic = kea( s.isSkippingInactivity, s.snapshotsLoaded, s.sessionPlayerSnapshotDataLoading, + s.snapshotsInvalid, ], ( playingState, @@ -353,8 +355,13 @@ export const sessionRecordingPlayerLogic = kea( isScrubbing, isSkippingInactivity, snapshotsLoaded, - snapshotsLoading + snapshotsLoading, + snapshotsInvalid ) => { + if (snapshotsInvalid) { + return SessionPlayerState.ERROR + } + if (isScrubbing) { // If scrubbing, playingState takes precedence return playingState