|
|
import { tmpdir } from "node:os" |
|
|
import { writeFile, rm } from "node:fs/promises" |
|
|
import { join } from "node:path" |
|
|
|
|
|
import ffmpeg from "fluent-ffmpeg" |
|
|
|
|
|
export type MediaMetadata = { |
|
|
durationInSec: number; |
|
|
durationInMs: number; |
|
|
hasAudio: boolean; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function getMediaInfo(input: string): Promise<MediaMetadata> { |
|
|
|
|
|
if (input.startsWith("data:")) { |
|
|
|
|
|
|
|
|
const [head, tail] = input.split(";base64,") |
|
|
if (!tail) { |
|
|
throw new Error("Invalid base64 data"); |
|
|
} |
|
|
|
|
|
const extension = head.split("/").pop() || "" |
|
|
const base64Content = tail || "" |
|
|
|
|
|
|
|
|
const buffer = Buffer.from(base64Content, 'base64') |
|
|
|
|
|
|
|
|
const tempFileName = join(tmpdir(), `temp-media-${Date.now()}.${extension}`); |
|
|
|
|
|
|
|
|
|
|
|
await writeFile(tempFileName, buffer); |
|
|
|
|
|
|
|
|
try { |
|
|
return await getMetaDataFromPath(tempFileName); |
|
|
} finally { |
|
|
await rm(tempFileName); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return await getMetaDataFromPath(input); |
|
|
} |
|
|
|
|
|
async function getMetaDataFromPath(filePath: string): Promise<MediaMetadata> { |
|
|
return new Promise((resolve, reject) => { |
|
|
ffmpeg.ffprobe(filePath, (err, metadata) => { |
|
|
|
|
|
let results = { |
|
|
durationInSec: 0, |
|
|
durationInMs: 0, |
|
|
hasAudio: false, |
|
|
} |
|
|
|
|
|
if (err) { |
|
|
console.error("getMediaInfo(): failed to analyze the source (might happen with empty files)") |
|
|
|
|
|
resolve(results); |
|
|
return; |
|
|
} |
|
|
|
|
|
try { |
|
|
results.durationInSec = metadata?.format?.duration || 0; |
|
|
results.durationInMs = results.durationInSec * 1000; |
|
|
results.hasAudio = (metadata?.streams || []).some((stream) => stream.codec_type === 'audio'); |
|
|
|
|
|
} catch (err) { |
|
|
console.error(`getMediaInfo(): failed to analyze the source (might happen with empty files)`) |
|
|
results.durationInSec = 0 |
|
|
results.durationInMs = 0 |
|
|
results.hasAudio = false |
|
|
} |
|
|
resolve(results); |
|
|
}); |
|
|
}); |
|
|
} |