get video restoration working on native

This commit is contained in:
Samuel Newman
2026-01-28 19:13:49 +02:00
parent b36a2e33d3
commit 67ecda2542
3 changed files with 50 additions and 13 deletions
+21 -10
View File
@@ -42,6 +42,7 @@ import Animated, {
ZoomOut,
} from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import * as FileSystem from 'expo-file-system'
import {type ImagePickerAsset} from 'expo-image-picker'
import {
AppBskyUnspeccedDefs,
@@ -320,7 +321,7 @@ export const ComposePost = ({
onInitVideo()
}, [onInitVideo])
const clearVideo = React.useCallback(
const clearVideo = useCallback(
(postId: string) => {
composerDispatch({
type: 'update_post',
@@ -333,7 +334,7 @@ export const ComposePost = ({
[composerDispatch],
)
const restoreVideo = React.useCallback(
const restoreVideo = useCallback(
async (postId: string, videoInfo: RestoredVideo) => {
try {
logger.debug('restoring video from draft', {
@@ -354,15 +355,25 @@ export const ComposePost = ({
})
asset = await getVideoMetadata(file)
} else {
// Native: Get video metadata using react-native-compressor
const {getVideoMetaData} = require('react-native-compressor')
const metadata = await getVideoMetaData(videoInfo.uri)
asset = {
uri: videoInfo.uri,
mimeType: videoInfo.mimeType,
width: metadata.width,
height: metadata.height,
let uri = videoInfo.uri
if (IS_ANDROID) {
// Android: expo-file-system double-encodes filenames with special chars.
// The file exists, but react-native-compressor's MediaMetadataRetriever
// can't handle the double-encoded URI. Copy to a temp file with a simple name.
const sourceFile = new FileSystem.File(videoInfo.uri)
const tempFileName = `draft-video-${Date.now()}.${mimeToExt(videoInfo.mimeType)}`
const tempFile = new FileSystem.File(
FileSystem.Paths.cache,
tempFileName,
)
sourceFile.copy(tempFile)
logger.debug('restoreVideo: copied to temp file', {
source: videoInfo.uri,
temp: tempFile.uri,
})
uri = tempFile.uri
}
asset = await getVideoMetadata(uri)
}
// Start video processing using existing flow
+22 -2
View File
@@ -1,3 +1,4 @@
import {getVideoMetaData} from 'react-native-compressor'
import {
type ImagePickerAsset,
launchImageLibraryAsync,
@@ -5,6 +6,7 @@ import {
} from 'expo-image-picker'
import {VIDEO_MAX_DURATION_MS} from '#/lib/constants'
import {extToMime} from '#/lib/media/video/util'
export async function pickVideo() {
return await launchImageLibraryAsync({
@@ -18,6 +20,24 @@ export async function pickVideo() {
})
}
export const getVideoMetadata = (_file: File): Promise<ImagePickerAsset> => {
throw new Error('getVideoMetadata is web only')
/**
* Gets video metadata from a file or uri, depending on the platform
*
* @param file File on web, uri on native
*/
export async function getVideoMetadata(
file: File | string,
): Promise<ImagePickerAsset> {
if (typeof file !== 'string')
throw new Error(
'getVideoMetadata was passed a File, when on native it should be a uri',
)
const metadata = await getVideoMetaData(file)
return {
uri: file,
mimeType: extToMime(metadata.extension),
width: metadata.width,
height: metadata.height,
duration: metadata.duration,
}
}
@@ -39,7 +39,13 @@ export async function pickVideo(): Promise<ImagePickerResult> {
// lets us use the ImagePickerAsset type, which the rest of the code expects.
// We should unwind this and just pass the ArrayBuffer/objectUrl through the system
// instead of a string -sfn
export const getVideoMetadata = (file: File): Promise<ImagePickerAsset> => {
export function getVideoMetadata(
file: File | string,
): Promise<ImagePickerAsset> {
if (typeof file === 'string')
throw new Error(
'getVideoMetadata was passed a uri, when on web it should be a File',
)
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => {