-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* state for video uploads * get upload working * add a debug log * add post progress * progress * fetch data * add some progress info, web uploads * post on finished uploading (wip) * add a note * add some todos * clear video * merge some stuff * convert to `createUploadTask` * patch expo modules core * working native upload progress * platform fork * upload progress for web * cleanup * cleanup * more tweaks * simplify * fix type errors --------- Co-authored-by: Samuel Newman <10959775+mozzius@users.noreply.github.com>
- Loading branch information
Showing
13 changed files
with
594 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/** | ||
* TEMPORARY: THIS IS A TEMPORARY PLACEHOLDER. THAT MEANS IT IS TEMPORARY. I.E. WILL BE REMOVED. NOT TO USE IN PRODUCTION. | ||
* @temporary | ||
* PS: This is a temporary placeholder for the video types. It will be removed once the actual types are implemented. | ||
* Not joking, this is temporary. | ||
*/ | ||
|
||
export interface JobStatus { | ||
jobId: string | ||
did: string | ||
cid: string | ||
state: JobState | ||
progress?: number | ||
errorHuman?: string | ||
errorMachine?: string | ||
} | ||
|
||
export enum JobState { | ||
JOB_STATE_UNSPECIFIED = 'JOB_STATE_UNSPECIFIED', | ||
JOB_STATE_CREATED = 'JOB_STATE_CREATED', | ||
JOB_STATE_ENCODING = 'JOB_STATE_ENCODING', | ||
JOB_STATE_ENCODED = 'JOB_STATE_ENCODED', | ||
JOB_STATE_UPLOADING = 'JOB_STATE_UPLOADING', | ||
JOB_STATE_UPLOADED = 'JOB_STATE_UPLOADED', | ||
JOB_STATE_CDN_PROCESSING = 'JOB_STATE_CDN_PROCESSING', | ||
JOB_STATE_CDN_PROCESSED = 'JOB_STATE_CDN_PROCESSED', | ||
JOB_STATE_FAILED = 'JOB_STATE_FAILED', | ||
JOB_STATE_COMPLETED = 'JOB_STATE_COMPLETED', | ||
} | ||
|
||
export interface UploadVideoResponse { | ||
job_id: string | ||
did: string | ||
cid: string | ||
state: JobState | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import {ImagePickerAsset} from 'expo-image-picker' | ||
import {useMutation} from '@tanstack/react-query' | ||
|
||
import {CompressedVideo, compressVideo} from 'lib/media/video/compress' | ||
|
||
export function useCompressVideoMutation({ | ||
onProgress, | ||
onSuccess, | ||
onError, | ||
}: { | ||
onProgress: (progress: number) => void | ||
onError: (e: any) => void | ||
onSuccess: (video: CompressedVideo) => void | ||
}) { | ||
return useMutation({ | ||
mutationFn: async (asset: ImagePickerAsset) => { | ||
return await compressVideo(asset.uri, { | ||
onProgress: num => onProgress(trunc2dp(num)), | ||
}) | ||
}, | ||
onError, | ||
onSuccess, | ||
onMutate: () => { | ||
onProgress(0) | ||
}, | ||
}) | ||
} | ||
|
||
function trunc2dp(num: number) { | ||
return Math.trunc(num * 100) / 100 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
const UPLOAD_ENDPOINT = process.env.EXPO_PUBLIC_VIDEO_ROOT_ENDPOINT ?? '' | ||
|
||
export const createVideoEndpointUrl = ( | ||
route: string, | ||
params?: Record<string, string>, | ||
) => { | ||
const url = new URL(`${UPLOAD_ENDPOINT}`) | ||
url.pathname = route | ||
if (params) { | ||
for (const key in params) { | ||
url.searchParams.set(key, params[key]) | ||
} | ||
} | ||
return url.href | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import {createUploadTask, FileSystemUploadType} from 'expo-file-system' | ||
import {useMutation} from '@tanstack/react-query' | ||
import {nanoid} from 'nanoid/non-secure' | ||
|
||
import {CompressedVideo} from 'lib/media/video/compress' | ||
import {UploadVideoResponse} from 'lib/media/video/types' | ||
import {createVideoEndpointUrl} from 'state/queries/video/util' | ||
import {useSession} from 'state/session' | ||
const UPLOAD_HEADER = process.env.EXPO_PUBLIC_VIDEO_HEADER ?? '' | ||
|
||
export const useUploadVideoMutation = ({ | ||
onSuccess, | ||
onError, | ||
setProgress, | ||
}: { | ||
onSuccess: (response: UploadVideoResponse) => void | ||
onError: (e: any) => void | ||
setProgress: (progress: number) => void | ||
}) => { | ||
const {currentAccount} = useSession() | ||
|
||
return useMutation({ | ||
mutationFn: async (video: CompressedVideo) => { | ||
const uri = createVideoEndpointUrl('/upload', { | ||
did: currentAccount!.did, | ||
name: `${nanoid(12)}.mp4`, // @TODO what are we limiting this to? | ||
}) | ||
|
||
const uploadTask = createUploadTask( | ||
uri, | ||
video.uri, | ||
{ | ||
headers: { | ||
'dev-key': UPLOAD_HEADER, | ||
'content-type': 'video/mp4', // @TODO same question here. does the compression step always output mp4? | ||
}, | ||
httpMethod: 'POST', | ||
uploadType: FileSystemUploadType.BINARY_CONTENT, | ||
}, | ||
p => { | ||
setProgress(p.totalBytesSent / p.totalBytesExpectedToSend) | ||
}, | ||
) | ||
const res = await uploadTask.uploadAsync() | ||
|
||
if (!res?.body) { | ||
throw new Error('No response') | ||
} | ||
|
||
// @TODO rm, useful for debugging/getting video cid | ||
console.log('[VIDEO]', res.body) | ||
const responseBody = JSON.parse(res.body) as UploadVideoResponse | ||
onSuccess(responseBody) | ||
return responseBody | ||
}, | ||
onError, | ||
onSuccess, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import {useMutation} from '@tanstack/react-query' | ||
import {nanoid} from 'nanoid/non-secure' | ||
|
||
import {CompressedVideo} from 'lib/media/video/compress' | ||
import {UploadVideoResponse} from 'lib/media/video/types' | ||
import {createVideoEndpointUrl} from 'state/queries/video/util' | ||
import {useSession} from 'state/session' | ||
const UPLOAD_HEADER = process.env.EXPO_PUBLIC_VIDEO_HEADER ?? '' | ||
|
||
export const useUploadVideoMutation = ({ | ||
onSuccess, | ||
onError, | ||
setProgress, | ||
}: { | ||
onSuccess: (response: UploadVideoResponse) => void | ||
onError: (e: any) => void | ||
setProgress: (progress: number) => void | ||
}) => { | ||
const {currentAccount} = useSession() | ||
|
||
return useMutation({ | ||
mutationFn: async (video: CompressedVideo) => { | ||
const uri = createVideoEndpointUrl('/upload', { | ||
did: currentAccount!.did, | ||
name: `${nanoid(12)}.mp4`, // @TODO what are we limiting this to? | ||
}) | ||
|
||
const bytes = await fetch(video.uri).then(res => res.arrayBuffer()) | ||
|
||
const xhr = new XMLHttpRequest() | ||
const res = (await new Promise((resolve, reject) => { | ||
xhr.upload.addEventListener('progress', e => { | ||
const progress = e.loaded / e.total | ||
setProgress(progress) | ||
}) | ||
xhr.onloadend = () => { | ||
if (xhr.readyState === 4) { | ||
const uploadRes = JSON.parse( | ||
xhr.responseText, | ||
) as UploadVideoResponse | ||
resolve(uploadRes) | ||
onSuccess(uploadRes) | ||
} else { | ||
reject() | ||
onError(new Error('Failed to upload video')) | ||
} | ||
} | ||
xhr.onerror = () => { | ||
reject() | ||
onError(new Error('Failed to upload video')) | ||
} | ||
xhr.open('POST', uri) | ||
xhr.setRequestHeader('Content-Type', 'video/mp4') // @TODO how we we set the proper content type? | ||
// @TODO remove this header for prod | ||
xhr.setRequestHeader('dev-key', UPLOAD_HEADER) | ||
xhr.send(bytes) | ||
})) as UploadVideoResponse | ||
|
||
// @TODO rm for prod | ||
console.log('[VIDEO]', res) | ||
return res | ||
}, | ||
onError, | ||
onSuccess, | ||
}) | ||
} |
Oops, something went wrong.