vision-agent / components /chat /ImageSelector.tsx
wuyiqunLu
feat: generate thumbnail and save on video upload (#43)
bc0330e unverified
raw
history blame
No virus
3.46 kB
'use client';
import React, { useCallback, useState } from 'react';
import useImageUpload from '../../lib/hooks/useImageUpload';
import { cn, fetcher } from '@/lib/utils';
import { SignedPayload, MessageBase, ChatEntity } from '@/lib/types';
import { useRouter } from 'next/navigation';
import Loading from '../ui/Loading';
import toast from 'react-hot-toast';
import {
generateVideoThumbnails,
getVideoCover,
} from '@rajesh896/video-thumbnails-generator';
export interface ImageSelectorProps {}
type Example = {
url: string;
initMessages: MessageBase[];
};
const ImageSelector: React.FC<ImageSelectorProps> = () => {
const router = useRouter();
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 } = useImageUpload(
undefined,
async files => {
const formData = new FormData();
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];
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);
});
}
await fetcher<ChatEntity>('/api/upload', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
id: resp.id,
url: resp.publicUrl,
}),
});
setUploading(false);
router.push(`/chat/${resp.id}`);
};
},
);
return (
<div
{...getRootProps()}
className={cn(
'dropzone border-2 border-dashed border-gray-400 w-full h-64 flex items-center justify-center rounded-lg mt-4 cursor-pointer',
isDragActive && 'bg-gray-500/50 border-solid',
)}
>
<input {...getInputProps()} />
<div className="text-gray-400 text-md">
{isUploading ? (
<Loading />
) : (
'Start using Vision Agent by selecting an image'
)}
</div>
</div>
);
};
export default ImageSelector;