Bluesky app fork with some witchin' additions 馃挮
at readme-update 95 lines 2.5 kB view raw
1import {type AppBskyVideoDefs} from '@atproto/api' 2import {type BskyAgent} from '@atproto/api' 3import {type I18n} from '@lingui/core' 4import {msg} from '@lingui/macro' 5import {nanoid} from 'nanoid/non-secure' 6 7import {AbortError} from '#/lib/async/cancelable' 8import {ServerError} from '#/lib/media/video/errors' 9import {type CompressedVideo} from '#/lib/media/video/types' 10import {getServiceAuthToken, getVideoUploadLimits} from './upload.shared' 11import {createVideoEndpointUrl, mimeToExt} from './util' 12 13export async function uploadVideo({ 14 video, 15 agent, 16 did, 17 setProgress, 18 signal, 19 _, 20}: { 21 video: CompressedVideo 22 agent: BskyAgent 23 did: string 24 setProgress: (progress: number) => void 25 signal: AbortSignal 26 _: I18n['_'] 27}) { 28 if (signal.aborted) { 29 throw new AbortError() 30 } 31 await getVideoUploadLimits(agent, _) 32 33 const uri = createVideoEndpointUrl('/xrpc/app.bsky.video.uploadVideo', { 34 did, 35 name: `${nanoid(12)}.${mimeToExt(video.mimeType)}`, 36 }) 37 38 let bytes = video.bytes 39 if (!bytes) { 40 if (signal.aborted) { 41 throw new AbortError() 42 } 43 bytes = await fetch(video.uri).then(res => res.arrayBuffer()) 44 } 45 46 if (signal.aborted) { 47 throw new AbortError() 48 } 49 const token = await getServiceAuthToken({ 50 agent, 51 lxm: 'com.atproto.repo.uploadBlob', 52 exp: Date.now() / 1000 + 60 * 30, // 30 minutes 53 }) 54 55 if (signal.aborted) { 56 throw new AbortError() 57 } 58 const xhr = new XMLHttpRequest() 59 const res = await new Promise<AppBskyVideoDefs.JobStatus>( 60 (resolve, reject) => { 61 xhr.upload.addEventListener('progress', e => { 62 const progress = e.loaded / e.total 63 setProgress(progress) 64 }) 65 xhr.onloadend = () => { 66 if (signal.aborted) { 67 reject(new AbortError()) 68 } else if (xhr.readyState === 4) { 69 const uploadRes = JSON.parse( 70 xhr.responseText, 71 ) as AppBskyVideoDefs.JobStatus 72 resolve(uploadRes) 73 } else { 74 reject(new ServerError(_(msg`Failed to upload video`))) 75 } 76 } 77 xhr.onerror = () => { 78 reject(new ServerError(_(msg`Failed to upload video`))) 79 } 80 xhr.open('POST', uri) 81 xhr.setRequestHeader('Content-Type', video.mimeType) 82 xhr.setRequestHeader('Authorization', `Bearer ${token}`) 83 xhr.send(bytes) 84 }, 85 ) 86 87 if (!res.jobId) { 88 throw new ServerError(res.error || _(msg`Failed to upload video`)) 89 } 90 91 if (signal.aborted) { 92 throw new AbortError() 93 } 94 return res 95}