From f4b8651708fcefd82828ed602a805b1c2aa158ae Mon Sep 17 00:00:00 2001 From: vineyardbovines Date: Thu, 25 Jun 2026 18:37:15 -0400 Subject: [PATCH] Plug native video probe into upload telemetry funnel Adds video:upload:probed event (uploadId/engine + raw container metadata: bitrate, codec, isHDR, frame rate, rotation, etc.) wired through a new VideoTelemetry.probed method. compressVideo gains an onProbe opt and fires it once after the probe call succeeds, before shouldCompress is evaluated. Renames the native engine label from native:react-native-compressor@1.13.0 to native:expo-bluesky-video-compress@1 so probed events stay paired with the engine that can actually surface this data; web/mediabunny is unchanged and will simply never call telemetry.probed(). --- src/analytics/metrics/types.ts | 19 +++++++++++++++++++ src/lib/media/video/compress.ts | 9 ++++++++- src/lib/media/video/telemetry.ts | 22 +++++++++++++++++++++- src/view/com/composer/state/video.ts | 1 + 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/analytics/metrics/types.ts b/src/analytics/metrics/types.ts index e34feae3a5..acb7587885 100644 --- a/src/analytics/metrics/types.ts +++ b/src/analytics/metrics/types.ts @@ -1352,6 +1352,25 @@ export type Events = { engine: string sourceBytes?: number } + // Native-only. Raw container metadata returned by the new module's probe() + // (bitrate, codec, HDR, frame rate, rotation, etc.). Fires once per upload + // between compressStarted and the compressSkipped/compressCompleted decision. + // The web (mediabunny) and legacy rn-compressor engines do not surface this. + 'video:upload:probed': { + uploadId: string + engine: string + mimeType: string + codec: string + width: number + height: number + duration: number + bitrate: number + fileSize: number + hasAudio: boolean + frameRate: number + rotation: number + isHDR: boolean + } 'video:upload:compressCompleted': { uploadId: string engine: string diff --git a/src/lib/media/video/compress.ts b/src/lib/media/video/compress.ts index 7b14961653..0be4c73954 100644 --- a/src/lib/media/video/compress.ts +++ b/src/lib/media/video/compress.ts @@ -6,7 +6,11 @@ import { VIDEO_MAX_SIZE, } from '#/lib/constants' import {logger} from '#/logger' -import {compress, probe} from '../../../../modules/expo-bluesky-video-compress' +import { + compress, + probe, + type VideoMetadata, +} from '../../../../modules/expo-bluesky-video-compress' import { COMPRESSION_MAX_DIMENSION, COMPRESSION_PASSTHROUGH_BITRATE, @@ -19,6 +23,7 @@ export async function compressVideo( opts?: { signal?: AbortSignal onProgress?: (progress: number) => void + onProbe?: (metadata: VideoMetadata) => void }, ): Promise { if (file.mimeType === 'image/gif') { @@ -49,6 +54,8 @@ export async function compressVideo( } } + opts?.onProbe?.(metadata) + if (!shouldCompress(metadata, isAcceptableFormat)) { return { uri: file.uri, diff --git a/src/lib/media/video/telemetry.ts b/src/lib/media/video/telemetry.ts index 7ef5a874ef..43f179030f 100644 --- a/src/lib/media/video/telemetry.ts +++ b/src/lib/media/video/telemetry.ts @@ -5,6 +5,7 @@ import {nanoid} from 'nanoid/non-secure' import {type VideoCompressSkipReason} from '#/lib/media/video/types' import {Sentry} from '#/logger/sentry/lib' import {type Metrics} from '#/analytics/metrics' +import {type VideoMetadata} from '../../../../modules/expo-bluesky-video-compress' type MetricFn = (event: E, payload: Metrics[E]) => void @@ -14,7 +15,7 @@ type MetricFn = (event: E, payload: Metrics[E]) => void const COMPRESS_ENGINE = Platform.OS === 'web' ? 'web:mediabunny@1.25.3' - : 'native:react-native-compressor@1.13.0' + : 'native:expo-bluesky-video-compress@1' type Phase = 'compress' | 'upload' | 'processing' @@ -28,6 +29,7 @@ export type VideoTelemetry = { readonly engine: string picked: () => void compressStarted: () => void + probed: (metadata: VideoMetadata) => void compressSkipped: (video: { size: number mimeType: string @@ -155,6 +157,24 @@ export function createVideoTelemetry({ }) }, + probed(metadata) { + metric('video:upload:probed', { + uploadId, + engine, + mimeType: metadata.mimeType, + codec: metadata.codec, + width: metadata.width, + height: metadata.height, + duration: metadata.duration, + bitrate: metadata.bitrate, + fileSize: metadata.fileSize, + hasAudio: metadata.hasAudio, + frameRate: metadata.frameRate, + rotation: metadata.rotation, + isHDR: metadata.isHDR, + }) + }, + compressSkipped({size, mimeType, skipReason}) { metric('video:upload:compressSkipped', { uploadId, diff --git a/src/view/com/composer/state/video.ts b/src/view/com/composer/state/video.ts index 6737d1f628..492eac1c5d 100644 --- a/src/view/com/composer/state/video.ts +++ b/src/view/com/composer/state/video.ts @@ -288,6 +288,7 @@ export async function processVideo( dispatch({type: 'update_progress', progress: trunc2dp(num), signal}) }, signal, + onProbe: metadata => telemetry.probed(metadata), }) } catch (e) { const message = getCompressErrorMessage(e, i18n)