forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {type ImagePickerAsset} from 'expo-image-picker'
2
3import {VIDEO_MAX_SIZE} from '#/lib/constants'
4import {VideoTooLargeError} from '#/lib/media/video/errors'
5import {type CompressedVideo} from './types'
6
7// doesn't actually compress, converts to ArrayBuffer
8export async function compressVideo(
9 asset: ImagePickerAsset,
10 _opts?: {
11 signal?: AbortSignal
12 onProgress?: (progress: number) => void
13 },
14): Promise<CompressedVideo> {
15 const {mimeType, base64} = parseDataUrl(asset.uri)
16 const blob = base64ToBlob(base64, mimeType)
17 const uri = URL.createObjectURL(blob)
18
19 if (blob.size > VIDEO_MAX_SIZE) {
20 throw new VideoTooLargeError()
21 }
22
23 return {
24 size: blob.size,
25 uri,
26 bytes: await blob.arrayBuffer(),
27 mimeType,
28 }
29}
30
31function parseDataUrl(dataUrl: string) {
32 const [mimeType, base64] = dataUrl.slice('data:'.length).split(';base64,')
33 if (!mimeType || !base64) {
34 throw new Error('Invalid data URL')
35 }
36 return {mimeType, base64}
37}
38
39function base64ToBlob(base64: string, mimeType: string) {
40 const byteCharacters = atob(base64)
41 const byteArrays = []
42
43 for (let offset = 0; offset < byteCharacters.length; offset += 512) {
44 const slice = byteCharacters.slice(offset, offset + 512)
45 const byteNumbers = new Array(slice.length)
46
47 for (let i = 0; i < slice.length; i++) {
48 byteNumbers[i] = slice.charCodeAt(i)
49 }
50
51 const byteArray = new Uint8Array(byteNumbers)
52 byteArrays.push(byteArray)
53 }
54
55 return new Blob(byteArrays, {type: mimeType})
56}