Remove multipart upload and 10-minute video feature gates (#11528)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-08-26 00:38:33 +03:00
committed by GitHub
parent 5118748cb1
commit a9c502c8cf
13 changed files with 40 additions and 356 deletions
+6 -40
View File
@@ -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,17 +269,10 @@ 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()
const queryClient = useQueryClient()
const currentDid = currentAccount!.did
/*
* The host the video service-auth token is minted for. This is the same value
* that seeds the session's PDS routing, so the audience always matches the host
@@ -451,7 +443,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 +451,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,
},
},
@@ -483,23 +473,12 @@ export const ComposePost = ({
},
pdsClient,
currentDispatchUrl,
currentDid,
abortController.signal,
i18n,
telemetry,
)
},
[
l,
i18n,
pdsClient,
currentDispatchUrl,
currentDid,
composerDispatch,
ax.metric,
videoMaxDurationMs,
allow10MinuteVideos,
],
[l, i18n, pdsClient, currentDispatchUrl, composerDispatch, ax.metric],
)
const onInitVideo = useNonReactiveCallback(() => {
@@ -596,7 +575,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 +583,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,
},
},
@@ -667,7 +644,6 @@ export const ComposePost = ({
},
pdsClient,
currentDispatchUrl,
currentDid,
abortController.signal,
i18n,
telemetry,
@@ -679,17 +655,7 @@ export const ComposePost = ({
})
}
},
[
l,
i18n,
pdsClient,
currentDispatchUrl,
currentDid,
composerDispatch,
ax.metric,
videoMaxDurationMs,
allow10MinuteVideos,
],
[l, i18n, pdsClient, currentDispatchUrl, composerDispatch, ax.metric],
)
const handleSelectDraft = useCallback(
+9 -23
View File
@@ -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(() => {
+3 -9
View File
@@ -6,11 +6,8 @@ import {msg} from '@lingui/core/macro'
import {AbortError} from '#/lib/async/cancelable'
import {VIDEO_MAX_SIZE_MB} from '#/lib/constants'
import {compressVideo} from '#/lib/media/video/compress'
import {
ServerError,
UploadLimitError,
VideoTooLargeError,
} from '#/lib/media/video/errors'
import {UploadLimitError, VideoTooLargeError} from '#/lib/media/video/errors'
import {MultipartUploadError} from '#/lib/media/video/multipart/api'
import {type VideoTelemetry} from '#/lib/media/video/telemetry'
import {type CompressedVideo} from '#/lib/media/video/types'
import {uploadVideo} from '#/lib/media/video/upload'
@@ -294,7 +291,6 @@ export async function processVideo(
dispatch: (action: VideoAction) => void,
client: Client,
dispatchUrl: string | URL,
did: string,
signal: AbortSignal,
i18n: I18n,
telemetry: VideoTelemetry,
@@ -344,10 +340,8 @@ export async function processVideo(
video,
client,
dispatchUrl,
did,
signal,
i18n,
onTransport: telemetry.uploadTransport,
setProgress: p => {
dispatch({type: 'update_progress', progress: p, signal})
},
@@ -513,7 +507,7 @@ function getUploadErrorMessage(e: unknown, i18n: I18n): string | null {
if (e instanceof AbortError) {
return null
}
if (e instanceof ServerError || e instanceof UploadLimitError) {
if (e instanceof MultipartUploadError || e instanceof UploadLimitError) {
// https://github.com/bluesky-social/tango/blob/lumi/lumi/worker/permissions.go#L77
switch (e.message) {
case 'User is not allowed to upload videos':