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
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) |
|
}) |
|
} |
|
|
|
|