Spaces:
Running
on
A100
Running
on
A100
| // Transcription limits and thresholds | |
| // These constants are shared across components to ensure consistency | |
| export const TRANSCRIPTION_LIMITS = { | |
| // Duration limits (in seconds) | |
| MIN_DURATION: 3, // Minimum recommended duration for accuracy | |
| MAX_DURATION: 180, // Maximum recommended duration (3 minutes) | |
| // File size limits (in MB) | |
| MAX_FILE_SIZE: 100, // Maximum recommended file size | |
| } as const; | |
| export const WARNING_MESSAGES = { | |
| TOO_SHORT: { | |
| title: "Media Too Short", | |
| message: "Your media file is less than 3 seconds long. This may result in reduced transcription accuracy due to insufficient context for the AI model. For best results, use media files at least 30 seconds long.", | |
| }, | |
| TOO_LONG: { | |
| title: "Media Too Long", | |
| message: "Your media file is longer than 3 minutes. This may exceed server resources and could result in processing failures or timeouts. Consider trimming your media to under 3 minutes for optimal results.", | |
| }, | |
| VERY_LARGE_FILE: { | |
| title: "File Too Large", | |
| message: "Your file exceeds the recommended 100MB limit. This may cause upload failures or processing timeouts. Please use a smaller file or compress your media before uploading.", | |
| }, | |
| } as const; | |
| type WarningType = keyof typeof WARNING_MESSAGES; | |
| interface MediaInfo { | |
| file: File; | |
| duration?: number; // Duration in seconds, may not be available immediately | |
| } | |
| /** | |
| * Get the duration of a media file | |
| * Returns a promise that resolves with the duration in seconds | |
| */ | |
| export const getMediaDuration = (file: File): Promise<number> => { | |
| return new Promise((resolve, reject) => { | |
| const url = URL.createObjectURL(file); | |
| if (file.type.startsWith('video/')) { | |
| const video = document.createElement('video'); | |
| video.preload = 'metadata'; | |
| video.onloadedmetadata = () => { | |
| URL.revokeObjectURL(url); | |
| resolve(video.duration); | |
| }; | |
| video.onerror = () => { | |
| URL.revokeObjectURL(url); | |
| reject(new Error('Failed to load video metadata')); | |
| }; | |
| video.src = url; | |
| } else if (file.type.startsWith('audio/')) { | |
| const audio = document.createElement('audio'); | |
| audio.preload = 'metadata'; | |
| audio.onloadedmetadata = () => { | |
| URL.revokeObjectURL(url); | |
| resolve(audio.duration); | |
| }; | |
| audio.onerror = () => { | |
| URL.revokeObjectURL(url); | |
| reject(new Error('Failed to load audio metadata')); | |
| }; | |
| audio.src = url; | |
| } else { | |
| URL.revokeObjectURL(url); | |
| reject(new Error('Unsupported media type')); | |
| } | |
| }); | |
| }; | |
| /** | |
| * Check for transcription warnings based on file size and duration | |
| * Returns an array of warning types that should be shown to the user | |
| */ | |
| export const checkTranscriptionWarnings = (mediaInfo: MediaInfo): WarningType[] => { | |
| const warnings: WarningType[] = []; | |
| const { file, duration } = mediaInfo; | |
| // Check file size (convert to MB) | |
| const fileSizeMB = file.size / (1024 * 1024); | |
| if (fileSizeMB > TRANSCRIPTION_LIMITS.MAX_FILE_SIZE) { | |
| warnings.push('VERY_LARGE_FILE'); | |
| } | |
| // Check duration if available | |
| if (duration !== undefined && !isNaN(duration)) { | |
| if (duration < TRANSCRIPTION_LIMITS.MIN_DURATION) { | |
| warnings.push('TOO_SHORT'); | |
| } else if (duration > TRANSCRIPTION_LIMITS.MAX_DURATION) { | |
| warnings.push('TOO_LONG'); | |
| } | |
| } | |
| return warnings; | |
| }; | |
| /** | |
| * Format file size in a human-readable format | |
| */ | |
| export const formatFileSize = (bytes: number): string => { | |
| if (bytes === 0) return '0 Bytes'; | |
| const k = 1024; | |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; | |
| }; | |
| /** | |
| * Format duration in a human-readable format | |
| */ | |
| export const formatDuration = (seconds: number): string => { | |
| if (isNaN(seconds) || seconds < 0) return 'Unknown'; | |
| const minutes = Math.floor(seconds / 60); | |
| const remainingSeconds = Math.floor(seconds % 60); | |
| if (minutes === 0) { | |
| return `${remainingSeconds}s`; | |
| } else { | |
| return `${minutes}m ${remainingSeconds}s`; | |
| } | |
| }; | |