export function isImageFile(file: File) { return file.type.startsWith('image/'); } export const EDITOR_ASSET_UPLOAD_ACCEPT = 'image/*,.mp3,.mp4,audio/mpeg,audio/mp3,video/mp4'; export const SEEDANCE_REFERENCE_ACCEPT = { image: '.jpg,.jpeg,.png,.webp,.bmp,.tiff,.tif,.gif,.heic,.heif,image/jpeg,image/png,image/webp,image/bmp,image/tiff,image/gif,image/heic,image/heif', video: '.mp4,.mov,video/mp4,video/quicktime', audio: '.wav,.mp3,audio/wav,audio/x-wav,audio/mpeg,audio/mp3', } as const; export function getEditorUploadAccept(target: string) { if (target === 'video-reference-video') { return SEEDANCE_REFERENCE_ACCEPT.video; } if (target === 'video-reference-audio') { return SEEDANCE_REFERENCE_ACCEPT.audio; } if (target === 'video-reference-image') { return SEEDANCE_REFERENCE_ACCEPT.image; } return EDITOR_ASSET_UPLOAD_ACCEPT; } export const SEEDANCE_REFERENCE_FILE_LIMITS = { imageBytes: 30 * 1024 * 1024, videoBytes: 50 * 1024 * 1024, audioBytes: 15 * 1024 * 1024, mediaMinDurationSeconds: 2, mediaMaxDurationSeconds: 15, mediaTotalDurationSeconds: 15, } as const; const SEEDANCE_IMAGE_EXTENSIONS = new Set([ 'jpg', 'jpeg', 'png', 'webp', 'bmp', 'tiff', 'tif', 'gif', 'heic', 'heif', ]); const SEEDANCE_VIDEO_EXTENSIONS = new Set(['mp4', 'mov']); const SEEDANCE_AUDIO_EXTENSIONS = new Set(['wav', 'mp3']); const EDITOR_ASSET_VIDEO_EXTENSIONS = new Set(['mp4']); const EDITOR_ASSET_AUDIO_EXTENSIONS = new Set(['mp3']); function getFileExtension(file: File) { return file.name.split('.').pop()?.toLowerCase() ?? ''; } function hasAllowedExtension(file: File, extensions: Set) { return extensions.has(getFileExtension(file)); } export function isSeedanceReferenceImageFile(file: File) { return ( (file.type.startsWith('image/') || file.type === '') && hasAllowedExtension(file, SEEDANCE_IMAGE_EXTENSIONS) ); } export function isSeedanceReferenceVideoFile(file: File) { return ( (file.type === 'video/mp4' || file.type === 'video/quicktime' || file.type === '') && hasAllowedExtension(file, SEEDANCE_VIDEO_EXTENSIONS) ); } export function isSeedanceReferenceAudioFile(file: File) { return ( (file.type === 'audio/wav' || file.type === 'audio/x-wav' || file.type === 'audio/mpeg' || file.type === 'audio/mp3' || file.type === '') && hasAllowedExtension(file, SEEDANCE_AUDIO_EXTENSIONS) ); } export function isEditorAssetVideoFile(file: File) { return ( (file.type === 'video/mp4' || file.type === '') && hasAllowedExtension(file, EDITOR_ASSET_VIDEO_EXTENSIONS) ); } export function isEditorAssetAudioFile(file: File) { return ( (file.type === 'audio/mpeg' || file.type === 'audio/mp3' || file.type === '') && hasAllowedExtension(file, EDITOR_ASSET_AUDIO_EXTENSIONS) ); } export function resolveEditorAssetUploadMediaType(file: File) { if (isEditorAssetVideoFile(file)) { return 'video' as const; } if (isEditorAssetAudioFile(file)) { return 'audio' as const; } if (isImageFile(file)) { return 'image' as const; } return null; } export function readFileAsDataUrl(file: File) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { if (typeof reader.result === 'string') { resolve(reader.result); return; } reject(new Error('文件读取失败')); }; reader.onerror = () => reject(reader.error ?? new Error('文件读取失败')); reader.readAsDataURL(file); }); } export function readImageFileAsDataUrl(file: File) { return readFileAsDataUrl(file); } export function probeMediaDurationSeconds( file: File, mediaType: 'video' | 'audio', ) { return new Promise((resolve, reject) => { if (typeof document === 'undefined' || typeof URL === 'undefined') { resolve(Number.NaN); return; } const element = mediaType === 'video' ? document.createElement('video') : document.createElement('audio'); const objectUrl = URL.createObjectURL(file); const cleanup = () => { URL.revokeObjectURL(objectUrl); element.removeAttribute('src'); element.load(); }; element.preload = 'metadata'; element.onloadedmetadata = () => { const duration = element.duration; cleanup(); resolve(duration); }; element.onerror = () => { cleanup(); reject(new Error('读取媒体时长失败')); }; element.src = objectUrl; }); }