Spaces:
Sleeping
Sleeping
import { | |
generateVideoThumbnails, | |
getVideoDurationFromVideoFile, | |
} from '@rajesh896/video-thumbnails-generator'; | |
import { useCallback, useState } from 'react'; | |
import { DropzoneOptions, useDropzone } from 'react-dropzone'; | |
import { toast } from 'react-hot-toast'; | |
import { fetcher } from '../utils'; | |
import { SignedPayload } from '../types'; | |
const useMediaUpload = ( | |
onUpload: (uploadUrl: string) => void, | |
options?: Partial<DropzoneOptions>, | |
) => { | |
const [isUploading, setUploading] = useState(false); | |
const upload = useCallback(async (file: File, chatId?: string) => { | |
const { id, signedUrl, publicUrl, fields } = await fetcher<SignedPayload>( | |
'/api/sign', | |
{ | |
method: 'POST', | |
body: JSON.stringify({ | |
id: chatId, | |
fileType: file.type, | |
fileName: file.name, | |
}), | |
}, | |
); | |
const formData = new FormData(); | |
Object.entries(fields).forEach(([key, value]) => { | |
formData.append(key, value as string); | |
}); | |
formData.append('file', file); | |
const uploadResponse = await fetch(signedUrl, { | |
method: 'POST', | |
body: formData, | |
}); | |
if (!uploadResponse.ok) { | |
toast.error(uploadResponse.statusText); | |
return; | |
} | |
return { | |
id, | |
publicUrl, | |
}; | |
}, []); | |
const { getRootProps, getInputProps, isDragActive, open } = useDropzone({ | |
accept: { | |
'image/*': ['.jpeg', '.png'], | |
'video/mp4': ['.mp4', '.MP4'], | |
}, | |
noClick: true, | |
noKeyboard: true, | |
multiple: false, | |
onDrop: async files => { | |
if (files.length !== 1) { | |
throw new Error('Only one image can be uploaded at a time'); | |
} | |
setUploading(true); | |
const reader = new FileReader(); | |
reader.readAsDataURL(files[0]); | |
reader.onload = async () => { | |
const file = files[0]; | |
if (file.type === 'video/mp4') { | |
const duration = await getVideoDurationFromVideoFile(file); | |
if (duration > 30) { | |
setUploading(false); | |
toast.error('Video duration must be less than 30 seconds'); | |
return; | |
} | |
} else if (file.size > 2.5 * 1024 * 1024) { | |
setUploading(false); | |
toast.error('Image size must be less than 2.5MB'); | |
return; | |
} | |
const resp = await upload(file); | |
if (!resp) { | |
return; | |
} | |
if (file.type === 'video/mp4') { | |
const thumbnails = await generateVideoThumbnails(file, 1, 'file'); | |
fetch(thumbnails[0]) | |
.then(res => res.blob()) | |
.then(blob => { | |
const thumbnailFile = new File( | |
[blob], | |
file.name.replace('.mp4', '.png').replace('.MP4', '.png'), | |
{ | |
type: 'image/png', | |
}, | |
); | |
return upload(thumbnailFile, resp.id); | |
}); | |
} | |
onUpload(resp.publicUrl); | |
setUploading(false); | |
}; | |
}, | |
...options, | |
}); | |
return { | |
getRootProps, | |
getInputProps, | |
isDragActive, | |
isUploading, | |
openUpload: open, | |
}; | |
}; | |
export default useMediaUpload; | |