duration.ts 1.2 KB

1234567891011121314151617181920212223242526272829303132333435363738
  1. import { parseMedia } from "@remotion/media-parser";
  2. import { nodeReader } from "@remotion/media-parser/node";
  3. import { execSync } from "node:child_process";
  4. export async function measureAudioDuration(
  5. filePath: string
  6. ): Promise<number> {
  7. // Primary: parse audio container headers via @remotion/media-parser (fast, no full decode)
  8. try {
  9. const result = await parseMedia({
  10. src: filePath,
  11. reader: nodeReader,
  12. fields: { durationInSeconds: true },
  13. });
  14. if (result.durationInSeconds != null && result.durationInSeconds > 0) {
  15. return result.durationInSeconds;
  16. }
  17. } catch {
  18. // Fall through to ffprobe
  19. }
  20. // Fallback: ffprobe (available on system)
  21. try {
  22. const output = execSync(
  23. `ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${filePath}"`,
  24. { encoding: "utf-8", timeout: 5000 }
  25. ).trim();
  26. const dur = parseFloat(output);
  27. if (Number.isFinite(dur) && dur > 0) return dur;
  28. } catch {
  29. // Fall through to bitrate estimate
  30. }
  31. // Last resort: estimate from file size (mp3 @ 128kbps)
  32. const { statSync } = await import("node:fs");
  33. const size = statSync(filePath).size;
  34. return (size * 8) / 128000;
  35. }