add thumbnails to videos
This commit is contained in:
parent
822d9ba225
commit
2b648bd8e6
3 changed files with 26 additions and 10 deletions
|
|
@ -143,15 +143,15 @@ export default function Recorder({ closeModal, step, setStep }: Props) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function generateThumbnail(video: HTMLVideoElement) {
|
const generateThumbnail = async (video: HTMLVideoElement) => {
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
canvas.width = video.videoWidth;
|
canvas.width = video.videoWidth;
|
||||||
canvas.height = video.videoHeight;
|
canvas.height = video.videoHeight;
|
||||||
canvas
|
canvas
|
||||||
.getContext("2d")
|
.getContext("2d")
|
||||||
?.drawImage(video, 0, 0, canvas.width, canvas.height);
|
?.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||||
return canvas.toDataURL();
|
return await new Promise((resolve) => canvas.toBlob(resolve));
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleUpload = async () => {
|
const handleUpload = async () => {
|
||||||
if (!blob || !videoRef.current) return;
|
if (!blob || !videoRef.current) return;
|
||||||
|
|
@ -169,11 +169,11 @@ export default function Recorder({ closeModal, step, setStep }: Props) {
|
||||||
.put(signedVideoUrl, blob.slice(), {
|
.put(signedVideoUrl, blob.slice(), {
|
||||||
headers: { "Content-Type": "video/webm" },
|
headers: { "Content-Type": "video/webm" },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(async () => {
|
||||||
if (!videoRef.current) return;
|
if (!videoRef.current) return;
|
||||||
return axios.put(
|
return axios.put(
|
||||||
signedThumbnailUrl,
|
signedThumbnailUrl,
|
||||||
generateThumbnail(videoRef.current),
|
await generateThumbnail(videoRef.current),
|
||||||
{
|
{
|
||||||
headers: { "Content-Type": "image/png" },
|
headers: { "Content-Type": "image/png" },
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,11 +94,12 @@ const VideoList: NextPage = () => {
|
||||||
) : (
|
) : (
|
||||||
<div className="flex-start grid w-full max-w-[1300px] grid-cols-[repeat(auto-fill,250px)] flex-row flex-wrap items-center justify-center gap-14 px-4 pb-16">
|
<div className="flex-start grid w-full max-w-[1300px] grid-cols-[repeat(auto-fill,250px)] flex-row flex-wrap items-center justify-center gap-14 px-4 pb-16">
|
||||||
{videos &&
|
{videos &&
|
||||||
videos.map(({ title, id, createdAt }) => (
|
videos.map(({ title, id, createdAt, thumbnailUrl }) => (
|
||||||
<VideoCard
|
<VideoCard
|
||||||
title={title}
|
title={title}
|
||||||
id={id}
|
id={id}
|
||||||
createdAt={createdAt}
|
createdAt={createdAt}
|
||||||
|
thumbnailUrl={thumbnailUrl}
|
||||||
key={id}
|
key={id}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
@ -122,6 +123,7 @@ const VideoList: NextPage = () => {
|
||||||
interface VideoCardProps {
|
interface VideoCardProps {
|
||||||
title: string;
|
title: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
thumbnailUrl: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,13 +139,13 @@ const VideoCardSkeleton = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const VideoCard = ({ title, id, createdAt }: VideoCardProps) => {
|
const VideoCard = ({ title, id, createdAt, thumbnailUrl }: VideoCardProps) => {
|
||||||
return (
|
return (
|
||||||
<Link href={`/share/${id}`}>
|
<Link href={`/share/${id}`}>
|
||||||
<div className="h-[240px] w-[250px] cursor-pointer overflow-hidden rounded-lg border border-[#6c668533] text-sm font-normal">
|
<div className="h-[240px] w-[250px] cursor-pointer overflow-hidden rounded-lg border border-[#6c668533] text-sm font-normal">
|
||||||
<figure>
|
<figure>
|
||||||
<Image
|
<Image
|
||||||
src="https://i3.ytimg.com/vi/BuaKzm7Kq9Q/maxresdefault.jpg"
|
src={thumbnailUrl}
|
||||||
alt="video thumbnail"
|
alt="video thumbnail"
|
||||||
width={248}
|
width={248}
|
||||||
height={139.5}
|
height={139.5}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,21 @@ export const videoRouter = createTRPCRouter({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return videos;
|
const videosWithThumbnailUrl = await Promise.all(
|
||||||
|
videos.map(async (video) => {
|
||||||
|
const thumbnailUrl = await getSignedUrl(
|
||||||
|
ctx.s3,
|
||||||
|
new GetObjectCommand({
|
||||||
|
Bucket: env.AWS_BUCKET_NAME,
|
||||||
|
Key: video.userId + "/" + video.id + "-thumbnail",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return { ...video, thumbnailUrl };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return videosWithThumbnailUrl;
|
||||||
}),
|
}),
|
||||||
get: publicProcedure
|
get: publicProcedure
|
||||||
.input(z.object({ videoId: z.string() }))
|
.input(z.object({ videoId: z.string() }))
|
||||||
|
|
@ -95,7 +109,7 @@ export const videoRouter = createTRPCRouter({
|
||||||
s3,
|
s3,
|
||||||
new PutObjectCommand({
|
new PutObjectCommand({
|
||||||
Bucket: env.AWS_BUCKET_NAME,
|
Bucket: env.AWS_BUCKET_NAME,
|
||||||
Key: video.userId + "/" + video.id + "thumbnail",
|
Key: video.userId + "/" + video.id + "-thumbnail",
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue