[Video] Add uploaded video to post (#4884)

* video uploads!

* use video upload lexicons

* add missing postgate

* remove references to prerelease package

* fix scrubber showing a "0"

* Delete types.ts

* rm logs

* rm upload header

---------

Co-authored-by: Samuel Newman <10959775+mozzius@users.noreply.github.com>
This commit is contained in:
Samuel Newman 2024-08-29 16:34:41 +01:00 committed by GitHub
parent d52d29621e
commit 551c4a4f32
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 116 additions and 126 deletions

View file

@ -1,5 +1,6 @@
import React from 'react'
import {ImagePickerAsset} from 'expo-image-picker'
import {AppBskyVideoDefs, BlobRef} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useQuery} from '@tanstack/react-query'
@ -7,37 +8,29 @@ import {useQuery} from '@tanstack/react-query'
import {logger} from '#/logger'
import {CompressedVideo} from 'lib/media/video/compress'
import {VideoTooLargeError} from 'lib/media/video/errors'
import {JobState, JobStatus} from 'lib/media/video/types'
import {useCompressVideoMutation} from 'state/queries/video/compress-video'
import {createVideoEndpointUrl} from 'state/queries/video/util'
import {useVideoAgent} from 'state/queries/video/util'
import {useUploadVideoMutation} from 'state/queries/video/video-upload'
type Status = 'idle' | 'compressing' | 'processing' | 'uploading' | 'done'
type Action =
| {
type: 'SetStatus'
status: Status
}
| {
type: 'SetProgress'
progress: number
}
| {
type: 'SetError'
error: string | undefined
}
| {type: 'SetStatus'; status: Status}
| {type: 'SetProgress'; progress: number}
| {type: 'SetError'; error: string | undefined}
| {type: 'Reset'}
| {type: 'SetAsset'; asset: ImagePickerAsset}
| {type: 'SetVideo'; video: CompressedVideo}
| {type: 'SetJobStatus'; jobStatus: JobStatus}
| {type: 'SetJobStatus'; jobStatus: AppBskyVideoDefs.JobStatus}
| {type: 'SetBlobRef'; blobRef: BlobRef}
export interface State {
status: Status
progress: number
asset?: ImagePickerAsset
video: CompressedVideo | null
jobStatus?: JobStatus
jobStatus?: AppBskyVideoDefs.JobStatus
blobRef?: BlobRef
error?: string
}
@ -54,6 +47,7 @@ function reducer(state: State, action: Action): State {
status: 'idle',
progress: 0,
video: null,
blobRef: undefined,
}
} else if (action.type === 'SetAsset') {
updatedState = {...state, asset: action.asset}
@ -61,6 +55,8 @@ function reducer(state: State, action: Action): State {
updatedState = {...state, video: action.video}
} else if (action.type === 'SetJobStatus') {
updatedState = {...state, jobStatus: action.jobStatus}
} else if (action.type === 'SetBlobRef') {
updatedState = {...state, blobRef: action.blobRef}
}
return updatedState
}
@ -80,7 +76,7 @@ export function useUploadVideo({
})
const {setJobId} = useUploadStatusQuery({
onStatusChange: (status: JobStatus) => {
onStatusChange: (status: AppBskyVideoDefs.JobStatus) => {
// This might prove unuseful, most of the job status steps happen too quickly to even be displayed to the user
// Leaving it for now though
dispatch({
@ -89,7 +85,11 @@ export function useUploadVideo({
})
setStatus(status.state.toString())
},
onSuccess: () => {
onSuccess: blobRef => {
dispatch({
type: 'SetBlobRef',
blobRef,
})
dispatch({
type: 'SetStatus',
status: 'idle',
@ -104,7 +104,7 @@ export function useUploadVideo({
type: 'SetStatus',
status: 'processing',
})
setJobId(response.job_id)
setJobId(response.jobId)
},
onError: e => {
dispatch({
@ -179,21 +179,27 @@ const useUploadStatusQuery = ({
onStatusChange,
onSuccess,
}: {
onStatusChange: (status: JobStatus) => void
onSuccess: () => void
onStatusChange: (status: AppBskyVideoDefs.JobStatus) => void
onSuccess: (blobRef: BlobRef) => void
}) => {
const videoAgent = useVideoAgent()
const [enabled, setEnabled] = React.useState(true)
const [jobId, setJobId] = React.useState<string>()
const {isLoading, isError} = useQuery({
queryKey: ['video-upload'],
queryKey: ['video-upload', jobId],
queryFn: async () => {
const url = createVideoEndpointUrl(`/job/${jobId}/status`)
const res = await fetch(url)
const status = (await res.json()) as JobStatus
if (status.state === JobState.JOB_STATE_COMPLETED) {
if (!jobId) return // this won't happen, can ignore
const {data} = await videoAgent.app.bsky.video.getJobStatus({jobId})
const status = data.jobStatus
if (status.state === 'JOB_STATE_COMPLETED') {
setEnabled(false)
onSuccess()
if (!status.blob)
throw new Error('Job completed, but did not return a blob')
onSuccess(status.blob)
} else if (status.state === 'JOB_STATE_FAILED') {
throw new Error('Job failed to process')
}
onStatusChange(status)
return status