diff options
author | dan <dan.abramov@gmail.com> | 2024-10-03 14:57:48 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-03 14:57:48 +0900 |
commit | 2aa365b5b6fed920d17d307252a7af7c52b95855 (patch) | |
tree | b8083438359e63367e983b461f5835cf8b75aa8c /src/lib/media/video/upload.web.ts | |
parent | 03704e2b48e6cdc348ce7277f2bcae0c61519d1e (diff) | |
download | voidsky-2aa365b5b6fed920d17d307252a7af7c52b95855.tar.zst |
Rename some files and variables (#5587)
* Move composer reducers together * videoUploadState -> videoState * Inline videoDispatch
Diffstat (limited to 'src/lib/media/video/upload.web.ts')
-rw-r--r-- | src/lib/media/video/upload.web.ts | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/src/lib/media/video/upload.web.ts b/src/lib/media/video/upload.web.ts new file mode 100644 index 000000000..ec65f96c9 --- /dev/null +++ b/src/lib/media/video/upload.web.ts @@ -0,0 +1,95 @@ +import {AppBskyVideoDefs} from '@atproto/api' +import {BskyAgent} from '@atproto/api' +import {I18n} from '@lingui/core' +import {msg} from '@lingui/macro' +import {nanoid} from 'nanoid/non-secure' + +import {AbortError} from '#/lib/async/cancelable' +import {ServerError} from '#/lib/media/video/errors' +import {CompressedVideo} from '#/lib/media/video/types' +import {createVideoEndpointUrl, mimeToExt} from './util' +import {getServiceAuthToken, getVideoUploadLimits} from './upload.shared' + +export async function uploadVideo({ + video, + agent, + did, + setProgress, + signal, + _, +}: { + video: CompressedVideo + agent: BskyAgent + did: string + setProgress: (progress: number) => void + signal: AbortSignal + _: I18n['_'] +}) { + if (signal.aborted) { + throw new AbortError() + } + await getVideoUploadLimits(agent, _) + + const uri = createVideoEndpointUrl('/xrpc/app.bsky.video.uploadVideo', { + did, + name: `${nanoid(12)}.${mimeToExt(video.mimeType)}`, + }) + + let bytes = video.bytes + if (!bytes) { + if (signal.aborted) { + throw new AbortError() + } + bytes = await fetch(video.uri).then(res => res.arrayBuffer()) + } + + if (signal.aborted) { + throw new AbortError() + } + const token = await getServiceAuthToken({ + agent, + lxm: 'com.atproto.repo.uploadBlob', + exp: Date.now() / 1000 + 60 * 30, // 30 minutes + }) + + if (signal.aborted) { + throw new AbortError() + } + const xhr = new XMLHttpRequest() + const res = await new Promise<AppBskyVideoDefs.JobStatus>( + (resolve, reject) => { + xhr.upload.addEventListener('progress', e => { + const progress = e.loaded / e.total + setProgress(progress) + }) + xhr.onloadend = () => { + if (signal.aborted) { + reject(new AbortError()) + } else if (xhr.readyState === 4) { + const uploadRes = JSON.parse( + xhr.responseText, + ) as AppBskyVideoDefs.JobStatus + resolve(uploadRes) + } else { + reject(new ServerError(_(msg`Failed to upload video`))) + } + } + xhr.onerror = () => { + reject(new ServerError(_(msg`Failed to upload video`))) + } + xhr.open('POST', uri) + xhr.setRequestHeader('Content-Type', video.mimeType) + xhr.setRequestHeader('Authorization', `Bearer ${token}`) + xhr.send(bytes) + }, + ) + + if (!res.jobId) { + throw new ServerError(res.error || _(msg`Failed to upload video`)) + } + + if (signal.aborted) { + throw new AbortError() + } + return res +} |