diff --git a/components/converter/FileConverter.tsx b/components/converter/FileConverter.tsx index e594006..8df8f97 100644 --- a/components/converter/FileConverter.tsx +++ b/components/converter/FileConverter.tsx @@ -9,6 +9,7 @@ import { FormatSelector } from './FormatSelector'; import { ConversionPreview } from './ConversionPreview'; import { ConversionOptionsPanel } from './ConversionOptions'; import { FileInfo } from './FileInfo'; +import { FormatPresets } from './FormatPresets'; import { useToast } from '@/components/ui/Toast'; import { SUPPORTED_FORMATS, @@ -20,6 +21,7 @@ import { convertWithFFmpeg } from '@/lib/converters/ffmpegService'; import { convertWithImageMagick } from '@/lib/converters/imagemagickService'; import { addToHistory } from '@/lib/storage/history'; import { downloadBlobsAsZip, generateOutputFilename } from '@/lib/utils/fileUtils'; +import { getPresetById, type FormatPreset } from '@/lib/utils/formatPresets'; import type { ConversionJob, ConversionFormat, ConversionOptions } from '@/types/conversion'; export function FileConverter() { @@ -209,6 +211,17 @@ export function FileConverter() { setSelectedFiles((prev) => prev.filter((_, i) => i !== index)); }; + const handlePresetSelect = (preset: FormatPreset) => { + // Find the output format that matches the preset + const format = compatibleFormats.find(f => f.extension === preset.outputFormat); + + if (format) { + setOutputFormat(format); + setConversionOptions(preset.options); + addToast(`Applied ${preset.name} preset`, 'success'); + } + }; + const handleDownloadAll = async () => { if (!outputFormat) return; @@ -375,6 +388,15 @@ export function FileConverter() { )} + {/* Format Presets */} + {inputFormat && ( + + )} + {/* Format selection */} {inputFormat && compatibleFormats.length > 0 && (
diff --git a/components/converter/FormatPresets.tsx b/components/converter/FormatPresets.tsx new file mode 100644 index 0000000..96bf136 --- /dev/null +++ b/components/converter/FormatPresets.tsx @@ -0,0 +1,67 @@ +'use client'; + +import * as React from 'react'; +import { Sparkles } from 'lucide-react'; +import { Card } from '@/components/ui/Card'; +import { Button } from '@/components/ui/Button'; +import { cn } from '@/lib/utils/cn'; +import { getPresetsByCategory, type FormatPreset } from '@/lib/utils/formatPresets'; +import type { ConversionFormat } from '@/types/conversion'; + +interface FormatPresetsProps { + inputFormat: ConversionFormat; + onPresetSelect: (preset: FormatPreset) => void; + disabled?: boolean; +} + +export function FormatPresets({ inputFormat, onPresetSelect, disabled = false }: FormatPresetsProps) { + const [selectedPresetId, setSelectedPresetId] = React.useState(null); + + // Get presets for the input format's category + const presets = getPresetsByCategory(inputFormat.category); + + if (presets.length === 0) { + return null; + } + + const handlePresetClick = (preset: FormatPreset) => { + setSelectedPresetId(preset.id); + onPresetSelect(preset); + }; + + return ( + +
+ +

Quick Presets

+
+ +
+ {presets.map((preset) => ( + + ))} +
+ +
+ Select a preset to automatically configure optimal settings for your use case +
+
+ ); +} diff --git a/lib/utils/formatPresets.ts b/lib/utils/formatPresets.ts new file mode 100644 index 0000000..666137b --- /dev/null +++ b/lib/utils/formatPresets.ts @@ -0,0 +1,188 @@ +import type { ConversionOptions } from '@/types/conversion'; + +export interface FormatPreset { + id: string; + name: string; + description: string; + icon: string; + category: 'video' | 'audio' | 'image'; + outputFormat: string; + options: ConversionOptions; +} + +/** + * Predefined format presets for common use cases + */ +export const FORMAT_PRESETS: FormatPreset[] = [ + // Video Presets + { + id: 'youtube-video', + name: 'YouTube Video', + description: '1080p MP4, optimized for YouTube', + icon: '🎬', + category: 'video', + outputFormat: 'mp4', + options: { + videoCodec: 'libx264', + videoBitrate: '5M', + videoResolution: '1920x-1', + videoFps: 30, + audioCodec: 'aac', + audioBitrate: '192k', + }, + }, + { + id: 'instagram-video', + name: 'Instagram Video', + description: 'Square 1:1 format for Instagram', + icon: '📸', + category: 'video', + outputFormat: 'mp4', + options: { + videoCodec: 'libx264', + videoBitrate: '3M', + videoResolution: '1080x-1', // Will be cropped to square + videoFps: 30, + audioCodec: 'aac', + audioBitrate: '128k', + }, + }, + { + id: 'twitter-video', + name: 'Twitter Video', + description: '720p, optimized for Twitter', + icon: '🐦', + category: 'video', + outputFormat: 'mp4', + options: { + videoCodec: 'libx264', + videoBitrate: '2M', + videoResolution: '1280x-1', + videoFps: 30, + audioCodec: 'aac', + audioBitrate: '128k', + }, + }, + { + id: 'web-video', + name: 'Web Optimized', + description: 'Small file size for web streaming', + icon: '🌐', + category: 'video', + outputFormat: 'mp4', + options: { + videoCodec: 'libx264', + videoBitrate: '1.5M', + videoResolution: '854x-1', + videoFps: 24, + audioCodec: 'aac', + audioBitrate: '96k', + }, + }, + + // Audio Presets + { + id: 'podcast-audio', + name: 'Podcast', + description: 'MP3, optimized for voice', + icon: '🎙️', + category: 'audio', + outputFormat: 'mp3', + options: { + audioCodec: 'libmp3lame', + audioBitrate: '128k', + audioSampleRate: 44100, + audioChannels: 2, + }, + }, + { + id: 'music-high-quality', + name: 'High Quality Music', + description: 'MP3, 320kbps for music', + icon: '🎵', + category: 'audio', + outputFormat: 'mp3', + options: { + audioCodec: 'libmp3lame', + audioBitrate: '320k', + audioSampleRate: 48000, + audioChannels: 2, + }, + }, + { + id: 'audiobook', + name: 'Audiobook', + description: 'Mono, small file size', + icon: '📚', + category: 'audio', + outputFormat: 'mp3', + options: { + audioCodec: 'libmp3lame', + audioBitrate: '64k', + audioSampleRate: 22050, + audioChannels: 1, + }, + }, + + // Image Presets + { + id: 'web-thumbnail', + name: 'Web Thumbnail', + description: 'JPG, 800px width, optimized', + icon: '🖼️', + category: 'image', + outputFormat: 'jpg', + options: { + imageQuality: 85, + imageWidth: 800, + }, + }, + { + id: 'hd-image', + name: 'HD Image', + description: 'PNG, high quality, lossless', + icon: '🎨', + category: 'image', + outputFormat: 'png', + options: { + imageQuality: 100, + }, + }, + { + id: 'social-media-image', + name: 'Social Media', + description: 'JPG, 1200px, optimized', + icon: '📱', + category: 'image', + outputFormat: 'jpg', + options: { + imageQuality: 90, + imageWidth: 1200, + }, + }, + { + id: 'web-optimized-image', + name: 'Web Optimized', + description: 'WebP, small file size', + icon: '⚡', + category: 'image', + outputFormat: 'webp', + options: { + imageQuality: 80, + }, + }, +]; + +/** + * Get presets by category + */ +export function getPresetsByCategory(category: 'video' | 'audio' | 'image'): FormatPreset[] { + return FORMAT_PRESETS.filter(preset => preset.category === category); +} + +/** + * Get preset by ID + */ +export function getPresetById(id: string): FormatPreset | undefined { + return FORMAT_PRESETS.find(preset => preset.id === id); +}