- Switch from VP9 to VP8 codec for WebM (less memory-intensive) - Add realtime encoding deadline and fast CPU preset - Add ultrafast preset for x264 encoding - Set explicit bitrates to control memory usage - Use libvorbis instead of libopus for better compatibility This fixes "memory access out of bounds" errors during video conversion in browser environments with limited WASM memory. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
320 lines
6.9 KiB
TypeScript
320 lines
6.9 KiB
TypeScript
import type { ConversionFormat, FormatPreset } from '@/types/conversion';
|
|
|
|
/**
|
|
* All supported conversion formats
|
|
*/
|
|
export const SUPPORTED_FORMATS: ConversionFormat[] = [
|
|
// Video formats (FFmpeg)
|
|
{
|
|
id: 'mp4',
|
|
name: 'MP4',
|
|
extension: 'mp4',
|
|
mimeType: 'video/mp4',
|
|
category: 'video',
|
|
converter: 'ffmpeg',
|
|
description: 'MPEG-4 video format',
|
|
},
|
|
{
|
|
id: 'webm',
|
|
name: 'WebM',
|
|
extension: 'webm',
|
|
mimeType: 'video/webm',
|
|
category: 'video',
|
|
converter: 'ffmpeg',
|
|
description: 'WebM video format',
|
|
},
|
|
{
|
|
id: 'avi',
|
|
name: 'AVI',
|
|
extension: 'avi',
|
|
mimeType: 'video/x-msvideo',
|
|
category: 'video',
|
|
converter: 'ffmpeg',
|
|
description: 'Audio Video Interleave',
|
|
},
|
|
{
|
|
id: 'mov',
|
|
name: 'MOV',
|
|
extension: 'mov',
|
|
mimeType: 'video/quicktime',
|
|
category: 'video',
|
|
converter: 'ffmpeg',
|
|
description: 'QuickTime movie',
|
|
},
|
|
{
|
|
id: 'mkv',
|
|
name: 'MKV',
|
|
extension: 'mkv',
|
|
mimeType: 'video/x-matroska',
|
|
category: 'video',
|
|
converter: 'ffmpeg',
|
|
description: 'Matroska video',
|
|
},
|
|
|
|
// Audio formats (FFmpeg)
|
|
{
|
|
id: 'mp3',
|
|
name: 'MP3',
|
|
extension: 'mp3',
|
|
mimeType: 'audio/mpeg',
|
|
category: 'audio',
|
|
converter: 'ffmpeg',
|
|
description: 'MPEG audio layer 3',
|
|
},
|
|
{
|
|
id: 'wav',
|
|
name: 'WAV',
|
|
extension: 'wav',
|
|
mimeType: 'audio/wav',
|
|
category: 'audio',
|
|
converter: 'ffmpeg',
|
|
description: 'Waveform audio',
|
|
},
|
|
{
|
|
id: 'ogg',
|
|
name: 'OGG',
|
|
extension: 'ogg',
|
|
mimeType: 'audio/ogg',
|
|
category: 'audio',
|
|
converter: 'ffmpeg',
|
|
description: 'Ogg Vorbis audio',
|
|
},
|
|
{
|
|
id: 'aac',
|
|
name: 'AAC',
|
|
extension: 'aac',
|
|
mimeType: 'audio/aac',
|
|
category: 'audio',
|
|
converter: 'ffmpeg',
|
|
description: 'Advanced Audio Coding',
|
|
},
|
|
{
|
|
id: 'flac',
|
|
name: 'FLAC',
|
|
extension: 'flac',
|
|
mimeType: 'audio/flac',
|
|
category: 'audio',
|
|
converter: 'ffmpeg',
|
|
description: 'Free Lossless Audio Codec',
|
|
},
|
|
|
|
// Image formats (ImageMagick)
|
|
{
|
|
id: 'png',
|
|
name: 'PNG',
|
|
extension: 'png',
|
|
mimeType: 'image/png',
|
|
category: 'image',
|
|
converter: 'imagemagick',
|
|
description: 'Portable Network Graphics',
|
|
},
|
|
{
|
|
id: 'jpg',
|
|
name: 'JPG',
|
|
extension: 'jpg',
|
|
mimeType: 'image/jpeg',
|
|
category: 'image',
|
|
converter: 'imagemagick',
|
|
description: 'JPEG image',
|
|
},
|
|
{
|
|
id: 'webp',
|
|
name: 'WebP',
|
|
extension: 'webp',
|
|
mimeType: 'image/webp',
|
|
category: 'image',
|
|
converter: 'imagemagick',
|
|
description: 'WebP image format',
|
|
},
|
|
{
|
|
id: 'gif',
|
|
name: 'GIF',
|
|
extension: 'gif',
|
|
mimeType: 'image/gif',
|
|
category: 'image',
|
|
converter: 'imagemagick',
|
|
description: 'Graphics Interchange Format',
|
|
},
|
|
{
|
|
id: 'bmp',
|
|
name: 'BMP',
|
|
extension: 'bmp',
|
|
mimeType: 'image/bmp',
|
|
category: 'image',
|
|
converter: 'imagemagick',
|
|
description: 'Bitmap image',
|
|
},
|
|
{
|
|
id: 'tiff',
|
|
name: 'TIFF',
|
|
extension: 'tiff',
|
|
mimeType: 'image/tiff',
|
|
category: 'image',
|
|
converter: 'imagemagick',
|
|
description: 'Tagged Image File Format',
|
|
},
|
|
{
|
|
id: 'svg',
|
|
name: 'SVG',
|
|
extension: 'svg',
|
|
mimeType: 'image/svg+xml',
|
|
category: 'image',
|
|
converter: 'imagemagick',
|
|
description: 'Scalable Vector Graphics',
|
|
},
|
|
|
|
// Document formats (Pandoc - future implementation)
|
|
{
|
|
id: 'pdf',
|
|
name: 'PDF',
|
|
extension: 'pdf',
|
|
mimeType: 'application/pdf',
|
|
category: 'document',
|
|
converter: 'pandoc',
|
|
description: 'Portable Document Format',
|
|
},
|
|
{
|
|
id: 'docx',
|
|
name: 'DOCX',
|
|
extension: 'docx',
|
|
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
category: 'document',
|
|
converter: 'pandoc',
|
|
description: 'Microsoft Word document',
|
|
},
|
|
{
|
|
id: 'markdown',
|
|
name: 'Markdown',
|
|
extension: 'md',
|
|
mimeType: 'text/markdown',
|
|
category: 'document',
|
|
converter: 'pandoc',
|
|
description: 'Markdown text',
|
|
},
|
|
{
|
|
id: 'html',
|
|
name: 'HTML',
|
|
extension: 'html',
|
|
mimeType: 'text/html',
|
|
category: 'document',
|
|
converter: 'pandoc',
|
|
description: 'HyperText Markup Language',
|
|
},
|
|
{
|
|
id: 'txt',
|
|
name: 'Plain Text',
|
|
extension: 'txt',
|
|
mimeType: 'text/plain',
|
|
category: 'document',
|
|
converter: 'pandoc',
|
|
description: 'Plain text file',
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Format presets for common conversions
|
|
*/
|
|
export const FORMAT_PRESETS: FormatPreset[] = [
|
|
{
|
|
id: 'web-video',
|
|
name: 'Web Video',
|
|
description: 'Optimize video for web playback (VP8 for better compatibility)',
|
|
category: 'video',
|
|
sourceFormats: ['mp4', 'avi', 'mov', 'mkv'],
|
|
targetFormat: 'webm',
|
|
options: {
|
|
videoCodec: 'libvpx',
|
|
videoBitrate: '1M',
|
|
audioCodec: 'libvorbis',
|
|
audioBitrate: '128k',
|
|
},
|
|
},
|
|
{
|
|
id: 'web-image',
|
|
name: 'Web Image',
|
|
description: 'Optimize image for web',
|
|
category: 'image',
|
|
sourceFormats: ['png', 'jpg', 'bmp', 'tiff'],
|
|
targetFormat: 'webp',
|
|
options: {
|
|
imageQuality: 85,
|
|
},
|
|
},
|
|
{
|
|
id: 'audio-compress',
|
|
name: 'Compress Audio',
|
|
description: 'Reduce audio file size',
|
|
category: 'audio',
|
|
sourceFormats: ['wav', 'flac'],
|
|
targetFormat: 'mp3',
|
|
options: {
|
|
audioBitrate: '192k',
|
|
audioCodec: 'libmp3lame',
|
|
},
|
|
},
|
|
{
|
|
id: 'video-gif',
|
|
name: 'Video to GIF',
|
|
description: 'Convert video to animated GIF',
|
|
category: 'video',
|
|
sourceFormats: ['mp4', 'webm', 'avi', 'mov'],
|
|
targetFormat: 'gif',
|
|
options: {
|
|
videoFps: 15,
|
|
videoResolution: '480x-1',
|
|
},
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Get format by ID
|
|
*/
|
|
export function getFormatById(id: string): ConversionFormat | undefined {
|
|
return SUPPORTED_FORMATS.find((f) => f.id === id);
|
|
}
|
|
|
|
/**
|
|
* Get format by extension
|
|
*/
|
|
export function getFormatByExtension(extension: string): ConversionFormat | undefined {
|
|
return SUPPORTED_FORMATS.find((f) => f.extension === extension.toLowerCase());
|
|
}
|
|
|
|
/**
|
|
* Get format by MIME type
|
|
*/
|
|
export function getFormatByMimeType(mimeType: string): ConversionFormat | undefined {
|
|
return SUPPORTED_FORMATS.find((f) => f.mimeType === mimeType);
|
|
}
|
|
|
|
/**
|
|
* Get all formats by category
|
|
*/
|
|
export function getFormatsByCategory(category: string): ConversionFormat[] {
|
|
return SUPPORTED_FORMATS.filter((f) => f.category === category);
|
|
}
|
|
|
|
/**
|
|
* Get compatible output formats for input format
|
|
*/
|
|
export function getCompatibleFormats(inputFormat: ConversionFormat): ConversionFormat[] {
|
|
// Same category and same converter
|
|
return SUPPORTED_FORMATS.filter(
|
|
(f) => f.category === inputFormat.category && f.converter === inputFormat.converter && f.id !== inputFormat.id
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if conversion is supported
|
|
*/
|
|
export function isConversionSupported(
|
|
inputFormat: ConversionFormat,
|
|
outputFormat: ConversionFormat
|
|
): boolean {
|
|
return (
|
|
inputFormat.category === outputFormat.category &&
|
|
inputFormat.converter === outputFormat.converter &&
|
|
inputFormat.id !== outputFormat.id
|
|
);
|
|
}
|