You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

103 lines
3.7 KiB

import { ExtendedKind } from '@/constants'
/**
* Get the appropriate event kind for a media file based on its type and duration
* @param file - The file to analyze
* @param isReply - Whether this is a reply/comment
* @returns The event kind number
*/
export async function getMediaKindFromFile(file: File, isReply: boolean = false): Promise<number> {
const fileType = file.type
const fileName = file.name.toLowerCase()
// Check if it's an image
if (fileType.startsWith('image/') || /\.(jpg|jpeg|png|gif|webp|heic|avif|apng)$/i.test(fileName)) {
return ExtendedKind.PICTURE // kind 20
}
// Check if it's audio or video
// mp4, m4a, and webm files can be either audio or video, so check MIME type first
// Mobile browsers may report m4a files as audio/m4a, audio/mp4, audio/x-m4a, or even video/mp4
const isAudioMime =
fileType.startsWith('audio/') ||
fileType === 'audio/mp4' ||
fileType === 'audio/x-m4a' ||
fileType === 'audio/m4a' ||
fileType === 'audio/webm' ||
fileType === 'audio/mpeg' ||
fileType === 'audio/x-matroska'
const isVideoMime = fileType.startsWith('video/')
const isAudioExt = /\.(mp3|m4a|mka|ogg|wav|opus|aac|flac|mpeg|mp4)$/i.test(fileName)
const isVideoExt = /\.(mp4|ogg|mov|avi|mkv|m4v|3gp|3g2)$/i.test(fileName)
// m4a files are always audio, even if MIME type is video/mp4 (mobile browsers sometimes report this)
const isM4aFile = /\.m4a$/i.test(fileName)
// mp4 files: check MIME type to determine if audio or video
const isMp4Audio = /\.mp4$/i.test(fileName) && isAudioMime
const isWebmAudio = /\.webm$/i.test(fileName) && isAudioMime
const isWebmVideo = /\.webm$/i.test(fileName) && isVideoMime
const isAudio = isAudioMime || isAudioExt || isM4aFile || isMp4Audio || isWebmAudio
const isVideo = isVideoMime || (isVideoExt && !isM4aFile && !isMp4Audio) || isWebmVideo
if (isAudio || isVideo) {
// Get duration for audio/video files
const duration = await getMediaDuration(file)
if (isAudio) {
// Audio mp4/m4a files longer than 60 seconds should be treated as video (for new posts only)
if (!isReply && (fileType === 'audio/mp4' || fileType === 'audio/x-m4a' || fileName.endsWith('.m4a') || fileName.endsWith('.mp4')) && duration > 60) {
// Determine if it should be long or short video based on duration
return duration > 600 ? ExtendedKind.VIDEO : ExtendedKind.SHORT_VIDEO
}
// Audio files <= 60 seconds, or any audio in replies
return isReply ? ExtendedKind.VOICE_COMMENT : ExtendedKind.VOICE
}
if (isVideo) {
// Video files longer than 10 minutes (600 seconds) are long videos
return duration > 600 ? ExtendedKind.VIDEO : ExtendedKind.SHORT_VIDEO
}
}
// Default: treat as picture if we can't determine
return ExtendedKind.PICTURE
}
/**
* Get the duration of a media file in seconds
* @param file - The file to analyze
* @returns Duration in seconds, or 0 if unable to determine
*/
function getMediaDuration(file: File): Promise<number> {
return new Promise((resolve) => {
const url = URL.createObjectURL(file)
const useAudio =
file.type.startsWith('audio/') ||
file.type === 'audio/x-matroska' ||
/\.mka$/i.test(file.name)
const media = document.createElement(useAudio ? 'audio' : 'video')
media.onloadedmetadata = () => {
const duration = media.duration || 0
URL.revokeObjectURL(url)
resolve(duration)
}
media.onerror = () => {
URL.revokeObjectURL(url)
resolve(0)
}
media.src = url
media.load()
// Timeout after 5 seconds
setTimeout(() => {
URL.revokeObjectURL(url)
resolve(0)
}, 5000)
})
}