[dyad] Added thumbnails to the clips overview page - wrote 3 file(s), executed 1 SQL queries
This commit is contained in:
@@ -15,6 +15,7 @@ import { formatDistanceToNow } from 'date-fns';
|
||||
import { MoreVertical, Edit, Trash2, Loader2, Copy, Check } from 'lucide-react';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
|
||||
type UserClipsProps = {
|
||||
clips: Clip[];
|
||||
@@ -61,7 +62,11 @@ export function UserClips({ clips, onClipDeleted, onClipUpdated }: UserClipsProp
|
||||
const handleDeleteClip = async (clip: Clip) => {
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
const { error: storageError } = await supabase.storage.from('clips').remove([clip.storage_path]);
|
||||
const pathsToRemove = [clip.storage_path];
|
||||
if (clip.thumbnail_storage_path) {
|
||||
pathsToRemove.push(clip.thumbnail_storage_path);
|
||||
}
|
||||
const { error: storageError } = await supabase.storage.from('clips').remove(pathsToRemove);
|
||||
if (storageError) throw storageError;
|
||||
|
||||
const { error: dbError } = await supabase.from('clips').delete().eq('id', clip.id);
|
||||
@@ -84,6 +89,11 @@ export function UserClips({ clips, onClipDeleted, onClipUpdated }: UserClipsProp
|
||||
setTimeout(() => setHasCopied(null), 2000);
|
||||
};
|
||||
|
||||
const getThumbnailUrl = (path: string) => {
|
||||
const { data } = supabase.storage.from('clips').getPublicUrl(path);
|
||||
return data.publicUrl;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toaster />
|
||||
@@ -120,8 +130,20 @@ export function UserClips({ clips, onClipDeleted, onClipUpdated }: UserClipsProp
|
||||
</CardHeader>
|
||||
<CardContent className="flex-grow">
|
||||
<Link href={`/clips/${clip.short_id}`}>
|
||||
<div className="aspect-video w-full bg-muted rounded-md flex items-center justify-center text-muted-foreground">
|
||||
Click to view
|
||||
<div className="aspect-video w-full bg-muted rounded-md overflow-hidden relative">
|
||||
{clip.thumbnail_storage_path ? (
|
||||
<Image
|
||||
src={getThumbnailUrl(clip.thumbnail_storage_path)}
|
||||
alt={clip.title || 'Clip thumbnail'}
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
className="transition-transform duration-300 hover:scale-105"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-muted-foreground">
|
||||
No thumbnail
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
</CardContent>
|
||||
@@ -139,7 +161,7 @@ export function UserClips({ clips, onClipDeleted, onClipUpdated }: UserClipsProp
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone. This will permanently delete your clip and remove its data from our servers.
|
||||
This action cannot be undone. This will permanently delete your clip and its thumbnail from our servers.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
|
||||
@@ -118,16 +118,26 @@ export function VideoEditor() {
|
||||
|
||||
const data = await ffmpeg.readFile('output.mp4');
|
||||
const trimmedBlob = new Blob([(data as any).buffer], { type: 'video/mp4' });
|
||||
|
||||
// Generate thumbnail
|
||||
await ffmpeg.exec(['-i', 'output.mp4', '-ss', '00:00:00.001', '-vframes', '1', 'thumbnail.jpg']);
|
||||
const thumbnailData = await ffmpeg.readFile('thumbnail.jpg');
|
||||
const thumbnailBlob = new Blob([(thumbnailData as any).buffer], { type: 'image/jpeg' });
|
||||
|
||||
const shortId = Math.random().toString(36).substring(2, 8);
|
||||
const storagePath = `${user.id}/${shortId}-${videoFile.name}`;
|
||||
const thumbnailStoragePath = `${user.id}/${shortId}-thumbnail.jpg`;
|
||||
|
||||
const { error: uploadError } = await supabase.storage.from('clips').upload(storagePath, trimmedBlob);
|
||||
if (uploadError) throw new Error(`Storage Error: ${uploadError.message}`);
|
||||
|
||||
const { error: thumbnailUploadError } = await supabase.storage.from('clips').upload(thumbnailStoragePath, thumbnailBlob);
|
||||
if (thumbnailUploadError) throw new Error(`Thumbnail Upload Error: ${thumbnailUploadError.message}`);
|
||||
|
||||
const { error: dbError } = await supabase.from('clips').insert({
|
||||
user_id: user.id,
|
||||
storage_path: storagePath,
|
||||
thumbnail_storage_path: thumbnailStoragePath,
|
||||
short_id: shortId,
|
||||
original_file_name: videoFile.name,
|
||||
title: videoTitle,
|
||||
|
||||
Reference in New Issue
Block a user