diff --git a/src/analytics/features/index.ts b/src/analytics/features/index.ts index 7b1d9cbbaf..6cbafeaa22 100644 --- a/src/analytics/features/index.ts +++ b/src/analytics/features/index.ts @@ -77,22 +77,6 @@ export function getFeatures() { export function getFeatureDescription(feature: Features, i18n: I18n) { switch (feature) { - case Features.VideoAllow10MinuteEnable: - return { - key: feature, - name: i18n._( - msg({ - message: 'Longer videos', - comment: 'Name for a feature flag (longer videos)', - }), - ), - description: i18n._( - msg({ - message: 'Enable 10-minute video uploads.', - comment: 'Description of a feature flag (10-minute video uploads)', - }), - ), - } case Features.CanonicalPostNumberingEnable: return { key: feature, diff --git a/src/analytics/features/types.ts b/src/analytics/features/types.ts index d86ad83ff7..70c8625e12 100644 --- a/src/analytics/features/types.ts +++ b/src/analytics/features/types.ts @@ -19,8 +19,6 @@ export enum Features { PostThreadKnownLikersEnable = 'post_thread:known_likers:enable', PostThreadKnownLikersFetchEnable = 'post_thread:known_likers:fetch:enable', CustomLogoJapanEnable = 'custom_logo:japan:enable', - VideoAllow10MinuteEnable = 'video:allow-10-minute:enable', - VideoMultipartUploadEnable = 'video:multipart_upload:enable', SearchStarterPacksV2Enable = 'search_starter_packs_v2:enable', FollowSortEnable = 'follow_sort:enable', OnboardingInterestsRequiredEnable = 'onboarding:interests:required:enable', diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 514312aeb9..d12c5a2aed 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -194,8 +194,7 @@ export const MAX_LABELERS = 20 export const VIDEO_SERVICE = 'https://video.bsky.app' export const VIDEO_SERVICE_DID = 'did:web:video.bsky.app' -export const VIDEO_MAX_DURATION_MS = 3 * 60 * 1000 // 3 minutes in milliseconds -export const VIDEO_10_MINUTE_MAX_DURATION_MS = 10 * 60 * 1000 +export const VIDEO_MAX_DURATION_MS = 10 * 60 * 1000 // 10 minutes in milliseconds /** * Maximum size of a video in megabytes, _not_ mebibytes. Backend uses * ISO megabytes. diff --git a/src/lib/media/video/telemetry.ts b/src/lib/media/video/telemetry.ts index 188abb6d80..717db0e0c7 100644 --- a/src/lib/media/video/telemetry.ts +++ b/src/lib/media/video/telemetry.ts @@ -72,7 +72,7 @@ export function createVideoTelemetry({ let phaseStartedAt = startedAt let jobId: string | undefined let uploadBytes: number | undefined - let uploadTransport: VideoUploadTransport = 'legacy' + let uploadTransport: VideoUploadTransport = 'multipart' let txnEnded = false let abortBound = true diff --git a/src/lib/media/video/types.ts b/src/lib/media/video/types.ts index f929c06664..607b0b2acc 100644 --- a/src/lib/media/video/types.ts +++ b/src/lib/media/video/types.ts @@ -5,7 +5,7 @@ export type VideoCompressSkipReason = 'gif' | 'below-byte-threshold' | 'no-webcodecs' | 'compress-error-fallback' -export type VideoUploadTransport = 'multipart' | 'legacy' | 'legacy-fallback' +export type VideoUploadTransport = 'multipart' | 'legacy-fallback' export type CompressedVideo = { uri: string diff --git a/src/lib/media/video/upload.ts b/src/lib/media/video/upload.ts index d5a2af5146..3a98c77647 100644 --- a/src/lib/media/video/upload.ts +++ b/src/lib/media/video/upload.ts @@ -10,7 +10,6 @@ import { type CompressedVideo, type VideoUploadTransport, } from '#/lib/media/video/types' -import {Features, features} from '#/analytics/features' import {type app} from '#/lexicons' import {MultipartFallbackError, uploadVideoMultipart} from './multipart/upload' import { @@ -45,23 +44,19 @@ export async function uploadVideo({ } await getVideoUploadLimits(client, i18n) - if (features.isOn(Features.VideoMultipartUploadEnable)) { - try { - return await uploadVideoMultipart({ - video, - client, - dispatchUrl, - setProgress, - signal, - onStarted: () => onTransport?.('multipart'), - }) - } catch (err) { - if (!(err instanceof MultipartFallbackError)) throw err - onTransport?.('legacy-fallback') - setProgress(0) - } - } else { - onTransport?.('legacy') + try { + return await uploadVideoMultipart({ + video, + client, + dispatchUrl, + setProgress, + signal, + onStarted: () => onTransport?.('multipart'), + }) + } catch (err) { + if (!(err instanceof MultipartFallbackError)) throw err + onTransport?.('legacy-fallback') + setProgress(0) } const uri = createVideoEndpointUrl('/xrpc/app.bsky.video.uploadVideo', { diff --git a/src/lib/media/video/upload.web.ts b/src/lib/media/video/upload.web.ts index b0fcbaaf09..97048271a4 100644 --- a/src/lib/media/video/upload.web.ts +++ b/src/lib/media/video/upload.web.ts @@ -9,7 +9,6 @@ import { type CompressedVideo, type VideoUploadTransport, } from '#/lib/media/video/types' -import {Features, features} from '#/analytics/features' import {type app} from '#/lexicons' import {MultipartFallbackError, uploadVideoMultipart} from './multipart/upload' import { @@ -44,23 +43,19 @@ export async function uploadVideo({ } await getVideoUploadLimits(client, i18n) - if (features.isOn(Features.VideoMultipartUploadEnable)) { - try { - return await uploadVideoMultipart({ - video, - client, - dispatchUrl, - setProgress, - signal, - onStarted: () => onTransport?.('multipart'), - }) - } catch (err) { - if (!(err instanceof MultipartFallbackError)) throw err - onTransport?.('legacy-fallback') - setProgress(0) - } - } else { - onTransport?.('legacy') + try { + return await uploadVideoMultipart({ + video, + client, + dispatchUrl, + setProgress, + signal, + onStarted: () => onTransport?.('multipart'), + }) + } catch (err) { + if (!(err instanceof MultipartFallbackError)) throw err + onTransport?.('legacy-fallback') + setProgress(0) } const uri = createVideoEndpointUrl('/xrpc/app.bsky.video.uploadVideo', { diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 6b4bf9a63f..6af000c3ff 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -62,7 +62,6 @@ import { MAX_GRAPHEME_LENGTH, SUPPORTED_MIME_TYPES, type SupportedMimeTypes, - VIDEO_10_MINUTE_MAX_DURATION_MS, VIDEO_MAX_DURATION_MS, } from '#/lib/constants' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' @@ -270,12 +269,6 @@ export const ComposePost = ({ const {currentAccount} = useSession() const t = useTheme() const ax = useAnalytics() - const allow10MinuteVideos = ax.features.enabled( - ax.features.VideoAllow10MinuteEnable, - ) - const videoMaxDurationMs = allow10MinuteVideos - ? VIDEO_10_MINUTE_MAX_DURATION_MS - : VIDEO_MAX_DURATION_MS const client = useAppviewClient() const chatClient = useChatClient() const pdsClient = usePdsClient() @@ -451,7 +444,7 @@ export const ComposePost = ({ * Fail early on duration so we don't spend time compressing a video the * server would reject anyway. */ - if (asset.duration != null && asset.duration > videoMaxDurationMs) { + if (asset.duration != null && asset.duration > VIDEO_MAX_DURATION_MS) { composerDispatch({ type: 'update_post', postId: postId, @@ -459,9 +452,7 @@ export const ComposePost = ({ type: 'embed_update_video', videoAction: { type: 'to_error', - error: allow10MinuteVideos - ? l`Videos must be 10 minutes or less.` - : l`Videos must be less than 3 minutes long.`, + error: l`Videos must be 10 minutes or less.`, signal: abortController.signal, }, }, @@ -497,8 +488,6 @@ export const ComposePost = ({ currentDid, composerDispatch, ax.metric, - videoMaxDurationMs, - allow10MinuteVideos, ], ) @@ -596,7 +585,7 @@ export const ComposePost = ({ }, }) - if (asset.duration != null && asset.duration > videoMaxDurationMs) { + if (asset.duration != null && asset.duration > VIDEO_MAX_DURATION_MS) { composerDispatch({ type: 'update_post', postId, @@ -604,9 +593,7 @@ export const ComposePost = ({ type: 'embed_update_video', videoAction: { type: 'to_error', - error: allow10MinuteVideos - ? l`Videos must be 10 minutes or less.` - : l`Videos must be less than 3 minutes long.`, + error: l`Videos must be 10 minutes or less.`, signal: abortController.signal, }, }, @@ -687,8 +674,6 @@ export const ComposePost = ({ currentDid, composerDispatch, ax.metric, - videoMaxDurationMs, - allow10MinuteVideos, ], ) diff --git a/src/view/com/composer/SelectMediaButton.tsx b/src/view/com/composer/SelectMediaButton.tsx index 15872a4733..83c371d336 100644 --- a/src/view/com/composer/SelectMediaButton.tsx +++ b/src/view/com/composer/SelectMediaButton.tsx @@ -6,7 +6,6 @@ import {msg, plural} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import { - VIDEO_10_MINUTE_MAX_DURATION_MS, VIDEO_MAX_DURATION_MS, VIDEO_MAX_SIZE, VIDEO_MAX_SIZE_MB, @@ -23,7 +22,6 @@ import {Button} from '#/components/Button' import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper' import {Image_Stroke2_Corner2_Rounded as ImageIcon} from '#/components/icons/Image' import * as toast from '#/components/Toast' -import {useAnalytics} from '#/analytics' import {IS_NATIVE, IS_WEB} from '#/env' import {isAnimatedGif} from './videos/isAnimatedGif' import {hasWebCodecs} from './videos/metadata' @@ -400,13 +398,6 @@ export function SelectMediaButton({ autoOpen, }: SelectMediaButtonProps) { const {_} = useLingui() - const ax = useAnalytics() - const allow10MinuteVideos = ax.features.enabled( - ax.features.VideoAllow10MinuteEnable, - ) - const videoMaxDurationMs = allow10MinuteVideos - ? VIDEO_10_MINUTE_MAX_DURATION_MS - : VIDEO_MAX_DURATION_MS const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() const {requestVideoAccessIfNeeded} = useVideoLibraryPermission() const sheetWrapper = useSheetWrapper() @@ -426,7 +417,7 @@ export function SelectMediaButton({ } = await processImagePickerAssets(rawAssets, { selectionCountRemaining, allowedAssetTypes, - videoMaxDurationMs, + videoMaxDurationMs: VIDEO_MAX_DURATION_MS, }) /* @@ -451,9 +442,9 @@ export function SelectMediaButton({ [SelectedAssetError.MaxVideos]: _( msg`You can only select one video at a time.`, ), - [SelectedAssetError.VideoTooLong]: allow10MinuteVideos - ? _(msg`Videos must be 10 minutes or less.`) - : _(msg`Videos must be less than 3 minutes long.`), + [SelectedAssetError.VideoTooLong]: _( + msg`Videos must be 10 minutes or less.`, + ), [SelectedAssetError.MaxGIFs]: _( msg`You can only select one GIF at a time.`, ), @@ -473,14 +464,7 @@ export function SelectMediaButton({ errors, }) }, - [ - _, - onSelectAssets, - selectionCountRemaining, - allowedAssetTypes, - videoMaxDurationMs, - allow10MinuteVideos, - ], + [_, onSelectAssets, selectionCountRemaining, allowedAssetTypes], ) const onPressSelectMedia = useCallback(async () => { @@ -503,7 +487,10 @@ export function SelectMediaButton({ } const {assets, canceled} = await sheetWrapper( - openUnifiedPicker({selectionCountRemaining, videoMaxDurationMs}), + openUnifiedPicker({ + selectionCountRemaining, + videoMaxDurationMs: VIDEO_MAX_DURATION_MS, + }), ) if (canceled) return @@ -516,7 +503,6 @@ export function SelectMediaButton({ sheetWrapper, processSelectedAssets, selectionCountRemaining, - videoMaxDurationMs, ]) useEffect(() => {