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